Example #1
0
def parse(material: Material, mat_data,
          mat_users: Dict[Material, List[Object]], mat_armusers) -> tuple:
    wrd = bpy.data.worlds['Arm']
    rpdat = arm.utils.get_rp()

    needs_sss = material_needs_sss(material)
    if needs_sss and rpdat.rp_sss_state != 'Off' and '_SSS' not in wrd.world_defs:
        # Must be set before calling make_shader.build()
        wrd.world_defs += '_SSS'

    # No batch - shader data per material
    if material.arm_custom_material != '':
        rpasses = ['mesh']

        con = {'vertex_elements': []}
        con['vertex_elements'].append({'name': 'pos', 'data': 'short4norm'})
        con['vertex_elements'].append({'name': 'nor', 'data': 'short2norm'})
        con['vertex_elements'].append({'name': 'tex', 'data': 'short2norm'})
        con['vertex_elements'].append({'name': 'tex1', 'data': 'short2norm'})

        sd = {'contexts': [con]}
        shader_data_name = material.arm_custom_material
        bind_constants = {'mesh': []}
        bind_textures = {'mesh': []}

        make_shader.make_instancing_and_skinning(material, mat_users)
    elif not wrd.arm_batch_materials or material.name.startswith('armdefault'):
        rpasses, shader_data, shader_data_name, bind_constants, bind_textures = make_shader.build(
            material, mat_users, mat_armusers)
        sd = shader_data.sd
    else:
        rpasses, shader_data, shader_data_name, bind_constants, bind_textures = mat_batch.get(
            material)
        sd = shader_data.sd

    sss_used = False

    # Material
    for rp in rpasses:
        c = {
            'name': rp,
            'bind_constants': [] + bind_constants[rp],
            'bind_textures': [] + bind_textures[rp],
        }
        mat_data['contexts'].append(c)

        if rp == 'mesh':
            c['bind_constants'].append({
                'name': 'receiveShadow',
                'bool': material.arm_receive_shadow
            })

            if material.arm_material_id != 0:
                c['bind_constants'].append({
                    'name': 'materialID',
                    'int': material.arm_material_id
                })

                if material.arm_material_id == 2:
                    wrd.world_defs += '_Hair'

            elif rpdat.rp_sss_state != 'Off':
                const = {'name': 'materialID'}
                if needs_sss:
                    const['int'] = 2
                    sss_used = True
                else:
                    const['int'] = 0
                c['bind_constants'].append(const)

            # TODO: Mesh only material batching
            if wrd.arm_batch_materials:
                # Set textures uniforms
                if len(c['bind_textures']) > 0:
                    c['bind_textures'] = []
                    for node in material.node_tree.nodes:
                        if node.type == 'TEX_IMAGE':
                            tex_name = arm.utils.safesrc(node.name)
                            tex = cycles.make_texture(node, tex_name)
                            # Empty texture
                            if tex is None:
                                tex = {'name': tex_name, 'file': ''}
                            c['bind_textures'].append(tex)

                # Set marked inputs as uniforms
                for node in material.node_tree.nodes:
                    for inp in node.inputs:
                        if inp.is_uniform:
                            uname = arm.utils.safesrc(
                                inp.node.name) + arm.utils.safesrc(
                                    inp.name)  # Merge with cycles module
                            c['bind_constants'].append({
                                'name':
                                uname,
                                cycles.glsl_type(inp.type):
                                glsl_value(inp.default_value)
                            })

        elif rp == 'translucent':
            c['bind_constants'].append({
                'name': 'receiveShadow',
                'bool': material.arm_receive_shadow
            })

    if wrd.arm_single_data_file:
        mat_data['shader'] = shader_data_name
    else:
        # Make sure that custom materials are not expected to be in .arm format
        ext = '' if wrd.arm_minimize and material.arm_custom_material == "" else '.json'
        mat_data['shader'] = shader_data_name + ext + '/' + shader_data_name

    return sd, rpasses, sss_used
