Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Envmap texture fix #79

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,20 @@ jobs:
environment:
- {
os: "ubuntu-latest",
mitsuba-version: "3.0.1"
mitsuba-version: "3.4.0"
}
- {
os: "windows-latest",
mitsuba-version: "3.0.1"
mitsuba-version: "3.4.0"
}
blender:
- {
version: "2.93"
version: "3.3"
}
- {
version: "3.3"
version: "3.6"
}

steps:
- name: Git checkout
uses: actions/checkout@v2
Expand Down
10 changes: 5 additions & 5 deletions mitsuba-blender/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

from . import io, engine

DEPS_MITSUBA_VERSION = '3.0.1'
DEPS_MITSUBA_VERSION = '3.4.0'

def get_addon_preferences(context):
return context.preferences.addons[__name__].preferences
Expand Down Expand Up @@ -108,7 +108,7 @@ def ensure_pip():
def check_pip_dependencies(context):
prefs = get_addon_preferences(context)
result = subprocess.run([sys.executable, '-m', 'pip', 'list'], capture_output=True)

prefs.has_pip_dependencies = False
prefs.has_valid_dependencies_version = False

Expand Down Expand Up @@ -139,7 +139,7 @@ def update_additional_custom_paths(self, context):
self.additional_path = build_path
if self.additional_path not in os.environ['PATH']:
os.environ['PATH'] += os.pathsep + self.additional_path

# Add path to python libs to sys.path
self.additional_python_path = os.path.join(build_path, 'python')
if self.additional_python_path not in sys.path:
Expand All @@ -161,7 +161,7 @@ def execute(self, context):
result = subprocess.run([sys.executable, '-m', 'pip', 'install', f'mitsuba=={DEPS_MITSUBA_VERSION}', '--force-reinstall'], capture_output=False)
if result.returncode != 0:
self.report({'ERROR'}, f'Failed to install Mitsuba with return code {result.returncode}.')
return {'CANCELLED'}
return {'CANCELLED'}

check_pip_dependencies(context)

Expand Down Expand Up @@ -280,7 +280,7 @@ def draw(self, context):
box.prop(self, 'using_mitsuba_custom_path', text=f'Use custom Mitsuba path (Supported version is v{DEPS_MITSUBA_VERSION})')
if self.using_mitsuba_custom_path:
box.prop(self, 'mitsuba_custom_path')

