Skip to content

Commit

Permalink
Merge branch 'Hubs-Foundation:master' into LightMapBakeOperator
Browse files Browse the repository at this point in the history
  • Loading branch information
GottfriedHofmann authored Jul 30, 2024
2 parents a6bad91 + 8ccf0ac commit 496b9f4
Show file tree
Hide file tree
Showing 54 changed files with 976 additions and 209 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,14 @@ jobs:
- name: Update build number
run: |
sed -i'' 's/"dev_build"/${{ github.run_number }}/g' $GITHUB_WORKSPACE/addons/io_hubs_addon/__init__.py
- name: Get version
id: get_version
run: |
VERSION=$(grep '"version"' $GITHUB_WORKSPACE/addons/io_hubs_addon/__init__.py | sed -E 's/.*\(([0-9]+), ([0-9]+), ([0-9]+), ([0-9]+)\).*/\1.\2.\3.\4/')
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Upload addon artifacts
uses: actions/upload-artifact@v3
with:
name: io_hubs_addon
name: io_hubs_addon_${{ steps.get_version.outputs.version }}
path: addons
if-no-files-found: error
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,8 @@ CMakeCache.txt
Makefile
lib

#Selenium
__hubs_selenium_profile
# Selenium
__hubs_selenium_profile

# Dependencies
addons/io_hubs_addon/.__deps__
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Hubs Blender Exporter and Importer

