Skip to content

Commit

Permalink
Adds material baking to Mitsuba addon
Browse files Browse the repository at this point in the history
  • Loading branch information
Leauyy committed Jan 5, 2024
1 parent 6a41193 commit 611735b
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 206 deletions.
44 changes: 43 additions & 1 deletion mitsuba-blender/io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
importlib.reload(exporter)

import bpy
import time
from bpy.props import (
StringProperty,
BoolProperty,
IntProperty,
)
from bpy_extras.io_utils import (
ImportHelper,
Expand Down Expand Up @@ -104,13 +106,46 @@ class ExportMitsuba(bpy.types.Operator, ExportHelper):
default = True
)

bake_materials: BoolProperty(
name = "Bake Materials",
description = "Bakes Materials into textures. Takes longer to export scenes. Make sure GPU is enabled in settings",
default = False
)
bake_mat_ids: BoolProperty(
name = "Unique Material IDs",
description = """If 'Bake Materials' is active bakes Materials with unique IDs.
Each object will have a unique material in final XML and will have correct blender representation.
Otherwise some materials are reused and textures may be inaccurate""",
default = False
)

bake_again: BoolProperty(
name = "Bake textures again",
description = """If 'Bake Materials' is active, this will bake the already existing textures if enabled.
This option allows to incrementally bake scene materials.""",
default = True
)

bake_res_x: IntProperty(
name = "Bake Resolution X",
description = "Resultion size of X coordinate in pixels. If \"Bake Materials\" is enabled will bake with this resolution",
default = 1024
)

bake_res_y: IntProperty(
name = "Bake Resolution Y",
description = "Resultion size of Y coordinate in pixels. If \"Bake Materials\" is enabled will bake with this resolution",
default = 1024
)

def __init__(self):
self.reset()

def reset(self):
self.converter = exporter.SceneConverter()

def execute(self, context):
start = time.time()
# Conversion matrix to shift the "Up" Vector. This can be useful when exporting single objects to an existing mitsuba scene.
axis_mat = axis_conversion(
to_forward=self.axis_forward,
Expand All @@ -122,6 +157,12 @@ def execute(self, context):
self.converter.export_ctx.export_ids = self.export_ids

self.converter.use_selection = self.use_selection
# Bake material feature options
self.converter.export_ctx.bake_materials = self.bake_materials
self.converter.export_ctx.bake_res_x = self.bake_res_x
self.converter.export_ctx.bake_res_y = self.bake_res_y
self.converter.export_ctx.bake_mat_ids = self.bake_mat_ids
self.converter.export_ctx.bake_again = self.bake_again

# Set path to scene .xml file
self.converter.set_path(self.filepath, split_files=self.split_files)
Expand All @@ -143,7 +184,8 @@ def execute(self, context):

#reset the exporter
self.reset()

end = time.time()
print(f"Export took {end-start}")
return {'FINISHED'}


Expand Down
5 changes: 2 additions & 3 deletions mitsuba-blender/io/exporter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,7 @@ def scene_to_dict(self, depsgraph, window_manager):
for object_instance in depsgraph.object_instances:
window_manager.progress_update(progress_counter)
progress_counter += 1
print(f"Progress: {progress_counter/len(depsgraph.object_instances)}")


if self.use_selection:
#skip if it's not selected or if it's an instance and the parent object is not selected
if not object_instance.is_instance and not object_instance.object.original.select_get():
Expand All @@ -94,7 +93,7 @@ def scene_to_dict(self, depsgraph, window_manager):
and evaluated_obj.parent and evaluated_obj.parent.original.hide_render):
self.export_ctx.log("Object: {} is hidden for render. Ignoring it.".format(evaluated_obj.name), 'INFO')
continue#ignore it since we don't want it rendered (TODO: hide_viewport)
if object_type in {'MESH', 'FONT', 'SURFACE', 'META'}:
if object_type in {'MESH', 'FONT', 'SURFACE', 'META'} and evaluated_obj.name in bpy.context.view_layer.objects:
geometry.export_object(object_instance, self.export_ctx, evaluated_obj.name in particles)
elif object_type == 'CAMERA':
# When rendering inside blender, export only the active camera
Expand Down
14 changes: 13 additions & 1 deletion mitsuba-blender/io/exporter/export_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ def __init__(self):
self.counter = 0 # Counter to create unique IDs.
self.exported_mats = ExportedMaterialsCache()
self.export_ids = False # Export Object IDs in the XML file
self.bake_materials = False
self.bake_res_x = 1024
self.bake_res_y = 1024
self.bake_mat_ids = False
self.bake_again = True
self.exported_ids = set()
# All the args defined below are set in the Converter
self.directory = ''
Expand Down Expand Up @@ -160,6 +165,13 @@ def export_texture(self, image):
image.filepath_raw = old_filepath
return f"{self.subfolders['texture']}/{name}"