classes = (
MITSUBA_OT_install_pip_dependencies,
MitsubaPreferences,
Expand Down
50 changes: 43 additions & 7 deletions mitsuba-blender/io/exporter/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ def convert_mesh(export_ctx, b_mesh, matrix_world, name, mat_nr):
for logging/debug purposes.
mat_nr: The material ID to export.
'''
from mitsuba import load_dict
props = {'type': 'blender'}
from mitsuba import load_dict, Point3i
props = {
'type': 'blender',
'version': ".".join(map(str,bpy.app.version))
}
b_mesh.calc_normals()
# Compute the triangle tesselation
b_mesh.calc_loop_triangles()
Expand All @@ -37,23 +40,56 @@ def convert_mesh(export_ctx, b_mesh, matrix_world, name, mat_nr):
export_ctx.log(f"Mesh: '{name}' has multiple UV layers. Mitsuba only supports one. Exporting the one set active for render.", 'WARN')
for uv_layer in b_mesh.uv_layers:
if uv_layer.active_render: # If there is only 1 UV layer, it is always active
props['uvs'] = uv_layer.data[0].as_pointer()
if uv_layer.name in b_mesh.attributes:
props['uvs'] = b_mesh.attributes[uv_layer.name].data[0].as_pointer()
else:
props['uvs'] = uv_layer.data[0].as_pointer()
break

for color_layer in b_mesh.vertex_colors:
props['vertex_%s' % color_layer.name] = color_layer.data[0].as_pointer()
if color_layer.name in b_mesh.attributes:
props[f'vertex_{color_layer.name}'] = b_mesh.attributes[color_layer.name].data[0].as_pointer()
else:
props[f'vertex_{color_layer.name}'] = color_layer.data[0].as_pointer()

props['loop_tris'] = b_mesh.loop_triangles[0].as_pointer()
props['loops'] = b_mesh.loops[0].as_pointer()
props['polys'] = b_mesh.polygons[0].as_pointer()
props['verts'] = b_mesh.vertices[0].as_pointer()

if '.corner_vert' in b_mesh.attributes:
# Blender 3.6+ layout
props['loops'] = b_mesh.attributes['.corner_vert'].data[0].as_pointer()
else:
props['loops'] = b_mesh.loops[0].as_pointer()

if 'sharp_face' in b_mesh.attributes:
props['sharp_face'] = b_mesh.attributes['sharp_face'].data[0].as_pointer()

if bpy.app.version >= (3, 6, 0):
props['polys'] = b_mesh.loop_triangle_polygons[0].as_pointer()
else:
props['polys'] = b_mesh.polygons[0].as_pointer()

if 'position' in b_mesh.attributes:
# Blender 3.5+ layout
props['verts'] = b_mesh.attributes['position'].data[0].as_pointer()
else:
props['verts'] = b_mesh.vertices[0].as_pointer()

if bpy.app.version > (3, 0, 0):
props['normals'] = b_mesh.vertex_normals[0].as_pointer()

props['vert_count'] = len(b_mesh.vertices)
# Apply coordinate change
if matrix_world:
props['to_world'] = export_ctx.transform_matrix(matrix_world)

# material index to export, as only a single material per mesh is suported in mitsuba
props['mat_nr'] = mat_nr
if 'material_index' in b_mesh.attributes:
# Blender 3.4+ layout
props['mat_indices'] = b_mesh.attributes['material_index'].data[0].as_pointer()
else:
props['mat_indices'] = 0

# Return the mitsuba mesh
return load_dict(props)

Expand Down
141 changes: 75 additions & 66 deletions mitsuba-blender/io/exporter/materials.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,76 +432,85 @@ def convert_world(export_ctx, world, ignore_background):
if not output_node.inputs["Surface"].is_linked:
return
surface_node = output_node.inputs["Surface"].links[0].from_node
if surface_node.inputs['Strength'].is_linked:
raise NotImplementedError("Only default emitter strength value is supported.")#TODO: value input
strength = surface_node.inputs['Strength'].default_value

if strength == 0: # Don't add an emitter if it emits nothing
export_ctx.log('Ignoring envmap with zero strength.', 'INFO')
return

# Perform environment map check at the start
if surface_node.type in ['BACKGROUND', 'EMISSION']:
if surface_node.inputs['Strength'].is_linked:
raise NotImplementedError(
"Only default emitter strength value is supported.") # TODO: value input
strength = surface_node.inputs['Strength'].default_value

if strength == 0: # Don't add an emitter if it emits nothing
export_ctx.log('Ignoring envmap with zero strength.', 'INFO')
return
socket = surface_node.inputs["Color"]
if socket.is_linked:
color_node = socket.links[0].from_node
if color_node.type == 'TEX_ENVIRONMENT':
params.update({
'type': 'envmap',
'filename': export_ctx.export_texture(color_node.image),
'scale': strength
})
coordinate_mat = Matrix(((0,0,1,0),(1,0,0,0),(0,1,0,0),(0,0,0,1)))
to_world = Matrix()#4x4 Identity
if color_node.inputs["Vector"].is_linked:
vector_node = color_node.inputs["Vector"].links[0].from_node
if vector_node.type != 'MAPPING':
raise NotImplementedError("Node: %s is not supported. Only a mapping node is supported" % vector_node.bl_idname)
if not vector_node.inputs["Vector"].is_linked:
raise NotImplementedError("The node %s should be linked with a Texture coordinate node." % vector_node.bl_idname)
coord_node = vector_node.inputs["Vector"].links[0].from_node
coord_socket = vector_node.inputs["Vector"].links[0].from_socket
if coord_node.type != 'TEX_COORD':
raise NotImplementedError("Unsupported node type: %s." % coord_node.bl_idname)
if coord_socket.name != 'Generated':
raise NotImplementedError("Link should come from 'Generated'.")
#only supported node setup for transform
if vector_node.vector_type != 'TEXTURE':
raise NotImplementedError("Only 'Texture' mapping mode is supported.")
if vector_node.inputs["Location"].is_linked or vector_node.inputs["Rotation"].is_linked or vector_node.inputs["Scale"].is_linked:
raise NotImplementedError("Transfrom inputs shouldn't be linked.")

rotation = vector_node.inputs["Rotation"].default_value.to_matrix()
scale = vector_node.inputs["Scale"].default_value
location = vector_node.inputs["Location"].default_value
for i in range(3):
for j in range(3):
to_world[i][j] = rotation[i][j]
to_world[i][i] *= scale[i]
to_world[i][3] = location[i]
to_world = to_world
#TODO: support other types of mappings (vector, point...)
#change default position, apply transform and change coordinates
params['to_world'] = export_ctx.transform_matrix(to_world @ coordinate_mat)
elif color_node.type == 'RGB':
color = color_node.color
else:
raise NotImplementedError("Node type %s is not supported. Consider using an environment texture or RGB node instead." % color_node.bl_idname)
elif surface_node.type == 'TEX_ENVIRONMENT':
# Set to current node in next step we will get texEnvironment
socket = output_node.inputs["Surface"]
else:
raise NotImplementedError(
"Only Background and Emission nodes are supported as final nodes for World export, got '%s'" % surface_node.name)

if socket.is_linked:
color_node = socket.links[0].from_node
if color_node.type == 'TEX_ENVIRONMENT':
envmap = {
'type': 'envmap',
'filename': export_ctx.export_texture(color_node.image),
}
# The default background texture does not require emitter strength
if surface_node.type != 'TEX_ENVIRONMENT':
envmap['scale'] = strength
params.update(envmap)
coordinate_mat = Matrix(((0,0,1,0),(1,0,0,0),(0,1,0,0),(0,0,0,1)))
to_world = Matrix()#4x4 Identity
if color_node.inputs["Vector"].is_linked:
vector_node = color_node.inputs["Vector"].links[0].from_node
if vector_node.type != 'MAPPING':
raise NotImplementedError("Node: %s is not supported. Only a mapping node is supported" % vector_node.bl_idname)
if not vector_node.inputs["Vector"].is_linked:
raise NotImplementedError("The node %s should be linked with a Texture coordinate node." % vector_node.bl_idname)
coord_node = vector_node.inputs["Vector"].links[0].from_node
coord_socket = vector_node.inputs["Vector"].links[0].from_socket
if coord_node.type != 'TEX_COORD':
raise NotImplementedError("Unsupported node type: %s." % coord_node.bl_idname)
if coord_socket.name != 'Generated':
raise NotImplementedError("Link should come from 'Generated'.")
#only supported node setup for transform
if vector_node.vector_type != 'TEXTURE':
raise NotImplementedError("Only 'Texture' mapping mode is supported.")
if vector_node.inputs["Location"].is_linked or vector_node.inputs["Rotation"].is_linked or vector_node.inputs["Scale"].is_linked:
raise NotImplementedError("Transfrom inputs shouldn't be linked.")

rotation = vector_node.inputs["Rotation"].default_value.to_matrix()
scale = vector_node.inputs["Scale"].default_value
location = vector_node.inputs["Location"].default_value
for i in range(3):
for j in range(3):
to_world[i][j] = rotation[i][j]
to_world[i][i] *= scale[i]
to_world[i][3] = location[i]
to_world = to_world
#TODO: support other types of mappings (vector, point...)
#change default position, apply transform and change coordinates
params['to_world'] = export_ctx.transform_matrix(to_world @ coordinate_mat)
elif color_node.type == 'RGB':
color = color_node.color
else:
color = socket.default_value
if 'type' not in params: # Not an envmap
radiance = [x * strength for x in color[:3]]
if ignore_background and radiance == [0.05087608844041824]*3:
export_ctx.log("Ignoring Blender's default background...", 'INFO')
return
if np.sum(radiance) == 0:
export_ctx.log("Ignoring background emitter with zero emission.", 'INFO')
return
params.update({
'type': 'constant',
'radiance': export_ctx.spectrum(radiance)
})
raise NotImplementedError("Node type %s is not supported. Consider using an environment texture or RGB node instead." % color_node.bl_idname)
else:
raise NotImplementedError("Only Background and Emission nodes are supported as final nodes for World export, got '%s'" % surface_node.name)
color = socket.default_value
if 'type' not in params: # Not an envmap
radiance = [x * strength for x in color[:3]]
if ignore_background and radiance == [0.05087608844041824]*3:
export_ctx.log("Ignoring Blender's default background...", 'INFO')
return
if np.sum(radiance) == 0:
export_ctx.log("Ignoring background emitter with zero emission.", 'INFO')
return
params.update({
'type': 'constant',
'radiance': export_ctx.spectrum(radiance)
})
else:
# Single color field for emission, no nodes
params.update({
Expand Down
Loading