Example #2
0
def parse(material, mat_data, mat_users, mat_armusers):
    wrd = bpy.data.worlds['Arm']
    rpdat = arm.utils.get_rp()

    # No batch - shader data per material
    if material.arm_custom_material != '':
        rpasses = ['mesh']
        sd = {}
        sd['contexts'] = []
        con = {}
        con['vertex_elements'] = []
        elem = {}
        elem['name'] = 'pos'
        elem['data'] = 'short4norm'
        con['vertex_elements'].append(elem)
        elem = {}
        elem['name'] = 'nor'
        elem['data'] = 'short2norm'
        con['vertex_elements'].append(elem)
        sd['contexts'].append(con)
        shader_data_name = material.arm_custom_material
        bind_constants = {}
        bind_constants['mesh'] = []
        bind_textures = {}
        bind_textures['mesh'] = []
    elif not wrd.arm_batch_materials or material.name.startswith('armdefault'):
        rpasses, shader_data, shader_data_name, bind_constants, bind_textures = make_shader.build(
            material, mat_users, mat_armusers)
        sd = shader_data.sd
    else:
        rpasses, shader_data, shader_data_name, bind_constants, bind_textures = mat_batch.get(
            material)
        sd = shader_data.sd

    # Material
    for rp in rpasses:

        c = {}
        c['name'] = rp
        c['bind_constants'] = [] + bind_constants[rp]
        c['bind_textures'] = [] + bind_textures[rp]
        mat_data['contexts'].append(c)

        if rp == 'mesh':
            const = {}
            const['name'] = 'receiveShadow'
            const['bool'] = material.arm_receive_shadow
            c['bind_constants'].append(const)

            if material.arm_material_id != 0:
                const = {}
                const['name'] = 'materialID'
                const['int'] = material.arm_material_id
                c['bind_constants'].append(const)
                if material.arm_material_id == 2:
                    wrd.world_defs += '_Hair'
            elif rpdat.rp_sss_state == 'On':
                sss = False
                sss_node = arm.node_utils.get_node_by_type(
                    material.node_tree, 'SUBSURFACE_SCATTERING')
                if sss_node != None and sss_node.outputs[
                        0].is_linked:  # Check linked node
                    sss = True
                sss_node = arm.node_utils.get_node_by_type(
                    material.node_tree, 'BSDF_PRINCIPLED')
                if sss_node != None and sss_node.outputs[0].is_linked and (
                        sss_node.inputs[1].is_linked
                        or sss_node.inputs[1].default_value != 0.0):
                    sss = True
                sss_node = arm.node_utils.get_node_armorypbr(
                    material.node_tree)
                if sss_node != None and sss_node.outputs[0].is_linked and (
                        sss_node.inputs[8].is_linked
                        or sss_node.inputs[8].default_value != 0.0):
                    sss = True
                const = {}
                const['name'] = 'materialID'
                if sss:
                    const['int'] = 2
                else:
                    const['int'] = 0
                c['bind_constants'].append(const)

            # TODO: Mesh only material batching
            if wrd.arm_batch_materials:
                # Set textures uniforms
                if len(c['bind_textures']) > 0:
                    c['bind_textures'] = []
                    for node in material.node_tree.nodes:
                        if node.type == 'TEX_IMAGE':
                            tex_name = arm.utils.safesrc(node.name)
                            tex = cycles.make_texture(node, tex_name)
                            if tex == None:  # Empty texture
                                tex = {}
                                tex['name'] = tex_name
                                tex['file'] = ''
                            c['bind_textures'].append(tex)

                # Set marked inputs as uniforms
                for node in material.node_tree.nodes:
                    for inp in node.inputs:
                        if inp.is_uniform:
                            uname = arm.utils.safesrc(
                                inp.node.name) + arm.utils.safesrc(
                                    inp.name)  # Merge with cycles
                            const = {}
                            const['name'] = uname
                            const[glsl_type(inp.type)] = glsl_value(
                                inp.default_value)
                            c['bind_constants'].append(const)

        elif rp == 'translucent':
            const = {}
            const['name'] = 'receiveShadow'
            const['bool'] = material.arm_receive_shadow
            c['bind_constants'].append(const)

    ext = '' if wrd.arm_minimize else '.json'
    mat_data['shader'] = shader_data_name + ext + '/' + shader_data_name

    return sd, rpasses
