Skip to content

Commit

Permalink
Merge branch 'main' into fix/4.0
Browse files Browse the repository at this point in the history
  • Loading branch information
othomson-roblox committed Nov 29, 2023
2 parents 93423fa + 975cb92 commit 33de04f
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 16 deletions.
15 changes: 15 additions & 0 deletions .github/workflows/clabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: "CLA Signature Bot"
on:
issue_comment:
types: [created]
pull_request:
types: [opened, closed, synchronize]

jobs:
call-clabot-workflow:
uses: Roblox/cla-signature-bot/.github/workflows/clabot-workflow.yml@v2
with:
whitelist: "cliffchapmanrbx,Nightriff,othomson-roblox"
use-remote-repo: true
remote-repo-name: "roblox/cla-bot-store"
secrets: inherit
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This project is licensed under the terms of the MIT license. See [LICENSE.md](ht

**Roblox is providing this plugin source as a reference implementation, and we encourage the community to extend and build upon this!**

https://github.com/Roblox/roblox-blender-plugin/assets/66378309/4e889d87-c0fc-4af5-b974-9eb129d16364
https://github.com/Roblox/roblox-blender-plugin/assets/66378309/ba8b1bd9-e431-409d-b336-9cb046e00886

# INSTALLATION
## UNINSTALL OLD VERSION
Expand All @@ -15,6 +15,8 @@ https://github.com/Roblox/roblox-blender-plugin/assets/66378309/4e889d87-c0fc-4a
4. Restart Blender after uninstalling

## INSTALL NEW VERSION
Prerequisite: Blender version 3.2 or greater is required

1. Be sure to [uninstall any old version](#uninstall-old-version) first, including restarting Blender afterward
2. Download the latest add-on zip file from the [repository releases page](https://github.com/Roblox/roblox-blender-plugin/releases)
3. Navigate to the add-ons menu in Blender at `Edit` > `Preferences` > `Add-ons`
Expand Down
63 changes: 61 additions & 2 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
PointerProperty,
FloatProperty,
IntProperty,
BoolProperty,
)

import traceback
Expand Down Expand Up @@ -99,9 +100,66 @@ class RbxAddonPreferences(AddonPreferences):
step=0.01,
description=f"Global scale applied to objects during export for upload.\nDEFAULT: {constants.DEFAULT_EXPORT_SCALE} (Blender Meters are 100:1 to Studio Studs)",
)
bake_anim: BoolProperty(
name="Baked Animation",
description="Export baked keyframe animation",
default=True,
)
bake_anim_use_all_bones: BoolProperty(
name="Key All Bones",
description="Force exporting at least one key of animation for all bones "
"(needed with some target applications, like UE4)",
default=True,
)
bake_anim_use_nla_strips: BoolProperty(
name="NLA Strips",
description="Export each non-muted NLA strip as a separated FBX's AnimStack, if any, "
"instead of global scene animation",
default=True,
)
bake_anim_use_all_actions: BoolProperty(
name="All Actions",
description="Export each action as a separated FBX's AnimStack, instead of global scene animation "
"(note that animated objects will get all actions compatible with them, "
"others will get no animation at all)",
default=True,
)
bake_anim_force_startend_keying: BoolProperty(
name="Force Start/End Keying",
description="Always add a keyframe at start and end of actions for animated channels",
default=True,
)
bake_anim_step: FloatProperty(
name="Sampling Rate",
description="How often to evaluate animated values (in frames)",
min=0.01,
max=100.0,
soft_min=0.1,
soft_max=10.0,
default=1.0,
)
bake_anim_simplify_factor: FloatProperty(
name="Simplify",
description="How much to simplify baked values (0.0 to disable, the higher the more simplified)",
min=0.0,
max=100.0, # No simplification to up to 10% of current magnitude tolerance.
soft_min=0.0,
soft_max=10.0,
default=1.0, # default: min slope: 0.005, max frame step: 10.
)

def draw(self, context):
self.layout.prop(self, "export_scale")
self.layout.prop(self, "bake_anim", text="Bake Animation")
bake_anim_box = self.layout.box()
bake_anim_box.use_property_split = True
bake_anim_box.enabled = self.bake_anim
bake_anim_box.prop(self, "bake_anim_use_all_bones")
bake_anim_box.prop(self, "bake_anim_use_nla_strips")
bake_anim_box.prop(self, "bake_anim_use_all_actions")
bake_anim_box.prop(self, "bake_anim_force_startend_keying")
bake_anim_box.prop(self, "bake_anim_step")
bake_anim_box.prop(self, "bake_anim_simplify_factor")


class RBX_PT_sidebar:
Expand Down Expand Up @@ -133,7 +191,8 @@ def draw(self, context):
rbx = context.window_manager.rbx
if not rbx.is_finished_installing_dependencies:
layout.row().label(
text=f"This plugin requires installation of dependencies the first time it is run.", icon="INFO"
text=f"This plugin requires installation of dependencies the first time it is run.",
icon="INFO",
)

layout.row().operator(
Expand Down Expand Up @@ -181,7 +240,7 @@ def draw(self, context):
from .lib.oauth2_client import RbxOAuth2Client

oauth2_client = RbxOAuth2Client(rbx)
top_row.label(text=f"Hello, {oauth2_client.preferred_username}")
top_row.label(text=f"Hello, {oauth2_client.name}")
except Exception as exception:
self.report({"ERROR"}, f"{str(exception)}\n{traceback.format_exc()}")

Expand Down
3 changes: 2 additions & 1 deletion lib/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
RBX_PACKAGE_ID_PROPERTY_NAME = "Roblox Package ID"
ASSET_DESCRIPTION = "Uploaded from Blender"
ERROR_MESSAGES = {
"TIMED_OUT": "Timed Out",
"UPLOAD_TIMED_OUT": "Upload Timed Out",
"OPERATION_TIMED_OUT": "Operation Timed Out",
"INVALID_RESPONSE": "Invalid Response",
"ADD_ON_ERROR": "Add-on Error",
}
Expand Down
7 changes: 7 additions & 0 deletions lib/export_fbx.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ def export_fbx(scene, view_layer, target_object, exported_file_path, preferences
bpy.ops.export_scene.fbx(
filepath=str(exported_file_path),
global_scale=preferences.export_scale,
bake_anim=preferences.bake_anim,
bake_anim_use_all_bones=preferences.bake_anim_use_all_bones,
bake_anim_use_nla_strips=preferences.bake_anim_use_nla_strips,
bake_anim_use_all_actions=preferences.bake_anim_use_all_actions,
bake_anim_force_startend_keying=preferences.bake_anim_force_startend_keying,
bake_anim_step=preferences.bake_anim_step,
bake_anim_simplify_factor=preferences.bake_anim_simplify_factor,
axis_forward="-Z",
axis_up="Y",
path_mode="COPY",
Expand Down
2 changes: 1 addition & 1 deletion lib/jwt_http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async def get_json(self, url: str):
:return: The JSON data as a dictionary.
:raise JWTHTTPFetchError: If there's a problem fetching or decoding the data.
"""
if not (url.startswith("https://") or url.startswith("http://")):
if not url.startswith("https://"):
raise JWTHTTPFetchError("Unsupported protocol in 'iss'")

try:
Expand Down
12 changes: 6 additions & 6 deletions lib/oauth2_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
from time import time


PORT = 8080
PORT = 8181
HOST = "localhost"
RESPONSE_TYPE = "code"
CODE_CHALLENGE_METHOD = "S256"
Expand Down Expand Up @@ -249,10 +249,10 @@ async def refresh_login_if_needed(self):

self.__complete_login(*await request_login_details(new_token_data))

def __complete_login(self, creator_ids, preferred_username, group_names_by_id, token_data):
def __complete_login(self, creator_ids, name, group_names_by_id, token_data):
# Set state values in rbx from the data fetched and processed above
self.__set_creators_from_ids(creator_ids, preferred_username, group_names_by_id)
self.preferred_username = preferred_username
self.__set_creators_from_ids(creator_ids, name, group_names_by_id)
self.name = name
self.token_data = token_data
self.rbx.is_logged_in = True

Expand Down Expand Up @@ -321,7 +321,7 @@ async def __refresh_tokens(self, refresh_token):
exception.message = error_description
raise exception

def __set_creators_from_ids(self, creator_ids, preferred_username, group_names_by_id):
def __set_creators_from_ids(self, creator_ids, name, group_names_by_id):
"""Populates a CollectionProperty with RbxCreatorData objects containing creator types, ids, and names
given ids and names. Used for persisting creator data across sessions and generating enum dropdown items.
"""
Expand All @@ -332,7 +332,7 @@ def __set_creators_from_ids(self, creator_ids, preferred_username, group_names_b
creator = self.rbx.creators.add()
creator.type = "USER"
creator.id = creator_ids["user"]
creator.name = preferred_username
creator.name = name

for group_id in creator_ids["groups"]:
creator = self.rbx.creators.add()
Expand Down
6 changes: 3 additions & 3 deletions lib/request_login_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ async def request_login_details(token_data):
# Raises jwt.exceptions.DecodeError
profile_data = await __decode_id_token(token_data.get("id_token"))

# Raises KeyError if missing preferred_username
preferred_username = profile_data["preferred_username"]
# Raises KeyError if missing name
name = profile_data["name"]

token_data = {
"refresh_after": time() + token_data["expires_in"] - REFRESH_SECONDS_BEFORE_EXPIRY,
Expand All @@ -85,7 +85,7 @@ async def request_login_details(token_data):
"id_token": token_data["id_token"],
}

return creator_ids, preferred_username, group_names_by_id, token_data
return creator_ids, name, group_names_by_id, token_data


async def __request_authorized_resources(access_token):
Expand Down
10 changes: 8 additions & 2 deletions lib/upload_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ async def upload_task(cls, window_manager, area, target_object, file_path, packa
asset_description=constants.ASSET_DESCRIPTION,
file_path=file_path,
asset_id=package_id or NO_ASSET_ID,
upload_request_timeout_seconds=25,
)

return operation
Expand All @@ -236,6 +237,7 @@ def upload_task_complete(task, window_manager, area, target_object, temporary_di
property and cleaning up from the operation."""
from . import status_indicators, constants
import openapi_client
import asyncio

try:
operation = task.result()
Expand All @@ -247,7 +249,7 @@ def upload_task_complete(task, window_manager, area, target_object, temporary_di
elif not operation.done:
# Timeout while polling for upload job to finish. It may yet finish or fail, but we stopped checking.
status_indicators.set_status(
window_manager, area, target_object, constants.ERROR_MESSAGES["TIMED_OUT"], "ERROR"
window_manager, area, target_object, constants.ERROR_MESSAGES["OPERATION_TIMED_OUT"], "ERROR"
)
elif operation.response:
# Success
Expand All @@ -265,7 +267,11 @@ def upload_task_complete(task, window_manager, area, target_object, temporary_di
window_manager, area, target_object, constants.ERROR_MESSAGES["INVALID_RESPONSE"], "ERROR"
)
print(f"Upload failed, invalid response:\n{operation}")

except asyncio.exceptions.TimeoutError as exception:
# Timeout while waiting for initial upload to return an operation ID. It may yet finish or fail, but we stopped waiting.
status_indicators.set_status(
window_manager, area, target_object, constants.ERROR_MESSAGES["UPLOAD_TIMED_OUT"], "ERROR"
)
except openapi_client.rest.ApiException as exception:
traceback.print_exception(exception)
from .extract_exception_message import extract_exception_message
Expand Down

0 comments on commit 33de04f

Please sign in to comment.