3D Print Toolbox: Add hollow out #105194
@ -102,12 +102,18 @@ class Import3DS(bpy.types.Operator, ImportHelper):
|
|||||||
description="Transform to matrix world",
|
description="Transform to matrix world",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
use_collection: BoolProperty(
|
||||||
|
name="Collection",
|
||||||
|
description="Create a new collection",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
use_cursor: BoolProperty(
|
use_cursor: BoolProperty(
|
||||||
name="Cursor Origin",
|
name="Cursor Origin",
|
||||||
description="Read the 3D cursor location",
|
description="Read the 3D cursor location",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
from . import import_3ds
|
from . import import_3ds
|
||||||
keywords = self.as_keywords(ignore=("axis_forward",
|
keywords = self.as_keywords(ignore=("axis_forward",
|
||||||
@ -166,6 +172,9 @@ class MAX3DS_PT_import_include(bpy.types.Panel):
|
|||||||
layrow.prop(operator, "use_keyframes")
|
layrow.prop(operator, "use_keyframes")
|
||||||
layrow.label(text="", icon='ANIM' if operator.use_keyframes else 'DECORATE_DRIVER')
|
layrow.label(text="", icon='ANIM' if operator.use_keyframes else 'DECORATE_DRIVER')
|
||||||
layrow = layout.row(align=True)
|
layrow = layout.row(align=True)
|
||||||
|
layrow.prop(operator, "use_collection")
|
||||||
|
layrow.label(text="", icon='OUTLINER_COLLECTION' if operator.use_collection else 'GROUP')
|
||||||
|
layrow = layout.row(align=True)
|
||||||
layrow.prop(operator, "use_cursor")
|
layrow.prop(operator, "use_cursor")
|
||||||
layrow.label(text="", icon='PIVOT_CURSOR' if operator.use_cursor else 'CURSOR')
|
layrow.label(text="", icon='PIVOT_CURSOR' if operator.use_cursor else 'CURSOR')
|
||||||
|
|
||||||
@ -249,14 +258,14 @@ class Export3DS(bpy.types.Operator, ExportHelper):
|
|||||||
description="Object types to export",
|
description="Object types to export",
|
||||||
default={'WORLD', 'MESH', 'LIGHT', 'CAMERA', 'EMPTY'},
|
default={'WORLD', 'MESH', 'LIGHT', 'CAMERA', 'EMPTY'},
|
||||||
)
|
)
|
||||||
use_hierarchy: BoolProperty(
|
|
||||||
name="Hierarchy",
|
|
||||||
description="Export hierarchy chunks",
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
use_keyframes: BoolProperty(
|
use_keyframes: BoolProperty(
|
||||||
name="Animation",
|
name="Animation",
|
||||||
description="Write the keyframe data",
|
description="Write the keyframe data",
|
||||||
|
default=True,
|
||||||
|
)
|
||||||
|
use_hierarchy: BoolProperty(
|
||||||
|
name="Hierarchy",
|
||||||
|
description="Export hierarchy chunks",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
use_cursor: BoolProperty(
|
use_cursor: BoolProperty(
|
||||||
@ -310,12 +319,12 @@ class MAX3DS_PT_export_include(bpy.types.Panel):
|
|||||||
layrow.label(text="", icon='RESTRICT_SELECT_OFF' if operator.use_selection else 'RESTRICT_SELECT_ON')
|
layrow.label(text="", icon='RESTRICT_SELECT_OFF' if operator.use_selection else 'RESTRICT_SELECT_ON')
|
||||||
layout.column().prop(operator, "object_filter")
|
layout.column().prop(operator, "object_filter")
|
||||||
layrow = layout.row(align=True)
|
layrow = layout.row(align=True)
|
||||||
layrow.prop(operator, "use_hierarchy")
|
|
||||||
layrow.label(text="", icon='OUTLINER' if operator.use_hierarchy else 'CON_CHILDOF')
|
|
||||||
layrow = layout.row(align=True)
|
|
||||||
layrow.prop(operator, "use_keyframes")
|
layrow.prop(operator, "use_keyframes")
|
||||||
layrow.label(text="", icon='ANIM' if operator.use_keyframes else 'DECORATE_DRIVER')
|
layrow.label(text="", icon='ANIM' if operator.use_keyframes else 'DECORATE_DRIVER')
|
||||||
layrow = layout.row(align=True)
|
layrow = layout.row(align=True)
|
||||||
|
layrow.prop(operator, "use_hierarchy")
|
||||||
|
layrow.label(text="", icon='OUTLINER' if operator.use_hierarchy else 'CON_CHILDOF')
|
||||||
|
layrow = layout.row(align=True)
|
||||||
layrow.prop(operator, "use_cursor")
|
layrow.prop(operator, "use_cursor")
|
||||||
layrow.label(text="", icon='PIVOT_CURSOR' if operator.use_cursor else 'CURSOR')
|
layrow.label(text="", icon='PIVOT_CURSOR' if operator.use_cursor else 'CURSOR')
|
||||||
|
|
||||||
|
@ -1563,7 +1563,7 @@ def make_ambient_node(world):
|
|||||||
##########
|
##########
|
||||||
|
|
||||||
def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, use_selection=False,
|
def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, use_selection=False,
|
||||||
object_filter=None, use_hierarchy=False, use_keyframes=False, global_matrix=None, use_cursor=False):
|
object_filter=None, use_keyframes=True, use_hierarchy=False, global_matrix=None, use_cursor=False):
|
||||||
"""Save the Blender scene to a 3ds file."""
|
"""Save the Blender scene to a 3ds file."""
|
||||||
|
|
||||||
# Time the export
|
# Time the export
|
||||||
|
@ -10,6 +10,7 @@ import struct
|
|||||||
import mathutils
|
import mathutils
|
||||||
from bpy_extras.image_utils import load_image
|
from bpy_extras.image_utils import load_image
|
||||||
from bpy_extras.node_shader_utils import PrincipledBSDFWrapper
|
from bpy_extras.node_shader_utils import PrincipledBSDFWrapper
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
BOUNDS_3DS = []
|
BOUNDS_3DS = []
|
||||||
|
|
||||||
@ -1772,11 +1773,25 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, UNITS=False, IMAGE_SEARCH=True,
|
|||||||
|
|
||||||
def load(operator, context, files=None, directory="", filepath="", constrain_size=0.0, use_scene_unit=False,
|
def load(operator, context, files=None, directory="", filepath="", constrain_size=0.0, use_scene_unit=False,
|
||||||
use_image_search=True, object_filter=None, use_world_matrix=False, use_keyframes=True,
|
use_image_search=True, object_filter=None, use_world_matrix=False, use_keyframes=True,
|
||||||
use_apply_transform=True, global_matrix=None, use_cursor=False, use_center_pivot=False):
|
use_apply_transform=True, global_matrix=None, use_cursor=False, use_center_pivot=False, use_collection=False):
|
||||||
|
|
||||||
for f in files:
|
# Get the active collection
|
||||||
load_3ds(os.path.join(directory, f.name), context, CONSTRAIN=constrain_size, UNITS=use_scene_unit,
|
collection_init = context.view_layer.active_layer_collection.collection
|
||||||
|
|
||||||
|
# Load each selected file
|
||||||
|
for file in files:
|
||||||
|
# Create new collections if activated (collection name = 3ds file name)
|
||||||
|
if use_collection:
|
||||||
|
collection = bpy.data.collections.new(Path(file.name).stem)
|
||||||
|
context.scene.collection.children.link(collection)
|
||||||
|
context.view_layer.active_layer_collection = context.view_layer.layer_collection.children[collection.name]
|
||||||
|
load_3ds(Path(directory, file.name), context, CONSTRAIN=constrain_size, UNITS=use_scene_unit,
|
||||||
IMAGE_SEARCH=use_image_search, FILTER=object_filter, WORLD_MATRIX=use_world_matrix, KEYFRAME=use_keyframes,
|
IMAGE_SEARCH=use_image_search, FILTER=object_filter, WORLD_MATRIX=use_world_matrix, KEYFRAME=use_keyframes,
|
||||||
APPLY_MATRIX=use_apply_transform, CONVERSE=global_matrix, CURSOR=use_cursor, PIVOT=use_center_pivot,)
|
APPLY_MATRIX=use_apply_transform, CONVERSE=global_matrix, CURSOR=use_cursor, PIVOT=use_center_pivot,)
|
||||||
|
|
||||||
|
# Retrive the initial collection as active
|
||||||
|
active = context.view_layer.layer_collection.children.get(collection_init.name)
|
||||||
|
if active is not None:
|
||||||
|
context.view_layer.active_layer_collection = active
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
@ -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, 2, 5),
|
"version": (4, 2, 8),
|
||||||
'blender': (4, 1, 0),
|
'blender': (4, 1, 0),
|
||||||
'location': 'File > Import-Export',
|
'location': 'File > Import-Export',
|
||||||
'description': 'Import-Export as glTF 2.0',
|
'description': 'Import-Export as glTF 2.0',
|
||||||
@ -648,6 +648,15 @@ class ExportGLTF2_Base(ConvertGLTF2_Base):
|
|||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export_leaf_bone: BoolProperty(
|
||||||
|
name='Add Leaf Bones',
|
||||||
|
description=(
|
||||||
|
'Append a final bone to the end of each chain to specify last bone length '
|
||||||
|
'(use this when you intend to edit the armature from exported data)'
|
||||||
|
),
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
export_optimize_animation_size: BoolProperty(
|
export_optimize_animation_size: BoolProperty(
|
||||||
name='Optimize Animation Size',
|
name='Optimize Animation Size',
|
||||||
description=(
|
description=(
|
||||||
@ -955,6 +964,8 @@ class ExportGLTF2_Base(ConvertGLTF2_Base):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
import os
|
import os
|
||||||
import datetime
|
import datetime
|
||||||
|
import logging
|
||||||
|
from .io.com.gltf2_io_debug import Log
|
||||||
from .blender.exp import gltf2_blender_export
|
from .blender.exp import gltf2_blender_export
|
||||||
from .io.com.gltf2_io_path import path_to_uri
|
from .io.com.gltf2_io_path import path_to_uri
|
||||||
|
|
||||||
@ -966,6 +977,8 @@ class ExportGLTF2_Base(ConvertGLTF2_Base):
|
|||||||
# All custom export settings are stored in this container.
|
# All custom export settings are stored in this container.
|
||||||
export_settings = {}
|
export_settings = {}
|
||||||
|
|
||||||
|
export_settings['loglevel'] = logging.INFO
|
||||||
|
|
||||||
export_settings['exported_images'] = {}
|
export_settings['exported_images'] = {}
|
||||||
export_settings['exported_texture_nodes'] = []
|
export_settings['exported_texture_nodes'] = []
|
||||||
export_settings['additional_texture_export'] = []
|
export_settings['additional_texture_export'] = []
|
||||||
@ -1035,6 +1048,7 @@ class ExportGLTF2_Base(ConvertGLTF2_Base):
|
|||||||
export_settings['gltf_flatten_bones_hierarchy'] = self.export_hierarchy_flatten_bones
|
export_settings['gltf_flatten_bones_hierarchy'] = self.export_hierarchy_flatten_bones
|
||||||
export_settings['gltf_flatten_obj_hierarchy'] = self.export_hierarchy_flatten_objs
|
export_settings['gltf_flatten_obj_hierarchy'] = self.export_hierarchy_flatten_objs
|
||||||
export_settings['gltf_armature_object_remove'] = self.export_armature_object_remove
|
export_settings['gltf_armature_object_remove'] = self.export_armature_object_remove
|
||||||
|
export_settings['gltf_leaf_bone'] = self.export_leaf_bone
|
||||||
if self.export_animations:
|
if self.export_animations:
|
||||||
export_settings['gltf_frame_range'] = self.export_frame_range
|
export_settings['gltf_frame_range'] = self.export_frame_range
|
||||||
export_settings['gltf_force_sampling'] = self.export_force_sampling
|
export_settings['gltf_force_sampling'] = self.export_force_sampling
|
||||||
@ -1154,7 +1168,19 @@ class ExportGLTF2_Base(ConvertGLTF2_Base):
|
|||||||
export_settings['pre_export_callbacks'] = pre_export_callbacks
|
export_settings['pre_export_callbacks'] = pre_export_callbacks
|
||||||
export_settings['post_export_callbacks'] = post_export_callbacks
|
export_settings['post_export_callbacks'] = post_export_callbacks
|
||||||
|
|
||||||
return gltf2_blender_export.save(context, export_settings)
|
|
||||||
|
# Initialize logging for export
|
||||||
|
export_settings['log'] = Log(export_settings['loglevel'])
|
||||||
|
|
||||||
|
res = gltf2_blender_export.save(context, export_settings)
|
||||||
|
|
||||||
|
# Display popup log, if any
|
||||||
|
for message_type, message in export_settings['log'].messages():
|
||||||
|
self.report({message_type}, message)
|
||||||
|
|
||||||
|
export_settings['log'].flush()
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
pass # Is needed to get panels available
|
pass # Is needed to get panels available
|
||||||
@ -2110,14 +2136,18 @@ class ImportGLTF2(Operator, ConvertGLTF2_Base, ImportHelper):
|
|||||||
gltf_importer.read()
|
gltf_importer.read()
|
||||||
gltf_importer.checks()
|
gltf_importer.checks()
|
||||||
|
|
||||||
print("Data are loaded, start creating Blender stuff")
|
gltf_importer.log.info("Data are loaded, start creating Blender stuff")
|
||||||
|
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
BlenderGlTF.create(gltf_importer)
|
BlenderGlTF.create(gltf_importer)
|
||||||
elapsed_s = "{:.2f}s".format(time.time() - start_time)
|
elapsed_s = "{:.2f}s".format(time.time() - start_time)
|
||||||
print("glTF import finished in " + elapsed_s)
|
gltf_importer.log.info("glTF import finished in " + elapsed_s)
|
||||||
|
|
||||||
gltf_importer.log.removeHandler(gltf_importer.log_handler)
|
# Display popup log, if any
|
||||||
|
for message_type, message in gltf_importer.log.messages():
|
||||||
|
self.report({message_type}, message)
|
||||||
|
|
||||||
|
gltf_importer.log.flush()
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
@ -2127,16 +2157,16 @@ class ImportGLTF2(Operator, ConvertGLTF2_Base, ImportHelper):
|
|||||||
|
|
||||||
def set_debug_log(self):
|
def set_debug_log(self):
|
||||||
import logging
|
import logging
|
||||||
if bpy.app.debug_value == 0:
|
if bpy.app.debug_value == 0: # Default values => Display all messages except debug ones
|
||||||
self.loglevel = logging.CRITICAL
|
|
||||||
elif bpy.app.debug_value == 1:
|
|
||||||
self.loglevel = logging.ERROR
|
|
||||||
elif bpy.app.debug_value == 2:
|
|
||||||
self.loglevel = logging.WARNING
|
|
||||||
elif bpy.app.debug_value == 3:
|
|
||||||
self.loglevel = logging.INFO
|
self.loglevel = logging.INFO
|
||||||
else:
|
elif bpy.app.debug_value == 1:
|
||||||
self.loglevel = logging.NOTSET
|
self.loglevel = logging.WARNING
|
||||||
|
elif bpy.app.debug_value == 2:
|
||||||
|
self.loglevel = logging.ERROR
|
||||||
|
elif bpy.app.debug_value == 3:
|
||||||
|
self.loglevel = logging.CRITICAL
|
||||||
|
elif bpy.app.debug_value == 4:
|
||||||
|
self.loglevel = logging.DEBUG
|
||||||
|
|
||||||
|
|
||||||
class GLTF2_filter_action(bpy.types.PropertyGroup):
|
class GLTF2_filter_action(bpy.types.PropertyGroup):
|
||||||
|
@ -6,7 +6,6 @@ import bpy
|
|||||||
import typing
|
import typing
|
||||||
from .....io.exp.gltf2_io_user_extensions import export_user_extensions
|
from .....io.exp.gltf2_io_user_extensions import export_user_extensions
|
||||||
from .....blender.com.gltf2_blender_data_path import skip_sk
|
from .....blender.com.gltf2_blender_data_path import skip_sk
|
||||||
from .....io.com import gltf2_io_debug
|
|
||||||
from .....io.com import gltf2_io
|
from .....io.com import gltf2_io
|
||||||
from ....exp.gltf2_blender_gather_cache import cached
|
from ....exp.gltf2_blender_gather_cache import cached
|
||||||
from ....com.gltf2_blender_data_path import get_target_object_path, get_target_property_name, get_rotation_modes
|
from ....com.gltf2_blender_data_path import get_target_object_path, get_target_property_name, get_rotation_modes
|
||||||
@ -76,7 +75,7 @@ def get_channel_groups(obj_uuid: str, blender_action: bpy.types.Action, export_s
|
|||||||
# example of target_property : location, rotation_quaternion, value
|
# example of target_property : location, rotation_quaternion, value
|
||||||
target_property = get_target_property_name(fcurve.data_path)
|
target_property = get_target_property_name(fcurve.data_path)
|
||||||
except:
|
except:
|
||||||
gltf2_io_debug.print_console("WARNING", "Invalid animation fcurve name on action {}".format(blender_action.name))
|
export_settings['log'].warning("Invalid animation fcurve data path on action {}".format(blender_action.name))
|
||||||
continue
|
continue
|
||||||
object_path = get_target_object_path(fcurve.data_path)
|
object_path = get_target_object_path(fcurve.data_path)
|
||||||
|
|
||||||
@ -123,10 +122,10 @@ def get_channel_groups(obj_uuid: str, blender_action: bpy.types.Action, export_s
|
|||||||
type_ = "SK"
|
type_ = "SK"
|
||||||
except:
|
except:
|
||||||
# Something is wrong, for example a bone animation is linked to an object mesh...
|
# Something is wrong, for example a bone animation is linked to an object mesh...
|
||||||
gltf2_io_debug.print_console("WARNING", "Animation target {} not found".format(object_path))
|
export_settings['log'].warning("Invalid animation fcurve data path on action {}".format(blender_action.name))
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
gltf2_io_debug.print_console("WARNING", "Animation target {} not found".format(object_path))
|
export_settings['log'].warning("Animation target {} not found".format(object_path))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Detect that object or bone are not multiple keyed for euler and quaternion
|
# Detect that object or bone are not multiple keyed for euler and quaternion
|
||||||
@ -169,7 +168,7 @@ def get_channel_groups(obj_uuid: str, blender_action: bpy.types.Action, export_s
|
|||||||
targets[target] = target_data
|
targets[target] = target_data
|
||||||
|
|
||||||
for targ in multiple_rotation_mode_detected.keys():
|
for targ in multiple_rotation_mode_detected.keys():
|
||||||
gltf2_io_debug.print_console("WARNING", "Multiple rotation mode detected for {}".format(targ.name))
|
export_settings['log'].warning("Multiple rotation mode detected for {}".format(targ.name))
|
||||||
|
|
||||||
# Now that all curves are extracted,
|
# Now that all curves are extracted,
|
||||||
# - check that there is no normal + delta transforms
|
# - check that there is no normal + delta transforms
|
||||||
@ -329,24 +328,24 @@ def needs_baking(obj_uuid: str,
|
|||||||
# Sampling due to unsupported interpolation
|
# Sampling due to unsupported interpolation
|
||||||
interpolation = [c for c in channels if c is not None][0].keyframe_points[0].interpolation
|
interpolation = [c for c in channels if c is not None][0].keyframe_points[0].interpolation
|
||||||
if interpolation not in ["BEZIER", "LINEAR", "CONSTANT"]:
|
if interpolation not in ["BEZIER", "LINEAR", "CONSTANT"]:
|
||||||
gltf2_io_debug.print_console("WARNING",
|
export_settings['log'].warning(
|
||||||
"Baking animation because of an unsupported interpolation method: {}".format(
|
"Baking animation because of an unsupported interpolation method: {}".format(interpolation)
|
||||||
interpolation)
|
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if any(any(k.interpolation != interpolation for k in c.keyframe_points) for c in channels if c is not None):
|
if any(any(k.interpolation != interpolation for k in c.keyframe_points) for c in channels if c is not None):
|
||||||
# There are different interpolation methods in one action group
|
# There are different interpolation methods in one action group
|
||||||
gltf2_io_debug.print_console("WARNING",
|
export_settings['log'].warning(
|
||||||
"Baking animation because there are keyframes with different "
|
"Baking animation because there are keyframes with different "
|
||||||
"interpolation methods in one channel"
|
"interpolation methods in one channel"
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if not all_equal([len(c.keyframe_points) for c in channels if c is not None]):
|
if not all_equal([len(c.keyframe_points) for c in channels if c is not None]):
|
||||||
gltf2_io_debug.print_console("WARNING",
|
export_settings['log'].warning(
|
||||||
"Baking animation because the number of keyframes is not "
|
"Baking animation because the number of keyframes is not "
|
||||||
"equal for all channel tracks")
|
"equal for all channel tracks"
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if len([c for c in channels if c is not None][0].keyframe_points) <= 1:
|
if len([c for c in channels if c is not None][0].keyframe_points) <= 1:
|
||||||
@ -355,8 +354,7 @@ def needs_baking(obj_uuid: str,
|
|||||||
|
|
||||||
if not all_equal(list(zip([[k.co[0] for k in c.keyframe_points] for c in channels if c is not None]))):
|
if not all_equal(list(zip([[k.co[0] for k in c.keyframe_points] for c in channels if c is not None]))):
|
||||||
# The channels have differently located keyframes
|
# The channels have differently located keyframes
|
||||||
gltf2_io_debug.print_console("WARNING",
|
export_settings['log'].warning("Baking animation because of differently located keyframes in one channel")
|
||||||
"Baking animation because of differently located keyframes in one channel")
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if export_settings['vtree'].nodes[obj_uuid].blender_object.type == "ARMATURE":
|
if export_settings['vtree'].nodes[obj_uuid].blender_object.type == "ARMATURE":
|
||||||
@ -364,8 +362,7 @@ def needs_baking(obj_uuid: str,
|
|||||||
if isinstance(animation_target, bpy.types.PoseBone):
|
if isinstance(animation_target, bpy.types.PoseBone):
|
||||||
if len(animation_target.constraints) != 0:
|
if len(animation_target.constraints) != 0:
|
||||||
# Constraints such as IK act on the bone -> can not be represented in glTF atm
|
# Constraints such as IK act on the bone -> can not be represented in glTF atm
|
||||||
gltf2_io_debug.print_console("WARNING",
|
export_settings['log'].warning("Baking animation because of unsupported constraints acting on the bone")
|
||||||
"Baking animation because of unsupported constraints acting on the bone")
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import typing
|
import typing
|
||||||
from ....io.com import gltf2_io
|
from ....io.com import gltf2_io
|
||||||
from ....io.com.gltf2_io_debug import print_console
|
|
||||||
from ....io.exp.gltf2_io_user_extensions import export_user_extensions
|
from ....io.exp.gltf2_io_user_extensions import export_user_extensions
|
||||||
from ....blender.com.gltf2_blender_conversion import get_gltf_interpolation
|
from ....blender.com.gltf2_blender_conversion import get_gltf_interpolation
|
||||||
from ...com.gltf2_blender_data_path import is_bone_anim_channel
|
from ...com.gltf2_blender_data_path import is_bone_anim_channel
|
||||||
@ -276,7 +275,7 @@ def gather_action_animations( obj_uuid: int,
|
|||||||
obj.hide_viewport = True
|
obj.hide_viewport = True
|
||||||
export_settings['vtree'].nodes[obj_uuid].blender_object.hide_viewport = False
|
export_settings['vtree'].nodes[obj_uuid].blender_object.hide_viewport = False
|
||||||
else:
|
else:
|
||||||
print_console("WARNING", "Can't disable viewport because of drivers")
|
export_settings['log'].warning("Can't disable viewport because of drivers")
|
||||||
export_settings['gltf_optimize_armature_disable_viewport'] = False # We changed the option here, so we don't need to re-check it later, during
|
export_settings['gltf_optimize_armature_disable_viewport'] = False # We changed the option here, so we don't need to re-check it later, during
|
||||||
|
|
||||||
|
|
||||||
@ -300,7 +299,7 @@ def gather_action_animations( obj_uuid: int,
|
|||||||
export_user_extensions('post_animation_switch_hook', export_settings, blender_object, blender_action, track_name, on_type)
|
export_user_extensions('post_animation_switch_hook', export_settings, blender_object, blender_action, track_name, on_type)
|
||||||
except:
|
except:
|
||||||
error = "Action is readonly. Please check NLA editor"
|
error = "Action is readonly. Please check NLA editor"
|
||||||
print_console("WARNING", "Animation '{}' could not be exported. Cause: {}".format(blender_action.name, error))
|
export_settings['log'].warning("Animation '{}' could not be exported. Cause: {}".format(blender_action.name, error))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if on_type == "SHAPEKEY":
|
if on_type == "SHAPEKEY":
|
||||||
@ -337,7 +336,7 @@ def gather_action_animations( obj_uuid: int,
|
|||||||
elif type_ == "EXTRA":
|
elif type_ == "EXTRA":
|
||||||
channel = None
|
channel = None
|
||||||
else:
|
else:
|
||||||
print("Type unknown. Should not happen")
|
export_settings['log'].error("Type unknown. Should not happen")
|
||||||
|
|
||||||
if animation is None and channel is not None:
|
if animation is None and channel is not None:
|
||||||
# If all channels need to be sampled, no animation was created
|
# If all channels need to be sampled, no animation was created
|
||||||
@ -503,6 +502,7 @@ def __get_blender_actions(obj_uuid: str,
|
|||||||
# so skip them for now and only write single-strip tracks.
|
# so skip them for now and only write single-strip tracks.
|
||||||
non_muted_strips = [strip for strip in track.strips if strip.action is not None and strip.mute is False]
|
non_muted_strips = [strip for strip in track.strips if strip.action is not None and strip.mute is False]
|
||||||
if track.strips is None or len(non_muted_strips) != 1:
|
if track.strips is None or len(non_muted_strips) != 1:
|
||||||
|
export_settings['log'].warning("NLA track '{}' has {} strips, but only single-strip tracks are supported in 'actions' mode.".format(track.name, len(track.strips)), popup=True)
|
||||||
continue
|
continue
|
||||||
for strip in non_muted_strips:
|
for strip in non_muted_strips:
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ from mathutils import Matrix
|
|||||||
from ....blender.com.gltf2_blender_data_path import get_sk_exported
|
from ....blender.com.gltf2_blender_data_path import get_sk_exported
|
||||||
from ....io.com import gltf2_io
|
from ....io.com import gltf2_io
|
||||||
from ....io.exp.gltf2_io_user_extensions import export_user_extensions
|
from ....io.exp.gltf2_io_user_extensions import export_user_extensions
|
||||||
from ....io.com.gltf2_io_debug import print_console
|
|
||||||
from ..gltf2_blender_gather_tree import VExportNode
|
from ..gltf2_blender_gather_tree import VExportNode
|
||||||
from .sampled.armature.armature_action_sampled import gather_action_armature_sampled
|
from .sampled.armature.armature_action_sampled import gather_action_armature_sampled
|
||||||
from .sampled.object.gltf2_blender_gather_object_action_sampled import gather_action_object_sampled
|
from .sampled.object.gltf2_blender_gather_object_action_sampled import gather_action_object_sampled
|
||||||
@ -130,7 +129,7 @@ def merge_tracks_perform(merged_tracks, animations, export_settings):
|
|||||||
|
|
||||||
for channel in animations[anim_idx].channels:
|
for channel in animations[anim_idx].channels:
|
||||||
if (channel.target.node, channel.target.path) in already_animated:
|
if (channel.target.node, channel.target.path) in already_animated:
|
||||||
print_console("WARNING", "Some strips have same channel animation ({}), on node {} !".format(channel.target.path, channel.target.node.name))
|
export_settings['log'].warning("Some strips have same channel animation ({}), on node {} !".format(channel.target.path, channel.target.node.name))
|
||||||
continue
|
continue
|
||||||
animations[base_animation_idx].channels.append(channel)
|
animations[base_animation_idx].channels.append(channel)
|
||||||
animations[base_animation_idx].channels[-1].sampler = animations[base_animation_idx].channels[-1].sampler + offset_sampler
|
animations[base_animation_idx].channels[-1].sampler = animations[base_animation_idx].channels[-1].sampler + offset_sampler
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import typing
|
import typing
|
||||||
from ......io.exp.gltf2_io_user_extensions import export_user_extensions
|
from ......io.exp.gltf2_io_user_extensions import export_user_extensions
|
||||||
from ......io.com.gltf2_io_debug import print_console
|
|
||||||
from ......io.com import gltf2_io
|
from ......io.com import gltf2_io
|
||||||
from .....com.gltf2_blender_extras import generate_extras
|
from .....com.gltf2_blender_extras import generate_extras
|
||||||
from ...fcurves.gltf2_blender_gather_fcurves_sampler import gather_animation_fcurves_sampler
|
from ...fcurves.gltf2_blender_gather_fcurves_sampler import gather_animation_fcurves_sampler
|
||||||
@ -32,7 +31,7 @@ def gather_action_armature_sampled(armature_uuid: str, blender_action: typing.Op
|
|||||||
samplers=[] # We need to gather the samplers after gathering all channels --> populate this list in __link_samplers
|
samplers=[] # We need to gather the samplers after gathering all channels --> populate this list in __link_samplers
|
||||||
)
|
)
|
||||||
except RuntimeError as error:
|
except RuntimeError as error:
|
||||||
print_console("WARNING", "Animation '{}' could not be exported. Cause: {}".format(name, error))
|
export_settings['log'].warning("Animation '{}' could not be exported. Cause: {}".format(name, error))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
export_user_extensions('pre_gather_animation_hook', export_settings, animation, blender_action, blender_object)
|
export_user_extensions('pre_gather_animation_hook', export_settings, animation, blender_action, blender_object)
|
||||||
|
@ -23,7 +23,7 @@ def gather_armature_sampled_channels(armature_uuid, blender_action_name, export_
|
|||||||
# Then bake all bones
|
# Then bake all bones
|
||||||
bones_to_be_animated = []
|
bones_to_be_animated = []
|
||||||
bones_uuid = export_settings["vtree"].get_all_bones(armature_uuid)
|
bones_uuid = export_settings["vtree"].get_all_bones(armature_uuid)
|
||||||
bones_to_be_animated = [export_settings["vtree"].nodes[b].blender_bone.name for b in bones_uuid]
|
bones_to_be_animated = [export_settings["vtree"].nodes[b].blender_bone.name for b in bones_uuid if export_settings["vtree"].nodes[b].leaf_reference is None]
|
||||||
|
|
||||||
# List of really animated bones is needed for optimization decision
|
# List of really animated bones is needed for optimization decision
|
||||||
list_of_animated_bone_channels = {}
|
list_of_animated_bone_channels = {}
|
||||||
|
@ -157,7 +157,7 @@ def get_cache_data(path: str,
|
|||||||
if 'bone' not in data[obj_uuid][obj_uuid].keys():
|
if 'bone' not in data[obj_uuid][obj_uuid].keys():
|
||||||
data[obj_uuid][obj_uuid]['bone'] = {}
|
data[obj_uuid][obj_uuid]['bone'] = {}
|
||||||
|
|
||||||
for bone_uuid in bones:
|
for bone_uuid in [bone for bone in bones if export_settings['vtree'].nodes[bone].leaf_reference is None]:
|
||||||
blender_bone = export_settings['vtree'].nodes[bone_uuid].blender_bone
|
blender_bone = export_settings['vtree'].nodes[bone_uuid].blender_bone
|
||||||
|
|
||||||
if export_settings['vtree'].nodes[bone_uuid].parent_uuid is not None and export_settings['vtree'].nodes[export_settings['vtree'].nodes[bone_uuid].parent_uuid].blender_type == VExportNode.BONE:
|
if export_settings['vtree'].nodes[bone_uuid].parent_uuid is not None and export_settings['vtree'].nodes[export_settings['vtree'].nodes[bone_uuid].parent_uuid].blender_type == VExportNode.BONE:
|
||||||
|
@ -10,7 +10,6 @@ import bpy
|
|||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from ...io.com.gltf2_io_debug import print_console, print_newline
|
|
||||||
from ...io.exp import gltf2_io_export
|
from ...io.exp import gltf2_io_export
|
||||||
from ...io.exp import gltf2_io_draco_compression_extension
|
from ...io.exp import gltf2_io_draco_compression_extension
|
||||||
from ...io.exp.gltf2_io_user_extensions import export_user_extensions
|
from ...io.exp.gltf2_io_user_extensions import export_user_extensions
|
||||||
@ -30,7 +29,7 @@ def save(context, export_settings):
|
|||||||
if not export_settings['gltf_current_frame']:
|
if not export_settings['gltf_current_frame']:
|
||||||
bpy.context.scene.frame_set(0)
|
bpy.context.scene.frame_set(0)
|
||||||
|
|
||||||
__notify_start(context)
|
__notify_start(context, export_settings)
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
pre_export_callbacks = export_settings["pre_export_callbacks"]
|
pre_export_callbacks = export_settings["pre_export_callbacks"]
|
||||||
for callback in pre_export_callbacks:
|
for callback in pre_export_callbacks:
|
||||||
@ -44,10 +43,11 @@ def save(context, export_settings):
|
|||||||
__write_file(json, buffer, export_settings)
|
__write_file(json, buffer, export_settings)
|
||||||
|
|
||||||
end_time = time.time()
|
end_time = time.time()
|
||||||
__notify_end(context, end_time - start_time)
|
__notify_end(context, end_time - start_time, export_settings)
|
||||||
|
|
||||||
if not export_settings['gltf_current_frame']:
|
if not export_settings['gltf_current_frame']:
|
||||||
bpy.context.scene.frame_set(int(original_frame))
|
bpy.context.scene.frame_set(int(original_frame))
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
@ -178,7 +178,7 @@ def __postprocess_with_gltfpack(export_settings):
|
|||||||
try:
|
try:
|
||||||
subprocess.run([gltfpack_binary_file_path] + options + parameters, check=True)
|
subprocess.run([gltfpack_binary_file_path] + options + parameters, check=True)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
print_console('ERROR', "Calling gltfpack was not successful")
|
export_settings['log'].error("Calling gltfpack was not successful")
|
||||||
|
|
||||||
def __write_file(json, buffer, export_settings):
|
def __write_file(json, buffer, export_settings):
|
||||||
try:
|
try:
|
||||||
@ -196,18 +196,18 @@ def __write_file(json, buffer, export_settings):
|
|||||||
tb_info = traceback.extract_tb(tb)
|
tb_info = traceback.extract_tb(tb)
|
||||||
for tbi in tb_info:
|
for tbi in tb_info:
|
||||||
filename, line, func, text = tbi
|
filename, line, func, text = tbi
|
||||||
print_console('ERROR', 'An error occurred on line {} in statement {}'.format(line, text))
|
export_settings['log'].error('An error occurred on line {} in statement {}'.format(line, text))
|
||||||
print_console('ERROR', str(e))
|
export_settings['log'].error(str(e))
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def __notify_start(context):
|
def __notify_start(context, export_settings):
|
||||||
print_console('INFO', 'Starting glTF 2.0 export')
|
export_settings['log'].info('Starting glTF 2.0 export')
|
||||||
context.window_manager.progress_begin(0, 100)
|
context.window_manager.progress_begin(0, 100)
|
||||||
context.window_manager.progress_update(0)
|
context.window_manager.progress_update(0)
|
||||||
|
|
||||||
|
|
||||||
def __notify_end(context, elapsed):
|
def __notify_end(context, elapsed, export_settings):
|
||||||
print_console('INFO', 'Finished glTF 2.0 export in {} s'.format(elapsed))
|
export_settings['log'].info('Finished glTF 2.0 export in {} s'.format(elapsed))
|
||||||
context.window_manager.progress_end()
|
context.window_manager.progress_end()
|
||||||
print_newline()
|
print()
|
||||||
|
@ -86,7 +86,7 @@ def gather_joint_vnode(vnode, export_settings):
|
|||||||
extras=__gather_extras(blender_bone, export_settings),
|
extras=__gather_extras(blender_bone, export_settings),
|
||||||
matrix=None,
|
matrix=None,
|
||||||
mesh=None,
|
mesh=None,
|
||||||
name=blender_bone.name,
|
name=blender_bone.name if vtree.nodes[vnode].leaf_reference is None else vtree.nodes[vtree.nodes[vnode].leaf_reference].blender_bone.name + '_leaf',
|
||||||
rotation=rotation,
|
rotation=rotation,
|
||||||
scale=scale,
|
scale=scale,
|
||||||
skin=None,
|
skin=None,
|
||||||
|
@ -36,7 +36,7 @@ def gather_lights_punctual(blender_lamp, export_settings) -> Optional[Dict[str,
|
|||||||
|
|
||||||
def __filter_lights_punctual(blender_lamp, export_settings) -> bool:
|
def __filter_lights_punctual(blender_lamp, export_settings) -> bool:
|
||||||
if blender_lamp.type in ["HEMI", "AREA"]:
|
if blender_lamp.type in ["HEMI", "AREA"]:
|
||||||
gltf2_io_debug.print_console("WARNING", "Unsupported light source {}".format(blender_lamp.type))
|
export_settings['log'].warning("Unsupported light source {}".format(blender_lamp.type))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@ -63,8 +63,7 @@ def __gather_intensity(blender_lamp, export_settings) -> Optional[float]:
|
|||||||
quadratic_falloff_node = result[0].shader_node
|
quadratic_falloff_node = result[0].shader_node
|
||||||
emission_strength = quadratic_falloff_node.inputs["Strength"].default_value / (math.pi * 4.0)
|
emission_strength = quadratic_falloff_node.inputs["Strength"].default_value / (math.pi * 4.0)
|
||||||
else:
|
else:
|
||||||
gltf2_io_debug.print_console('WARNING',
|
export_settings['log'].warning('No quadratic light falloff node attached to emission strength property')
|
||||||
'No quadratic light falloff node attached to emission strength property')
|
|
||||||
emission_strength = blender_lamp.energy
|
emission_strength = blender_lamp.energy
|
||||||
else:
|
else:
|
||||||
emission_strength = emission_node.inputs["Strength"].default_value
|
emission_strength = emission_node.inputs["Strength"].default_value
|
||||||
|
@ -6,7 +6,6 @@ import bpy
|
|||||||
from typing import Optional, Dict, List, Any, Tuple
|
from typing import Optional, Dict, List, Any, Tuple
|
||||||
from ...io.com import gltf2_io
|
from ...io.com import gltf2_io
|
||||||
from ...blender.com.gltf2_blender_data_path import get_sk_exported
|
from ...blender.com.gltf2_blender_data_path import get_sk_exported
|
||||||
from ...io.com.gltf2_io_debug import print_console
|
|
||||||
from ...io.exp.gltf2_io_user_extensions import export_user_extensions
|
from ...io.exp.gltf2_io_user_extensions import export_user_extensions
|
||||||
from ..com.gltf2_blender_extras import generate_extras
|
from ..com.gltf2_blender_extras import generate_extras
|
||||||
from . import gltf2_blender_gather_primitives
|
from . import gltf2_blender_gather_primitives
|
||||||
@ -59,7 +58,7 @@ def gather_mesh(blender_mesh: bpy.types.Mesh,
|
|||||||
)
|
)
|
||||||
|
|
||||||
if len(mesh.primitives) == 0:
|
if len(mesh.primitives) == 0:
|
||||||
print_console("WARNING", "Mesh '{}' has no primitives and will be omitted.".format(mesh.name))
|
export_settings['log'].warning("Mesh '{}' has no primitives and will be omitted.".format(mesh.name))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
blender_object = None
|
blender_object = None
|
||||||
|
@ -6,7 +6,6 @@ import math
|
|||||||
import bpy
|
import bpy
|
||||||
from mathutils import Matrix, Quaternion, Vector
|
from mathutils import Matrix, Quaternion, Vector
|
||||||
|
|
||||||
from ...io.com.gltf2_io_debug import print_console
|
|
||||||
from ...io.com import gltf2_io
|
from ...io.com import gltf2_io
|
||||||
from ...io.com import gltf2_io_extensions
|
from ...io.com import gltf2_io_extensions
|
||||||
from ...io.exp.gltf2_io_user_extensions import export_user_extensions
|
from ...io.exp.gltf2_io_user_extensions import export_user_extensions
|
||||||
@ -251,7 +250,7 @@ def __gather_mesh(vnode, blender_object, export_settings):
|
|||||||
# Be sure that object is valid (no NaN for example)
|
# Be sure that object is valid (no NaN for example)
|
||||||
res = blender_object.data.validate()
|
res = blender_object.data.validate()
|
||||||
if res is True:
|
if res is True:
|
||||||
print_console("WARNING", "Mesh " + blender_object.data.name + " is not valid, and may be exported wrongly")
|
export_settings['log'].warning("Mesh " + blender_object.data.name + " is not valid, and may be exported wrongly")
|
||||||
|
|
||||||
modifiers = blender_object.modifiers
|
modifiers = blender_object.modifiers
|
||||||
if len(modifiers) == 0:
|
if len(modifiers) == 0:
|
||||||
|
@ -73,8 +73,10 @@ def __gather_skins(blender_primitive, export_settings):
|
|||||||
# Set warning, for the case where there are more group of 4 weights needed
|
# Set warning, for the case where there are more group of 4 weights needed
|
||||||
# Warning for the case where we are in the same group, will be done later (for example, 3 weights needed, but 2 wanted by user)
|
# Warning for the case where we are in the same group, will be done later (for example, 3 weights needed, but 2 wanted by user)
|
||||||
if max_bone_set_index > wanted_max_bone_set_index:
|
if max_bone_set_index > wanted_max_bone_set_index:
|
||||||
gltf2_io_debug.print_console("WARNING", "There are more than {} joint vertex influences."
|
export_settings['log'].warning(
|
||||||
"The {} with highest weight will be used (and normalized).".format(export_settings['gltf_vertex_influences_nb'], export_settings['gltf_vertex_influences_nb']))
|
"There are more than {} joint vertex influences."
|
||||||
|
"The {} with highest weight will be used (and normalized).".format(export_settings['gltf_vertex_influences_nb'], export_settings['gltf_vertex_influences_nb'])
|
||||||
|
)
|
||||||
|
|
||||||
# Take into account only the first set of 4 weights
|
# Take into account only the first set of 4 weights
|
||||||
max_bone_set_index = wanted_max_bone_set_index
|
max_bone_set_index = wanted_max_bone_set_index
|
||||||
@ -99,9 +101,10 @@ def __gather_skins(blender_primitive, export_settings):
|
|||||||
idx = 4-1-i
|
idx = 4-1-i
|
||||||
if not all(weight[:, idx]):
|
if not all(weight[:, idx]):
|
||||||
if warning_done is False:
|
if warning_done is False:
|
||||||
gltf2_io_debug.print_console("WARNING", "There are more than {} joint vertex influences."
|
export_settings['log'].warning(
|
||||||
"The {} with highest weight will be used (and normalized).".format(export_settings['gltf_vertex_influences_nb'], export_settings['gltf_vertex_influences_nb']))
|
"There are more than {} joint vertex influences."
|
||||||
|
"The {} with highest weight will be used (and normalized).".format(export_settings['gltf_vertex_influences_nb'], export_settings['gltf_vertex_influences_nb'])
|
||||||
|
)
|
||||||
warning_done = True
|
warning_done = True
|
||||||
weight[:, idx] = 0.0
|
weight[:, idx] = 0.0
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ import bpy
|
|||||||
from typing import List, Optional, Tuple
|
from typing import List, Optional, Tuple
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from ...io.com import gltf2_io, gltf2_io_constants, gltf2_io_extensions
|
from ...io.com import gltf2_io, gltf2_io_constants, gltf2_io_extensions
|
||||||
from ...io.com.gltf2_io_debug import print_console
|
|
||||||
from ...blender.com.gltf2_blender_data_path import get_sk_exported
|
from ...blender.com.gltf2_blender_data_path import get_sk_exported
|
||||||
from ...io.exp import gltf2_io_binary_data
|
from ...io.exp import gltf2_io_binary_data
|
||||||
from .gltf2_blender_gather_cache import cached, cached_by_key
|
from .gltf2_blender_gather_cache import cached, cached_by_key
|
||||||
@ -192,7 +191,7 @@ def __gather_indices(blender_primitive, blender_mesh, modifiers, export_settings
|
|||||||
component_type = gltf2_io_constants.ComponentType.UnsignedInt
|
component_type = gltf2_io_constants.ComponentType.UnsignedInt
|
||||||
indices = indices.astype(np.uint32, copy=False)
|
indices = indices.astype(np.uint32, copy=False)
|
||||||
else:
|
else:
|
||||||
print_console('ERROR', 'A mesh contains too many vertices (' + str(max_index) + ') and needs to be split before export.')
|
export_settings['log'].error('A mesh contains too many vertices (' + str(max_index) + ') and needs to be split before export.')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
element_type = gltf2_io_constants.DataType.Scalar
|
element_type = gltf2_io_constants.DataType.Scalar
|
||||||
|
@ -6,7 +6,6 @@ import numpy as np
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from ...blender.com.gltf2_blender_data_path import get_sk_exported
|
from ...blender.com.gltf2_blender_data_path import get_sk_exported
|
||||||
from ...io.com.gltf2_io_debug import print_console
|
|
||||||
from ...io.com.gltf2_io_constants import ROUNDING_DIGIT
|
from ...io.com.gltf2_io_constants import ROUNDING_DIGIT
|
||||||
from ...io.exp.gltf2_io_user_extensions import export_user_extensions
|
from ...io.exp.gltf2_io_user_extensions import export_user_extensions
|
||||||
from ...io.com import gltf2_io_constants
|
from ...io.com import gltf2_io_constants
|
||||||
@ -19,7 +18,7 @@ from . import gltf2_blender_gather_skins
|
|||||||
|
|
||||||
def extract_primitives(materials, blender_mesh, uuid_for_skined_data, blender_vertex_groups, modifiers, export_settings):
|
def extract_primitives(materials, blender_mesh, uuid_for_skined_data, blender_vertex_groups, modifiers, export_settings):
|
||||||
"""Extract primitives from a mesh."""
|
"""Extract primitives from a mesh."""
|
||||||
print_console('INFO', 'Extracting primitive: ' + blender_mesh.name)
|
export_settings['log'].info("Extracting primitive: " + blender_mesh.name)
|
||||||
|
|
||||||
primitive_creator = PrimitiveCreator(materials, blender_mesh, uuid_for_skined_data, blender_vertex_groups, modifiers, export_settings)
|
primitive_creator = PrimitiveCreator(materials, blender_mesh, uuid_for_skined_data, blender_vertex_groups, modifiers, export_settings)
|
||||||
primitive_creator.prepare_data()
|
primitive_creator.prepare_data()
|
||||||
@ -78,7 +77,7 @@ class PrimitiveCreator:
|
|||||||
self.blender_mesh.calc_tangents()
|
self.blender_mesh.calc_tangents()
|
||||||
self.use_tangents = True
|
self.use_tangents = True
|
||||||
except Exception:
|
except Exception:
|
||||||
print_console('WARNING', 'Could not calculate tangents. Please try to triangulate the mesh first.')
|
self.export_settings['log'].warning("{}: Could not calculate tangents. Please try to triangulate the mesh first.".format(self.blender_mesh.name), popup=True)
|
||||||
|
|
||||||
self.tex_coord_max = 0
|
self.tex_coord_max = 0
|
||||||
if self.export_settings['gltf_texcoords']:
|
if self.export_settings['gltf_texcoords']:
|
||||||
@ -187,7 +186,7 @@ class PrimitiveCreator:
|
|||||||
# Seems we sometime can have name collision about attributes
|
# Seems we sometime can have name collision about attributes
|
||||||
# Avoid crash and ignoring one of duplicated attribute name
|
# Avoid crash and ignoring one of duplicated attribute name
|
||||||
if attr['gltf_attribute_name'] in [a['gltf_attribute_name'] for a in self.blender_attributes]:
|
if attr['gltf_attribute_name'] in [a['gltf_attribute_name'] for a in self.blender_attributes]:
|
||||||
print_console('WARNING', 'Attribute collision name: ' + blender_attribute.name + ", ignoring one of them")
|
self.export_settings['log'].warning('Attribute collision name: ' + blender_attribute.name + ", ignoring one of them")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.blender_attributes.append(attr)
|
self.blender_attributes.append(attr)
|
||||||
@ -426,7 +425,7 @@ class PrimitiveCreator:
|
|||||||
self.blender_mesh.attributes[attr].data.foreach_get('vector', data)
|
self.blender_mesh.attributes[attr].data.foreach_get('vector', data)
|
||||||
data = data.reshape(-1, 2)
|
data = data.reshape(-1, 2)
|
||||||
else:
|
else:
|
||||||
print_console('WARNING', 'We are not managing this case yet (UVMap as custom attribute for unknown type)')
|
self.export_settings['log'].warning('We are not managing this case (UVMap as custom attribute for unknown type)')
|
||||||
continue
|
continue
|
||||||
# Blender UV space -> glTF UV space
|
# Blender UV space -> glTF UV space
|
||||||
# u,v -> u,1-v
|
# u,v -> u,1-v
|
||||||
@ -448,7 +447,7 @@ class PrimitiveCreator:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
elif material_info['vc_info']['color_type'] is None and material_info['vc_info']['alpha_type'] is not None:
|
elif material_info['vc_info']['color_type'] is None and material_info['vc_info']['alpha_type'] is not None:
|
||||||
print_console('WARNING', 'We are not managing this case (Vertex Color alpha without color)')
|
self.export_settings['log'].warning('We are not managing this case (Vertex Color alpha without color)')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
vc_color_name = None
|
vc_color_name = None
|
||||||
@ -475,7 +474,7 @@ class PrimitiveCreator:
|
|||||||
|
|
||||||
if materials_use_vc is not None and materials_use_vc != vc_key:
|
if materials_use_vc is not None and materials_use_vc != vc_key:
|
||||||
if warning_already_displayed is False:
|
if warning_already_displayed is False:
|
||||||
print_console('WARNING', 'glTF specification does not allow this case (multiple materials with different Vertex Color)')
|
self.export_settings['log'].warning('glTF specification does not allow this case (multiple materials with different Vertex Color)')
|
||||||
warning_already_displayed = True
|
warning_already_displayed = True
|
||||||
materials_use_vc = vc_key
|
materials_use_vc = vc_key
|
||||||
|
|
||||||
@ -520,12 +519,12 @@ class PrimitiveCreator:
|
|||||||
all_uvmaps[tex] = uvmap_name
|
all_uvmaps[tex] = uvmap_name
|
||||||
|
|
||||||
if len(set(all_uvmaps.values())) > 1:
|
if len(set(all_uvmaps.values())) > 1:
|
||||||
print_console('WARNING', 'We are not managing this case (multiple UVMap for UDIM)')
|
self.export_settings['log'].warning('We are not managing this case (multiple UVMap for UDIM)')
|
||||||
new_prim_indices[material_idx] = self.prim_indices[material_idx]
|
new_prim_indices[material_idx] = self.prim_indices[material_idx]
|
||||||
self.additional_materials.append(None)
|
self.additional_materials.append(None)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print_console('INFO', 'Splitting UDIM tiles into different primitives/materials')
|
self.export_settings['log'].info('Splitting UDIM tiles into different primitives/materials')
|
||||||
# Retrieve UDIM images
|
# Retrieve UDIM images
|
||||||
tex = list(material_info['udim_info'].keys())[0]
|
tex = list(material_info['udim_info'].keys())[0]
|
||||||
image = material_info['udim_info'][tex]['image']
|
image = material_info['udim_info'][tex]['image']
|
||||||
@ -623,7 +622,7 @@ class PrimitiveCreator:
|
|||||||
elif tex == "anisotropyTexture":
|
elif tex == "anisotropyTexture":
|
||||||
new_material.extensions["KHR_materials_anisotropy"].extension['anisotropyTexture'] = new_tex
|
new_material.extensions["KHR_materials_anisotropy"].extension['anisotropyTexture'] = new_tex
|
||||||
else:
|
else:
|
||||||
print_console('WARNING', 'We are not managing this case yet (UDIM for {})'.format(tex))
|
self.export_settings['log'].warning('We are not managing this case (UDIM for {})'.format(tex))
|
||||||
|
|
||||||
self.additional_materials.append((new_material, material_info, int(str(id(base_material)) + str(u) + str(v))))
|
self.additional_materials.append((new_material, material_info, int(str(id(base_material)) + str(u) + str(v))))
|
||||||
|
|
||||||
@ -696,7 +695,7 @@ class PrimitiveCreator:
|
|||||||
has_triangle_primitive = len(primitives) != 0
|
has_triangle_primitive = len(primitives) != 0
|
||||||
primitives.extend(self.primitive_creation_edges_and_points())
|
primitives.extend(self.primitive_creation_edges_and_points())
|
||||||
|
|
||||||
print_console('INFO', 'Primitives created: %d' % len(primitives))
|
self.export_settings['log'].info('Primitives created: %d' % len(primitives))
|
||||||
|
|
||||||
return primitives, [None]*len(primitives), self.attributes if has_triangle_primitive else None
|
return primitives, [None]*len(primitives), self.attributes if has_triangle_primitive else None
|
||||||
|
|
||||||
@ -769,7 +768,7 @@ class PrimitiveCreator:
|
|||||||
# No material for them, so only one primitive for each
|
# No material for them, so only one primitive for each
|
||||||
primitives.extend(self.primitive_creation_edges_and_points())
|
primitives.extend(self.primitive_creation_edges_and_points())
|
||||||
|
|
||||||
print_console('INFO', 'Primitives created: %d' % len(primitives))
|
self.export_settings['log'].info('Primitives created: %d' % len(primitives))
|
||||||
|
|
||||||
return primitives
|
return primitives
|
||||||
|
|
||||||
@ -1061,7 +1060,7 @@ class PrimitiveCreator:
|
|||||||
elif attr['blender_domain'] in ['FACE']:
|
elif attr['blender_domain'] in ['FACE']:
|
||||||
data = np.empty(len(self.blender_mesh.polygons) * attr['len'], dtype=attr['type'])
|
data = np.empty(len(self.blender_mesh.polygons) * attr['len'], dtype=attr['type'])
|
||||||
else:
|
else:
|
||||||
print_console("ERROR", "domain not known")
|
self.export_settings['log'].error("domain not known")
|
||||||
|
|
||||||
if attr['blender_data_type'] == "BYTE_COLOR":
|
if attr['blender_data_type'] == "BYTE_COLOR":
|
||||||
self.blender_mesh.attributes[attr['blender_attribute_index']].data.foreach_get('color', data)
|
self.blender_mesh.attributes[attr['blender_attribute_index']].data.foreach_get('color', data)
|
||||||
@ -1093,7 +1092,7 @@ class PrimitiveCreator:
|
|||||||
self.blender_mesh.attributes[attr['blender_attribute_index']].data.foreach_get('value', data)
|
self.blender_mesh.attributes[attr['blender_attribute_index']].data.foreach_get('value', data)
|
||||||
data = data.reshape(-1, attr['len'])
|
data = data.reshape(-1, attr['len'])
|
||||||
else:
|
else:
|
||||||
print_console('ERROR',"blender type not found " + attr['blender_data_type'])
|
self.export_settings['log'].error("blender type not found " + attr['blender_data_type'])
|
||||||
|
|
||||||
if attr['blender_domain'] in ['CORNER']:
|
if attr['blender_domain'] in ['CORNER']:
|
||||||
for i in range(attr['len']):
|
for i in range(attr['len']):
|
||||||
@ -1129,7 +1128,7 @@ class PrimitiveCreator:
|
|||||||
self.dots[attr['gltf_attribute_name'] + str(i)] = data_attr[:, i]
|
self.dots[attr['gltf_attribute_name'] + str(i)] = data_attr[:, i]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print_console("ERROR", "domain not known")
|
self.export_settings['log'].error("domain not known")
|
||||||
|
|
||||||
def __get_uvs_attribute(self, blender_uv_idx, attr):
|
def __get_uvs_attribute(self, blender_uv_idx, attr):
|
||||||
layer = self.blender_mesh.uv_layers[blender_uv_idx]
|
layer = self.blender_mesh.uv_layers[blender_uv_idx]
|
||||||
|
@ -90,7 +90,17 @@ def __gather_inverse_bind_matrices(armature_uuid, export_settings):
|
|||||||
|
|
||||||
matrices = []
|
matrices = []
|
||||||
for b in bones_uuid:
|
for b in bones_uuid:
|
||||||
|
if export_settings['vtree'].nodes[b].leaf_reference is None:
|
||||||
__collect_matrices(blender_armature_object.pose.bones[export_settings['vtree'].nodes[b].blender_bone.name])
|
__collect_matrices(blender_armature_object.pose.bones[export_settings['vtree'].nodes[b].blender_bone.name])
|
||||||
|
else:
|
||||||
|
inverse_bind_matrix = (
|
||||||
|
axis_basis_change @
|
||||||
|
(
|
||||||
|
blender_armature_object.matrix_world @
|
||||||
|
export_settings['vtree'].nodes[export_settings['vtree'].nodes[b].leaf_reference].matrix_world_tail
|
||||||
|
)
|
||||||
|
).inverted_safe()
|
||||||
|
matrices.append(inverse_bind_matrix) # Leaf bone
|
||||||
|
|
||||||
# flatten the matrices
|
# flatten the matrices
|
||||||
inverse_matrices = []
|
inverse_matrices = []
|
||||||
|
@ -54,6 +54,7 @@ class VExportNode:
|
|||||||
|
|
||||||
self.blender_object = None
|
self.blender_object = None
|
||||||
self.blender_bone = None
|
self.blender_bone = None
|
||||||
|
self.leaf_reference = None # For leaf bones only
|
||||||
|
|
||||||
self.default_hide_viewport = False # Need to store the default value for meshes in case of animation baking on armature
|
self.default_hide_viewport = False # Need to store the default value for meshes in case of animation baking on armature
|
||||||
|
|
||||||
@ -181,8 +182,10 @@ class VExportTree:
|
|||||||
if parent_uuid is None or not self.nodes[parent_uuid].blender_type == VExportNode.ARMATURE:
|
if parent_uuid is None or not self.nodes[parent_uuid].blender_type == VExportNode.ARMATURE:
|
||||||
# correct workflow is to parent skinned mesh to armature, but ...
|
# correct workflow is to parent skinned mesh to armature, but ...
|
||||||
# all users don't use correct workflow
|
# all users don't use correct workflow
|
||||||
print("WARNING: Armature must be the parent of skinned mesh")
|
self.export_settings['log'].warning(
|
||||||
print("Armature is selected by its name, but may be false in case of instances")
|
"Armature must be the parent of skinned mesh"
|
||||||
|
"Armature is selected by its name, but may be false in case of instances"
|
||||||
|
)
|
||||||
# Search an armature by name, and use the first found
|
# Search an armature by name, and use the first found
|
||||||
# This will be done after all objects are setup
|
# This will be done after all objects are setup
|
||||||
node.armature_needed = modifiers["ARMATURE"].object.name
|
node.armature_needed = modifiers["ARMATURE"].object.name
|
||||||
@ -246,9 +249,13 @@ class VExportTree:
|
|||||||
if self.export_settings['gltf_rest_position_armature'] is False:
|
if self.export_settings['gltf_rest_position_armature'] is False:
|
||||||
# Use pose bone for TRS
|
# Use pose bone for TRS
|
||||||
node.matrix_world = self.nodes[node.armature].matrix_world @ blender_bone.matrix
|
node.matrix_world = self.nodes[node.armature].matrix_world @ blender_bone.matrix
|
||||||
|
if self.export_settings['gltf_leaf_bone'] is True:
|
||||||
|
node.matrix_world_tail = self.nodes[node.armature].matrix_world @ Matrix.Translation(blender_bone.tail)
|
||||||
|
node.matrix_world_tail = node.matrix_world_tail @ self.axis_basis_change
|
||||||
else:
|
else:
|
||||||
# Use edit bone for TRS --> REST pose will be used
|
# Use edit bone for TRS --> REST pose will be used
|
||||||
node.matrix_world = self.nodes[node.armature].matrix_world @ blender_bone.bone.matrix_local
|
node.matrix_world = self.nodes[node.armature].matrix_world @ blender_bone.bone.matrix_local
|
||||||
|
# Tail will be set after, as we need to be in edit mode
|
||||||
node.matrix_world = node.matrix_world @ self.axis_basis_change
|
node.matrix_world = node.matrix_world @ self.axis_basis_change
|
||||||
|
|
||||||
if delta is True:
|
if delta is True:
|
||||||
@ -423,7 +430,7 @@ class VExportTree:
|
|||||||
elif parent_keep_tag is False:
|
elif parent_keep_tag is False:
|
||||||
self.nodes[uuid].keep_tag = False
|
self.nodes[uuid].keep_tag = False
|
||||||
else:
|
else:
|
||||||
print("This should not happen!")
|
self.export_settings['log'].error("This should not happen")
|
||||||
|
|
||||||
for child in self.nodes[uuid].children:
|
for child in self.nodes[uuid].children:
|
||||||
if self.nodes[uuid].blender_type == VExportNode.INST_COLLECTION or self.nodes[uuid].is_instancier == VExportNode.INSTANCIER:
|
if self.nodes[uuid].blender_type == VExportNode.INST_COLLECTION or self.nodes[uuid].is_instancier == VExportNode.INSTANCIER:
|
||||||
@ -556,13 +563,56 @@ class VExportTree:
|
|||||||
del n.armature_needed
|
del n.armature_needed
|
||||||
|
|
||||||
def bake_armature_bone_list(self):
|
def bake_armature_bone_list(self):
|
||||||
|
|
||||||
|
if self.export_settings['gltf_leaf_bone'] is True:
|
||||||
|
self.add_leaf_bones()
|
||||||
|
|
||||||
# Used to store data in armature vnode
|
# Used to store data in armature vnode
|
||||||
# If armature is removed from export
|
# If armature is removed from export
|
||||||
# Data are still available, even if armature is not exported (so bones are re-parented)
|
# Data are still available, even if armature is not exported (so bones are re-parented)
|
||||||
for n in [n for n in self.nodes.values() if n.blender_type == VExportNode.ARMATURE]:
|
for n in [n for n in self.nodes.values() if n.blender_type == VExportNode.ARMATURE]:
|
||||||
|
|
||||||
self.get_all_bones(n.uuid)
|
self.get_all_bones(n.uuid)
|
||||||
self.get_root_bones_uuid(n.uuid)
|
self.get_root_bones_uuid(n.uuid)
|
||||||
|
|
||||||
|
def add_leaf_bones(self):
|
||||||
|
|
||||||
|
# If we are using rest pose, we need to get tail of editbone, going to edit mode for each armature
|
||||||
|
if self.export_settings['gltf_rest_position_armature'] is True:
|
||||||
|
for obj_uuid in [n for n in self.nodes if self.nodes[n].blender_type == VExportNode.ARMATURE]:
|
||||||
|
armature = self.nodes[obj_uuid].blender_object
|
||||||
|
bpy.context.view_layer.objects.active = armature
|
||||||
|
bpy.ops.object.mode_set(mode="EDIT")
|
||||||
|
|
||||||
|
for bone in armature.data.edit_bones:
|
||||||
|
if len(bone.children) == 0:
|
||||||
|
self.nodes[self.nodes[obj_uuid].bones[bone.name]].matrix_world_tail = armature.matrix_world @ Matrix.Translation(bone.tail) @ self.axis_basis_change
|
||||||
|
|
||||||
|
bpy.ops.object.mode_set(mode="OBJECT")
|
||||||
|
|
||||||
|
|
||||||
|
for bone_uuid in [n for n in self.nodes if self.nodes[n].blender_type == VExportNode.BONE \
|
||||||
|
and len(self.nodes[n].children) == 0]:
|
||||||
|
|
||||||
|
bone_node = self.nodes[bone_uuid]
|
||||||
|
|
||||||
|
# Add a new node
|
||||||
|
node = VExportNode()
|
||||||
|
node.uuid = str(uuid.uuid4())
|
||||||
|
node.parent_uuid = bone_uuid
|
||||||
|
node.parent_bone_uuid = bone_uuid
|
||||||
|
node.blender_object = bone_node.blender_object
|
||||||
|
node.armature = bone_node.armature
|
||||||
|
node.blender_type = VExportNode.BONE
|
||||||
|
node.leaf_reference = bone_uuid
|
||||||
|
node.keep_tag = True
|
||||||
|
|
||||||
|
node.matrix_world = bone_node.matrix_world_tail.copy()
|
||||||
|
|
||||||
|
self.add_children(bone_uuid, node.uuid)
|
||||||
|
self.add_node(node)
|
||||||
|
|
||||||
|
|
||||||
def add_neutral_bones(self):
|
def add_neutral_bones(self):
|
||||||
added_armatures = []
|
added_armatures = []
|
||||||
for n in [n for n in self.nodes.values() if \
|
for n in [n for n in self.nodes.values() if \
|
||||||
@ -575,7 +625,7 @@ class VExportTree:
|
|||||||
|
|
||||||
# Be sure to add it to really exported meshes
|
# Be sure to add it to really exported meshes
|
||||||
if n.node.skin is None:
|
if n.node.skin is None:
|
||||||
print("WARNING: {} has no skin, skipping adding neutral bone data on it.".format(n.blender_object.name))
|
self.export_settings['log'].warning("{} has no skin, skipping adding neutral bone data on it.".format(n.blender_object.name))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if n.armature not in added_armatures:
|
if n.armature not in added_armatures:
|
||||||
@ -691,5 +741,5 @@ class VExportTree:
|
|||||||
if len(self.get_root_bones_uuid(arma_uuid)) > 1:
|
if len(self.get_root_bones_uuid(arma_uuid)) > 1:
|
||||||
# We can't remove armature
|
# We can't remove armature
|
||||||
self.export_settings['gltf_armature_object_remove'] = False
|
self.export_settings['gltf_armature_object_remove'] = False
|
||||||
print("WARNING: We can't remove armature object because some armatures have multiple root bones.")
|
self.export_settings['log'].warning("We can't remove armature object because some armatures have multiple root bones.")
|
||||||
break
|
break
|
||||||
|
@ -47,7 +47,7 @@ def gather_image(
|
|||||||
# In case we can't retrieve image (for example packed images, with original moved)
|
# In case we can't retrieve image (for example packed images, with original moved)
|
||||||
# We don't create invalid image without uri
|
# We don't create invalid image without uri
|
||||||
factor_uri = None
|
factor_uri = None
|
||||||
if uri is None: return None, None, None, False
|
if uri is None: return None, None, None, None
|
||||||
|
|
||||||
buffer_view, factor_buffer_view = __gather_buffer_view(image_data, mime_type, name, export_settings)
|
buffer_view, factor_buffer_view = __gather_buffer_view(image_data, mime_type, name, export_settings)
|
||||||
|
|
||||||
@ -340,8 +340,7 @@ def __get_image_data_mapping(sockets, results, use_tile, export_settings) -> Exp
|
|||||||
keys = list(composed_image.fills.keys()) # do not loop on dict, we may have to delete an element
|
keys = list(composed_image.fills.keys()) # do not loop on dict, we may have to delete an element
|
||||||
for k in [k for k in keys if isinstance(composed_image.fills[k], FillImage)]:
|
for k in [k for k in keys if isinstance(composed_image.fills[k], FillImage)]:
|
||||||
if composed_image.fills[k].image.size[0] == 0 or composed_image.fills[k].image.size[1] == 0:
|
if composed_image.fills[k].image.size[0] == 0 or composed_image.fills[k].image.size[1] == 0:
|
||||||
gltf2_io_debug.print_console("WARNING",
|
export_settings['log'].warning("Image '{}' has no size and cannot be exported.".format(
|
||||||
"Image '{}' has no size and cannot be exported.".format(
|
|
||||||
composed_image.fills[k].image))
|
composed_image.fills[k].image))
|
||||||
del composed_image.fills[k]
|
del composed_image.fills[k]
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import bpy
|
|||||||
from ....io.com import gltf2_io
|
from ....io.com import gltf2_io
|
||||||
from ....io.com.gltf2_io_extensions import Extension
|
from ....io.com.gltf2_io_extensions import Extension
|
||||||
from ....io.exp.gltf2_io_user_extensions import export_user_extensions
|
from ....io.exp.gltf2_io_user_extensions import export_user_extensions
|
||||||
from ....io.com.gltf2_io_debug import print_console
|
|
||||||
from ...com.gltf2_blender_extras import generate_extras
|
from ...com.gltf2_blender_extras import generate_extras
|
||||||
from ..gltf2_blender_gather_cache import cached, cached_by_key
|
from ..gltf2_blender_gather_cache import cached, cached_by_key
|
||||||
from . import gltf2_blender_gather_materials_unlit
|
from . import gltf2_blender_gather_materials_unlit
|
||||||
@ -328,9 +327,10 @@ def __gather_orm_texture(blender_material, export_settings):
|
|||||||
result = (occlusion, roughness_socket, metallic_socket)
|
result = (occlusion, roughness_socket, metallic_socket)
|
||||||
|
|
||||||
if not gltf2_blender_gather_texture_info.check_same_size_images(result, export_settings):
|
if not gltf2_blender_gather_texture_info.check_same_size_images(result, export_settings):
|
||||||
print_console("INFO",
|
export_settings['log'].info(
|
||||||
"Occlusion and metal-roughness texture will be exported separately "
|
"Occlusion and metal-roughness texture will be exported separately "
|
||||||
"(use same-sized images if you want them combined)")
|
"(use same-sized images if you want them combined)"
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Double-check this will past the filter in texture_info
|
# Double-check this will past the filter in texture_info
|
||||||
@ -508,7 +508,7 @@ def __get_final_material_with_indices(blender_material, base_material, caching_i
|
|||||||
elif tex.startswith("additional"):
|
elif tex.startswith("additional"):
|
||||||
export_settings['additional_texture_export'][export_settings['additional_texture_export_current_idx'] + int(tex[10:])].tex_coord = ind
|
export_settings['additional_texture_export'][export_settings['additional_texture_export_current_idx'] + int(tex[10:])].tex_coord = ind
|
||||||
else:
|
else:
|
||||||
print_console("ERROR", "some Textures tex coord are not managed")
|
export_settings['log'].error("some Textures tex coord are not managed")
|
||||||
|
|
||||||
export_settings['additional_texture_export_current_idx'] = len(export_settings['additional_texture_export'])
|
export_settings['additional_texture_export_current_idx'] = len(export_settings['additional_texture_export'])
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ def gather_texture(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if not __filter_texture(blender_shader_sockets, export_settings):
|
if not __filter_texture(blender_shader_sockets, export_settings):
|
||||||
return None, None, False
|
return None, None, None
|
||||||
|
|
||||||
source, webp_image, image_data, factor, udim_image = __gather_source(blender_shader_sockets, use_tile, export_settings)
|
source, webp_image, image_data, factor, udim_image = __gather_source(blender_shader_sockets, use_tile, export_settings)
|
||||||
|
|
||||||
@ -168,9 +168,10 @@ def __gather_name(blender_shader_sockets, export_settings):
|
|||||||
def __gather_sampler(blender_shader_sockets, export_settings):
|
def __gather_sampler(blender_shader_sockets, export_settings):
|
||||||
shader_nodes = [get_texture_node_from_socket(socket, export_settings) for socket in blender_shader_sockets]
|
shader_nodes = [get_texture_node_from_socket(socket, export_settings) for socket in blender_shader_sockets]
|
||||||
if len(shader_nodes) > 1:
|
if len(shader_nodes) > 1:
|
||||||
gltf2_io_debug.print_console("WARNING",
|
export_settings['log'].warning(
|
||||||
"More than one shader node tex image used for a texture. "
|
"More than one shader node tex image used for a texture. "
|
||||||
"The resulting glTF sampler will behave like the first shader node tex image.")
|
"The resulting glTF sampler will behave like the first shader node tex image."
|
||||||
|
)
|
||||||
first_valid_shader_node = next(filter(lambda x: x is not None, shader_nodes))
|
first_valid_shader_node = next(filter(lambda x: x is not None, shader_nodes))
|
||||||
|
|
||||||
# group_path can't be a list, so transform it to str
|
# group_path can't be a list, so transform it to str
|
||||||
|
@ -204,7 +204,7 @@ def __gather_texture_transform_and_tex_coord(primary_socket, export_settings):
|
|||||||
|
|
||||||
texture_transform = None
|
texture_transform = None
|
||||||
if node.node and node.node.type == 'MAPPING':
|
if node.node and node.node.type == 'MAPPING':
|
||||||
texture_transform = get_texture_transform_from_mapping_node(node)
|
texture_transform = get_texture_transform_from_mapping_node(node, export_settings)
|
||||||
node = previous_node(NodeSocket(node.node.inputs['Vector'], node.group_path))
|
node = previous_node(NodeSocket(node.node.inputs['Vector'], node.group_path))
|
||||||
|
|
||||||
uvmap_info = {}
|
uvmap_info = {}
|
||||||
|
@ -11,7 +11,6 @@ from mathutils import Vector, Matrix
|
|||||||
from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
|
from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
|
||||||
from ...com.gltf2_blender_material_helpers import get_gltf_node_name, get_gltf_node_old_name, get_gltf_old_group_node_name
|
from ...com.gltf2_blender_material_helpers import get_gltf_node_name, get_gltf_node_old_name, get_gltf_old_group_node_name
|
||||||
from ....blender.com.gltf2_blender_conversion import texture_transform_blender_to_gltf
|
from ....blender.com.gltf2_blender_conversion import texture_transform_blender_to_gltf
|
||||||
from io_scene_gltf2.io.com import gltf2_io_debug
|
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
|
|
||||||
@ -494,9 +493,9 @@ def previous_node(socket: NodeSocket):
|
|||||||
return ShNode(prev_socket.socket.node, prev_socket.group_path)
|
return ShNode(prev_socket.socket.node, prev_socket.group_path)
|
||||||
return ShNode(None, None)
|
return ShNode(None, None)
|
||||||
|
|
||||||
def get_texture_transform_from_mapping_node(mapping_node):
|
def get_texture_transform_from_mapping_node(mapping_node, export_settings):
|
||||||
if mapping_node.node.vector_type not in ["TEXTURE", "POINT", "VECTOR"]:
|
if mapping_node.node.vector_type not in ["TEXTURE", "POINT", "VECTOR"]:
|
||||||
gltf2_io_debug.print_console("WARNING",
|
export_settings['log'].warning(
|
||||||
"Skipping exporting texture transform because it had type " +
|
"Skipping exporting texture transform because it had type " +
|
||||||
mapping_node.node.vector_type + "; recommend using POINT instead"
|
mapping_node.node.vector_type + "; recommend using POINT instead"
|
||||||
)
|
)
|
||||||
@ -506,7 +505,7 @@ def get_texture_transform_from_mapping_node(mapping_node):
|
|||||||
rotation_0, rotation_1 = mapping_node.node.inputs['Rotation'].default_value[0], mapping_node.node.inputs['Rotation'].default_value[1]
|
rotation_0, rotation_1 = mapping_node.node.inputs['Rotation'].default_value[0], mapping_node.node.inputs['Rotation'].default_value[1]
|
||||||
if rotation_0 or rotation_1:
|
if rotation_0 or rotation_1:
|
||||||
# TODO: can we handle this?
|
# TODO: can we handle this?
|
||||||
gltf2_io_debug.print_console("WARNING",
|
export_settings['log'].warning(
|
||||||
"Skipping exporting texture transform because it had non-zero "
|
"Skipping exporting texture transform because it had non-zero "
|
||||||
"rotations in the X/Y direction; only a Z rotation can be exported!"
|
"rotations in the X/Y direction; only a Z rotation can be exported!"
|
||||||
)
|
)
|
||||||
@ -542,7 +541,7 @@ def get_texture_transform_from_mapping_node(mapping_node):
|
|||||||
|
|
||||||
mapping_transform = inverted(mapping_transform)
|
mapping_transform = inverted(mapping_transform)
|
||||||
if mapping_transform is None:
|
if mapping_transform is None:
|
||||||
gltf2_io_debug.print_console("WARNING",
|
export_settings['log'].warning(
|
||||||
"Skipping exporting texture transform with type TEXTURE because "
|
"Skipping exporting texture transform with type TEXTURE because "
|
||||||
"we couldn't convert it to TRS; recommend using POINT instead"
|
"we couldn't convert it to TRS; recommend using POINT instead"
|
||||||
)
|
)
|
||||||
|
@ -6,7 +6,6 @@ import bpy
|
|||||||
from mathutils import Matrix
|
from mathutils import Matrix
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from ...io.imp.gltf2_io_user_extensions import import_user_extensions
|
from ...io.imp.gltf2_io_user_extensions import import_user_extensions
|
||||||
from ...io.com.gltf2_io_debug import print_console
|
|
||||||
from ...io.imp.gltf2_io_binary import BinaryData
|
from ...io.imp.gltf2_io_binary import BinaryData
|
||||||
from ...io.com.gltf2_io_constants import DataType, ComponentType
|
from ...io.com.gltf2_io_constants import DataType, ComponentType
|
||||||
from ...blender.com.gltf2_blender_conversion import get_attribute_type
|
from ...blender.com.gltf2_blender_conversion import get_attribute_type
|
||||||
@ -157,7 +156,8 @@ def do_primitives(gltf, mesh_idx, skin_idx, mesh, ob):
|
|||||||
vert_index_base = len(vert_locs)
|
vert_index_base = len(vert_locs)
|
||||||
|
|
||||||
if prim.extensions is not None and 'KHR_draco_mesh_compression' in prim.extensions:
|
if prim.extensions is not None and 'KHR_draco_mesh_compression' in prim.extensions:
|
||||||
print_console('INFO', 'Draco Decoder: Decode primitive {}'.format(pymesh.name or '[unnamed]'))
|
|
||||||
|
gltf.log.info('Draco Decoder: Decode primitive {}'.format(pymesh.name or '[unnamed]'))
|
||||||
decode_primitive(gltf, prim)
|
decode_primitive(gltf, prim)
|
||||||
|
|
||||||
import_user_extensions('gather_import_decode_primitive', gltf, pymesh, prim, skin_idx)
|
import_user_extensions('gather_import_decode_primitive', gltf, pymesh, prim, skin_idx)
|
||||||
@ -319,7 +319,7 @@ def do_primitives(gltf, mesh_idx, skin_idx, mesh, ob):
|
|||||||
layer = mesh.uv_layers.new(name=name)
|
layer = mesh.uv_layers.new(name=name)
|
||||||
|
|
||||||
if layer is None:
|
if layer is None:
|
||||||
print("WARNING: UV map is ignored because the maximum number of UV layers has been reached.")
|
gltf.log.warning("WARNING: UV map is ignored because the maximum number of UV layers has been reached.")
|
||||||
break
|
break
|
||||||
|
|
||||||
layer.uv.foreach_set('vector', squish(loop_uvs[uv_i], np.float32))
|
layer.uv.foreach_set('vector', squish(loop_uvs[uv_i], np.float32))
|
||||||
@ -639,7 +639,7 @@ def skin_into_bind_pose(gltf, skin_idx, vert_joints, vert_weights, locs, vert_no
|
|||||||
# We set all weight ( aka 1.0 ) to the first bone
|
# We set all weight ( aka 1.0 ) to the first bone
|
||||||
zeros_indices = np.where(weight_sums == 0)[0]
|
zeros_indices = np.where(weight_sums == 0)[0]
|
||||||
if zeros_indices.shape[0] > 0:
|
if zeros_indices.shape[0] > 0:
|
||||||
print_console('ERROR', 'File is invalid: Some vertices are not assigned to bone(s) ')
|
gltf.log.error('File is invalid: Some vertices are not assigned to bone(s) ')
|
||||||
vert_weights[0][:, 0][zeros_indices] = 1.0 # Assign to first bone with all weight
|
vert_weights[0][:, 0][zeros_indices] = 1.0 # Assign to first bone with all weight
|
||||||
|
|
||||||
# Reprocess IBM for these vertices
|
# Reprocess IBM for these vertices
|
||||||
|
@ -269,7 +269,7 @@ class BlenderNode():
|
|||||||
if cache_key is not None and cache_key in pymesh.blender_name:
|
if cache_key is not None and cache_key in pymesh.blender_name:
|
||||||
mesh = bpy.data.meshes[pymesh.blender_name[cache_key]]
|
mesh = bpy.data.meshes[pymesh.blender_name[cache_key]]
|
||||||
else:
|
else:
|
||||||
gltf.log.info("Blender create Mesh node %s", pymesh.name or pynode.mesh)
|
gltf.log.info("Blender create Mesh node {}".format(pymesh.name or pynode.mesh))
|
||||||
mesh = BlenderMesh.create(gltf, pynode.mesh, pynode.skin)
|
mesh = BlenderMesh.create(gltf, pynode.mesh, pynode.skin)
|
||||||
if cache_key is not None:
|
if cache_key is not None:
|
||||||
pymesh.blender_name[cache_key] = mesh.name
|
pymesh.blender_name[cache_key] = mesh.name
|
||||||
|
@ -6,7 +6,6 @@ from ctypes import *
|
|||||||
|
|
||||||
from ...io.com.gltf2_io import BufferView
|
from ...io.com.gltf2_io import BufferView
|
||||||
from ...io.imp.gltf2_io_binary import BinaryData
|
from ...io.imp.gltf2_io_binary import BinaryData
|
||||||
from ...io.com.gltf2_io_debug import print_console
|
|
||||||
from ...io.com.gltf2_io_draco_compression_extension import dll_path
|
from ...io.com.gltf2_io_draco_compression_extension import dll_path
|
||||||
|
|
||||||
|
|
||||||
@ -63,7 +62,7 @@ def decode_primitive(gltf, prim):
|
|||||||
# Create Draco decoder.
|
# Create Draco decoder.
|
||||||
draco_buffer = bytes(BinaryData.get_buffer_view(gltf, extension['bufferView']))
|
draco_buffer = bytes(BinaryData.get_buffer_view(gltf, extension['bufferView']))
|
||||||
if not dll.decoderDecode(decoder, draco_buffer, len(draco_buffer)):
|
if not dll.decoderDecode(decoder, draco_buffer, len(draco_buffer)):
|
||||||
print_console('ERROR', 'Draco Decoder: Unable to decode. Skipping primitive {}.'.format(name))
|
gltf.log.error('Draco Decoder: Unable to decode. Skipping primitive {}.'.format(name))
|
||||||
return
|
return
|
||||||
|
|
||||||
# Choose a buffer index which does not yet exist, skipping over existing glTF buffers yet to be loaded
|
# Choose a buffer index which does not yet exist, skipping over existing glTF buffers yet to be loaded
|
||||||
@ -76,10 +75,10 @@ def decode_primitive(gltf, prim):
|
|||||||
# Read indices.
|
# Read indices.
|
||||||
index_accessor = gltf.data.accessors[prim.indices]
|
index_accessor = gltf.data.accessors[prim.indices]
|
||||||
if dll.decoderGetIndexCount(decoder) != index_accessor.count:
|
if dll.decoderGetIndexCount(decoder) != index_accessor.count:
|
||||||
print_console('WARNING', 'Draco Decoder: Index count of accessor and decoded index count does not match. Updating accessor.')
|
gltf.log.warning('Draco Decoder: Index count of accessor and decoded index count does not match. Updating accessor.')
|
||||||
index_accessor.count = dll.decoderGetIndexCount(decoder)
|
index_accessor.count = dll.decoderGetIndexCount(decoder)
|
||||||
if not dll.decoderReadIndices(decoder, index_accessor.component_type):
|
if not dll.decoderReadIndices(decoder, index_accessor.component_type):
|
||||||
print_console('ERROR', 'Draco Decoder: Unable to decode indices. Skipping primitive {}.'.format(name))
|
gltf.log.error('Draco Decoder: Unable to decode indices. Skipping primitive {}.'.format(name))
|
||||||
return
|
return
|
||||||
|
|
||||||
indices_byte_length = dll.decoderGetIndicesByteLength(decoder)
|
indices_byte_length = dll.decoderGetIndicesByteLength(decoder)
|
||||||
@ -102,15 +101,15 @@ def decode_primitive(gltf, prim):
|
|||||||
for attr_idx, attr in enumerate(extension['attributes']):
|
for attr_idx, attr in enumerate(extension['attributes']):
|
||||||
dracoId = extension['attributes'][attr]
|
dracoId = extension['attributes'][attr]
|
||||||
if attr not in prim.attributes:
|
if attr not in prim.attributes:
|
||||||
print_console('ERROR', 'Draco Decoder: Draco attribute {} not in primitive attributes. Skipping primitive {}.'.format(attr, name))
|
gltf.log.error('Draco Decoder: Draco attribute {} not in primitive attributes. Skipping primitive {}.'.format(attr, name))
|
||||||
return
|
return
|
||||||
|
|
||||||
accessor = gltf.data.accessors[prim.attributes[attr]]
|
accessor = gltf.data.accessors[prim.attributes[attr]]
|
||||||
if dll.decoderGetVertexCount(decoder) != accessor.count:
|
if dll.decoderGetVertexCount(decoder) != accessor.count:
|
||||||
print_console('WARNING', 'Draco Decoder: Vertex count of accessor and decoded vertex count does not match for attribute {}. Updating accessor.'.format(attr, name))
|
gltf.log.warning('Draco Decoder: Vertex count of accessor and decoded vertex count does not match for attribute {}. Updating accessor.'.format(attr, name))
|
||||||
accessor.count = dll.decoderGetVertexCount(decoder)
|
accessor.count = dll.decoderGetVertexCount(decoder)
|
||||||
if not dll.decoderReadAttribute(decoder, dracoId, accessor.component_type, accessor.type.encode()):
|
if not dll.decoderReadAttribute(decoder, dracoId, accessor.component_type, accessor.type.encode()):
|
||||||
print_console('ERROR', 'Draco Decoder: Could not decode attribute {}. Skipping primitive {}.'.format(attr, name))
|
gltf.log.error('Draco Decoder: Could not decode attribute {}. Skipping primitive {}.'.format(attr, name))
|
||||||
return
|
return
|
||||||
|
|
||||||
byte_length = dll.decoderGetAttributeByteLength(decoder, dracoId)
|
byte_length = dll.decoderGetAttributeByteLength(decoder, dracoId)
|
||||||
|
@ -42,7 +42,7 @@ def from_union(fs, x):
|
|||||||
tb_info = traceback.extract_tb(tb)
|
tb_info = traceback.extract_tb(tb)
|
||||||
for tbi in tb_info:
|
for tbi in tb_info:
|
||||||
filename, line, func, text = tbi
|
filename, line, func, text = tbi
|
||||||
gltf2_io_debug.print_console('ERROR', 'An error occurred on line {} in statement {}'.format(line, text))
|
print('ERROR', 'An error occurred on line {} in statement {}'.format(line, text))
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,48 +8,17 @@
|
|||||||
|
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
import logging.handlers
|
||||||
|
|
||||||
#
|
#
|
||||||
# Globals
|
# Globals
|
||||||
#
|
#
|
||||||
|
|
||||||
OUTPUT_LEVELS = ['ERROR', 'WARNING', 'INFO', 'PROFILE', 'DEBUG', 'VERBOSE']
|
|
||||||
|
|
||||||
g_current_output_level = 'DEBUG'
|
|
||||||
g_profile_started = False
|
g_profile_started = False
|
||||||
g_profile_start = 0.0
|
g_profile_start = 0.0
|
||||||
g_profile_end = 0.0
|
g_profile_end = 0.0
|
||||||
g_profile_delta = 0.0
|
g_profile_delta = 0.0
|
||||||
|
|
||||||
#
|
|
||||||
# Functions
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
def set_output_level(level):
|
|
||||||
"""Set an output debug level."""
|
|
||||||
global g_current_output_level
|
|
||||||
|
|
||||||
if OUTPUT_LEVELS.index(level) < 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
g_current_output_level = level
|
|
||||||
|
|
||||||
|
|
||||||
def print_console(level, output):
|
|
||||||
"""Print to Blender console with a given header and output."""
|
|
||||||
global OUTPUT_LEVELS
|
|
||||||
global g_current_output_level
|
|
||||||
|
|
||||||
if OUTPUT_LEVELS.index(level) > OUTPUT_LEVELS.index(g_current_output_level):
|
|
||||||
return
|
|
||||||
|
|
||||||
print(get_timestamp() + " | " + level + ': ' + output)
|
|
||||||
|
|
||||||
|
|
||||||
def print_newline():
|
|
||||||
"""Print a new line to Blender console."""
|
|
||||||
print()
|
|
||||||
|
|
||||||
|
|
||||||
def get_timestamp():
|
def get_timestamp():
|
||||||
@ -57,23 +26,13 @@ def get_timestamp():
|
|||||||
return time.strftime("%H:%M:%S", current_time)
|
return time.strftime("%H:%M:%S", current_time)
|
||||||
|
|
||||||
|
|
||||||
def print_timestamp(label=None):
|
|
||||||
"""Print a timestamp to Blender console."""
|
|
||||||
output = 'Timestamp: ' + get_timestamp()
|
|
||||||
|
|
||||||
if label is not None:
|
|
||||||
output = output + ' (' + label + ')'
|
|
||||||
|
|
||||||
print_console('PROFILE', output)
|
|
||||||
|
|
||||||
|
|
||||||
def profile_start():
|
def profile_start():
|
||||||
"""Start profiling by storing the current time."""
|
"""Start profiling by storing the current time."""
|
||||||
global g_profile_start
|
global g_profile_start
|
||||||
global g_profile_started
|
global g_profile_started
|
||||||
|
|
||||||
if g_profile_started:
|
if g_profile_started:
|
||||||
print_console('ERROR', 'Profiling already started')
|
print('ERROR', 'Profiling already started')
|
||||||
return
|
return
|
||||||
|
|
||||||
g_profile_started = True
|
g_profile_started = True
|
||||||
@ -88,7 +47,7 @@ def profile_end(label=None):
|
|||||||
global g_profile_started
|
global g_profile_started
|
||||||
|
|
||||||
if not g_profile_started:
|
if not g_profile_started:
|
||||||
print_console('ERROR', 'Profiling not started')
|
print('ERROR', 'Profiling not started')
|
||||||
return
|
return
|
||||||
|
|
||||||
g_profile_started = False
|
g_profile_started = False
|
||||||
@ -101,16 +60,60 @@ def profile_end(label=None):
|
|||||||
if label is not None:
|
if label is not None:
|
||||||
output = output + ' (' + label + ')'
|
output = output + ' (' + label + ')'
|
||||||
|
|
||||||
print_console('PROFILE', output)
|
print('PROFILE', output)
|
||||||
|
|
||||||
|
|
||||||
# TODO: need to have a unique system for logging importer/exporter
|
|
||||||
# TODO: this logger is used for importer, but in io and in blender part, but is written here in a _io_ file
|
|
||||||
class Log:
|
class Log:
|
||||||
def __init__(self, loglevel):
|
def __init__(self, loglevel):
|
||||||
self.logger = logging.getLogger('glTFImporter')
|
self.logger = logging.getLogger('glTFImporter')
|
||||||
self.hdlr = logging.StreamHandler()
|
|
||||||
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
|
# For console display
|
||||||
self.hdlr.setFormatter(formatter)
|
self.console_handler = logging.StreamHandler()
|
||||||
self.logger.addHandler(self.hdlr)
|
formatter = logging.Formatter('%(asctime)s | %(levelname)s: %(message)s', "%H:%M:%S")
|
||||||
|
self.console_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
# For popup display
|
||||||
|
self.popup_handler = logging.handlers.MemoryHandler(1024*10)
|
||||||
|
|
||||||
|
self.logger.addHandler(self.console_handler)
|
||||||
|
#self.logger.addHandler(self.popup_handler) => Make sure to not attach the popup handler to the logger
|
||||||
|
|
||||||
self.logger.setLevel(int(loglevel))
|
self.logger.setLevel(int(loglevel))
|
||||||
|
|
||||||
|
def error(self, message, popup=False):
|
||||||
|
self.logger.error(message)
|
||||||
|
if popup:
|
||||||
|
self.popup_handler.buffer.append(('ERROR', message))
|
||||||
|
|
||||||
|
def warning(self, message, popup=False):
|
||||||
|
self.logger.warning(message)
|
||||||
|
if popup:
|
||||||
|
self.popup_handler.buffer.append(('WARNING', message))
|
||||||
|
|
||||||
|
def info(self, message, popup=False):
|
||||||
|
self.logger.info(message)
|
||||||
|
if popup:
|
||||||
|
self.popup_handler.buffer.append(('INFO', message))
|
||||||
|
|
||||||
|
def debug(self, message, popup=False):
|
||||||
|
self.logger.debug(message)
|
||||||
|
if popup:
|
||||||
|
self.popup_handler.buffer.append(('DEBUG', message))
|
||||||
|
|
||||||
|
def critical(self, message, popup=False):
|
||||||
|
self.logger.critical(message)
|
||||||
|
if popup:
|
||||||
|
self.popup_handler.buffer.append(('ERROR', message)) # There is no Critical level in Blender, so we use error
|
||||||
|
|
||||||
|
def profile(self, message, popup=False): # There is no profile level in logging, so we use info
|
||||||
|
self.logger.info(message)
|
||||||
|
if popup:
|
||||||
|
self.popup_handler.buffer.append(('PROFILE', message))
|
||||||
|
|
||||||
|
def messages(self):
|
||||||
|
return self.popup_handler.buffer
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
self.logger.removeHandler(self.console_handler)
|
||||||
|
self.popup_handler.flush()
|
||||||
|
self.logger.removeHandler(self.popup_handler)
|
||||||
|
@ -7,9 +7,6 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
from ...io.com.gltf2_io_debug import print_console
|
|
||||||
|
|
||||||
|
|
||||||
def dll_path() -> Path:
|
def dll_path() -> Path:
|
||||||
"""
|
"""
|
||||||
Get the DLL path depending on the underlying platform.
|
Get the DLL path depending on the underlying platform.
|
||||||
@ -37,7 +34,7 @@ def dll_path() -> Path:
|
|||||||
}.get(sys.platform)
|
}.get(sys.platform)
|
||||||
|
|
||||||
if path is None or library_name is None:
|
if path is None or library_name is None:
|
||||||
print_console('WARNING', 'Unsupported platform {}, Draco mesh compression is unavailable'.format(sys.platform))
|
print('WARNING', 'Unsupported platform {}, Draco mesh compression is unavailable'.format(sys.platform))
|
||||||
|
|
||||||
return path / library_name
|
return path / library_name
|
||||||
|
|
||||||
@ -51,7 +48,7 @@ def dll_exists(quiet=False) -> bool:
|
|||||||
exists = path.exists() and path.is_file()
|
exists = path.exists() and path.is_file()
|
||||||
if quiet is False:
|
if quiet is False:
|
||||||
if exists:
|
if exists:
|
||||||
print_console('INFO', 'Draco mesh compression is available, use library at %s' % dll_path().absolute())
|
print('INFO', 'Draco mesh compression is available, use library at %s' % dll_path().absolute())
|
||||||
else:
|
else:
|
||||||
print_console('ERROR', 'Draco mesh compression is not available because library could not be found at %s' % dll_path().absolute())
|
print('ERROR', 'Draco mesh compression is not available because library could not be found at %s' % dll_path().absolute())
|
||||||
return exists
|
return exists
|
||||||
|
@ -6,7 +6,6 @@ from ctypes import *
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from ...io.exp.gltf2_io_binary_data import BinaryData
|
from ...io.exp.gltf2_io_binary_data import BinaryData
|
||||||
from ...io.com.gltf2_io_debug import print_console
|
|
||||||
from ...io.com.gltf2_io_draco_compression_extension import dll_path
|
from ...io.com.gltf2_io_draco_compression_extension import dll_path
|
||||||
|
|
||||||
|
|
||||||
@ -90,7 +89,7 @@ def __traverse_node(node, f):
|
|||||||
|
|
||||||
def __encode_node(node, dll, export_settings, encoded_primitives_cache):
|
def __encode_node(node, dll, export_settings, encoded_primitives_cache):
|
||||||
if node.mesh is not None:
|
if node.mesh is not None:
|
||||||
print_console('INFO', 'Draco encoder: Encoding mesh {}.'.format(node.name))
|
export_settings['log'].info('Draco encoder: Encoding mesh {}.'.format(node.name))
|
||||||
for primitive in node.mesh.primitives:
|
for primitive in node.mesh.primitives:
|
||||||
__encode_primitive(primitive, dll, export_settings, encoded_primitives_cache)
|
__encode_primitive(primitive, dll, export_settings, encoded_primitives_cache)
|
||||||
|
|
||||||
@ -112,7 +111,7 @@ def __encode_primitive(primitive, dll, export_settings, encoded_primitives_cache
|
|||||||
return
|
return
|
||||||
|
|
||||||
if 'POSITION' not in attributes:
|
if 'POSITION' not in attributes:
|
||||||
print_console('WARNING', 'Draco encoder: Primitive without positions encountered. Skipping.')
|
export_settings['log'].warning('Draco encoder: Primitive without positions encountered. Skipping.')
|
||||||
return
|
return
|
||||||
|
|
||||||
positions = attributes['POSITION']
|
positions = attributes['POSITION']
|
||||||
@ -141,7 +140,7 @@ def __encode_primitive(primitive, dll, export_settings, encoded_primitives_cache
|
|||||||
|
|
||||||
preserve_triangle_order = primitive.targets is not None and len(primitive.targets) > 0
|
preserve_triangle_order = primitive.targets is not None and len(primitive.targets) > 0
|
||||||
if not dll.encoderEncode(encoder, preserve_triangle_order):
|
if not dll.encoderEncode(encoder, preserve_triangle_order):
|
||||||
print_console('ERROR', 'Could not encode primitive. Skipping primitive.')
|
export_settings['log'].error('Could not encode primitive. Skipping primitive.')
|
||||||
|
|
||||||
byte_length = dll.encoderGetByteLength(encoder)
|
byte_length = dll.encoderGetByteLength(encoder)
|
||||||
encoded_data = bytes(byte_length)
|
encoded_data = bytes(byte_length)
|
||||||
|
@ -13,5 +13,6 @@ def export_user_extensions(hook_name, export_settings, *args):
|
|||||||
try:
|
try:
|
||||||
hook(*args, export_settings)
|
hook(*args, export_settings)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(hook_name, "fails on", extension)
|
export_settings['log'].error("Extension hook", hook_name, "fails on", extension)
|
||||||
print(str(e))
|
export_settings['log'].error(str(e))
|
||||||
|
|
||||||
|
@ -32,11 +32,9 @@ class glTFImporter():
|
|||||||
self.variant_mapping = {} # Used to map between mgltf material idx and blender material, for Variants
|
self.variant_mapping = {} # Used to map between mgltf material idx and blender material, for Variants
|
||||||
|
|
||||||
if 'loglevel' not in self.import_settings.keys():
|
if 'loglevel' not in self.import_settings.keys():
|
||||||
self.import_settings['loglevel'] = logging.ERROR
|
self.import_settings['loglevel'] = logging.CRITICAL
|
||||||
|
|
||||||
log = Log(import_settings['loglevel'])
|
self.log = Log(import_settings['loglevel'])
|
||||||
self.log = log.logger
|
|
||||||
self.log_handler = log.hdlr
|
|
||||||
|
|
||||||
# TODO: move to a com place?
|
# TODO: move to a com place?
|
||||||
self.extensions_managed = [
|
self.extensions_managed = [
|
||||||
|
@ -9,5 +9,5 @@ def import_user_extensions(hook_name, gltf, *args):
|
|||||||
try:
|
try:
|
||||||
hook(*args, gltf)
|
hook(*args, gltf)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(hook_name, "fails on", extension)
|
gltf.log.error(hook_name, "fails on", extension)
|
||||||
print(str(e))
|
gltf.log.error(str(e))
|
||||||
|
@ -417,7 +417,7 @@ def export(file,
|
|||||||
|
|
||||||
location = matrix.to_translation()[:]
|
location = matrix.to_translation()[:]
|
||||||
|
|
||||||
radius = lamp.distance * math.cos(beamWidth)
|
radius = lamp.cutoff_distance * math.cos(beamWidth)
|
||||||
# radius = lamp.dist*math.cos(beamWidth)
|
# radius = lamp.dist*math.cos(beamWidth)
|
||||||
ident_step = ident + (' ' * (-len(ident) + \
|
ident_step = ident + (' ' * (-len(ident) + \
|
||||||
fw('%s<SpotLight ' % ident)))
|
fw('%s<SpotLight ' % ident)))
|
||||||
@ -479,7 +479,7 @@ def export(file,
|
|||||||
fw(ident_step + 'color="%.4f %.4f %.4f"\n' % clamp_color(light.color))
|
fw(ident_step + 'color="%.4f %.4f %.4f"\n' % clamp_color(light.color))
|
||||||
|
|
||||||
fw(ident_step + 'intensity="%.4f"\n' % intensity)
|
fw(ident_step + 'intensity="%.4f"\n' % intensity)
|
||||||
fw(ident_step + 'radius="%.4f" \n' % light.distance)
|
fw(ident_step + 'radius="%.4f" \n' % light.cutoff_distance)
|
||||||
fw(ident_step + 'location="%.4f %.4f %.4f"\n' % location)
|
fw(ident_step + 'location="%.4f %.4f %.4f"\n' % location)
|
||||||
fw(ident_step + '/>\n')
|
fw(ident_step + '/>\n')
|
||||||
|
|
||||||
|
@ -3171,7 +3171,7 @@ def importLamp_PointLight(node, ancestry):
|
|||||||
|
|
||||||
bpylamp = bpy.data.lights.new(vrmlname, 'POINT')
|
bpylamp = bpy.data.lights.new(vrmlname, 'POINT')
|
||||||
bpylamp.energy = intensity
|
bpylamp.energy = intensity
|
||||||
bpylamp.distance = radius
|
bpylamp.cutoff_distance = radius
|
||||||
bpylamp.color = color
|
bpylamp.color = color
|
||||||
|
|
||||||
mtx = Matrix.Translation(Vector(location))
|
mtx = Matrix.Translation(Vector(location))
|
||||||
@ -3220,7 +3220,7 @@ def importLamp_SpotLight(node, ancestry):
|
|||||||
|
|
||||||
bpylamp = bpy.data.lights.new(vrmlname, 'SPOT')
|
bpylamp = bpy.data.lights.new(vrmlname, 'SPOT')
|
||||||
bpylamp.energy = intensity
|
bpylamp.energy = intensity
|
||||||
bpylamp.distance = radius
|
bpylamp.cutoff_distance = radius
|
||||||
bpylamp.color = color
|
bpylamp.color = color
|
||||||
bpylamp.spot_size = cutOffAngle
|
bpylamp.spot_size = cutOffAngle
|
||||||
if beamWidth > cutOffAngle:
|
if beamWidth > cutOffAngle:
|
||||||
|
@ -1839,6 +1839,7 @@ class NWAddPrincipledSetup(Operator, NWBase, ImportHelper):
|
|||||||
# If Bump add bump node in between
|
# If Bump add bump node in between
|
||||||
bump_node_texture = nodes.new(type='ShaderNodeTexImage')
|
bump_node_texture = nodes.new(type='ShaderNodeTexImage')
|
||||||
img = bpy.data.images.load(path.join(import_path, sname[2]))
|
img = bpy.data.images.load(path.join(import_path, sname[2]))
|
||||||
|
img.colorspace_settings.is_data = True
|
||||||
bump_node_texture.image = img
|
bump_node_texture.image = img
|
||||||
bump_node_texture.label = 'Bump'
|
bump_node_texture.label = 'Bump'
|
||||||
|
|
||||||
@ -1857,6 +1858,7 @@ class NWAddPrincipledSetup(Operator, NWBase, ImportHelper):
|
|||||||
# If Normal add normal node in between
|
# If Normal add normal node in between
|
||||||
normal_node_texture = nodes.new(type='ShaderNodeTexImage')
|
normal_node_texture = nodes.new(type='ShaderNodeTexImage')
|
||||||
img = bpy.data.images.load(path.join(import_path, sname[2]))
|
img = bpy.data.images.load(path.join(import_path, sname[2]))
|
||||||
|
img.colorspace_settings.is_data = True
|
||||||
normal_node_texture.image = img
|
normal_node_texture.image = img
|
||||||
normal_node_texture.label = 'Normal'
|
normal_node_texture.label = 'Normal'
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user