Skip to content

Commit

Permalink
Updated FST Export, Fixed Issue with Scene Export
Browse files Browse the repository at this point in the history
  • Loading branch information
Menithal committed Jun 27, 2018
1 parent e8ce3a6 commit d9ab4ca
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 93 deletions.
3 changes: 3 additions & 0 deletions MMD.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## MMD to High Fidelity Guide

### Suggested Watching

https://www.youtube.com/watch?v=tJX8VUPZLKQ

## Requirements

Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ Plugin ("Project Hermes") is a plugin for Blender to allow for easier content cr

----


# Dependencies Included with source

- py-ipfs-api is from https://github.com/ipfs/py-ipfs-api under MIT license. This is used to export avatars directly to be hosted online of ipfs, a distributed web.


# Installation Guide

## Simple
Expand Down Expand Up @@ -70,3 +76,4 @@ You can then set materials to the objects via the material panel, modify the mes
If Entity is not Child of another entity, no Join is done. Only Children are merged with their Parents

Note that Boolean operations work differently, and some may not keep the UV Unwrapping correctly in some situations. Use at your own risk

21 changes: 15 additions & 6 deletions hifi_tools/files/fst/operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,8 @@
EnumProperty
)
import hifi_tools.files.fst.writer as FSTWriter

from hifi_tools.utils.bones import find_armatures


class HifiBoneOperator(bpy.types.Operator):
bl_idname = "hifi_warn.bone_count"
bl_label = ""
Expand Down Expand Up @@ -162,7 +160,7 @@ class FSTWriterOperator(bpy.types.Operator, ExportHelper):
selected_only = BoolProperty(
default=False, name="Selected Only", description="Selected Only")

