Skip to content

Commit

Permalink
Merge pull request #1 from vircadia/bug_fixes/2-29-2024
Browse files Browse the repository at this point in the history
Bug fixes, ver 1.0.1.
  • Loading branch information
digisomni authored Mar 4, 2024
2 parents 0f54d9a + 8aa5ecd commit 9b6c1ce
Show file tree
Hide file tree
Showing 14 changed files with 202 additions and 38 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ You'll need:
- Unity 2021.3.4.f1

- gltfast package (by name, `com.unity.cloud.gltfast`)
- Unity2Vircadia package [**(this repo)**](https://github.com/vircadia/unity-to-vircadia-pipeline/blob/master/dist/Unity2Vircadia_v1.0.0.unitypackage)
- Unity2Vircadia package [**(this repo)**](https://github.com/vircadia/unity-to-vircadia-pipeline/blob/master/dist/Unity2Vircadia_v1.0.1.unitypackage)

- Blender 4.0.2 or newer

- UnityToVircadia Blender addon [**(this repo)**](https://github.com/vircadia/unity-to-vircadia-pipeline/blob/master/dist/UnityToVircadia_BlenderAddon_v1.0.0.zip)
- UnityToVircadia Blender addon [**(this repo)**](https://github.com/vircadia/unity-to-vircadia-pipeline/blob/master/dist/UnityToVircadia_BlenderAddon_v1.0.1.zip)

### Step 1. Unity

Expand All @@ -31,13 +31,14 @@ You'll need:
2. Install the `UnityToVircadia` Blender plugin by going to "Edit/Preferences," select the "Add-on" tab and press the "Install" button. Select the `UnityToVircadia.zip` file and Press "Install Add-on". Press the check checkbox to enable the plugin. This will create a `UnityToVircadia` tab on the N panel.
3. Open the `UnityToVircadia` tab on the N Panel
4. Select "Import glTF 2.0" and Import the `.glb` file you exported from Unity
5. Press "Correct Scale" Button"
6. Press "Import Lightmap Info" and a dialog will open. Select the "LightmapInfo.txt" file from the folder we created earlier. This will generate a mesh container for the lightmaps called "vircadia_lightmapData." \* Note: At the moment you will not see these lightmaps until export. container will be removed automatically in Vircadia-web
7. Press "Adjust Shaders" to correct material properties and repeats.
5. Press "Import Lightmap Info" and a dialog will open. Select the "LightmapInfo.txt" file from the folder we created earlier. This will generate a mesh container for the lightmaps called "vircadia_lightmapData." \* Note: At the moment you will not see these lightmaps until export. container will be removed automatically in Vircadia-web
6. Press "Adjust Shaders" to correct material properties and repeats.
7. Press "Preview Lightmaps" to preview lightmaps. You can then "Clear Lightmaps" to revert to your previous state if you choose.
8. Press the "Export glTF 2.0 button to export your model. Set your format to `glTF Binary (.glb)`. From within the export dialog, open the "include" tab and be sure "Custom Properties" is selected. We recommend setting your Material Image format to `Webp` and checking "Create Webp". Compression can be used as well to lower file size, but minor artifacts may appear. Export `glTF 2.0`.

You are now ready to upload your `.glb` file to the public folder. An S3 bucket is recommended. Amazon (AWS), Digital Ocean, Linode, and more all offer S3 compatible storage.

## Notes

- Use URP shaders with a "Metallic" workflow. Specular is not recommended and may cause visual inconsistencies.
- At the moment ".gltf" export (separate .bin and textures) from unity's glTFast plugin will not import properly into blender, so be sure to use ".glb"
35 changes: 28 additions & 7 deletions blender/UnityToVircadia/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,34 @@
from . import makeObjectsSingleUserFixScale
from . import getLightmapInformation
from . import fixMappingNodesRemoveExtras
# Assuming the combined script is saved as 'previewLightmaps.py' in the same addon directory
from . import previewLightmaps

bl_info = {
"name": "UnityToVircadia",
"blender": (4, 0, 2),
"category": "Object",
"description": "Tools for converting Unity assets to Vircadia",
"author": "Ben Brennan, Vircadia",
"version": (1, 0, 0),
"version": (1, 0),
"location": "View3D > Sidebar > UnityToVircadia Tab",
}

def get_script_path(script_name):
addon_dir = os.path.dirname(__file__)
return os.path.join(addon_dir, script_name)

class ToggleLightmapsOperator(bpy.types.Operator):
"""Toggle Lightmaps"""
bl_idname = "object.toggle_lightmaps"
bl_label = "Preview Lightmaps"
bl_description = "Toggle lightmap textures on objects"

def execute(self, context):
previewLightmaps.main(context.scene.lightmaps_applied)
context.scene.lightmaps_applied = not context.scene.lightmaps_applied
return {'FINISHED'}

class UnityToVircadiaPanel(bpy.types.Panel):
bl_label = "UnityToVircadia"
bl_idname = "OBJECT_PT_UnityToVircadia"
Expand All @@ -29,26 +42,34 @@ class UnityToVircadiaPanel(bpy.types.Panel):
def draw(self, context):
layout = self.layout

# Add the new button for importing glTF 2.0 at the top
layout.operator("import_scene.gltf", text="Import glTF 2.0")

# Existing buttons
layout.operator("object.correct_scale")
# layout.operator("object.correct_scale") # TODO later
layout.operator("import.lightmap_info")
layout.operator("object.adjust_shaders")
layout.operator("export_scene.gltf")

# Toggle button for applying/clearing lightmaps
if context.scene.lightmaps_applied:
layout.operator("object.toggle_lightmaps", text="Clear Lightmaps")
else:
layout.operator("object.toggle_lightmaps", text="Preview Lightmaps")

layout.operator("export_scene.gltf", text="Export glTF 2.0")

def register():
bpy.utils.register_class(ToggleLightmapsOperator)
bpy.utils.register_class(UnityToVircadiaPanel)
bpy.utils.register_class(makeObjectsSingleUserFixScale.CorrectScale)
bpy.utils.register_class(getLightmapInformation.GetLightmapInfo)
bpy.utils.register_class(fixMappingNodesRemoveExtras.AdjustShaders)
bpy.types.Scene.lightmaps_applied = bpy.props.BoolProperty(default=False)

def unregister():
bpy.utils.unregister_class(ToggleLightmapsOperator)
bpy.utils.unregister_class(UnityToVircadiaPanel)
bpy.utils.unregister_class(makeObjectsSingleUserFixScale.CorrectScale)
bpy.utils.unregister_class(getLightmapInformation.GetLightmapInfo)
bpy.utils.unregister_class(fixMappingNodesRemoveExtras.AdjustShaders)
del bpy.types.Scene.lightmaps_applied

if __name__ == "__main__":
register()
register()
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
18 changes: 9 additions & 9 deletions blender/UnityToVircadia/fixMappingNodesRemoveExtras.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import bpy

def connect_mapping_to_textures_and_adjust_gloss(material):
def connect_mapping_to_textures_and_adjust_gloss_and_spec(material):
if not material.node_tree:
return

nodes = material.node_tree.nodes
mapping_node = None
gloss_texture_node = None
target_texture_node = None # This will be used for both gloss and spec textures

# Find the mapping node
for node in nodes:
Expand All @@ -19,13 +19,13 @@ def connect_mapping_to_textures_and_adjust_gloss(material):
if node.type == 'TEX_IMAGE':
material.node_tree.links.new(mapping_node.outputs['Vector'], node.inputs['Vector'])

# Find the texture node with "GLOSS" in its name
# Find the texture node with "GLOSS" or "SPEC" in its name
for node in nodes:
if node.type == 'TEX_IMAGE' and "gloss" in node.image.name.lower():
gloss_texture_node = node
if node.type == 'TEX_IMAGE' and ("gloss" in node.image.name.lower() or "spec" in node.image.name.lower()):
target_texture_node = node
break

if gloss_texture_node:
if target_texture_node:
# Check for Separate Color and Roughness Factor nodes
separate_color_node = None
roughness_factor_node = None
Expand All @@ -46,15 +46,15 @@ def connect_mapping_to_textures_and_adjust_gloss(material):
if separate_color_node:
nodes.remove(separate_color_node)

# Find the Principled BSDF node and connect gloss texture
# Find the Principled BSDF node and connect gloss/spec texture
for node in nodes:
if node.type == 'BSDF_PRINCIPLED':
material.node_tree.links.new(gloss_texture_node.outputs['Color'], node.inputs['Specular IOR Level'])
material.node_tree.links.new(target_texture_node.outputs['Color'], node.inputs['Specular IOR Level'])

def adjust_shaders():
# Iterate over all materials in the scene
for material in bpy.data.materials:
connect_mapping_to_textures_and_adjust_gloss(material)
connect_mapping_to_textures_and_adjust_gloss_and_spec(material)

class AdjustShaders(bpy.types.Operator):
"""Adjust Shaders"""
Expand Down
75 changes: 62 additions & 13 deletions blender/UnityToVircadia/getLightmapInformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,66 @@ def parse_info(content):
for line in lines[1:]:
if line.startswith('Lightmap Texture:'):
obj_info['lightmap_texture'] = line.split(': ')[1].strip()
elif line.startswith('Tiling X:'):
obj_info['tiling_x'] = float(line.split(': ')[1].strip())
elif line.startswith('Tiling Y:'):
obj_info['tiling_y'] = float(line.split(': ')[1].strip())
elif line.startswith('Offset X:'):
obj_info['offset_x'] = float(line.split(': ')[1].strip())
elif line.startswith('Offset Y:'):
obj_info['offset_y'] = float(line.split(': ')[1].strip())
elif line.startswith('Path:'):
obj_info['path'] = line.split(': ')[1].strip()
info.append(obj_info)
if 'lightmap_texture' in obj_info:
info.append(obj_info)
return info

def create_materials(obj_info):
materials = []
for info in obj_info:
mat_name = f"vircadia_lightmapData_{os.path.splitext(info['lightmap_texture'])[0]}"
mat = bpy.data.materials.new(name=mat_name)
mat.use_nodes = True
bsdf = mat.node_tree.nodes.get('Principled BSDF')
tex_image = mat.node_tree.nodes.new('ShaderNodeTexImage')
tex_image.image = bpy.data.images.load(os.path.join(info['path'], info['lightmap_texture']))
mat.node_tree.links.new(bsdf.inputs['Base Color'], tex_image.outputs['Color'])
# Remove the file extension for the lightmap texture name
lightmap_texture_name_without_extension = os.path.splitext(info['lightmap_texture'])[0]
mat_name = f"vircadia_lightmapData_{lightmap_texture_name_without_extension}"

if mat_name in bpy.data.materials:
mat = bpy.data.materials[mat_name]
else:
mat = bpy.data.materials.new(name=mat_name)
mat.use_nodes = True
bsdf = mat.node_tree.nodes.get('Principled BSDF')
tex_image = mat.node_tree.nodes.new('ShaderNodeTexImage')
tex_image.label = "BASE COLOR" # Set the label to "BASE COLOR"

image_path = os.path.join(info['path'], info['lightmap_texture'])
image_name = os.path.splitext(os.path.basename(image_path))[0] # Remove the extension from the image name

if image_name in bpy.data.images:
tex_image.image = bpy.data.images[image_name]
else:
# Load the image and remove the extension from its name in Blender
loaded_image = bpy.data.images.load(image_path)
loaded_image.name = image_name
tex_image.image = loaded_image

mat.node_tree.links.new(bsdf.inputs['Base Color'], tex_image.outputs['Color'])

materials.append(mat)
return materials

def adjust_uv_maps(obj_info):
for info in obj_info:
obj = bpy.data.objects.get(info['name'])
if obj and 'tiling_x' in info and 'tiling_y' in info and 'offset_x' in info and 'offset_y' in info:
if len(obj.data.uv_layers) < 2:
obj.data.uv_layers.new(name="LightmapUV")
uv_layer = obj.data.uv_layers[1].data

for poly in obj.data.polygons:
for loop_index in poly.loop_indices:
loop_uv = uv_layer[loop_index]
loop_uv.uv[0] = (loop_uv.uv[0] * info['tiling_x']) + info['offset_x']
loop_uv.uv[1] = (loop_uv.uv[1] * info['tiling_y']) + info['offset_y']

def create_and_join_planes(materials):
created_objects = []
for i, mat in enumerate(materials):
Expand All @@ -42,15 +84,12 @@ def create_and_join_planes(materials):
plane.data.materials.append(mat)
created_objects.append(plane)

# Ensure all created planes are selected
bpy.ops.object.select_all(action='DESELECT')
for obj in created_objects:
obj.select_set(True)

# Set one of the planes as the active object
bpy.context.view_layer.objects.active = created_objects[0]

# Join the planes into a single object
bpy.ops.object.join()
bpy.context.active_object.name = "vircadia_lightmapData"

Expand All @@ -60,7 +99,7 @@ def add_custom_properties(obj_info, materials):
if obj:
mat_name = f"vircadia_lightmapData_{os.path.splitext(info['lightmap_texture'])[0]}"
obj['vircadia_lightmap'] = mat_name
obj['vircadia_lightmap_texcoord'] = 1 # Default value set to 1
obj['vircadia_lightmap_texcoord'] = 1

def make_materials_single_user(obj_info):
for info in obj_info:
Expand All @@ -74,9 +113,10 @@ def import_lightmap_info(filepath):
content = load_lightmap_info(filepath)
obj_info = parse_info(content)
materials = create_materials(obj_info)
adjust_uv_maps(obj_info)
create_and_join_planes(materials)
add_custom_properties(obj_info, materials)
make_materials_single_user(obj_info) # Make specified object materials single-user
make_materials_single_user(obj_info)

class GetLightmapInfo(bpy.types.Operator):
"""Get Lightmap Information"""
Expand All @@ -93,3 +133,12 @@ def execute(self, context):
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}

def register():
bpy.utils.register_class(GetLightmapInfo)

def unregister():
bpy.utils.unregister_class(GetLightmapInfo)

if __name__ == "__main__":
register()
85 changes: 85 additions & 0 deletions blender/UnityToVircadia/previewLightmaps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import bpy

def clear_lightmap_materials():
for obj in bpy.data.objects:
for mat_slot in obj.material_slots:
mat = mat_slot.material
if mat and mat.use_nodes:
# Find the Mix node added for the lightmap
mix_nodes = [node for node in mat.node_tree.nodes if node.name.startswith("LM_Mix")]
for mix_node in mix_nodes:
# Assuming the original texture/color is connected to the first input of the Mix node
if mix_node.inputs[1].is_linked:
original_input = mix_node.inputs[1].links[0].from_socket
bsdf = mat.node_tree.nodes.get('Principled BSDF')
if bsdf:
# Reconnect the original texture/color to the Principled BSDF's 'Base Color'
mat.node_tree.links.new(original_input, bsdf.inputs['Base Color'])

# Remove the Mix node and any other lightmap-specific nodes
mat.node_tree.nodes.remove(mix_node)

uv_map_nodes = [node for node in mat.node_tree.nodes if node.name.startswith("LM_UVMap")]
tex_image_nodes = [node for node in mat.node_tree.nodes if node.name.startswith("LM_TexImage")]
for node in uv_map_nodes + tex_image_nodes:
mat.node_tree.nodes.remove(node)

print(f"Reverted material {mat.name} on object {obj.name}.")

def add_lightmap_to_materials():
for obj in bpy.data.objects:
if "vircadia_lightmap" not in obj or len(obj.data.uv_layers) < 2:
continue

lightmap_mat_name = obj["vircadia_lightmap"]
lightmap_mat = bpy.data.materials.get(lightmap_mat_name)
if not (lightmap_mat and lightmap_mat.use_nodes):
continue

lightmap_texture_node = next((node for node in lightmap_mat.node_tree.nodes if node.type == 'TEX_IMAGE'), None)
if not lightmap_texture_node:
continue

for mat_slot in obj.material_slots:
mat = mat_slot.material
if not (mat and mat.use_nodes):
continue

bsdf = mat.node_tree.nodes.get('Principled BSDF')
if not bsdf:
continue

uv_map_node = mat.node_tree.nodes.new('ShaderNodeUVMap')
uv_map_node.name = "LM_UVMap"
uv_map_node.uv_map = obj.data.uv_layers[1].name

new_tex_node = mat.node_tree.nodes.new('ShaderNodeTexImage')
new_tex_node.name = "LM_TexImage"
new_tex_node.image = lightmap_texture_node.image

mix_node = mat.node_tree.nodes.new('ShaderNodeMixRGB')
mix_node.name = "LM_Mix"
mix_node.blend_type = 'MULTIPLY'
mix_node.inputs[0].default_value = 1.0

mat.node_tree.links.new(uv_map_node.outputs[0], new_tex_node.inputs[0])

if bsdf.inputs['Base Color'].is_linked:
original_input = bsdf.inputs['Base Color'].links[0].from_socket
mat.node_tree.links.new(original_input, mix_node.inputs[1])
else:
mix_node.inputs[1].default_value = bsdf.inputs['Base Color'].default_value

mat.node_tree.links.new(new_tex_node.outputs[0], mix_node.inputs[2])
mat.node_tree.links.new(mix_node.outputs[0], bsdf.inputs['Base Color'])

print(f"Updated material {mat.name} on object {obj.name} with lightmap texture.")

def main(applied):
if applied:
clear_lightmap_materials()
else:
add_lightmap_to_materials()

if __name__ == "__main__":
main(False)
Binary file added dist/Unity2Vircadia_v1.0.1.unitypackage
Binary file not shown.
Binary file added dist/UnityToVircadia_BlenderAddon_v1.0.1.zip
Binary file not shown.
Loading

0 comments on commit 9b6c1ce

Please sign in to comment.