def blackbody(self, value):
param = {
"type" : "blackbody",
"temperature" : value
}
return param

def spectrum(self, value, mode='rgb'):
'''
Given a spectrum value, format it for the scene dict.
Expand All @@ -185,7 +197,7 @@ def spectrum(self, value, mode='rgb'):
if any(type(value[i]) != type(value[i+1]) for i in range(len(value)-1)):
raise ValueError("Mixed types in spectrum entry %s" % value)
totitems = len(value)
if isinstance(value[0], (float, int)):
if (value[0], (float, int)):
if totitems == 3 or totitems == 4:
spec = {
'type': 'rgb',
Expand Down
56 changes: 36 additions & 20 deletions mitsuba-blender/io/exporter/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,9 @@ def export_object(deg_instance, export_ctx, is_particle):
# Remove spurious characters such as slashes
name_clean = bpy.path.clean_name(b_object.name_full)
object_id = f"mesh-{name_clean}"

print(f"Exporting object {name_clean}")
is_instance_emitter = b_object.parent is not None and b_object.parent.is_instancer
is_instance = deg_instance.is_instance

# Only write to file objects that have never been exported before
if export_ctx.data_get(object_id) is None:
if b_object.type == 'MESH':
Expand All @@ -123,26 +122,42 @@ def export_object(deg_instance, export_ctx, is_particle):
transform = b_object.matrix_world

if mat_count == 0: # No assigned material
converted_parts.append((-1, convert_mesh(export_ctx,
b_mesh,
transform,
name_clean,
0)))
for mat_nr in range(mat_count):
if b_mesh.materials[mat_nr]:
converted_parts.append((
name_clean,
-1,
convert_mesh(export_ctx, b_mesh, transform, name_clean, 0)
))
else:
refs_per_mat = {}
for mat_nr in range(mat_count):
mat = b_mesh.materials[mat_nr]
if not mat:
continue

# Ensures that the exported mesh parts have unique names,
# even if multiple material slots refer to the same material.
n_mat_refs = refs_per_mat.get(mat.name, 0)
name = f'{name_clean}-{mat.name}'

if n_mat_refs >= 1:
name += f'-{n_mat_refs:03d}'

mts_mesh = convert_mesh(export_ctx,
b_mesh,
transform,
f"{name_clean}-{b_mesh.materials[mat_nr].name}",
name,
mat_nr)
if mts_mesh is not None and mts_mesh.face_count() > 0:
converted_parts.append((mat_nr, mts_mesh))
export_material(export_ctx, b_mesh.materials[mat_nr], b_object.name)
converted_parts.append((name, mat_nr, mts_mesh))
refs_per_mat[mat.name] = n_mat_refs + 1

if n_mat_refs == 0:
# Only export this material once
export_material(export_ctx, b_mesh.materials[mat_nr], b_object.name)

if b_object.type != 'MESH':
b_object.to_mesh_clear()

part_count = len(converted_parts)
# Use a ShapeGroup for instances and split meshes
use_shapegroup = is_instance or is_instance_emitter or is_particle
# TODO: Check if shapegroups for split meshes is worth it
Expand All @@ -151,12 +166,8 @@ def export_object(deg_instance, export_ctx, is_particle):
'type': 'shapegroup'
}

for (mat_nr, mts_mesh) in converted_parts:
# Determine the file name
if part_count == 1:
name = f"{name_clean}"
else:
name = f"{name_clean}-{b_mesh.materials[mat_nr].name}"
for (name, mat_nr, mts_mesh) in converted_parts:
name = name_clean if len(converted_parts) == 1 else name
mesh_id = f"mesh-{name}"

# Save as binary ply
Expand Down Expand Up @@ -187,7 +198,12 @@ def export_object(deg_instance, export_ctx, is_particle):
export_ctx.data_add(default_bsdf)
params['bsdf'] = {'type':'ref', 'id':'default-bsdf'}
else:
mat_id = f"mat-{b_object.data.materials[mat_nr].name}"
# For each object to have a unique material to prevent inaccurate textures
# We must ID all the objects in a unique way.
if export_ctx.bake_materials and export_ctx.bake_mat_ids:
mat_id = f"{name_clean}-{b_object.data.materials[mat_nr].name}"
else:
mat_id = f"mat-{b_object.data.materials[mat_nr].name}"
if export_ctx.exported_mats.has_mat(mat_id): # Add one emitter *and* one bsdf
mixed_mat = export_ctx.exported_mats.mats[mat_id]
params['bsdf'] = {'type':'ref', 'id':mixed_mat['bsdf']}
Expand Down
Loading

0 comments on commit 611735b

Please sign in to comment.