mat_res = resources[material.name+".mat"]
- st = Statement("method", "")
- if mat_res:
- st.sub.append(tech_res.create_embed_statement("material", mat_res))
-
if material.render_mode=='CUSTOM':
- shader = material.shader
- if shader.endswith(".glsl"):
- shader += ".shader"
- st.sub.append(Statement("shader", shader))
-
- if material.uniforms:
- ss = Statement("uniforms")
- for u in material.uniforms:
- ss.sub.append(Statement("uniform", u.name, *u.values[:u.size]))
- st.sub.append(ss)
- else:
- if material.receive_shadows:
- st.sub.append(Statement("receive_shadows", True))
- if material.image_based_lighting:
- st.sub.append(Statement("image_based_lighting", True))
+ for m in material.render_methods:
+ st = Statement("method", m.tag)
+ if mat_res and m.use_material:
+ st.sub.append(tech_res.create_reference_statement("material", mat_res))
- tech_res.statements.append(st)
-
- if material.shadow_method!='NONE':
- st = Statement("method", "shadow")
- if material.render_mode=='CUSTOM':
- shader = material.shadow_shader or material.shader
+ shader = m.shader
if shader.endswith(".glsl"):
shader += ".shader"
st.sub.append(Statement("shader", shader))
- else:
- st.sub.append(Statement("shader", "occluder.glsl.shader"))
+
+ if material.uniforms:
+ ss = Statement("uniforms")
+ for u in material.uniforms:
+ ss.sub.append(Statement("uniform", u.name, *u.values[:u.size]))
+ st.sub.append(ss)
+
+ tech_res.statements.append(st)
+ else:
+ st = Statement("method", "")
+ if mat_res:
+ st.sub.append(tech_res.create_embed_statement("material", mat_res))
+
+ if material.render_mode!='CUSTOM':
+ if material.receive_shadows:
+ st.sub.append(Statement("receive_shadows", True))
+ if material.image_based_lighting:
+ st.sub.append(Statement("image_based_lighting", True))
+
tech_res.statements.append(st)
+ if material.shadow_method!='NONE':
+ st = Statement("method", "shadow")
+ st.sub.append(Statement("shader", "occluder.glsl.shader"))
+ tech_res.statements.append(st)
+
+ st = Statement("method", "shadow_thsm")
+ st.sub.append(Statement("shader", "occluder_thsm.glsl.shader"))
+ tech_res.statements.append(st)
+
return tech_res
class MaterialExporter:
import os
+def compute_render_method_hash(material):
+ descr = ""
+ for m in material.render_methods:
+ if descr:
+ descr += ","
+ descr += "{}={}".format(m.tag, m.shader)
+ return hash(descr)
+
def check_group(node_tree, group, func):
from .util import get_linked_node_and_socket
self.render_mode = material.render_mode
self.technique = material.technique
- self.shader = material.shader
+ self.render_methods = material.render_methods[:]
self.receive_shadows = material.receive_shadows
self.cast_shadows = (material.shadow_method!='NONE')
self.image_based_lighting = material.image_based_lighting
if self.render_mode=='EXTERNAL' and not self.technique:
raise Exception("Invalid configuration on material {}: No technique for external rendering".format(self.name))
- elif self.render_mode=='CUSTOM' and not self.shader:
- raise Exception("Invalid configuration on material {}: No shader for custom rendering".format(self.name))
+ elif self.render_mode=='CUSTOM' and not self.render_methods:
+ raise Exception("Invalid configuration on material {}: No render methods for custom rendering".format(self.name))
out_node = next((n for n in material.node_tree.nodes if n.type=='OUTPUT_MATERIAL'), None)
if not out_node:
if self.render_mode=='EXTERNAL':
raise Exception("Material atlas with external render mode does not make sense")
- self.shader = materials[0].shader
- if self.shader:
- self.name = "material_atlas_"+os.path.splitext(self.shader)[0]
+ if self.render_mode=='CUSTOM':
+ self.render_methods = materials[0].render_methods
+ else:
+ self.render_methods = None
+ if self.render_methods:
+ self.name = "material_atlas_"+os.path.splitext(self.render_methods[0].shader)[0]
else:
self.name = "material_atlas"
self.receive_shadows = materials[0].receive_shadows
self.cast_shadows = (materials[0].shadow_method!='NONE')
self.materials = materials
self.material_names = [m.name for m in self.materials]
+ self.uniforms = None
+ method_hash = compute_render_method_hash(self)
for m in self.materials:
if m.render_mode!=self.render_mode:
raise Exception("Conflicting render modes in MaterialAtlas constructor")
- if self.render_mode=='CUSTOM' and m.shader!=self.shader:
+ if self.render_mode=='CUSTOM' and compute_render_method_hash(m)!=method_hash:
raise Exception("Conflicting shaders in MaterialAtlas constructor")
if m.receive_shadows!=self.receive_shadows or m.shadow_method!=materials[0].shadow_method:
raise Exception("Conflicting shadow settings in MaterialAtlas constructor")
if not material.material_atlas:
raise Exception("Material is not part of a material atlas")
- shader = material.shader
+ method_hash = compute_render_method_hash(material)
materials = []
for m in context.blend_data.materials:
- if m.material_atlas and m.shader==shader:
+ if m.material_atlas and compute_render_method_hash(m)==method_hash:
materials.append(m)
return MaterialAtlas(materials)
exporter.export_to_directory(context, self.directory)
return {'FINISHED'}
+class AddRenderMethod(bpy.types.Operator):
+ bl_idname = "material.add_render_method"
+ bl_label = "Add Render Method"
+ bl_description = "Add a new render method to the material"
+
+ def execute(self, context):
+ mat = context.active_object.active_material
+ mat.render_methods.add()
+ mat.active_render_method_index = len(mat.uniforms)-1
+
+ return {"FINISHED"}
+
+class RemoveRenderMethod(bpy.types.Operator):
+ bl_idname = "material.remove_render_method"
+ bl_label = "Remove Render Method"
+ bl_description = "Remove the selected render method from the material"
+
+ def execute(self, context):
+ mat = context.active_object.active_material
+ mat.render_methods.remove(mat.active_render_method_index)
+ mat.active_render_method_index = min(mat.active_render_method_index, len(mat.render_methods)-1)
+
+ return {"FINISHED"}
+
class AddUniform(bpy.types.Operator):
bl_idname = "material.add_uniform"
bl_label = "Add Uniform"
return {"FINISHED"}
-classes = [ExportMspGLData, ExportMspGLAnimation, ExportMspGLScene, ExportMspGLProject, AddUniform, RemoveUniform]
+classes = [ExportMspGLData, ExportMspGLAnimation, ExportMspGLScene, ExportMspGLProject, AddRenderMethod,
+ RemoveRenderMethod, AddUniform, RemoveUniform]
def register_operators():
for c in classes:
self.layout.prop(mat, "render_mode")
if mat.render_mode=='CUSTOM':
- self.layout.prop(mat, "shader")
- if mat.shadow_method!='NONE':
- self.layout.prop(mat, "shadow_shader")
+ self.layout.label(text="Render methods")
+ self.layout.template_list("MATERIAL_UL_mspgl_render_methods", "", mat, "render_methods", mat, "active_render_method_index")
+ row = self.layout.row()
+ row.operator("material.add_render_method")
+ row.operator("material.remove_render_method")
+
+ if mat.active_render_method_index<len(mat.render_methods):
+ method = mat.render_methods[mat.active_render_method_index]
+ self.layout.prop(method, "tag")
+ self.layout.prop(method, "shader")
+ self.layout.prop(method, "use_material")
+
+ self.layout.separator()
elif mat.render_mode=='EXTERNAL':
self.layout.prop(mat, "technique")
if mat.render_mode=='BUILTIN':
if scene.eevee.use_gtao:
self.layout.prop(scene, "ao_samples")
+class MspGLRenderMethod(bpy.types.PropertyGroup):
+ tag: bpy.props.StringProperty(name="Tag", description="Tag of the render method")
+ shader: bpy.props.StringProperty(name="Shader", description="Shader to use")
+ use_material: bpy.props.BoolProperty(name="Use material", description="Use material properties in this render method")
+
+class MspGLRenderMethodList(bpy.types.UIList):
+ bl_idname = "MATERIAL_UL_mspgl_render_methods"
+
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
+ method = item
+ if self.layout_type=="GRID":
+ layout.label(text="", icon_value=icon)
+ else:
+ layout.prop(method, "tag", text="", emboss=False, icon_value=icon)
+ layout.label(text=(method.shader or "(no shader)"))
+
class MspGLUniform(bpy.types.PropertyGroup):
name: bpy.props.StringProperty(name="Name", description="Name of the uniform variable")
size: bpy.props.IntProperty(name="Size", description="Number of elements in the uniform", min=1, max=4, default=4)
layout.label(text="({})".format(", ".join("{:.3f}".format(v) for v in uniform.values[:uniform.size])))
classes = [MspGLSceneProperties, MspGLMeshProperties, MspGLObjectProperties, MspGLMaterialProperties,
- MspGLTextureNodeProperties, MspGLRenderProperties, MspGLUniform, MspGLUniformList]
+ MspGLTextureNodeProperties, MspGLLightProperties, MspGLRenderProperties, MspGLRenderMethod,
+ MspGLRenderMethodList, MspGLUniform, MspGLUniformList]
def register_properties():
for c in classes:
bpy.types.Material.render_mode = bpy.props.EnumProperty(name="Render mode", description="How this material should be rendered", default="BUILTIN",
items=(("BUILTIN", "Built-in", "Use built-in shaders"),
- ("CUSTOM", "Custom shader", "Use a custom shader"),
+ ("CUSTOM", "Custom shaders", "Use custom shaders"),
("EXTERNAL", "External technique", "Use an externally defined technique")))
bpy.types.Material.technique = bpy.props.StringProperty(name="Custom technique", description="Name of an external technique to use for rendering")
- bpy.types.Material.shader = bpy.props.StringProperty(name="Custom shader", description="Name of a custom shader to use for rendering")
- bpy.types.Material.shadow_shader = bpy.props.StringProperty(name="Custom shadow shader", description="Name of a custom shader to use for shadow pass")
+ bpy.types.Material.render_methods = bpy.props.CollectionProperty(type=MspGLRenderMethod, name="Render methods", description="Custom render methods to use for rendering")
+ bpy.types.Material.active_render_method_index = bpy.props.IntProperty("Active render method index")
bpy.types.Material.receive_shadows = bpy.props.BoolProperty(name="Receive shadows", description="Receive shadows from a shadow map", default=True)
bpy.types.Material.image_based_lighting = bpy.props.BoolProperty(name="Image based lighting", description="Use an environment map for ambient lighting", default=False)
bpy.types.Material.array_atlas = bpy.props.BoolProperty(name="Texture array atlas", description="The material is stored in a texture array")
bpy.types.Material.array_layer = bpy.props.IntProperty("Texture array layer", description="Layer of the texture array atlas to use")
bpy.types.Material.material_atlas = bpy.props.BoolProperty(name="Material atlas", description="Make this material part of a material atlas")
- bpy.types.Material.uniforms = bpy.props.CollectionProperty(type=MspGLUniform, name="Uniform", description="Uniform variables to add to the technique")
+ bpy.types.Material.uniforms = bpy.props.CollectionProperty(type=MspGLUniform, name="Uniforms", description="Uniform variables to add to the technique")
bpy.types.Material.active_uniform_index = bpy.props.IntProperty("Active uniform index")
bpy.types.ShaderNodeTexImage.use_mipmap = bpy.props.BoolProperty(name="Use mipmaps", description="Use mipmaps (automatically generated) for the texture", default=True)