Example #3
0
def parse_tex_image(node: bpy.types.ShaderNodeTexImage, out_socket: bpy.types.NodeSocket, state: ParserState) -> Union[floatstr, vec3str]:
    if state.context == ParserContext.OBJECT:
        # Color or Alpha output
        use_color_out = out_socket == node.outputs[0]

        # Already fetched
        if c.is_parsed(c.store_var_name(node)):
            if use_color_out:
                return f'{c.store_var_name(node)}.rgb'
            else:
                return f'{c.store_var_name(node)}.a'

        tex_name = c.node_name(node.name)
        tex = c.make_texture(node, tex_name)
        tex_link = None
        tex_default_file = None
        is_arm_mat_param = None
        if node.arm_material_param:
            tex_link = node.name
            is_arm_mat_param = True

        if tex is not None:
            state.curshader.write_textures += 1
            if node.arm_material_param and tex['file'] is not None:
                tex_default_file = tex['file']
            if use_color_out:
                to_linear = node.image is not None and node.image.colorspace_settings.name == 'sRGB'
                res = f'{c.texture_store(node, tex, tex_name, to_linear, tex_link=tex_link, default_value=tex_default_file, is_arm_mat_param=is_arm_mat_param)}.rgb'
            else:
                res = f'{c.texture_store(node, tex, tex_name, tex_link=tex_link, default_value=tex_default_file, is_arm_mat_param=is_arm_mat_param)}.a'
            state.curshader.write_textures -= 1
            return res

        # Empty texture
        elif node.image is None:
            tex = {
                'name': tex_name,
                'file': ''
            }
            if use_color_out:
                return '{0}.rgb'.format(c.texture_store(node, tex, tex_name, to_linear=False, tex_link=tex_link, is_arm_mat_param=is_arm_mat_param))
            return '{0}.a'.format(c.texture_store(node, tex, tex_name, to_linear=True, tex_link=tex_link, is_arm_mat_param=is_arm_mat_param))

        # Pink color for missing texture
        else:
            tex_store = c.store_var_name(node)

            if use_color_out:
                state.parsed.add(tex_store)
                state.curshader.write_textures += 1
                state.curshader.write(f'vec4 {tex_store} = vec4(1.0, 0.0, 1.0, 1.0);')
                state.curshader.write_textures -= 1
                return f'{tex_store}.rgb'
            else:
                state.curshader.write(f'vec4 {tex_store} = vec4(1.0, 0.0, 1.0, 1.0);')
                return f'{tex_store}.a'

    # World context
    # TODO: Merge with above implementation to also allow mappings other than using view coordinates
    else:
        world = state.world
        world.world_defs += '_EnvImg'

        # Background texture
        state.curshader.add_uniform('sampler2D envmap', link='_envmap')
        state.curshader.add_uniform('vec2 screenSize', link='_screenSize')

        image = node.image
        filepath = image.filepath

        if image.packed_file is not None:
            # Extract packed data
            filepath = arm.utils.build_dir() + '/compiled/Assets/unpacked'
            unpack_path = arm.utils.get_fp() + filepath
            if not os.path.exists(unpack_path):
                os.makedirs(unpack_path)
            unpack_filepath = unpack_path + '/' + image.name
            if not os.path.isfile(unpack_filepath) or os.path.getsize(unpack_filepath) != image.packed_file.size:
                with open(unpack_filepath, 'wb') as f:
                    f.write(image.packed_file.data)
            assets.add(unpack_filepath)
        else:
            # Link image path to assets
            assets.add(arm.utils.asset_path(image.filepath))

        # Reference image name
        tex_file = arm.utils.extract_filename(image.filepath)
        base = tex_file.rsplit('.', 1)
        ext = base[1].lower()

        if ext == 'hdr':
            target_format = 'HDR'
        else:
            target_format = 'JPEG'

        # Generate prefiltered envmaps
        world.arm_envtex_name = tex_file
        world.arm_envtex_irr_name = tex_file.rsplit('.', 1)[0]

        disable_hdr = target_format == 'JPEG'
        from_srgb = image.colorspace_settings.name == "sRGB"

        rpdat = arm.utils.get_rp()
        mip_count = world.arm_envtex_num_mips
        mip_count = write_probes.write_probes(filepath, disable_hdr, from_srgb, mip_count, arm_radiance=rpdat.arm_radiance)

        world.arm_envtex_num_mips = mip_count

        # Will have to get rid of gl_FragCoord, pass texture coords from vertex shader
        state.curshader.write_init('vec2 texco = gl_FragCoord.xy / screenSize;')
        return 'texture(envmap, vec2(texco.x, 1.0 - texco.y)).rgb * envmapStrength'