This addon extends the glTF 2.0 exporter to support the `MOZ_hubs_components` and `MOZ_lightmap` extensions allowing you to add behavior to glTF assets for [Mozilla Hubs](https://hubs.mozilla.com).
This addon extends the glTF 2.0 exporter to support the `MOZ_hubs_components` and `MOZ_lightmap` extensions allowing you to add behavior to glTF assets for Hubs.

[![Test](https://github.com/MozillaReality/hubs-blender-exporter/actions/workflows/test.yml/badge.svg)](https://github.com/MozillaReality/hubs-blender-exporter/actions/workflows/test.yml)
[![Publish](https://github.com/MozillaReality/hubs-blender-exporter/actions/workflows/publish.yml/badge.svg)](https://github.com/MozillaReality/hubs-blender-exporter/actions/workflows/publish.yml)
[![Test](https://github.com/Hubs-Foundation/hubs-blender-exporter/actions/workflows/test.yml/badge.svg)](https://github.com/Hubs-Foundation/hubs-blender-exporter/actions/workflows/test.yml)
[![Publish](https://github.com/Hubs-Foundation/hubs-blender-exporter/actions/workflows/publish.yml/badge.svg)](https://github.com/Hubs-Foundation/hubs-blender-exporter/actions/workflows/publish.yml)

# To Install

Find the latest [release](https://github.com/MozillaReality/hubs-blender-exporter/releases) and download the add-on zip file.
Find the latest [release](https://github.com/Hubs-Foundation/hubs-blender-exporter/releases) and download the add-on zip file.

<img alt="select add-on zip file" src="https://user-images.githubusercontent.com/837184/204576860-316b32de-8654-48a7-b9a7-3c0de1c1b652.png" width=685px height=330px />

Expand Down Expand Up @@ -46,7 +46,7 @@ This addon works in conjunction with the official glTF add-on, so exporting is d

# Import into Hubs

The easiest way to use your scene file is through the Spoke [project creation page](https://hubs.mozilla.com/spoke/projects/create) and selecting _Import From Blender_:
The easiest way to use your scene file is through the Spoke project creation page and selecting _Import From Blender_:

<img width="710" alt="Screenshot 2021-10-31 at 14 05 21" src="https://user-images.githubusercontent.com/303516/139588457-8d9d7835-6101-4cfc-886b-ad3e86c37846.png">

Expand All @@ -58,7 +58,7 @@ It is also possible to use the GLB file to replace the scene for an existing Hub

# Scene debugger

The Hubs Blender add-on includes a scene debugger, enabling you to see the Blender scene updates in Hubs with just one click. For additional information, please visit: https://github.com/MozillaReality/hubs-blender-exporter/wiki/Hubs-scene-debugger/
The Hubs Blender add-on includes a scene debugger, enabling you to see the Blender scene updates in Hubs with just one click. For additional information, please visit: https://github.com/Hubs-Foundation/hubs-blender-exporter/wiki/Hubs-scene-debugger/

# Development

Expand Down
12 changes: 5 additions & 7 deletions addons/io_hubs_addon/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from .utils import create_prefs_dir
from .utils import get_user_python_path
import sys
import bpy
from .io import gltf_exporter, gltf_importer, panels
Expand All @@ -10,19 +9,18 @@
from . import icons
bl_info = {
"name": "Hubs Blender Addon",
"author": "Mozilla Hubs",
"description": "Tools for developing glTF assets for Mozilla Hubs",
"author": "The Hubs Community",
"description": "Tools for developing glTF assets for Hubs",
"blender": (3, 1, 2),
"version": (1, 5, 0, "dev_build"),
"version": (1, 6, 0, "dev_build"),
"location": "",
"wiki_url": "https://github.com/MozillaReality/hubs-blender-exporter",
"tracker_url": "https://github.com/MozillaReality/hubs-blender-exporter/issues",
"wiki_url": "https://github.com/Hubs-Foundation/hubs-blender-exporter",
"tracker_url": "https://github.com/Hubs-Foundation/hubs-blender-exporter/issues",
"support": "COMMUNITY",
"warning": "",
"category": "Generic"
}

sys.path.insert(0, get_user_python_path())

create_prefs_dir()

Expand Down
70 changes: 70 additions & 0 deletions addons/io_hubs_addon/components/components_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,43 @@ def get_components_in_dir(dir):
return sorted(components)


def get_user_component_names():
component_names = []
from ..preferences import get_addon_pref
addon_prefs = get_addon_pref(bpy.context)
for entry in addon_prefs.user_components_paths:
if entry.path and os.path.isdir(entry.path):
component_names.append(get_components_in_dir(entry.path))
return component_names


def get_user_component_paths():
component_paths = []
from ..preferences import get_addon_pref
addon_prefs = get_addon_pref(bpy.context)
for entry in addon_prefs.user_components_paths:
if entry.path and os.path.isdir(entry.path):
components = get_components_in_dir(entry.path)
for component in components:
component_paths.append(os.path.join(entry.path, component + ".py"))
return component_paths


def get_user_component_definitions():
modules = []
component_paths = get_user_component_paths()
for component_path in component_paths:
try:
spec = importlib.util.spec_from_file_location(component_path, component_path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
modules.append(mod)

except Exception as e:
print(f'Failed import of component {component_path}', e)
return modules


def get_component_definitions():
components_dir = join(dirname(realpath(__file__)), "definitions")
component_module_names = get_components_in_dir(components_dir)
Expand Down Expand Up @@ -113,6 +150,32 @@ def unregister_component(component_class):
print(f"Component unregistered: {component_class.get_name()}")


def load_user_components():
global __components_registry
for module in get_user_component_definitions():
for _, member in inspect.getmembers(module):
if inspect.isclass(member) and issubclass(member, HubsComponent) and module.__name__ == member.__module__:
try:
if hasattr(module, 'register_module'):
module.register_module()
register_component(member)
__components_registry[member.get_name()] = member
except Exception:
import traceback
traceback.print_exc()


def unload_user_components():
global __components_registry
for _, component_class in __components_registry.items():
for module_name in get_user_component_names():
if module_name == component_class.get_name():
unregister_component(component_class)
for module in get_user_component_definitions():
if hasattr(module, 'unregister_module'):
module.unregister_module()


def load_components_registry():
"""Recurse in the components directory to build the components registry"""
global __components_registry
Expand All @@ -125,6 +188,13 @@ def load_components_registry():
register_component(member)
__components_registry[member.get_name()] = member

# When running Blender in factory startup mode and specifying an addon, that addon's register function is called.
# As preferences are not available until the addon is enabled, the user component load fails when accessing them.
# This happens when running tests and this guard avoids crashing in that scenario.
from ..utils import is_addon_enabled
if is_addon_enabled():
load_user_components()


def unload_components_registry():
"""Recurse in the components directory to unload the registered components"""
Expand Down
2 changes: 1 addition & 1 deletion addons/io_hubs_addon/components/definitions/ammo_shape.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class AmmoShape(HubsComponent):
_definition = {
'name': 'ammo-shape',
'display_name': 'Ammo Shape',
'display_name': 'Ammo Shape (deprecated)',
'category': Category.OBJECT,
'node_type': NodeType.NODE,
'panel_type': [PanelType.OBJECT, PanelType.BONE],
Expand Down
2 changes: 1 addition & 1 deletion addons/io_hubs_addon/components/definitions/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Audio(HubsComponent):
}

src: StringProperty(
name="Audio URL", description="Audio URL", default='https://mozilla.org')
name="Audio URL", description="Audio URL", default='https://example.org/AudioFile.mp3')

autoPlay: BoolProperty(name="Auto Play",
description="Auto Play",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def migrate(self, migration_type, panel_type, instance_version, host, migration_
self.coneOuterAngle = radians(
self.coneOuterAngle)

if migration_type != MigrationType.GLOBAL or is_linked(ob) or type(ob) == bpy.types.Armature:
if migration_type != MigrationType.GLOBAL or is_linked(ob) or type(ob) is bpy.types.Armature:
host_reference = get_host_reference_message(panel_type, host, ob=ob)
migration_report.append(
f"Warning: The Media Cone angles may not have migrated correctly for the Audio Params component on the {panel_type.value} {host_reference}")
Expand Down
11 changes: 11 additions & 0 deletions addons/io_hubs_addon/components/definitions/audio_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from ..ui import add_link_indicator
from bpy.types import Object
from ...utils import delayed_gather
from ...io.utils import import_component, assign_property
from .audio_source import AudioSource


Expand Down Expand Up @@ -175,3 +176,13 @@ def gather(self, export_settings, object):
'minDelay': self.minDelay,
'debug': self.debug
}

@classmethod
def gather_import(cls, gltf, blender_host, component_name, component_value, import_report, blender_ob=None):
component = import_component(component_name, blender_host)
for property_name, property_value in component_value.items():
if property_name == "srcNode" and type(property_value) is int:
# This srcNode property was generated from an older version of the exporter which stored the index directly as an integer.
property_value = {"__mhc_link_type": "node", "index": property_value}
assign_property(gltf.vnodes, component,
property_name, property_value)
44 changes: 44 additions & 0 deletions addons/io_hubs_addon/components/definitions/grabbable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from bpy.props import BoolProperty
from ..hubs_component import HubsComponent
from ..types import NodeType, PanelType, Category
from ..utils import remove_component, add_component
from .networked_transform import NetworkedTransform


class Grabbable(HubsComponent):
_definition = {
'name': 'grabbable',
'display_name': 'Grabbable',
'category': Category.OBJECT,
'node_type': NodeType.NODE,
'panel_type': [PanelType.OBJECT],
'icon': 'VIEW_PAN',
'deps': ['rigidbody', 'networked-transform'],
'version': (1, 0, 1)
}

cursor: BoolProperty(
name="By Cursor", description="Can be grabbed by a cursor", default=True)

hand: BoolProperty(
name="By Hand", description="Can be grabbed by VR hands", default=True)

@classmethod
def init(cls, obj):
obj.hubs_component_list.items.get('rigidbody').isDependency = True

def migrate(self, migration_type, panel_type, instance_version, host, migration_report, ob=None):
migration_occurred = False
if instance_version <= (1, 0, 0):
migration_occurred = True

# This was a component that has disappeared but it was usually added together with grababble so we try to remove those instances.
if "capturable" in host.hubs_component_list.items:
remove_component(host, "capturable")

if "networked-object-properties" in host.hubs_component_list.items:
remove_component(host, "networked-object-properties")

add_component(host, NetworkedTransform.get_name())

return migration_occurred
2 changes: 1 addition & 1 deletion addons/io_hubs_addon/components/definitions/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Image(HubsComponent):
}

src: StringProperty(
name="Image URL", description="The web address of the image", default="https://mozilla.org")
name="Image URL", description="The web address of the image", default="https://example.org/ImageFile.webp")

controls: BoolProperty(
name="Controls",
Expand Down
2 changes: 1 addition & 1 deletion addons/io_hubs_addon/components/definitions/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Link(HubsComponent):
}

href: StringProperty(name="Link URL", description="Link URL",
default="https://mozilla.org")
default="https://example.org")

def migrate(self, migration_type, panel_type, instance_version, host, migration_report, ob=None):
migration_occurred = False
Expand Down
4 changes: 4 additions & 0 deletions addons/io_hubs_addon/components/definitions/loop_animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,10 @@ def gather_import(cls, gltf, blender_host, component_name, component_value, impo
tracks = property_value.split(",")
import_tracks(tracks, blender_ob, blender_component)
else:
if property_name == 'startOffset':
fps = bpy.context.scene.render.fps / bpy.context.scene.render.fps_base
property_value = round(property_value * fps)

assign_property(gltf.vnodes, blender_component,
property_name, property_value)

Expand Down
4 changes: 2 additions & 2 deletions addons/io_hubs_addon/components/definitions/media_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@


def is_bone(ob):
return type(ob) == EditBone or type(ob) == Bone
return type(ob) is EditBone or type(ob) is Bone


class MediaFrameGizmo(Gizmo):
Expand Down Expand Up @@ -158,7 +158,7 @@ def migrate(self, migration_type, panel_type, instance_version, host, migration_
bounds = Vector((bounds.x, bounds.z, bounds.y))
self.bounds = bounds

if migration_type != MigrationType.GLOBAL or is_linked(ob) or type(ob) == bpy.types.Armature:
if migration_type != MigrationType.GLOBAL or is_linked(ob) or type(ob) is bpy.types.Armature:
host_reference = get_host_reference_message(panel_type, host, ob=ob)
migration_report.append(
f"Warning: The Media Frame component's Y and Z bounds on the {panel_type.value} {host_reference} may not have migrated correctly")
Expand Down
2 changes: 1 addition & 1 deletion addons/io_hubs_addon/components/definitions/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Model(HubsComponent):
}

src: StringProperty(name="Model URL", description="Model URL",
default="https://mozilla.org")
default="https://example.org/ModelFile.glb")

def migrate(self, migration_type, panel_type, instance_version, host, migration_report, ob=None):
migration_occurred = False
Expand Down
14 changes: 14 additions & 0 deletions addons/io_hubs_addon/components/definitions/networked_transform.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from io_hubs_addon.components.hubs_component import HubsComponent
from io_hubs_addon.components.types import NodeType, PanelType


class NetworkedTransform(HubsComponent):
_definition = {
'name': 'networked-transform',
'display_name': 'Networked Transform',
'node_type': NodeType.NODE,
'panel_type': [PanelType.OBJECT],
'icon': 'EMPTY_AXIS',
'deps': ['networked'],
'version': (1, 0, 0)
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class ParticleEmitter(HubsComponent):

src: StringProperty(
name="Image Source", description="The web address (URL) of the image to use for each particle",
default="https://hmc-assets.reticulum.io/spoke/assets/images/dot-75db99b125fe4e9afbe58696320bea73.png")
default="https://example.org/spoke/assets/images/dot-75db99b125fe4e9afbe58696320bea73.png")

ageRandomness: FloatProperty(
name="Age Randomness", description="Age Randomness", default=10.0)
Expand Down
2 changes: 1 addition & 1 deletion addons/io_hubs_addon/components/definitions/pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class PDF(HubsComponent):
'version': (1, 0, 0)
}
src: StringProperty(
name="PDF URL", description="The web address of the PDF", default='https://mozilla.org')
name="PDF URL", description="The web address of the PDF", default='https://example.org/PdfFile.pdf')
controls: BoolProperty(
name="Controls",
description="When enabled, shows pagination buttons when hovering your cursor over it in Hubs that allow you to switch pages",
Expand Down
Loading

0 comments on commit 496b9f4

Please sign in to comment.