anim_url = StringProperty(default="", name="Animation JSON Url",
anim_graph_url = StringProperty(default="", name="Animation JSON Url",
description="Avatar Animation JSON url")

script = StringProperty(default="", name="Avatar Script Path",
Expand All @@ -171,26 +169,37 @@ class FSTWriterOperator(bpy.types.Operator, ExportHelper):

flow = BoolProperty(default=True, name="Add Flow Script",
description="Adds flow script template as an additional Avatar script")


embed = BoolProperty(default=False, name="Embed Textures",
description="Embed Textures to Exported Model")

bake = BoolProperty(default=False, name="Oven Bake (Experimental)",
description="Use the HiFi Oven Tool to bake")

ipfs = BoolProperty(default=False, name="IPFS",
description="Upload files to the \n InterPlanetary File System Blockchain")

ipfs_server = StringProperty(default="", name="IPFS Server Url",
description="")


def draw(self, context):
layout = self.layout
layout.prop(self, "selected_only")
layout.prop(self, "anim_url")
layout.prop(self, "anim_graph_url")
layout.prop(self, "script")
#layout.prop(self, "flow")
layout.prop(self, "embed")

oven_tool = context.user_preferences.addons[hifi_tools.__name__].preferences.oventool

if(oven_tool is not None and "oven" in oven_tool):
if (oven_tool is not None and "oven" in oven_tool):
layout.prop(self, "bake")

#layout.prop(self, "ipfs")
#if (self.ipfs):
#layout.prop(self, "ipfs_server")


def execute(self, context):
if not self.filepath:
Expand Down
31 changes: 25 additions & 6 deletions hifi_tools/files/fst/writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,24 @@
prefix_texdir = "texdir = $\n"
prefix_filename = "filename = $\n"

prefix_blendshape = "bs = $\n"

prefix_joint = "joint = ¤ = $\n"
prefix_free_joint = "freeJoint = $\n"

prefix_script = "script = $\n"
prefix_anim_graph_url = "animGraphUrl = $\n"


def default_blend_shape(selected):
print("Blend Shakes")

for obj in selected:
if obj.type == "MESH":
print("Searching Blend Shapes")
# TODO: Make a map of common blendshape names
# if something does not already exist in model

def fst_export(context, selected):

# file = open
Expand Down Expand Up @@ -94,23 +108,28 @@ def fst_export(context, selected):
f.write(prefix_texdir.replace('$', scene_id + '.fbm/'))
f.write(prefix_filename.replace('$', avatar_file))

f.write(prefix_script.replace('$', context.script))
f.write(prefix_anim_graph_url.replace('$', context.anim_graph_url))

if len(context.script) > 0:
f.write(prefix_script.replace('$', context.script))
if context.flow:
print("Add Script")
print("Add Flow Script")


if len(context.anim_graph_url) > 0:
f.write(prefix_anim_graph_url.replace('$', context.anim_graph_url))


# Writing these in separate loops because they need to done in order.
for bone in armature.data.bones:
if bone.name in joint_maps:
print("Writing joint map",
prefix_joint_maps[bone.name] + " = " + bone.name)
f.write("joint = " + prefix_joint_maps[bone.name] + " = " + bone.name + "\n")
f.write(prefix_joint.replace('¤',prefix_joint_maps[bone.name]).replace('$', bone.name))

for bone in armature.data.bones:
if bone.name in prefix_free_joints:
print("Writing joint index", "freeJoint = " + bone.name + "\n")
f.write("freeJoint = " + bone.name + "\n")
f.write(prefix_free_joint.replace('$', bone.name))

retarget_armature({"apply": True}, selected)

Expand Down
94 changes: 65 additions & 29 deletions hifi_tools/files/hifi_json/writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,32 @@ def apply_all_modifiers(modifiers):
if modifier.type != 'ARMATURE':
bpy.ops.object.modifier_apply(apply_as='DATA', modifier=modifier.name)

def set_relative_to_parent(blender_object, json_data):
if blender_object.parent:
parent = blender_object.parent

parent_uuid = uuid.uuid5(uuid.NAMESPACE_DNS, parent.name)

parent_orientation = quat_swap_nzy(relative_rotation(blender_object))
parent_position = swap_nzy(relative_position(blender_object))

json_data["position"] = {
'x': parent_position.x,
'y': parent_position.y,
'z': parent_position.z
}

json_data["rotation"] = {
'x': parent_orientation.x,
'y': parent_orientation.y,
'z': parent_orientation.z,
'w': parent_orientation.w
}

json_data["parentID"] = str(parent_uuid)

return json_data


def parse_object(blender_object, path, options):
# Store existing rotation mode, just in case.
Expand All @@ -143,7 +169,6 @@ def parse_object(blender_object, path, options):
uuid_gen = uuid.uuid5(uuid.NAMESPACE_DNS, blender_object.name)
scene_id = str(uuid_gen)

reference_name = blender_object.data.name
bo_type = blender_object.type

stored_rotation_mode = str(blender_object.rotation_mode)
Expand All @@ -155,6 +180,10 @@ def parse_object(blender_object, path, options):
original_object = None
blender_object.select = True
uid = ""
reference_name = blender_object.data.name

# TODO: If Child of armature, skip logic

# Here comes the fun part: Apply all modifiers prior to using them in the instance
if len(blender_object.modifiers) > 0:
# Lets do a LOW-LEVEL duplicate, too much automation in duplicate
Expand Down Expand Up @@ -187,7 +216,6 @@ def parse_object(blender_object, path, options):

# TODO: Option to also export via gltf instead of fbx
# TODO: Add Option to not embedtextures / copy paths

file_path = path + reference_name + uid + ".fbx"

atp_enabled = options.atp
Expand Down Expand Up @@ -239,35 +267,11 @@ def parse_object(blender_object, path, options):
'userData': '{"blender_export":"' + scene_id +'"}, "grabbable_key":["grabbable":false]}'
}


if blender_object.parent:
parent = blender_object.parent

parent_uuid = uuid.uuid5(uuid.NAMESPACE_DNS, parent.name)

parent_orientation = quat_swap_nzy(relative_rotation(blender_object))
parent_position = swap_nzy(relative_position(blender_object))

json_data["position"] = {
'x': parent_position.x,
'y': parent_position.y,
'z': parent_position.z
}

json_data["rotation"] = {
'x': parent_orientation.x,
'y': parent_orientation.y,
'z': parent_orientation.z,
'w': parent_orientation.w
}

json_data["parentID"] = str(parent_uuid)
json_data = set_relative_to_parent(blender_object, json_data)

if original_object:
print("removing duplicate")
bpy.ops.object.delete()
blender_object = original_object
print("new set", blender_object)
blender_object.select = True

elif bo_type == 'LAMP':
Expand Down Expand Up @@ -315,8 +319,40 @@ def parse_object(blender_object, path, options):

# TODO: Spot Lights require rotation by 90 degrees to get pointing in the right direction
elif bo_type == 'ARMATURE': # Same as Mesh actually.
print(name, 'is armature')

# Get all children export as a single file.
print(name, 'is armature. Not Supported as of the moment')

elif bo_type == 'EMPTY':
print(name, 'Adding an Empty')

json_data = {
'id': scene_id,
'visible': False,
'collisionless': True,
'ignoreForCollisions': True,
'position': {
'x': position.x,
'y': position.y,
'z': position.z
},
'dimensions':{
'x': 1,
'y': 1,
'z': 1,
},
'name': 'EMPTY-' + name,
"color": {
"blue": 128,
"green": 0,
"red": 255
},
"shape": "Cube",
"type": "Box",
'userData': '{"blender_export":"' + scene_id +'", "grabbableKey":{"grabbable":false,"ignoreIK":false}}',
}

json_data = set_relative_to_parent(blender_object, json_data)

else:
print('Skipping unsupported feature', name, bo_type)

Expand Down
28 changes: 1 addition & 27 deletions hifi_tools/files/js/flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,33 +72,7 @@ Script.include(Script.resolvePath("VectorMath.js"));

// CUSTOM DATA STARTS HERE

// |||***PYTHONFILL***||| //

/*
CUSTOM_FLOW_DATA = {
"hair": {
"active": true,
"stiffness": 0.0,
"radius": 0.04,
"gravity": -0.035,
"damping": 0.8,
"inertia": 0.8,
"delta": 0.35
}
};
CUSTOM_COLLISION_DATA = {
"Spine2": {
"type": "sphere",
"radius": 0.14,
"offset": {
"x": 0,
"y": 0.2,
"z": 0
}
}
};
*/
//__PYTHONFILL__CUSTOM_SETS__//

// CUSTOM DATA ENDS HERE

Expand Down
26 changes: 1 addition & 25 deletions hifi_tools/files/js/writer.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,6 @@
js_flow = "flow.js"
js_flow_library = "VectorMath.js"
js_custom_writer = "// |||***PYTHONFILL***||| //"
#CUSTOM_FLOW_DATA = {
# "hair": {
# "active": true,
# "stiffness": 0.0,
# "radius": 0.04,
# "gravity": -0.035,
# "damping": 0.8,
# "inertia": 0.8,
# "delta": 0.35
# }
#}

#CUSTOM_COLLISION_DATA = {
# "Spine2": {
# "type": "sphere",
# "radius": 0.14,
# "offset": {
# "x": 0,
# "y": 0.2,
# "z": 0
# }
q# }
#}
js_custom_writer = "//__PYTHONFILL__CUSTOM_SETS__//"


class FlowData:
Expand All @@ -45,7 +22,6 @@ class CollisionData:
radius = 0.14
offset = Vec3Data()


def js_writer(flow_data, collision_data):
print("Open JS Writer")

0 comments on commit d9ab4ca

Please sign in to comment.