Skip to content

Commit

Permalink
initial commit for blender 2.80 support
Browse files Browse the repository at this point in the history
  • Loading branch information
Jason0214 committed Dec 17, 2018
1 parent 66f3d73 commit 3f89937
Show file tree
Hide file tree
Showing 101 changed files with 823 additions and 1,016 deletions.
2 changes: 1 addition & 1 deletion .pylintrc
Expand Up @@ -165,7 +165,7 @@ expected-line-ending-format=
[MISCELLANEOUS]

# List of note tags to take in consideration, separated by a comma.
notes=FIXME,XXX,TODO
notes=


[SIMILARITIES]
Expand Down
9 changes: 6 additions & 3 deletions .travis.yml
@@ -1,18 +1,21 @@
dist: xenial
language: python
python:
- 3.4
- 3.6

cache:
directories:
- $HOME/.blender-cache

before_install:
- sudo apt-get update
- sudo apt-get install --no-install-recommends -y libsdl1.2debian python3-pip
- sudo apt-get install --no-install-recommends -y libsdl1.2debian libglu1 python3-pip
- sudo pip3 install --upgrade pip
- sudo pip3 install --upgrade setuptools
- sudo pip3 install -r requirements.txt

install:
- tests/install_blender.sh
- bash tests/install_blender.sh
- source .envs

script:
Expand Down
4 changes: 2 additions & 2 deletions Makefile
@@ -1,5 +1,5 @@
PYLINT = pylint3
PEP8 = pep8
PYLINT = pylint
PEP8 = pycodestyle
BLENDER = blender
GODOT = godot

Expand Down
12 changes: 4 additions & 8 deletions README.md
Expand Up @@ -32,14 +32,10 @@ This repository includes a Makefile to assist with development. Running
be elegible to merge.


