Skip to content

Commit

Permalink
Merge pull request #6 from MozillaReality/component-improvments
Browse files Browse the repository at this point in the history
Improvments to components panel
  • Loading branch information
netpro2k authored Jun 30, 2020
2 parents b5899c6 + 731feaa commit c56e0fe
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 44 deletions.
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
# Hubs Exporter for Blender

This addon extends the glTF 2.0 exporter to support the `MOZ_hubs_components` extension 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 [Mozilla Hubs](https://hubs.mozilla.com).

# Adding Components

To add components, go to the "Hubs" section of the properties panel for the thing you want to add a component to. Currently adding components is supported on Scenes, Objects, Bones, and Materials.

<img src="https://user-images.githubusercontent.com/130735/84547528-97440a00-acb8-11ea-9f07-24c919796a3c.png" width="300px"/>

Click "Add Component" and select the component you wish to add from the list. Only components appropriate for the object type you are currently editing and have not already been added will be shown.

# Using Lightmaps

To use a lightmap, create a `MOZ_lightamp` node from the `Add > Hubs` menu and hook up a image texture to the `Lightmap` input. Use a `UV Map` node to control what UV set should be used for the lightmap, as you would any other texture in Blender.

![lightmap node](https://user-images.githubusercontent.com/130735/83931408-65c7bd80-a751-11ea-86b9-a2ae889ec5df.png)

Note that for use in Hubs, you currently **MUST** use the second UV set, as ThreeJS is currently hardcoded to use that for lightmaps. This will likely be fixed in the future so the exporter does not enforce this.

![setting bake UV](https://user-images.githubusercontent.com/130735/83697782-b9e96b00-a5b4-11ea-986b-6690c69d8a3f.png)

# Exporting

This addon works in conjunction with the official glTF exporter, so exporting is done through it. Select "File > Export > glTF 2.0" and then ensure "Hubs Components" is enabled under "Extensions".

![gltf export window](https://user-images.githubusercontent.com/130735/84547591-be9ad700-acb8-11ea-8c58-7b1104f0a3a7.png)
7 changes: 6 additions & 1 deletion __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"author" : "MozillaReality",
"description" : "Tools for developing GLTF assets for Mozilla Hubs",
"blender" : (2, 83, 0),
"version" : (0, 0, 3),
"version" : (0, 0, 4),
"location" : "",
"wiki_url": "https://github.com/MozillaReality/hubs-blender-exporter",
"tracker_url": "https://github.com/MozillaReality/hubs-blender-exporter/issues",
Expand Down Expand Up @@ -114,6 +114,11 @@ def hubs_gather_gltf_hook(self, gltf2_object, export_settings):
def gather_node_hook(self, gltf2_object, blender_object, export_settings):
if not self.properties.enabled: return

# Don't include hubs component data again in extras, even if "include custom properties" is enabled
if gltf2_object.extras:
for key in list(gltf2_object.extras):
if key.startswith("hubs_"): del gltf2_object.extras[key]

self.add_hubs_components(gltf2_object, blender_object, export_settings)

def gather_material_hook(self, gltf2_object, blender_material, export_settings):
Expand Down
67 changes: 56 additions & 11 deletions components.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import bpy
from bpy.props import IntVectorProperty, BoolProperty, FloatProperty, StringProperty
from bpy.props import IntVectorProperty, BoolProperty, FloatProperty, StringProperty, EnumProperty
from bpy.props import PointerProperty, FloatVectorProperty, CollectionProperty, IntProperty
from bpy.types import PropertyGroup, Material
from bpy.types import PropertyGroup, Material, Image

class StringArrayValueProperty(PropertyGroup):
value: StringProperty(name="value", default="")
Expand All @@ -14,6 +14,7 @@ class Material2DArrayValueProperty(PropertyGroup):

class HubsComponentName(PropertyGroup):
name: StringProperty(name="name")
expanded: BoolProperty(name="expanded", default=True)

class HubsComponentList(PropertyGroup):
items: CollectionProperty(type=HubsComponentName)
Expand Down Expand Up @@ -57,15 +58,16 @@ def define_class(class_name, class_definition, hubs_context):
if class_name in registered_classes:
return registered_classes[class_name]

class_property_dict = {}
class_property_dict = {
'__annotations__': {},
'definition': class_definition
}

for property_name, property_definition in class_definition['properties'].items():
property_class = define_property(class_name, property_name, property_definition, hubs_context)

if property_class:
class_property_dict[property_name] = property_class

class_property_dict['definition'] = class_definition
class_property_dict['__annotations__'][property_name] = property_class

component_class = type(class_name, (PropertyGroup,), class_property_dict)

Expand Down Expand Up @@ -100,61 +102,102 @@ def define_property(class_name, property_name, property_definition, hubs_context

if property_type == 'int':
return IntProperty(
name=property_name
name=property_name,
description=property_definition.get("description") or "",
subtype=property_definition.get("subType") or "NONE",
)
elif property_type == 'float':
return FloatProperty(
name=property_name
name=property_name,
description=property_definition.get("description") or "",
subtype=property_definition.get("subType") or "NONE",
unit=property_definition.get("unit") or "NONE"
)
elif property_type == 'bool':
return BoolProperty(
name=property_name
name=property_name,
description=property_definition.get("description") or "",
subtype=property_definition.get("subType") or "NONE"
)
elif property_type == 'string':
return StringProperty(
name=property_name
name=property_name,
description=property_definition.get("description") or "",
subtype=property_definition.get("subType") or "NONE"
)
elif property_type == 'ivec2':
return IntVectorProperty(
name=property_name,
description=property_definition.get("description") or "",
subtype=property_definition.get("subType") or "NONE",
size=2
)
elif property_type == 'ivec3':
return IntVectorProperty(
name=property_name,
description=property_definition.get("description") or "",
subtype=property_definition.get("subType") or "NONE",
size=3
)
elif property_type == 'ivec4':
return IntVectorProperty(
name=property_name,
description=property_definition.get("description") or "",
subtype=property_definition.get("subType") or "NONE",
size=4
)
elif property_type == 'vec2':
return FloatVectorProperty(
name=property_name,
description=property_definition.get("description") or "",
subtype=property_definition.get("subType") or "NONE",
unit=property_definition.get("unit") or "NONE",
size=2
)
elif property_type == 'vec3':
return FloatVectorProperty(
name=property_name,
description=property_definition.get("description") or "",
subtype=property_definition.get("subType") or "NONE",
unit=property_definition.get("unit") or "NONE",
size=3
)
elif property_type == 'vec4':
return FloatVectorProperty(
name=property_name,
description=property_definition.get("description") or "",
subtype=property_definition.get("subType") or "NONE",
unit=property_definition.get("unit") or "NONE",
size=4
)
elif property_type == 'enum':
return EnumProperty(
name=property_name,
description=property_definition.get("description") or "",
items=[tuple(i) for i in property_definition.get("items")]
)
elif property_type == 'color':
return FloatVectorProperty(
name=property_name,
description=property_definition.get("description") or "",
subtype='COLOR',
default=(1.0, 1.0, 1.0, 1.0),
size=4,
min=0,
max=1
)
elif property_type == 'material':
return PointerProperty(name=property_name, type=Material)
return PointerProperty(
name=property_name,
description=property_definition.get("description") or "",
type=Material
)
elif property_type == 'image':
return PointerProperty(
name=property_name,
description=property_definition.get("description") or "",
type=Image
)
elif property_type == 'collections':
# collections come from the object's users_collection property
# and don't have an associated Property
Expand All @@ -174,6 +217,7 @@ def define_property(class_name, property_name, property_definition, hubs_context

return CollectionProperty(
name=property_name,
description=property_definition.get("description") or "",
type=property_class
)
else:
Expand All @@ -185,6 +229,7 @@ def define_property(class_name, property_name, property_definition, hubs_context

return PointerProperty(
name=property_name,
description=property_definition.get("description") or "",
type=property_class
)

Expand Down
44 changes: 44 additions & 0 deletions default-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,45 @@
}
},
"components": {
"waypoint": {
"category": "Scene",
"node": true,
"properties": {
"canBeSpawnPoint": {"type": "bool", "default": false},
"canBeOccupied": {"type": "bool", "default": false},
"canBeClicked": {"type": "bool", "default": false},
"willDisableMotion": {"type": "bool", "default": false},
"willDisableTeleporting": {"type": "bool", "default": false},
"snapToNavMesh": {"type": "bool", "default": false},
"willMaintainInitialOrientation": {"type": "bool", "default": false},
"willMaintainWorldUp": {"type": "bool", "default": true},
"isOccupied": {"type": "bool", "default": false}
}
},
"uv-scroll": {
"category": "Animation",
"node": true,
"properties": {
"speed": {"type": "vec2", "default": {"x": 0, "y": 0}},
"increment": {"type": "vec2", "default": {"x": 0, "y": 0}}
}
},
"personal-space-invader": {
"category": "Avatar",
"node": true,
"properties": {
"radius": { "type": "float", "default": 0.1 },
"useMaterial": { "type": "bool", "default": false },
"invadingOpacity": { "type": "float", "default": 0.3 }
}
},
"nav-mesh": {
"category": "Scene",
"node": true,
"properties": {}
},
"kit": {
"category": "Architecture Kit",
"scene": true,
"node": false,
"properties": {
Expand All @@ -51,6 +89,7 @@
}
},
"kit-piece": {
"category": "Architecture Kit",
"properties": {
"id": {
"type": "string",
Expand All @@ -67,6 +106,7 @@
}
},
"kit-alt-materials": {
"category": "Architecture Kit",
"node": true,
"properties": {
"id": {
Expand All @@ -88,6 +128,7 @@
}
},
"material-id": {
"category": "Architecture Kit",
"node": false,
"scene": false,
"material": true,
Expand All @@ -103,6 +144,7 @@
}
},
"loop-animation": {
"category": "Animation",
"node": true,
"properties": {
"clip": {
Expand All @@ -115,6 +157,7 @@
}
},
"scale-audio-feedback": {
"category": "Avatar",
"node": true,
"properties": {
"minScale": {
Expand All @@ -128,6 +171,7 @@
}
},
"morph-audio-feedback": {
"category": "Avatar",
"node": true,
"properties": {
"name": {
Expand Down
48 changes: 47 additions & 1 deletion gather_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import datetime
import re
import bpy
from io_scene_gltf2.blender.exp import gltf2_blender_gather_materials
from io_scene_gltf2.blender.exp import gltf2_blender_gather_materials, gltf2_blender_gather_image
from io_scene_gltf2.blender.exp.gltf2_blender_image import ExportImage
from io_scene_gltf2.blender.com import gltf2_blender_extras
from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached
from io_scene_gltf2.blender.exp.gltf2_blender_gather_texture_info import (
Expand All @@ -25,10 +26,14 @@ def gather_property(export_settings, blender_object, target, property_name, prop

if property_type == 'material':
return gather_material_property(export_settings, blender_object, target, property_name, property_definition, hubs_config)
elif property_type == 'image':
return gather_image_property(export_settings, blender_object, target, property_name, property_definition, hubs_config)
elif property_type == 'collections':
return gather_collections_property(export_settings, blender_object, target, property_name, property_definition, hubs_config)
elif property_type == 'array':
return gather_array_property(export_settings, blender_object, target, property_name, property_definition, hubs_config)
elif property_type in ['vec2', 'vec3', 'vec4', 'ivec2', 'ivec3', 'ivec4']:
return gather_vec_property(export_settings, blender_object, target, property_name, property_definition, hubs_config)
else:
return gltf2_blender_extras.__to_json_compatible(getattr(target, property_name))

Expand Down Expand Up @@ -60,6 +65,47 @@ def gather_material_property(export_settings, blender_object, target, property_n
else:
return None

def gather_vec_property(export_settings, blender_object, target, property_name, property_definition, hubs_config):
vec = getattr(target, property_name)

out = {
"x": vec[0],
"y": vec[1],
}

if len(vec) > 2:
out["z"] = vec[2]
if len(vec) > 3:
out["w"] = vec[4]

return out

def gather_image_property(export_settings, blender_object, target, property_name, property_definition, hubs_config):
blender_image = getattr(target, property_name)

if blender_image:
image_data = ExportImage.from_blender_image(blender_image)
if image_data.empty():
return None

mime_type = gltf2_blender_gather_image.__gather_mime_type((), image_data, export_settings)
name = gltf2_blender_gather_image.__gather_name(image_data, export_settings)

uri = gltf2_blender_gather_image.__gather_uri(image_data, mime_type, name, export_settings)
buffer_view = gltf2_blender_gather_image.__gather_buffer_view(image_data, mime_type, name, export_settings)

return gltf2_blender_gather_image.__make_image(
buffer_view,
None,
None,
mime_type,
name,
uri,
export_settings
)
else:
return None

def gather_collections_property(export_settings, blender_object, target, property_name, property_definition, hubs_config):
filtered_collection_names = []

Expand Down
Loading

0 comments on commit c56e0fe

Please sign in to comment.