Example #4
0
def parse(material: Material, mat_data,
          mat_users: Dict[Material, List[Object]], mat_armusers):
    wrd = bpy.data.worlds['Arm']
    rpdat = arm.utils.get_rp()

    # No batch - shader data per material
    if material.arm_custom_material != '':
        rpasses = ['mesh']

        con = {'vertex_elements': []}
        con['vertex_elements'].append({'name': 'pos', 'data': 'short4norm'})
        con['vertex_elements'].append({'name': 'nor', 'data': 'short2norm'})
        con['vertex_elements'].append({'name': 'tex', 'data': 'short2norm'})
        con['vertex_elements'].append({'name': 'tex1', 'data': 'short2norm'})

        sd = {'contexts': [con]}
        shader_data_name = material.arm_custom_material
        bind_constants = {'mesh': []}
        bind_textures = {'mesh': []}

        make_shader.make_instancing_and_skinning(material, mat_users)
    elif not wrd.arm_batch_materials or material.name.startswith('armdefault'):
        rpasses, shader_data, shader_data_name, bind_constants, bind_textures = make_shader.build(
            material, mat_users, mat_armusers)
        sd = shader_data.sd
    else:
        rpasses, shader_data, shader_data_name, bind_constants, bind_textures = mat_batch.get(
            material)
        sd = shader_data.sd

    # Material
    for rp in rpasses:
        c = {
            'name': rp,
            'bind_constants': [] + bind_constants[rp],
            'bind_textures': [] + bind_textures[rp],
        }
        mat_data['contexts'].append(c)

        if rp == 'mesh':
            c['bind_constants'].append({
                'name': 'receiveShadow',
                'bool': material.arm_receive_shadow
            })

            if material.arm_material_id != 0:
                c['bind_constants'].append({
                    'name': 'materialID',
                    'int': material.arm_material_id
                })

                if material.arm_material_id == 2:
                    wrd.world_defs += '_Hair'

            elif rpdat.rp_sss_state == 'On':
                sss = False
                sss_node = arm.node_utils.get_node_by_type(
                    material.node_tree, 'SUBSURFACE_SCATTERING')
                if sss_node is not None and sss_node.outputs[
                        0].is_linked:  # Check linked node
                    sss = True
                sss_node = arm.node_utils.get_node_by_type(
                    material.node_tree, 'BSDF_PRINCIPLED')
                if sss_node is not None and sss_node.outputs[0].is_linked and (
                        sss_node.inputs[1].is_linked
                        or sss_node.inputs[1].default_value != 0.0):
                    sss = True
                sss_node = arm.node_utils.get_node_armorypbr(
                    material.node_tree)
                if sss_node is not None and sss_node.outputs[0].is_linked and (
                        sss_node.inputs[8].is_linked
                        or sss_node.inputs[8].default_value != 0.0):
                    sss = True

                const = {'name': 'materialID'}
                if sss:
                    const['int'] = 2
                else:
                    const['int'] = 0
                c['bind_constants'].append(const)

            # TODO: Mesh only material batching
            if wrd.arm_batch_materials:
                # Set textures uniforms
                if len(c['bind_textures']) > 0:
                    c['bind_textures'] = []
                    for node in material.node_tree.nodes:
                        if node.type == 'TEX_IMAGE':
                            tex_name = arm.utils.safesrc(node.name)
                            tex = cycles.make_texture(node, tex_name)
                            # Empty texture
                            if tex is None:
                                tex = {'name': tex_name, 'file': ''}
                            c['bind_textures'].append(tex)

                # Set marked inputs as uniforms
                for node in material.node_tree.nodes:
                    for inp in node.inputs:
                        if inp.is_uniform:
                            uname = arm.utils.safesrc(
                                inp.node.name) + arm.utils.safesrc(
                                    inp.name)  # Merge with cycles module
                            c['bind_constants'].append({
                                'name':
                                uname,
                                cycles.glsl_type(inp.type):
                                glsl_value(inp.default_value)
                            })

        elif rp == 'translucent':
            c['bind_constants'].append({
                'name': 'receiveShadow',
                'bool': material.arm_receive_shadow
            })

    if wrd.arm_single_data_file:
        mat_data['shader'] = shader_data_name
    else:
        ext = '' if wrd.arm_minimize else '.json'
        mat_data['shader'] = shader_data_name + ext + '/' + shader_data_name

    return sd, rpasses