Due to differences in blender versions creating minor differences in the
output files (even with the same blender release number), the regression tests
are best run with blender 2.79 downloaded from
[this exact url](http://mirror.cs.umn.edu/blender.org/release/Blender2.79/),
which is used for the Travis builds. If you think your blender version is
adequate, the hash (visble in the upper right of blender's splash screen)
should be `5bd8ac9abfa`. The exporter itself should run on all modern versions
of blender, but the output may differ slightly.
Current regression tests use the daily build of blender 2.80 from blender [official
site](https://builder.blender.org/download/) and runs on ubuntu 16.04. If you run
on different blender version or different platform, the output may differ slightly
mostly causing by different float precision.


## License
Expand Down
27 changes: 12 additions & 15 deletions io_scene_godot/__init__.py
Expand Up @@ -27,8 +27,7 @@
bl_info = { # pylint: disable=invalid-name
"name": "Godot Engine Exporter",
"author": "Juan Linietsky",
"blender": (2, 5, 8),
"api": 38691,
"blender": (2, 80, 0),
"location": "File > Import-Export",
"description": ("Export Godot Scenes to a format that can be efficiently "
"imported. "),
Expand Down Expand Up @@ -57,15 +56,15 @@ class ExportGodot(bpy.types.Operator, ExportHelper):
items=(
("EMPTY", "Empty", ""),
("CAMERA", "Camera", ""),
("LAMP", "Lamp", ""),
("LIGHT", "LIGHT", ""),
("ARMATURE", "Armature", ""),
("MESH", "Mesh", ""),
# ("CURVE", "Curve", ""),
),
default={
"EMPTY",
"CAMERA",
"LAMP",
"LIGHT",
"ARMATURE",
"MESH",
# "CURVE"
Expand All @@ -79,8 +78,7 @@ class ExportGodot(bpy.types.Operator, ExportHelper):
)
use_export_selected = BoolProperty(
name="Selected Objects",
description="Export only selected objects (and visible in active "
"layers if that applies).",
description="Export only selected objects",
default=False,
)
use_exclude_ctrl_bone = BoolProperty(
Expand All @@ -101,9 +99,10 @@ class ExportGodot(bpy.types.Operator, ExportHelper):
description="Apply modifiers to mesh objects (on a copy!).",
default=True,
)
use_active_layers = BoolProperty(
name="Active Layers",
description="Export only objects on the active layers.",
use_visible_objects = BoolProperty(
name="Visible Object",
description="Export only objects which are in the current view layer "
"and are visible.",
default=True,
)
use_stashed_action = BoolProperty(
Expand Down Expand Up @@ -201,16 +200,14 @@ def menu_func(self, context):

def register():
"""Add addon to blender"""
bpy.utils.register_module(__name__)

bpy.types.INFO_MT_file_export.append(menu_func)
bpy.utils.register_class(ExportGodot)
bpy.types.TOPBAR_MT_file_export.append(menu_func)


def unregister():
"""Remove addon from blender"""
bpy.utils.unregister_module(__name__)

bpy.types.INFO_MT_file_export.remove(menu_func)
bpy.utils.unregister_class(ExportGodot)
bpy.types.TOPBAR_MT_file_export.remove(menu_func)


def export(filename, overrides=None):
Expand Down
2 changes: 1 addition & 1 deletion io_scene_godot/converters/__init__.py
Expand Up @@ -26,7 +26,7 @@
"MESH": export_mesh_node,
"ARMATURE": export_armature_node,
"CAMERA": export_camera_node,
"LAMP": export_lamp_node,
"LIGHT": export_light_node,
"EMPTY": export_empty_node
}

Expand Down
6 changes: 0 additions & 6 deletions io_scene_godot/converters/animation/action.py
Expand Up @@ -247,12 +247,6 @@ def export_light_action(light_node, animation_player, blender_lamp,
)

fcurves = action_strip.action.fcurves
animation_resource.add_attribute_track(
action_strip,
fcurves.find('use_negative'),
lambda x: x > 0.0,
base_node_path.new_copy('light_negative'),
)

animation_resource.add_attribute_track(
action_strip,
Expand Down
23 changes: 3 additions & 20 deletions io_scene_godot/converters/animation/animation_data.py
Expand Up @@ -38,8 +38,6 @@ def __init__(self, godot_node, blender_object, action_type):
self.action_exporter_func = ACTION_EXPORTER_MAP[action_type]
self.animation_player = None

self.has_object_constraint = False
self.has_pose_constraint = False
self.need_baking = False

self.unmute_nla_tracks = []
Expand All @@ -56,8 +54,6 @@ def check_baking_condition(self, action_type):
self.need_baking = (
action_type == 'transform' and (has_obj_cst or has_pose_cst)
)
self.has_object_constraint = has_obj_cst
self.has_pose_constraint = has_pose_cst

def preprocess_nla_tracks(self, blender_object):
"""Iterative through nla tracks, separately store mute and unmuted
Expand All @@ -77,22 +73,9 @@ def bake_to_new_action(self, action_to_bake):
Note that it accept a action to bake (which would not be modified)
and always return a new created baked actiony"""
if self.has_object_constraint and self.has_pose_constraint:
tmp = bake_constraint_to_action(
self.blender_object, action_to_bake, "OBJECT", False
)
ret = bake_constraint_to_action(
self.blender_object, action_to_bake, "POSE", True
)
elif self.has_pose_constraint:
ret = bake_constraint_to_action(
self.blender_object, action_to_bake, "POSE", False
)
elif self.has_object_constraint:
ret = bake_constraint_to_action(
self.blender_object, action_to_bake, "OBJECT", False
)
return ret
return bake_constraint_to_action(
self.blender_object, action_to_bake, False
)

def export_active_action(self, escn_file, active_action):
"""Export the active action, if needed, would call bake.
Expand Down
51 changes: 14 additions & 37 deletions io_scene_godot/converters/animation/constraint_baking.py
Expand Up @@ -24,7 +24,7 @@ def action_baking_finalize(action):
def check_object_constraint(blender_object):
"""Return bool indicate if object has constraint"""
if isinstance(blender_object, bpy.types.Object):
return True if blender_object.constraints else False
return bool(blender_object.constraints)
return False


Expand All @@ -38,14 +38,15 @@ def check_pose_constraint(blender_object):
return False


def bake_constraint_to_action(blender_object, base_action, bake_type,
in_place):
def bake_constraint_to_action(blender_object, base_action, in_place):
"""Bake pose or object constrainst (e.g. IK) to action"""
if base_action is not None:
blender_object.animation_data.action = base_action
frame_range = tuple([int(x) for x in base_action.frame_range])
frame_range = range(
int(base_action.frame_range[0]),
int(base_action.frame_range[1]) + 1)
else:
frame_range = (1, 250) # default, can be improved
frame_range = range(1, 251) # default, can be improved

# if action_bake_into is None, it would create a new one
# and baked into it
Expand All @@ -54,38 +55,14 @@ def bake_constraint_to_action(blender_object, base_action, bake_type,
else:
action_bake_into = None

do_pose = bake_type == "POSE"
do_object = not do_pose

if bpy.app.version <= (2, 79, 0):
active_obj_backup = bpy.context.scene.objects.active

# the object to bake is the current active object
bpy.context.scene.objects.active = blender_object
baked_action = bpy_extras.anim_utils.bake_action(
frame_start=frame_range[0],
frame_end=frame_range[1],
frame_step=1,
only_selected=False,
action=action_bake_into,
do_pose=do_pose,
do_object=do_object,
do_visual_keying=True,
)

bpy.context.scene.objects.active = active_obj_backup
else:
baked_action = bpy_extras.anim_utils.bake_action(
obj=blender_object,
frame_start=frame_range[0],
frame_end=frame_range[1],
frame_step=1,
only_selected=False,
action=action_bake_into,
do_pose=do_pose,
do_object=do_object,
do_visual_keying=True,
)
baked_action = bpy_extras.anim_utils.bake_action_objects(
object_action_pairs=[(blender_object, action_bake_into)],
frames=frame_range,
only_selected=False,
do_pose=True,
do_object=True,
do_visual_keying=True,
)[0]

if in_place:
return action_bake_into
Expand Down
12 changes: 6 additions & 6 deletions io_scene_godot/converters/animation/serializer.py
Expand Up @@ -75,7 +75,7 @@ def factory(cls, trans_mat, rotation_mode):
"""Factory function, create cls from a transform matrix"""
ret = cls()
ret.location = trans_mat.to_translation()
# fixme: lose negative scale
# FIXME: lose negative scale
ret.scale = trans_mat.to_scale()

# quaternion and euler fcurves may both exist in fcurves
Expand Down Expand Up @@ -112,7 +112,7 @@ def to_matrix(self):
(0, self.scale[1], 0),
(0, 0, self.scale[2]),
)).to_4x4()
return loc_mat * rot_mat * sca_mat
return loc_mat @ rot_mat @ sca_mat


class Track:
Expand Down Expand Up @@ -255,7 +255,7 @@ def set_parent_inverse(self, parent_inverse):

def blend_frames(self, frame_val1, frame_val2):
"""Blend two transform frames into one"""
# fixme: currently only blend with ADD
# FIXME: currently only blend with ADD
new_frame = TransformFrame()
for frame in (frame_val1, frame_val2):
if frame.rotation_mode != 'QUATERNION':
Expand All @@ -264,7 +264,7 @@ def blend_frames(self, frame_val1, frame_val2):
)

new_frame.rotation_quaternion = (
frame_val1.rotation_quaternion * frame_val2.rotation_quaternion
frame_val1.rotation_quaternion @ frame_val2.rotation_quaternion
)

new_frame.location = frame_val1.location + frame_val2.location
Expand All @@ -291,7 +291,7 @@ def convert_to_keys_object(self):
if frame < scene_frame_start:
continue

mat = self.parent_trans_inverse * trans_frame.to_matrix()
mat = self.parent_trans_inverse @ trans_frame.to_matrix()
if self.is_directional:
mat = fix_directional_transform(mat)
# convert from z-up to y-up
Expand Down Expand Up @@ -326,7 +326,7 @@ def __init__(self, track_path, interp=LINEAR_INTERPOLATION,
self.interp = interp

def blend_frames(self, frame_val1, frame_val2):
# xxx: default use REPLACE
# FIXME: default use REPLACE
return max(frame_val1, frame_val2)

def convert_to_keys_object(self):
Expand Down
5 changes: 1 addition & 4 deletions io_scene_godot/converters/armature.py
Expand Up @@ -73,7 +73,7 @@ def export_bone(pose_bone, bl_bones_to_export):
while ps_bone_ptr is not None and ps_bone_ptr not in bl_bones_to_export:
ps_bone_ptr = ps_bone_ptr.parent
if ps_bone_ptr is not None:
rest_mat = (ps_bone_ptr.bone.matrix_local.inverted_safe() *
rest_mat = (ps_bone_ptr.bone.matrix_local.inverted_safe() @
rest_bone.matrix_local)
parent_bone_name = ps_bone_ptr.name
else:
Expand Down Expand Up @@ -151,9 +151,6 @@ def add_bones(self, bone_list):

def export_armature_node(escn_file, export_settings, node, parent_gd_node):
"""Export an armature node"""
if "ARMATURE" not in export_settings['object_types']:
return parent_gd_node

skeleton_node = SkeletonNode(node.name, parent_gd_node)
skeleton_node['transform'] = node.matrix_local

Expand Down
6 changes: 0 additions & 6 deletions io_scene_godot/converters/material.py
Expand Up @@ -85,13 +85,7 @@ def generate_material_resource(escn_file, export_settings, material):

if mat is None:
mat = InternalResource("SpatialMaterial", material_rsc_name)

mat['flags_unshaded'] = material.use_shadeless
mat['flags_vertex_lighting'] = material.use_vertex_color_light
mat['flags_transparent'] = material.use_transparency
mat['vertex_color_use_as_albedo'] = material.use_vertex_color_paint
mat['albedo_color'] = gamma_correct(material.diffuse_color)
mat['subsurf_scatter_enabled'] = material.subsurface_scattering.use
return escn_file.add_internal_resource(mat, material)


Expand Down
14 changes: 3 additions & 11 deletions io_scene_godot/converters/material_node_tree/node_vistors.py
Expand Up @@ -440,12 +440,12 @@ def visit_mapping_node(shader, node):
if node.vector_type == "TEXTURE":
# Texture: Transform a texture by inverse
# mapping the texture coordinate
transform_mat = (loc_mat * rot_mat * sca_mat).inverted_safe()
transform_mat = (loc_mat @ rot_mat @ sca_mat).inverted_safe()
elif node.vector_type == "POINT":
transform_mat = loc_mat * rot_mat * sca_mat
transform_mat = loc_mat @ rot_mat @ sca_mat
else: # node.vector_type in ("VECTOR", "NORMAL")
# no translation for vectors
transform_mat = rot_mat * sca_mat
transform_mat = rot_mat @ sca_mat

mat = Value.create_from_blender_value(transform_mat)
clamp_min = Value.create_from_blender_value(node.min)
Expand Down Expand Up @@ -520,14 +520,6 @@ def visit_tangent_node(shader, node):

def visit_uvmap_node(shader, node):
"""Visit UV Map node"""
if node.from_dupli:
raise ValidationError(
"'{}' from_dupli not supported, at '{}'".format(
node.bl_idname,
node.name
)
)

shader.assign_variable_to_socket(
node.outputs['UV'],
Value("vec3", ('UV', 0.0)),
Expand Down

0 comments on commit 3f89937

Please sign in to comment.