def execute(self, context):
     
     if bpy.context.scene.exportGlslOptions == "0":
         Scene = bpy.context.scene
         Materials = bpy.data.materials
         
         #self.filepath = bpy.path.relpath(self.filepath)
         self.filepath = os.path.dirname(self.filepath)
         
         for mat in Materials:
 
             Shader = gpu.export_shader(Scene,mat)
             frag = open(self.filepath + "\mat_" + mat.name + ".frag","w")
             vertex = open(self.filepath + "\mat_" + mat.name + ".vert" ,"w")
             
             frag.write(Shader["fragment"])
             vertex.write(Shader["vertex"])
             
     elif bpy.context.scene.exportGlslOptions == "1":
         Scene = bpy.context.scene
         
         ObjList = bpy.context.selected_objects
         for obj in ObjList:
             print()
             print(obj)
             for matSL in obj.material_slots:
                 
                 mat = matSL.material
                 #print(mat)
         
                 #self.filepath = bpy.path.relpath(self.filepath)
                 path = os.path.dirname(self.filepath)
                 print(path)
         
                 Shader = gpu.export_shader(Scene,mat)
                 
                 frag = open(path + "\mat_" + mat.name + ".frag","w")
                 vertex = open(path + "\mat_" + mat.name + ".vert" ,"w")
                 
                 frag.write(Shader["fragment"])
                 vertex.write(Shader["vertex"])
             
     elif bpy.context.scene.exportGlslOptions == "2":
         Scene = bpy.context.scene
         mat = bpy.context.material
         
         #self.filepath = bpy.path.relpath(self.filepath)
         self.filepath = os.path.dirname(self.filepath)
 
         Shader = gpu.export_shader(Scene,mat)
         frag = open(self.filepath + "\mat_" + mat.name + ".frag","w")
         vertex = open(self.filepath + "\mat_" + mat.name + ".vert" ,"w")
         
         frag.write(Shader["fragment"])
         vertex.write(Shader["vertex"])
     
             
     return {'FINISHED'}
Example #2
0
def get_dynamic_constants(mat, scn, paths):
    # Example:
    #paths = [
    #'node_tree.nodes["slicep"].outputs[0].default_value',
    #'node_tree.nodes["slicen"].outputs[0].default_value',
    #]

    shader = gpu.export_shader(scn, mat)
    code = shader['fragment'].rsplit('}', 2)[1]
    sentinel = 0.406198  # random()
    while ("%f" % sentinel) in code:
        sentinel = random.random()
    # We're assuming "%f"%sentinel yelds exactly the same string
    # as the code generator (sprintf is used in both cases)

    # Possible optimization: use a different sentinel per path
    # and call export_shader and update() only once

    varnames = []
    for p in paths:
        try:
            orig_obj = mat.path_resolve(p)
        except ValueError:
            varnames.append(None)
            continue
        obj, attr = ('.' + p).rsplit('.', 1)
        obj = eval('mat' + obj)
        print(obj, attr)
        is_vector = hasattr(orig_obj, '__getitem__')
        if is_vector:
            orig_val = orig_obj[0]
            orig_obj[0] = sentinel
        else:
            setattr(obj, attr, sentinel)
        scn.update()
        sh = gpu.export_shader(scn, mat)
        c = sh['fragment'].rsplit('}', 2)[1]
        # restore original
        if is_vector:
            orig_obj[0] = orig_val
        else:
            setattr(obj, attr, orig_obj)
        pos = c.find("%f" % sentinel)
        if pos != -1:
            varnames.append(c[:pos].rsplit(' ', 3)[1])
        else:
            varnames.append(None)
    if any(varnames):
        scn.update()
    return varnames
Example #3
0
def get_dynamic_constants(mat, scn, paths):
    # Example:
    #paths = [
        #'node_tree.nodes["slicep"].outputs[0].default_value',
        #'node_tree.nodes["slicen"].outputs[0].default_value',
    #]
    
    shader = gpu.export_shader(scn, mat)
    code = shader['fragment'].rsplit('}', 2)[1]
    sentinel = 0.406198 # random()
    while ("%f"%sentinel) in code:
        sentinel = random.random()
    # We're assuming "%f"%sentinel yelds exactly the same string
    # as the code generator (sprintf is used in both cases)
    
    # Possible optimization: use a different sentinel per path
    # and call export_shader and update() only once
    
    varnames = []
    for p in paths:
        try:
            orig_obj = mat.path_resolve(p)
        except ValueError:
            varnames.append(None)
            continue
        obj, attr = ('.'+p).rsplit('.', 1)
        obj = eval('mat'+obj)
        print(obj, attr)
        is_vector = hasattr(orig_obj, '__getitem__')
        if is_vector:
            orig_val = orig_obj[0]
            orig_obj[0] = sentinel
        else:
            setattr(obj, attr, sentinel)
        scn.update()
        sh = gpu.export_shader(scn, mat)
        c = sh['fragment'].rsplit('}', 2)[1]
        # restore original
        if is_vector:
            orig_obj[0] = orig_val
        else:
            setattr(obj, attr, orig_obj)
        pos = c.find("%f"%sentinel)
        if pos!=-1:
            varnames.append(c[:pos].rsplit(' ',3)[1])
        else:
            varnames.append(None)
    if any(varnames):
        scn.update()
    return varnames
def invoke(all_data, target_data, material, context, fname, flags=None):
    dirname = os.path.dirname(fname)
    if 'paths' in all_data['scene']:
        dirname = os.path.join(dirname,
                               all_data['scene']['paths']['materials'])
    if not os.path.exists(dirname):
        os.makedirs(dirname)
    sha = gpu.export_shader(context.scene, material)
    add_lamp_name_for_unf_type16(sha)
    add_environment_values_for_unf_type21_28(sha, material,
                                             context.scene.world)
    find_and_correct_spot_light_uniforms(material, sha)
    replace_names_for_shared_uniforms(sha)
    replace_common_uniforms(sha)
    replace_sampler_for_textures(material, sha)
    replace_attributes(material, sha)
    f = open(os.path.join(dirname, material.name + '.vert'), 'w')
    f.write(sha['vertex'])
    f.close()
    f = open(os.path.join(dirname, material.name + '.frag'), 'w')
    f.write(sha['fragment'])
    f.close()
    target_data['vert'] = material.name + '.vert'
    target_data['frag'] = material.name + '.frag'
    target_data['name'] = material.name
    uniforms = []
    for unf in sha['uniforms']:
        uniform = {}
        for key, val in tuple(unf.items()):
            if key == 'lamp':
                val = val.name
            elif key == 'image':
                if unf['type'] != 'p3d_texture':
                    saved_img = save_image(
                        val, fname, all_data['scene']['paths']['images'])
                    #val = saved_img
                    val = os.path.split(saved_img)[1]
                else:
                    val = ''
                #val = os.path.split(val.filepath)[1]
            elif key == 'texpixels':
                #val = ''
                fname = material.name + '-' + unf['varname'] + '.dat'
                f = open(os.path.join(dirname, fname), 'wb')
                f.write(val)
                f.close()
                #val = os.path.join(all_data['scene']['paths']['materials'],
                #                   fname).replace('\\','/')
                val = fname

            if not key in uniform.keys():
                uniform[key] = val
        uniforms.append(uniform)
    target_data['uniforms'] = uniforms
    def write_shader(self, scene, mat):
        """Writes a shader for the given material to two GLSL files."""

        shader = gpu.export_shader(scene, mat)

        path = os.path.join(self.filepath, "mat_%s.%%s" % mat.name)
        with open(path % "frag", "w") as frag:
            frag.write(shader["fragment"])

        with open(path % "vert", "w") as vertex:
            vertex.write(shader["vertex"])

        self.export_count += 1
def set_shader_lib(fragment='', mat=None, scn=None):
    global SHADER_LIB
    if not SHADER_LIB or debug_lib:
        if not fragment:
            if mat and scn:
                import gpu
                fragment = gpu.export_shader(scn, mat)['fragment']
            else:
                raise Exception("Wrong arguments")
        print('Converting shader lib')
        parts = fragment.rsplit('}', 2)
        SHADER_LIB = \
"""
#ifdef GL_ES
#if __VERSION__ < 130
#extension GL_OES_standard_derivatives : enable
#extension GL_EXT_shader_texture_lod : enable
#else
#define texture2D texture
#define texture2DLod textureLod
#define textureCube texture
#define textureCubeLod textureLod
#define texture2DProj textureProj
#define sample sample_
#endif
precision highp float;
precision highp int;
#if __VERSION__ < 130
#ifdef GL_EXT_shader_texture_lod
#define texture2DLod texture2DLodEXT
#define textureCubeLod textureCubeLodEXT
#else
#define texture2DLod texture2D
#define textureCubeLod textureCube
#endif
#endif
#endif
""" \
        +defines+uniforms+(parts[0]+'}').replace('\r','')+'\n'
        SHADER_LIB = do_lib_replacements(SHADER_LIB).encode('ascii',
                                                            'ignore').decode()
        # This section below is necessary because something is interpreted as non ascii for some reason
        # despite the line above (which is also necessary, mysteriously...)
        splits = SHADER_LIB.split('BIT_OPERATIONS', 2)
        if len(splits) == 3:
            a, b, c = splits
            SHADER_LIB = a + 'BIT_OPERATIONS\n#endif' + c
        if debug_lib:
            open('/tmp/shader_lib.orig.glsl',
                 'w').write((parts[0] + '}').replace('\r', '') + '\n')
            open('/tmp/shader_lib.glsl', 'w').write(SHADER_LIB)
Example #7
0
def export_materials(materials, shaders, programs, techniques):
    def export_material(material):
        return {
                'values': {
                    'diffuse': list((material.diffuse_color * material.diffuse_intensity)[:]) + [material.alpha],
                    'specular': list((material.specular_color * material.specular_intensity)[:]) + [material.specular_alpha],
                    'emission': list((material.diffuse_color * material.emit)[:]) + [material.alpha],
                    'ambient': [material.ambient] * 4,
                    'shininess': material.specular_hardness,
                    'textures': [ts.texture.name for ts in material.texture_slots if ts and ts.texture.type == 'IMAGE'],
                    'uv_layers': [ts.uv_layer for ts in material.texture_slots if ts]
                }
            }
    exp_materials = {}
    for material in materials:
        exp_materials[material.name] = export_material(material)

        if not EXPORT_SHADERS:
            continue

        # Handle shaders
        shader_data = gpu.export_shader(bpy.context.scene, material)
        fs_bytes = shader_data['fragment'].encode()
        fs_uri = 'data:text/plain;base64,' + base64.b64encode(fs_bytes).decode('ascii')
        shaders[material.name+'FS'] = {'type': 35632, 'uri': fs_uri}
        vs_bytes = shader_data['vertex'].encode()
        vs_uri = 'data:text/plain;base64,' + base64.b64encode(vs_bytes).decode('ascii')
        shaders[material.name+'VS'] = {'type': 35633, 'uri': vs_uri}

        # Handle programs
        programs[material.name+'Program'] = {
            'attributes' : [],
            'fragmentShader' : material.name+'FS',
            'vertexShader' : material.name+'VS',
        }

        # Handle techniques
        techniques['material.name'+'Technique'] = {
            'program' : material.name+'Program',
            'attributes' : {a['varname'] : a['varname'] for a in shader_data['attributes']},
            'uniforms' : {u['varname'] : u['varname'] for u in shader_data['uniforms']},
        }

    return exp_materials
Example #8
0
    def __init__(self, blenderMaterial, useGL3=False):
        self.resourceFiles = []
        self._blenderMaterialName = blenderMaterial.name
        self.shadersDirectoryName = "assets/shaders/"
        self._materialPropertyName = propertyName(blenderMaterial.name)

        try:
            shader = gpu.export_shader(bpy.context.scene, blenderMaterial)
            vertexShaderName = os.path.join(
                self.shadersDirectoryName,
                self._materialPropertyName.lower() + ".vert")
            self.resourceFiles.append(vertexShaderName)
            with open(vertexShaderName, "w") as f:
                f.write(preprocessVertexShaderForQt3D(shader["vertex"]))
            fragmentShaderName = os.path.join(
                self.shadersDirectoryName,
                self._materialPropertyName.lower() + ".frag")
            self.resourceFiles.append(fragmentShaderName)
            with open(fragmentShaderName, "w") as f:
                f.write(preprocessFragmentShaderForQt3D(shader["fragment"]))
            parameters = [
                "            Parameter { name: \"" + param["varname"] +
                "\"; value: " + parameterUniformValue(param) + " }"
                for param in shader["uniforms"]
            ]
        except:
            pass

        self._content = (
            "    readonly property Material " + self._materialPropertyName +
            ": PhongMaterial {\n"
            "        ambient: " + blenderColorToQColor(
                [blenderMaterial.ambient * 0.2 for i in range(0, 3)]) + "\n"
            "        diffuse: " +
            blenderColorToQColor(blenderMaterial.diffuse_color) + "\n"
            "        specular: " +
            blenderColorToQColor(blenderMaterial.specular_color) + "\n"
            "    }\n")
Example #9
0
def embed_shaders(scene):
    for mat in bpy.data.materials:
        shader = gpu.export_shader(scene, mat)

        mat.gamesettings.fragment_shader = shader["fragment"]
        mat.gamesettings.vertex_shader = shader["vertex"]

        attributes = shader["attributes"]
        uniforms = []

        for u in shader["uniforms"]:
            u2 = {}
            uniforms.append(u2)

            for k2 in u:
                u2[k2] = u[k2]

            if "lamp" in u2 and u2["lamp"] is not None:
                u2["lamp"] = u2["lamp"].name
            if "image" in u2 and u2["image"] is not None:
                u2["image"] = u2["image"].name

        mat.gamesettings.uniforms = json.dumps(uniforms)
        mat.gamesettings.attributes = json.dumps(attributes)
Example #10
0
def embed_shaders(scene):
  for mat in bpy.data.materials:
    shader = gpu.export_shader(scene, mat);
    
    mat.gamesettings.fragment_shader = shader["fragment"]
    mat.gamesettings.vertex_shader = shader["vertex"]
    
    attributes = shader["attributes"]
    uniforms = [];
    
    for u in shader["uniforms"]:
      u2 = {};
      uniforms.append(u2)
      
      for k2 in u:
        u2[k2] = u[k2]
      
      if "lamp" in u2 and u2["lamp"] is not None:
        u2["lamp"] = u2["lamp"].name
      if "image" in u2 and u2["image"] is not None:
        u2["image"] = u2["image"].name
      
    mat.gamesettings.uniforms = json.dumps(uniforms)
    mat.gamesettings.attributes = json.dumps(attributes)
Example #11
0
def export_materials(settings, materials, shaders, programs, techniques):
    def export_material(material):
        all_textures = [ts for ts in material.texture_slots if ts and ts.texture.type == 'IMAGE']
        diffuse_textures = [t.texture.name for t in all_textures if t.use_map_color_diffuse]
        emission_textures = [t.texture.name for t in all_textures if t.use_map_emit]
        specular_textures = [t.texture.name for t in all_textures if t.use_map_color_spec]
        diffuse_color = list((material.diffuse_color * material.diffuse_intensity)[:]) + [material.alpha]
        emission_color = list((material.diffuse_color * material.emit)[:]) + [material.alpha]
        specular_color = list((material.specular_color * material.specular_intensity)[:]) + [material.specular_alpha]
        technique = 'PHONG'
        if material.use_shadeless:
            technique = 'CONSTANT'
        elif material.specular_intensity == 0.0:
            technique = 'LAMBERT'
        elif material.specular_shader == 'BLINN':
            technique = 'BLINN'
        return {
                'extensions': {
                    'KHR_materials_common': {
                        'technique': technique,
                        'values': {
                            'ambient': ([material.ambient]*3) + [1.0],
                            'diffuse': diffuse_textures[-1] if diffuse_textures else diffuse_color,
                            'doubleSided': not material.game_settings.use_backface_culling,
                            'emission': emission_textures[-1] if emission_textures else emission_color,
                            'specular': specular_textures[-1] if specular_textures else specular_color,
                            'shininess': material.specular_hardness,
                            'transparency': material.alpha,
                            'transparent': material.use_transparency,
                        }
                    }
                }
            }
    exp_materials = {}
    for material in materials:
        if settings['materials_export_shader'] == False:
            exp_materials[material.name] = export_material(material)
        else:
            # Handle shaders
            shader_data = gpu.export_shader(bpy.context.scene, material)
            if settings['asset_profile'] == 'DESKTOP':
                shader_converter.to_130(shader_data)
            else:
                shader_converter.to_web(shader_data)

            fs_bytes = shader_data['fragment'].encode()
            fs_uri = 'data:text/plain;base64,' + base64.b64encode(fs_bytes).decode('ascii')
            shaders[material.name+'FS'] = {'type': 35632, 'uri': fs_uri}
            vs_bytes = shader_data['vertex'].encode()
            vs_uri = 'data:text/plain;base64,' + base64.b64encode(vs_bytes).decode('ascii')
            shaders[material.name+'VS'] = {'type': 35633, 'uri': vs_uri}

            # Handle programs
            programs[material.name+'Program'] = {
                'attributes' : [a['varname'] for a in shader_data['attributes']],
                'fragmentShader' : material.name+'FS',
                'vertexShader' : material.name+'VS',
            }

            # Handle parameters/values
            values = {}
            parameters = {}
            for attribute in shader_data['attributes']:
                name = attribute['varname']
                semantic = gpu_luts.TYPE_TO_SEMANTIC[attribute['type']]
                _type = gpu_luts.DATATYPE_TO_GLTF_TYPE[attribute['datatype']]
                parameters[name] = {'semantic': semantic, 'type': _type}

            for uniform in shader_data['uniforms']:
                valname = gpu_luts.TYPE_TO_NAME.get(uniform['type'], uniform['varname'])
                rnaname = valname
                semantic = None
                node = None
                value = None

                if uniform['varname'] == 'bl_ModelViewMatrix':
                    semantic = 'MODELVIEW'
                elif uniform['varname'] == 'bl_ProjectionMatrix':
                    semantic = 'PROJECTION'
                elif uniform['varname'] == 'bl_NormalMatrix':
                    semantic = 'MODELVIEWINVERSETRANSPOSE'
                else:
                    if uniform['type'] in gpu_luts.LAMP_TYPES:
                        node = uniform['lamp'].name
                        valname = node + '_' + valname
                        semantic = gpu_luts.TYPE_TO_SEMANTIC.get(uniform['type'], None)
                        if not semantic:
                            lamp_obj = bpy.data.objects[node]
                            value = getattr(lamp_obj.data, rnaname)
                    elif uniform['type'] in gpu_luts.MIST_TYPES:
                        valname = 'mist_' + valname
                        mist_settings = bpy.context.scene.world.mist_settings
                        if valname == 'mist_color':
                            value = bpy.context.scene.world.horizon_color
                        else:
                            value = getattr(mist_settings, rnaname)

                        if valname == 'mist_falloff':
                            value = 0.0 if value == 'QUADRATIC' else 1.0 if 'LINEAR' else 2.0
                    elif uniform['type'] in gpu_luts.WORLD_TYPES:
                        world = bpy.context.scene.world
                        value = getattr(world, rnaname)
                    elif uniform['type'] in gpu_luts.MATERIAL_TYPES:
                        value = gpu_luts.DATATYPE_TO_CONVERTER[uniform['datatype']](getattr(material, rnaname))
                        values[valname] = value
                    elif uniform['type'] == gpu.GPU_DYNAMIC_SAMPLER_2DIMAGE:
                        for ts in [ts for ts in material.texture_slots if ts and ts.texture.type == 'IMAGE']:
                            if ts.texture.image.name == uniform['image'].name:
                                value = ts.texture.name
                                values[uniform['varname']] = value
                    else:
                        print('Unconverted uniform:', uniform)

                parameter = {}
                if semantic:
                    parameter['semantic'] = semantic
                    if node:
                        parameter['node'] = node
                else:
                    parameter['value'] = gpu_luts.DATATYPE_TO_CONVERTER[uniform['datatype']](value)
                if uniform['type'] == gpu.GPU_DYNAMIC_SAMPLER_2DIMAGE:
                    parameter['type'] = 35678 #SAMPLER_2D
                else:
                    parameter['type'] = gpu_luts.DATATYPE_TO_GLTF_TYPE[uniform['datatype']]
                parameters[valname] = parameter
                uniform['valname'] = valname

            # Handle techniques
            tech_name = material.name + 'Technique'
            techniques[tech_name] = {
                'parameters' : parameters,
                'program' : material.name+'Program',
                'attributes' : {a['varname'] : a['varname'] for a in shader_data['attributes']},
                'uniforms' : {u['varname'] : u['valname'] for u in shader_data['uniforms']},
            }

            exp_materials[material.name] = {'technique': tech_name, 'values': values}
            # exp_materials[material.name] = {}

    return exp_materials
Example #12
0
def mat_to_json(mat, scn):
    global SHADER_LIB
    shader = gpu.export_shader(scn, mat)
    parts = shader['fragment'].rsplit('}', 2)
    if not SHADER_LIB:
        SHADER_LIB = "#extension GL_OES_standard_derivatives : enable\n"\
        +"precision highp float;\n"\
        +"precision highp int;\n"+(parts[0]+'}')\
        .replace('gl_ModelViewMatrixInverse','mat4(1)')\
        .replace('gl_ModelViewMatrix','mat4(1)')\
        .replace('gl_ProjectionMatrixInverse','mat4(1)')\
        .replace('gl_ProjectionMatrix','mat4(1)')\
        .replace('gl_NormalMatrixInverse','mat3(1)')\
        .replace('gl_NormalMatrix','mat3(1)')\
        .replace('sampler2DShadow','sampler2D')\
        .replace('shadow2DProj(shadowmap, co).x',
                'step(co.z,texture2D(shadowmap, co.xy).x)')\
        .replace('gl_LightSource[i].position','vec3(0,0,0)')\
        .replace('gl_LightSource[i].diffuse','vec3(0,0,0)')\
        .replace('gl_LightSource[i].specular','vec3(0,0,0)')\
        .replace('gl_LightSource[i].halfVector','vec3(0,0,0)')\
        .replace('float rad[4], fac;', 'float rad[4];float fac;')\
        .replace('''/* These are needed for high quality bump mapping */
#version 130
#extension GL_ARB_texture_query_lod: enable
#define BUMP_BICUBIC''','')
        #open('/tmp/shader_lib','w').write(SHADER_LIB)
        try:
            import shader_lib_filter, imp
            imp.reload(shader_lib_filter)
            print('Applying shader_lib_filter.py')
            SHADER_LIB = shader_lib_filter.shader_lib_filter(SHADER_LIB)
        except:
            pass

    shader['fragment'] = (parts[1] + '}').replace('sampler2DShadow',
                                                  'sampler2D')

    # Stuff for debugging shaders
    # TODO write only when they have changed
    if os.name != 'nt':
        SHM = "/run/shm/"
        open(SHM + mat.name + '.v', 'w').write(shader['vertex'])
        open(SHM + mat.name + '.f', 'w').write(shader['fragment'])
        try:
            shader['fragment'] = open(SHM + mat.name + '.f2').read()
        except:
            pass
    #from pprint import pprint
    #pprint(shader['attributes'])
    # ---------------------------

    dyn_consts = loads(mat.get('dyn_consts') or '[]')
    replacements = loads(mat.get('replacements') or '[]')

    # Checking hash of main() is not enough
    code_hash = hash(shader['fragment']) % 2147483648
    #print(shader['fragment'].split('{')[0])
    premain, main = shader['fragment'].split('{')

    if mat.get('code_hash') != code_hash:
        # Dynamic uniforms (for animation, per object variables,
        #                   particle layers or other custom stuff)
        dyn_consts = []
        block = bpy.data.texts.get('custom_uniforms')
        if block:
            paths = [x for x in block.as_string().split('\n') if x]
            print(paths)
            dyn_consts = get_dynamic_constants(mat, scn, paths)
        else:
            print('no block')
        mat['dyn_consts'] = dumps(dyn_consts)
        # Get list of unknown varyings and save them
        # as replacement strings
        known = [
            'var' + a['varname'][3:] for a in shader['attributes']
            if a['varname']
        ]
        varyings = [
            x[:-1].split() for x in premain.split('\n')
            if x.startswith('varying')
            and len(x) < 21  # this filters varposition/varnormal
        ]
        replacements = []
        for v in varyings:
            if v[2] not in known:
                replacements.append(
                    (' '.join(v), 'const {t} {n} = {t}(0.0)'.format(t=v[1],
                                                                    n=v[2])))
        mat['replacements'] = dumps(replacements)

    if any(dyn_consts):
        # Separate constants from the rest
        lines = premain.split('\n')
        # This generates a dictionary with the var name
        # and comma-separated values in a string, like this:
        # {'cons123': '1.23, 4.56, 7.89', ...}
        consts = dict([(c[2], c[3].split('(')[1][:-2])
                       for c in [l.split(' ', 3) for l in lines]
                       if c[0] == 'const'])

        premain = '\n'.join(l for l in lines if not l.startswith('cons'))

        # Convert them back to constants, except for dynamic ones
        lines = []
        TYPES = [
            'float', 'vec2', 'vec3', 'vec4', '', '', '', '', 'mat3', '', '',
            '', '', '', '', 'mat4'
        ]
        types = [None] * len(dyn_consts)
        for k, v in consts.items():
            t = TYPES[v.count(',')]
            #print(k, dyn_consts, k in dyn_consts)
            if k in dyn_consts:
                lines.append('uniform {0} {1};'.format(t, k))
                types[dyn_consts.index(k)] = t
            else:
                lines.append('const {0} {1} = {0}({2});'.format(t, k, v))

        shader['fragment'] = '\n'.join(lines) + '\n' + premain + '{' + main

        shader['uniforms'] += [{
            'type': -1,
            'varname': c,
            'gltype': types[i],
            'index': i
        } for i, c in enumerate(dyn_consts)]

    mat['code_hash'] = code_hash

    for a, b in replacements:
        shader['fragment'] = shader['fragment'].replace(a, b)

    # Find number of required shape attributes (excluding basis)
    # And number of bones
    num_shapes = 0
    num_bones = 0
    num_partsegments = 0
    for ob in scn.objects:
        if ob.type == 'MESH':
            # TODO: manage materials attached to object
            if mat in list(ob.data.materials):
                if ob.data.shape_keys:
                    num_shapes = max(num_shapes,
                                     len(ob.data.shape_keys.key_blocks) - 1)
                if ob.particle_systems:
                    num_partsegments = 1  # TODO check correct p systems and segments
                if ob.parent and ob.parent.type == 'ARMATURE' and not ob.parent_bone and not ob.get(
                        'apply_armature'):
                    num_bones = max(
                        num_bones,
                        len([b for b in ob.parent.data.bones if b.use_deform]))
    if num_shapes:
        shader['attributes'].append({
            'type': 99,
            'count': num_shapes,
            'varname': ''
        })
    if num_partsegments:
        shader['attributes'].append({
            'type': 77,
            'count': num_partsegments,
            'varname': ''
        })
    if num_bones:
        shader['attributes'].append({
            'type': 88,
            'count': num_bones,
            'varname': ''
        })

    last_lamp = ""
    for u in shader['uniforms']:
        if 'lamp' in u:
            u['lamp'] = last_lamp = u['lamp'].name
        if 'image' in u:
            # TODO: if the image is used in several textures, how can we know which?
            slots = list(mat.texture_slots)
            if mat.use_nodes:
                for node in mat.node_tree.nodes:
                    if node.type == 'MATERIAL' and node.material:
                        slots.extend(node.material.texture_slots)
            texture_slot = [
                t for t in slots if t and t.texture
                and t.texture.type == 'IMAGE' and t.texture.image == u['image']
            ]
            if not texture_slot:
                print("Warning: image %s not found in material %s." %
                      (u['image'].name, mat.name))
                u['filter'] = True
                u['wrap'] = 'R'
            else:
                u['filter'] = texture_slot[0].texture.use_interpolation
                u['wrap'] = 'R' if texture_slot[
                    0].texture.extension == 'REPEAT' else 'C'
            u['size'] = 0
            fpath = bpy.path.abspath(u['image'].filepath)
            if os.path.exists(fpath):
                u['size'] = os.path.getsize(fpath)
            u['filepath'] = u['image'].name + '.' + u['image'].filepath.rsplit(
                '.', 1)[1]
            u['image'] = u['image'].name
            tex_sizes[u['image']] = u['size']
        if 'texpixels' in u:
            # Minimum shadow buffer is 128x128
            if u['texsize'] > 16000:
                # This is a shadow buffer
                u['type'] = gpu.GPU_DYNAMIC_SAMPLER_2DSHADOW
                del u['texpixels']  # we don't need this
                # Assuming a lamp uniform is always sent before this one
                u['lamp'] = last_lamp
                # TODO: send lamp stuff
            else:
                # It's a ramp
                # encode as PNG data URI
                import struct, zlib

                def png_chunk(ty, data):
                    return struct.pack('>I',len(data)) + ty + data +\
                        struct.pack('>I',zlib.crc32(ty + data))

                u['filepath'] = 'data:image/png;base64,' + base64.b64encode(
                    b'\x89PNG\r\n\x1a\n' +
                    png_chunk(b'IHDR',
                              struct.pack('>IIBBBBB', 256, 1, 8, 6, 0, 0, 0)) +
                    png_chunk(b'IDAT',
                              zlib.compress(b'\x00' + u['texpixels'][:1024])) +
                    png_chunk(b'IEND', b'')
                    #for some reason is 257px?
                ).decode()

                u['image'] = hex(hash(u['filepath']))[-10:]
                u['wrap'] = 'C'  # clamp to edge
                u['type'] = gpu.GPU_DYNAMIC_SAMPLER_2DIMAGE
                u['size'] = 0
                del u['texpixels']

    # Engine builds its own vertex shader
    del shader['vertex']
    shader['double_sided'] = not mat.game_settings.use_backface_culling
    shader['type'] = 'MATERIAL'
    shader['name'] = mat.name
    shader['scene'] = scn.name

    ret = dumps(shader).encode('utf8')
    mat['hash'] = hash(ret) % 2147483648
    return ret
Example #13
0
def mat_to_json(mat, scn):
    global SHADER_LIB
    shader = gpu.export_shader(scn, mat)
    parts = shader['fragment'].rsplit('}',2)
    if not SHADER_LIB:
        SHADER_LIB = "#extension GL_OES_standard_derivatives : enable\n"\
        +"precision highp float;\n"\
        +"precision highp int;\n"+(parts[0]+'}')\
        .replace('gl_ModelViewMatrixInverse','mat4(1)')\
        .replace('gl_ModelViewMatrix','mat4(1)')\
        .replace('gl_ProjectionMatrixInverse','mat4(1)')\
        .replace('gl_ProjectionMatrix','mat4(1)')\
        .replace('gl_NormalMatrixInverse','mat3(1)')\
        .replace('gl_NormalMatrix','mat3(1)')\
        .replace('sampler2DShadow','sampler2D')\
        .replace('shadow2DProj(shadowmap, co).x',
                'step(co.z,texture2D(shadowmap, co.xy).x)')\
        .replace('gl_LightSource[i].position','vec3(0,0,0)')\
        .replace('gl_LightSource[i].diffuse','vec3(0,0,0)')\
        .replace('gl_LightSource[i].specular','vec3(0,0,0)')\
        .replace('gl_LightSource[i].halfVector','vec3(0,0,0)')\
        .replace('float rad[4], fac;', 'float rad[4];float fac;')\
        .replace('''/* These are needed for high quality bump mapping */
#version 130
#extension GL_ARB_texture_query_lod: enable
#define BUMP_BICUBIC''','')
        #open('/tmp/shader_lib','w').write(SHADER_LIB)
        try:
            import shader_lib_filter, imp
            imp.reload(shader_lib_filter)
            print('Applying shader_lib_filter.py')
            SHADER_LIB = shader_lib_filter.shader_lib_filter(SHADER_LIB)
        except:
            pass
        
    shader['fragment'] = (parts[1]+'}').replace('sampler2DShadow','sampler2D')
    
    # Stuff for debugging shaders
    # TODO write only when they have changed
    if os.name != 'nt':
        SHM = "/run/shm/"
        open(SHM + mat.name+'.v','w').write(shader['vertex'])
        open(SHM + mat.name+'.f','w').write(shader['fragment'])
        try:
            shader['fragment']=open(SHM + mat.name+'.f2').read()
        except:
            pass
    #from pprint import pprint
    #pprint(shader['attributes'])
    # ---------------------------
    
    dyn_consts = loads(mat.get('dyn_consts') or '[]')
    replacements = loads(mat.get('replacements') or '[]')
    
    # Checking hash of main() is not enough
    code_hash = hash(shader['fragment']) % 2147483648
    #print(shader['fragment'].split('{')[0])
    premain, main = shader['fragment'].split('{')
    
    if mat.get('code_hash') != code_hash:
        # Dynamic uniforms (for animation, per object variables,
        #                   particle layers or other custom stuff)
        dyn_consts = []
        block = bpy.data.texts.get('custom_uniforms')
        if block:
            paths = [x for x in block.as_string().split('\n')
                     if x]
            print(paths)
            dyn_consts = get_dynamic_constants(mat, scn, paths)
        else:
            print('no block')
        mat['dyn_consts'] = dumps(dyn_consts)
        # Get list of unknown varyings and save them
        # as replacement strings
        known = ['var'+a['varname'][3:] for a in shader['attributes'] if a['varname']]
        varyings = [x[:-1].split()
                    for x in premain.split('\n')
                    if x.startswith('varying')
                    and len(x) < 21 # this filters varposition/varnormal
                    ]
        replacements = []
        for v in varyings:
            if v[2] not in known:
                replacements.append((' '.join(v),
                    'const {t} {n} = {t}(0.0)'.format(t=v[1], n=v[2])))
        mat['replacements'] = dumps(replacements)
    
    if any(dyn_consts):
        # Separate constants from the rest
        lines = premain.split('\n')
        # This generates a dictionary with the var name
        # and comma-separated values in a string, like this:
        # {'cons123': '1.23, 4.56, 7.89', ...}
        consts = dict([(c[2], c[3].split('(')[1][:-2]) for c in
                [l.split(' ', 3) for l in lines]
                if c[0]=='const'])
        
        premain = '\n'.join(l for l in lines if not l.startswith('cons'))
        
        # Convert them back to constants, except for dynamic ones
        lines = []
        TYPES = ['float', 'vec2', 'vec3', 'vec4', '','','','','mat3','','','','','','','mat4']
        types = [None]*len(dyn_consts)
        for k,v in consts.items():
            t = TYPES[v.count(',')]
            #print(k, dyn_consts, k in dyn_consts)
            if k in dyn_consts:
                lines.append('uniform {0} {1};'.format(t, k))
                types[dyn_consts.index(k)] = t
            else:
                lines.append('const {0} {1} = {0}({2});'.format(t, k, v))
        
        shader['fragment'] = '\n'.join(lines) + '\n' + premain + '{' + main
        
        shader['uniforms'] += [
            {'type': -1, 'varname': c, 'gltype': types[i], 'index': i}
            for i,c in enumerate(dyn_consts)]
        
    mat['code_hash'] = code_hash
    
    for a,b in replacements:
        shader['fragment'] = shader['fragment'].replace(a, b)
    
    # Find number of required shape attributes (excluding basis)
    # And number of bones
    num_shapes = 0
    num_bones = 0
    num_partsegments = 0
    for ob in scn.objects:
        if ob.type == 'MESH':
            # TODO: manage materials attached to object
            if mat in list(ob.data.materials):
                if ob.data.shape_keys:
                    num_shapes = max(num_shapes, len(ob.data.shape_keys.key_blocks) - 1)
                if ob.particle_systems:
                    num_partsegments = 1  # TODO check correct p systems and segments
                if ob.parent and ob.parent.type == 'ARMATURE' and not ob.parent_bone and not ob.get('apply_armature'):
                    num_bones = max(num_bones, len([b for b in ob.parent.data.bones if b.use_deform]))
    if num_shapes:
        shader['attributes'].append({'type':99, 'count': num_shapes, 'varname': ''})
    if num_partsegments:
        shader['attributes'].append({'type':77, 'count': num_partsegments, 'varname': ''})
    if num_bones:
        shader['attributes'].append({'type':88, 'count': num_bones, 'varname': ''})
    
    last_lamp = ""
    for u in shader['uniforms']:
        if 'lamp' in u:
            u['lamp'] = last_lamp = u['lamp'].name
        if 'image' in u:
            # TODO: if the image is used in several textures, how can we know which?
            slots = list(mat.texture_slots)
            if mat.use_nodes:
                for node in mat.node_tree.nodes:
                    if node.type=='MATERIAL' and node.material:
                        slots.extend(node.material.texture_slots)
            texture_slot = [t for t in slots
                if t and t.texture and t.texture.type=='IMAGE' and t.texture.image==u['image']]
            if not texture_slot:
                print("Warning: image %s not found in material %s."%(u['image'].name, mat.name))
                u['filter'] = True
                u['wrap'] = 'R'
            else:
                u['filter'] = texture_slot[0].texture.use_interpolation
                u['wrap'] = 'R' if texture_slot[0].texture.extension == 'REPEAT' else 'C'
            u['size'] = 0
            fpath = bpy.path.abspath(u['image'].filepath)
            if os.path.exists(fpath):
                u['size'] = os.path.getsize(fpath)
            u['filepath'] = u['image'].name + '.' + u['image'].filepath.rsplit('.',1)[1]
            u['image'] = u['image'].name
            tex_sizes[u['image']] = u['size']
        if 'texpixels' in u:
            # Minimum shadow buffer is 128x128
            if u['texsize'] > 16000:
                # This is a shadow buffer
                u['type'] = gpu.GPU_DYNAMIC_SAMPLER_2DSHADOW
                del u['texpixels'] # we don't need this
                # Assuming a lamp uniform is always sent before this one
                u['lamp'] = last_lamp
                # TODO: send lamp stuff
            else:
                # It's a ramp
                # encode as PNG data URI
                import struct, zlib
                def png_chunk(ty, data):
                    return struct.pack('>I',len(data)) + ty + data +\
                        struct.pack('>I',zlib.crc32(ty + data))
                
                u['filepath'] = 'data:image/png;base64,' + base64.b64encode(
                    b'\x89PNG\r\n\x1a\n'+png_chunk(b'IHDR',
                    struct.pack('>IIBBBBB', 256, 1, 8, 6, 0, 0, 0))+
                    png_chunk(b'IDAT', zlib.compress(
                    b'\x00'+u['texpixels'][:1024])) + png_chunk(b'IEND', b'')
                    #for some reason is 257px?
                ).decode()
                
                u['image'] = hex(hash(u['filepath']))[-10:]
                u['wrap'] = 'C' # clamp to edge
                u['type'] = gpu.GPU_DYNAMIC_SAMPLER_2DIMAGE
                u['size'] = 0
                del u['texpixels']

    # Engine builds its own vertex shader
    del shader['vertex']
    shader['double_sided'] = not mat.game_settings.use_backface_culling
    shader['type'] = 'MATERIAL'
    shader['name'] = mat.name
    shader['scene'] = scn.name
    
    ret = dumps(shader).encode('utf8')
    mat['hash'] = hash(ret) % 2147483648
    return ret
    def export_material(self, state, material):
        shader_data = gpu.export_shader(bpy.context.scene, material)
        if state['settings']['asset_profile'] == 'DESKTOP':
            shader_converter.to_130(shader_data)
        else:
            shader_converter.to_web(shader_data)

        if self.settings.embed_shaders is True:
            fs_bytes = shader_data['fragment'].encode()
            fs_uri = 'data:text/plain;base64,' + base64.b64encode(
                fs_bytes).decode('ascii')
            vs_bytes = shader_data['vertex'].encode()
            vs_uri = 'data:text/plain;base64,' + base64.b64encode(
                vs_bytes).decode('ascii')
        else:
            names = [
                bpy.path.clean_name(name) + '.glsl'
                for name in (material.name + 'VS', material.name + 'FS')
            ]
            data = (shader_data['vertex'], shader_data['fragment'])
            for name, data in zip(names, data):
                filename = os.path.join(state['settings']['gltf_output_dir'],
                                        name)
                with open(filename, 'w') as fout:
                    fout.write(data)
            vs_uri, fs_uri = names

        state['shaders'].append({'type': 35632, 'uri': fs_uri})
        state['shaders'].append({'type': 35633, 'uri': vs_uri})

        # Handle programs
        state['programs'].append({
            'attributes': [a['varname'] for a in shader_data['attributes']],
            'fragmentShader':
            'shader_{}_FS'.format(material.name),
            'vertexShader':
            'shader_{}_VS'.format(material.name),
        })

        # Handle parameters/values
        values = {}
        parameters = {}
        for attribute in shader_data['attributes']:
            name = attribute['varname']
            semantic = gpu_luts.TYPE_TO_SEMANTIC[attribute['type']]
            _type = gpu_luts.DATATYPE_TO_GLTF_TYPE[attribute['datatype']]
            parameters[name] = {'semantic': semantic, 'type': _type}

        for uniform in shader_data['uniforms']:
            valname = gpu_luts.TYPE_TO_NAME.get(uniform['type'],
                                                uniform['varname'])
            rnaname = valname
            semantic = None
            node = None
            value = None

            if uniform['varname'] == 'bl_ModelViewMatrix':
                semantic = 'MODELVIEW'
            elif uniform['varname'] == 'bl_ProjectionMatrix':
                semantic = 'PROJECTION'
            elif uniform['varname'] == 'bl_NormalMatrix':
                semantic = 'MODELVIEWINVERSETRANSPOSE'
            else:
                if uniform['type'] in gpu_luts.LAMP_TYPES:
                    node = uniform['lamp'].name
                    valname = node + '_' + valname
                    semantic = gpu_luts.TYPE_TO_SEMANTIC.get(
                        uniform['type'], None)
                    if not semantic:
                        lamp_obj = bpy.data.objects[node]
                        value = getattr(lamp_obj.data, rnaname)
                elif uniform['type'] in gpu_luts.MIST_TYPES:
                    valname = 'mist_' + valname
                    mist_settings = bpy.context.scene.world.mist_settings
                    if valname == 'mist_color':
                        value = bpy.context.scene.world.horizon_color
                    else:
                        value = getattr(mist_settings, rnaname)

                    if valname == 'mist_falloff':
                        if value == 'QUADRATIC':
                            value = 0.0
                        elif value == 'LINEAR':
                            value = 1.0
                        else:
                            value = 2.0
                elif uniform['type'] in gpu_luts.WORLD_TYPES:
                    world = bpy.context.scene.world
                    value = getattr(world, rnaname)
                elif uniform['type'] in gpu_luts.MATERIAL_TYPES:
                    converter = gpu_luts.DATATYPE_TO_CONVERTER[
                        uniform['datatype']]
                    value = converter(getattr(material, rnaname))
                    values[valname] = value
                elif uniform['type'] == gpu.GPU_DYNAMIC_SAMPLER_2DIMAGE:
                    texture_slots = [
                        slot for slot in material.texture_slots
                        if slot and slot.texture.type == 'IMAGE'
                    ]
                    for slot in texture_slots:
                        if slot.texture.image.name == uniform['image'].name:
                            value = 'texture_' + slot.texture.name
                            values[uniform['varname']] = value
                else:
                    print('Unconverted uniform:', uniform)

            parameter = {}
            if semantic:
                parameter['semantic'] = semantic
                if node:
                    parameter['node'] = 'node_' + node
            elif value:
                parameter['value'] = gpu_luts.DATATYPE_TO_CONVERTER[
                    uniform['datatype']](value)
            else:
                parameter['value'] = None

            if uniform['type'] == gpu.GPU_DYNAMIC_SAMPLER_2DIMAGE:
                parameter['type'] = 35678  # SAMPLER_2D
            else:
                parameter['type'] = gpu_luts.DATATYPE_TO_GLTF_TYPE[
                    uniform['datatype']]
            parameters[valname] = parameter
            uniform['valname'] = valname

        # Handle techniques
        tech_name = 'technique_' + material.name
        state['techniques'].append({
            'parameters': parameters,
            'program': 'program_' + material.name,
            'attributes':
            {a['varname']: a['varname']
             for a in shader_data['attributes']},
            'uniforms':
            {u['varname']: u['valname']
             for u in shader_data['uniforms']},
        })

        return {'technique': tech_name, 'values': values}
import bpy, gpu
uniform_types = {}
for k,v in gpu.__dict__.items():
    if isinstance(v,int) and k.startswith('GPU_DYNAMIC_'):
        uniform_types[v] = k[12:]

data_types = ['0','1i','1f','2f','3f','4f','m3','m4','4ub']

attr_types = {6: 'CD_MCOL', 5: 'CD_MTFACE', 14: 'CD_ORCO', 18: 'CD_TANGENT'}

shader = gpu.export_shader(bpy.context.scene, bpy.context.object.material_slots[0].material)
shader['uniforms'].sort(key=lambda e: uniform_types.get(e['type'],'zzz'))
print('------')
for uniform in shader['uniforms']:
    ucopy = uniform.copy()
    if 'texpixels' in ucopy:
        del ucopy['texpixels']
    del ucopy['type']
    del ucopy['datatype']
    #del ucopy['varname']
    utype_str = uniform_types.get(uniform['type'],'unknown '+str(uniform['type']))
    print(utype_str, data_types[uniform['datatype']], ucopy)
import bpy, gpu
uniform_types = {}
for k, v in gpu.__dict__.items():
    if isinstance(v, int) and k.startswith('GPU_DYNAMIC_'):
        uniform_types[v] = k[12:]

data_types = ['0', '1i', '1f', '2f', '3f', '4f', 'm3', 'm4', '4ub']

attr_types = {6: 'CD_MCOL', 5: 'CD_MTFACE', 14: 'CD_ORCO', 18: 'CD_TANGENT'}

shader = gpu.export_shader(bpy.context.scene,
                           bpy.context.object.material_slots[0].material)
shader['uniforms'].sort(key=lambda e: uniform_types.get(e['type'], 'zzz'))
print('------')
for uniform in shader['uniforms']:
    ucopy = uniform.copy()
    if 'texpixels' in ucopy:
        del ucopy['texpixels']
    del ucopy['type']
    del ucopy['datatype']
    #del ucopy['varname']
    utype_str = uniform_types.get(uniform['type'],
                                  'unknown ' + str(uniform['type']))
    print(utype_str, data_types[uniform['datatype']], ucopy)
Example #17
0
def export_materials(settings, materials, shaders, programs, techniques):
    def export_material(material):
        return {
            'values': {
                'diffuse':
                list(
                    (material.diffuse_color * material.diffuse_intensity)[:]) +
                [material.alpha],
                'specular':
                list(
                    (material.specular_color * material.specular_intensity)[:])
                + [material.specular_alpha],
                'emission':
                list((material.diffuse_color * material.emit)[:]) +
                [material.alpha],
                'ambient': [material.ambient] * 4,
                'shininess':
                material.specular_hardness,
                'textures': [
                    ts.texture.name for ts in material.texture_slots
                    if ts and ts.texture.type == 'IMAGE'
                ],
                'uv_layers':
                [ts.uv_layer for ts in material.texture_slots if ts]
            }
        }

    exp_materials = {}
    for material in materials:
        if settings['materials_export_shader'] == False:
            exp_materials[material.name] = export_material(material)
        else:
            # Handle shaders
            shader_data = gpu.export_shader(bpy.context.scene, material)
            if settings['asset_profile'] == 'DESKTOP':
                shader_converter.to_130(shader_data)
            else:
                shader_converter.to_web(shader_data)
            fs_bytes = shader_data['fragment'].encode()
            fs_uri = 'data:text/plain;base64,' + base64.b64encode(
                fs_bytes).decode('ascii')
            shaders[material.name + 'FS'] = {'type': 35632, 'uri': fs_uri}
            vs_bytes = shader_data['vertex'].encode()
            vs_uri = 'data:text/plain;base64,' + base64.b64encode(
                vs_bytes).decode('ascii')
            shaders[material.name + 'VS'] = {'type': 35633, 'uri': vs_uri}

            # Handle programs
            programs[material.name + 'Program'] = {
                'attributes':
                [a['varname'] for a in shader_data['attributes']],
                'fragmentShader': material.name + 'FS',
                'vertexShader': material.name + 'VS',
            }

            # Handle parameters/values
            values = {}
            parameters = {}
            for attribute in shader_data['attributes']:
                name = attribute['varname']
                semantic = gpu_luts.TYPE_TO_SEMANTIC[attribute['type']]
                _type = gpu_luts.DATATYPE_TO_GLTF_TYPE[attribute['datatype']]
                parameters[name] = {'semantic': semantic, 'type': _type}

            for uniform in shader_data['uniforms']:
                valname = gpu_luts.TYPE_TO_NAME.get(uniform['type'],
                                                    uniform['varname'])
                rnaname = valname
                semantic = None
                node = None
                value = None

                if uniform['type'] in gpu_luts.LAMP_TYPES:
                    node = uniform['lamp'].name
                    valname = node + '_' + valname
                    semantic = gpu_luts.TYPE_TO_SEMANTIC.get(
                        uniform['type'], None)
                    if not semantic:
                        lamp_obj = bpy.data.objects[node]
                        value = getattr(lamp_obj.data, rnaname)
                elif uniform['type'] in gpu_luts.MIST_TYPES:
                    valname = 'mist_' + valname
                    settings = bpy.context.scene.world.mist_settings
                    if valname == 'mist_color':
                        value = bpy.context.scene.world.horizon_color
                    else:
                        value = getattr(settings, rnaname)

                    if valname == 'mist_falloff':
                        value = 0.0 if value == 'QUADRATIC' else 1.0 if 'LINEAR' else 2.0
                elif uniform['type'] in gpu_luts.WORLD_TYPES:
                    world = bpy.context.scene.world
                    value = getattr(world, rnaname)
                elif uniform['type'] in gpu_luts.MATERIAL_TYPES:
                    value = gpu_luts.DATATYPE_TO_CONVERTER[
                        uniform['datatype']](getattr(material, rnaname))
                    values[valname] = value
                else:
                    print('Unconverted uniform:', uniform)

                parameter = {}
                if semantic:
                    parameter['semantic'] = semantic
                    parameter['node'] = node
                else:
                    parameter['value'] = gpu_luts.DATATYPE_TO_CONVERTER[
                        uniform['datatype']](value)
                parameter['type'] = gpu_luts.DATATYPE_TO_GLTF_TYPE[
                    uniform['datatype']]
                parameters[valname] = parameter
                uniform['valname'] = valname

            # Handle techniques
            tech_name = material.name + 'Technique'
            techniques[tech_name] = {
                'parameters': parameters,
                'program': material.name + 'Program',
                'attributes': {
                    a['varname']: a['varname']
                    for a in shader_data['attributes']
                },
                'uniforms':
                {u['varname']: u['valname']
                 for u in shader_data['uniforms']},
            }

            exp_materials[material.name] = {
                'technique': tech_name,
                'values': values
            }
            # exp_materials[material.name] = {}

    return exp_materials
Example #18
0
def export(scene, mat):

    shader = gpu.export_shader(scene, mat)

    us1 = []
    us2 = []
    ats = []

    texvars = []
    preload = []
    bindtex = []

    for x in mat.texture_slots:
        if x:
            print("huh", x.name, x.texture)

    print("DYNCO!!", gpu.GPU_DYNAMIC_LAMP_DYNCO)

    for u in shader["uniforms"]:
        if ((u["type"] != gpu.GPU_DYNAMIC_SAMPLER_2DBUFFER)
                and (u["type"] != gpu.GPU_DYNAMIC_SAMPLER_2DIMAGE)
                and (u["type"] != gpu.GPU_DYNAMIC_SAMPLER_2DSHADOW)):
            continue

        identifier = "sampler-" + str(u['texnumber']) + "-texture"
        gvar = identifier
        env = "usr"

        # TODO: move this to s_expr

        if u["type"] == gpu.GPU_DYNAMIC_SAMPLER_2DBUFFER:
            line = "(preload-buffer \"%s\" %d %d %s)" % (identifier,
                                                         u['texsize'], env)

        elif u["type"] == gpu.GPU_DYNAMIC_SAMPLER_2DIMAGE:
            line = "(preload-image %d %d \"%s\" %s)" % (
                u['image'].size[0], u['image'].size[1],
                "/home/nha/data/blender_glsl_material/test" +
                u['image'].filepath, env)

        elif u["type"] == gpu.GPU_DYNAMIC_SAMPLER_2DSHADOW:
            line = "(preload-shadow-buffer \"lamp-%s\" %d %s)" % (
                io_material_glsl.format.s_expression.valid_id(
                    u['lamp'].name), u['lamp'].data.shadow_buffer_size, env)

        texvars.append("(define %s #<unspecified>)" % (gvar))
        bindtex.append("(sampler \"%s\" sampler-%d-bind-index %s %s)" %
                       (u['varname'], u['texnumber'], gvar, env))
        preload.append("(set! %s %s)" % (gvar, line))

    seen = set()
    for uniform in shader["uniforms"]:
        defs, u = map_u(mat, uniform, seen,
                        io_material_glsl.format.s_expression)
        us1 += defs
        us2 += [u]

    for attribute in shader["attributes"]:
        ats += [map_atr(mat, attribute, io_material_glsl.format.s_expression)]

    #us1.sort()
    #us2.sort()

    #print('\n'.join(us1))
# print('\n'.join(us2))
#print(ats)

# TODO: fix this header stuff
    header = """
#version 130
#extension GL_ARB_texture_query_lod : enable
#extension GL_EXT_gpu_shader4: enable
#extension GL_ARB_draw_instanced: enable
#define GPU_ATI
#define CLIP_WORKAROUND

#define BUMP_BICUBIC 1
"""
    file = "/home/nha/data/blender_glsl_material/render-osg/test/" + mat.name
    try:
        os.remove(file + ".vert")
        os.remove(file + ".frag")
        os.remove(file + ".scm")

    except:
        pass

    with open(file + ".vert.in", "w") as f:
        #        f.write(header)
        f.write(shader["vertex"])

    with open(file + ".frag.in", "w") as f:
        #       f.write(header)
        f.write(shader["fragment"])

    with open(file + ".scm", "w") as f:
        preload_txt = "(define (preload usr)\n  (begin\n    %s))\n\n" % (
            '\n    '.join(preload))
        bind_txt = "(define (bind-samplers usr)\n  (begin\n    %s))\n\n" % (
            '\n    '.join(bindtex))
        texvars_txt = '\n'.join(texvars) + '\n\n'

        f.write(
            io_material_glsl.format.s_expression.group_defines(
                io_material_glsl.dnalink.is_global_group,
                io_material_glsl.dnalink.get_group, us1))

        f.write(texvars_txt)
        f.write(preload_txt)
        f.write(bind_txt)

        f.write(
            io_material_glsl.format.s_expression.group_uniforms(
                io_material_glsl.dnalink.all_groups, us2))

        f.write(io_material_glsl.format.s_expression.group_attributes(ats))
def get_dynamic_constants(mat, scn, paths, code):
    # Example:
    #paths = [
    #'node_tree.nodes["slicep"].outputs[0].default_value',
    #'node_tree.nodes["slicen"].outputs[0].default_value',
    #]
    restore = []
    sentinels = []
    lookup_data = []
    varnames_types = []
    for p in paths:
        # We're assuming "%f"%sentinel yelds exactly the same string
        # as the code generator (sprintf is used in both cases)
        sentinel = random()
        while ("%f" % sentinel) in code or sentinel in sentinels:
            sentinel = random()
        if p.startswith('nodes['):
            p = 'node_tree.' + p
        try:
            orig_obj = mat.path_resolve(p)
        except ValueError:
            lookup_data.append([0, 0, 0])
            continue
        obj, attr = ('.' + p).rsplit('.', 1)
        obj = eval('mat' + obj)
        #print(obj, attr)
        value = orig_obj
        is_vector = hasattr(orig_obj, '__getitem__')
        orig_val = None
        if is_vector:
            value = list(orig_obj)
            orig_val = orig_obj[0]
            orig_obj[0] = sentinel
            vlen = len(orig_obj)
        else:
            vlen = 1
            setattr(obj, attr, sentinel)
        lookup_data.append([sentinel, vlen, value])
        restore.append([is_vector, obj, attr, orig_obj, orig_val])

    scn.update()
    sh = gpu.export_shader(scn, mat)
    c = sh['fragment'].rsplit('}', 2)[1]

    for sentinel, vlen, value in lookup_data:
        if not sentinel:
            varnames_types.append([None, None, 0])
            continue
        pos = c.find("%f" % sentinel)
        if pos != -1:
            varname = c[:pos].rsplit(' ', 3)[1]
            varnames_types.append([varname, vlen, value])
        else:
            varnames_types.append([None, vlen, value])

    for is_vector, obj, attr, orig_obj, orig_val in restore:
        # restore original
        if is_vector:
            orig_obj[0] = orig_val
        else:
            setattr(obj, attr, orig_obj)

    if any(varnames_types):
        scn.update()
    #pprint(varnames_types)
    return [varnames_types, sh]
Example #20
0
def export_materials(settings, materials, shaders, programs, techniques):
    def export_material(material):
        return {
                'values': {
                    'diffuse': list((material.diffuse_color * material.diffuse_intensity)[:]) + [material.alpha],
                    'specular': list((material.specular_color * material.specular_intensity)[:]) + [material.specular_alpha],
                    'emission': list((material.diffuse_color * material.emit)[:]) + [material.alpha],
                    'ambient': [material.ambient] * 4,
                    'shininess': material.specular_hardness,
                    'textures': [ts.texture.name for ts in material.texture_slots if ts and ts.texture.type == 'IMAGE'],
                    'uv_layers': [ts.uv_layer for ts in material.texture_slots if ts]
                }
            }
    exp_materials = {}
    for material in materials:
        if settings['materials_export_shader'] == False:
            exp_materials[material.name] = export_material(material)
        else:
            # Handle shaders
            shader_data = gpu.export_shader(bpy.context.scene, material)
            if settings['asset_profile'] == 'DESKTOP':
                shader_converter.to_130(shader_data)
            else:
                shader_converter.to_web(shader_data)
            fs_bytes = shader_data['fragment'].encode()
            fs_uri = 'data:text/plain;base64,' + base64.b64encode(fs_bytes).decode('ascii')
            shaders[material.name+'FS'] = {'type': 35632, 'uri': fs_uri}
            vs_bytes = shader_data['vertex'].encode()
            vs_uri = 'data:text/plain;base64,' + base64.b64encode(vs_bytes).decode('ascii')
            shaders[material.name+'VS'] = {'type': 35633, 'uri': vs_uri}

            # Handle programs
            programs[material.name+'Program'] = {
                'attributes' : [a['varname'] for a in shader_data['attributes']],
                'fragmentShader' : material.name+'FS',
                'vertexShader' : material.name+'VS',
            }

            # Handle parameters/values
            values = {}
            parameters = {}
            for attribute in shader_data['attributes']:
                name = attribute['varname']
                semantic = gpu_luts.TYPE_TO_SEMANTIC[attribute['type']]
                _type = gpu_luts.DATATYPE_TO_GLTF_TYPE[attribute['datatype']]
                parameters[name] = {'semantic': semantic, 'type': _type}

            for uniform in shader_data['uniforms']:
                valname = gpu_luts.TYPE_TO_NAME.get(uniform['type'], uniform['varname'])
                rnaname = valname
                semantic = None
                node = None
                value = None

                if uniform['type'] in gpu_luts.LAMP_TYPES:
                    node = uniform['lamp'].name
                    valname = node + '_' + valname
                    semantic = gpu_luts.TYPE_TO_SEMANTIC.get(uniform['type'], None)
                    if not semantic:
                        lamp_obj = bpy.data.objects[node]
                        value = getattr(lamp_obj.data, rnaname)
                elif uniform['type'] in gpu_luts.MIST_TYPES:
                    valname = 'mist_' + valname
                    settings = bpy.context.scene.world.mist_settings
                    if valname == 'mist_color':
                        value = bpy.context.scene.world.horizon_color
                    else:
                        value = getattr(settings, rnaname)

                    if valname == 'mist_falloff':
                        value = 0.0 if value == 'QUADRATIC' else 1.0 if 'LINEAR' else 2.0
                elif uniform['type'] in gpu_luts.WORLD_TYPES:
                    world = bpy.context.scene.world
                    value = getattr(world, rnaname)
                elif uniform['type'] in gpu_luts.MATERIAL_TYPES:
                    value = gpu_luts.DATATYPE_TO_CONVERTER[uniform['datatype']](getattr(material, rnaname))
                    values[valname] = value
                else:
                    print('Unconverted uniform:', uniform)

                parameter = {}
                if semantic:
                    parameter['semantic'] = semantic
                    parameter['node'] = node
                else:
                    parameter['value'] = gpu_luts.DATATYPE_TO_CONVERTER[uniform['datatype']](value)
                parameter['type'] = gpu_luts.DATATYPE_TO_GLTF_TYPE[uniform['datatype']]
                parameters[valname] = parameter
                uniform['valname'] = valname

            # Handle techniques
            tech_name = material.name + 'Technique'
            techniques[tech_name] = {
                'parameters' : parameters,
                'program' : material.name+'Program',
                'attributes' : {a['varname'] : a['varname'] for a in shader_data['attributes']},
                'uniforms' : {u['varname'] : u['valname'] for u in shader_data['uniforms']},
            }

            exp_materials[material.name] = {'technique': tech_name, 'values': values}
            # exp_materials[material.name] = {}

    return exp_materials
def mat_to_json_try(mat, scn):
    global SHADER_LIB
    global get_animation_data_strips
    if not get_animation_data_strips:
        from . import exporter
        get_animation_data_strips = exporter.get_animation_data_strips

    # NOTE: This export is replaced later
    shader = gpu.export_shader(scn, mat)
    set_shader_lib(shader['fragment'])
    parts = shader['fragment'].rsplit('}', 2)
    shader['fragment'] = ('\n' + parts[1] + '}')

    # Stuff for debugging shaders
    # TODO write only when they have changed
    # if os.name != 'nt':
    #     SHM = "/run/shm/"
    #     open(SHM + mat.name+'.v','w').write(shader['vertex'])
    #     open(SHM + mat.name+'.f','w').write(shader['fragment'])
    #     try:
    #         shader['fragment']=open(SHM + mat.name+'.f2').read()
    #     except:
    #         pass
    #from pprint import pprint
    #pprint(shader['attributes'])
    # ---------------------------

    # # Checking hash of main() is not enough
    # code_hash = hash(shader['fragment']) % 2147483648
    #print(shader['fragment'].split('{')[0])

    print(mat.name)
    strips, drivers = get_animation_data_strips(mat.animation_data)
    if mat.node_tree and mat.node_tree.nodes:
        s, d = get_animation_data_strips(mat.node_tree.animation_data)
        strips += s
        drivers += d

    if 1:
        # Dynamic uniforms (for animation, per object variables,
        #                   particle layers or other custom stuff)
        dyn_consts = []
        # Old custom_uniforms API
        block = bpy.data.texts.get('custom_uniforms')
        if block:
            paths = [x for x in block.as_string().split('\n') if x]
            #print(paths)
        else:
            paths = []
        # Animated custom_uniforms
        to_unmute = []
        for strip in strips:
            last_path = ''
            for f in bpy.data.actions[strip['action']].fcurves:
                if not f.mute:
                    f.mute = True
                    to_unmute.append(f)
                if f.data_path != last_path:
                    last_path = f.data_path
                    if last_path not in paths:
                        paths.append(last_path)
        paths += drivers  # drivers is a list of data_paths for now
        dyn_consts = []
        dyn_ecounts = []
        dyn_values = []
        # NOTE: This _replaces_ the previously exported shader
        dyn_stuff, shader = get_dynamic_constants(mat, scn, paths,
                                                  shader['fragment'])
        for a, b, c in dyn_stuff:
            dyn_consts.append(a)
            dyn_ecounts.append(b)
            dyn_values.append(c)
        # We're repeating what we did at the beginning
        shader['fragment'] = ('\n' + shader['fragment'].rsplit('}', 2)[1] +
                              '}')
        premain, main = shader['fragment'].split('{')

        # Restore previously muted fcurves
        for f in to_unmute:
            f.mute = False

        # Get list of unknown varyings and save them
        # as replacement strings
        known = [
            'var' + a['varname'][3:] for a in shader['attributes']
            if a['varname']
        ]
        varyings = [
            x[:-1].split() for x in premain.split('\n')
            if x.startswith('varying')
            and len(x) < 21  # this filters varposition/varnormal
        ]
        replacements = []
        for v in varyings:
            if v[2] not in known:
                replacements.append(
                    (' '.join(v), 'const {t} {n} = {t}(0.0)'.format(t=v[1],
                                                                    n=v[2])))

    shader['fragment'] = shader['fragment'].replace('sampler2DShadow','sampler2D')\
        .replace('\nin ', '\nvarying ')
    shader['fragment'] = re.sub(r'[^\x00-\x7f]', r'', shader['fragment'])

    if any(dyn_consts):
        # Separate constants from the rest
        #print(mat.name,'===>',premain)
        lines = premain.split('\n')
        # This generates a dictionary with the var name
        # and comma-separated values in a string, like this:
        # {'cons123': '1.23, 4.56, 7.89', ...}
        consts = dict([(c[2], c[3].split('(')[1][:-2])
                       for c in [l.split(' ', 3) for l in lines]
                       if c[0] == 'const'])

        premain = '\n'.join(l for l in lines if not l.startswith('cons'))

        # Convert them back to constants, except for dynamic ones
        lines = []
        TYPES = [
            'float', 'vec2', 'vec3', 'vec4', '', '', '', '', 'mat3', '', '',
            '', '', '', '', 'mat4'
        ]
        types = [None] * len(dyn_consts)
        for k, v in consts.items():
            t = TYPES[v.count(',')]
            #print(k, dyn_consts, k in dyn_consts)
            if k in dyn_consts:
                lines.append('uniform {0} {1};'.format(t, k))
                # GL type may differ from value
                dyn_ecounts[dyn_consts.index(k)] = v.count(',') + 1
            else:
                lines.append('const {0} {1} = {0}({2});'.format(t, k, v))

        shader['fragment'] = '\n'.join(lines) + '\n' + premain + '{' + main

        shader['uniforms'] += [
            # TODO: index is never used as it's always in order,
            # remove it after ensuring in legacy engine
            {
                'type': -1,
                'varname': c or '',
                'count': dyn_ecounts[i],
                'index': i,
                'path': paths[i],
                'value': dyn_values[i]
            } for i, c in enumerate(dyn_consts)
        ]

    # mat['code_hash'] = code_hash

    for a, b in replacements:
        shader['fragment'] = shader['fragment'].replace(a, b)

    if mat.game_settings.alpha_blend == 'CLIP':
        shader['fragment'] = re.sub(
            r'gl_FragColor = ([^;]*)',
            r'if(\1.a < 0.5)discard; gl_FragColor = \1', shader['fragment'])

    # Find number of required shape attributes (excluding basis)
    # And number of bones
    num_shapes = 0
    num_bones = 0
    num_partsegments = 0
    weights6 = False
    for ob in scn.objects:
        if ob.type == 'MESH':
            # TODO: manage materials attached to object
            if mat in list(ob.data.materials):
                if ob.data.shape_keys:
                    num_shapes = max(num_shapes,
                                     len(ob.data.shape_keys.key_blocks) - 1)
                if ob.particle_systems:
                    num_partsegments = 1  # TODO check correct p systems and segments
                if ob.parent and ob.parent.type == 'ARMATURE' and ob.parent_type != 'BONE' and not ob.get(
                        'apply_armature'):
                    #print("Material", mat.name, "has armature", ob.parent.name, "because of mesh", ob.name)
                    num_bones = max(
                        num_bones,
                        len([b for b in ob.parent.data.bones if b.use_deform]))
                    if ob.get('weights6'): weights6 = True
    if num_shapes:
        shader['attributes'].append({
            'type': 99,
            'count': num_shapes,
            'varname': ''
        })
    if num_partsegments:
        shader['attributes'].append({
            'type': 77,
            'count': num_partsegments,
            'varname': ''
        })
    if num_bones:
        t = 86 if weights6 else 88
        shader['attributes'].append({
            'type': t,
            'count': num_bones,
            'varname': ''
        })

    last_lamp = ""
    param_mats = {}
    for u in shader['uniforms']:
        if 'lamp' in u:
            u['lamp'] = last_lamp = u['lamp'].name
        if 'image' in u:
            # TODO: if the image is used in several textures, how can we know which?
            slots = list(mat.texture_slots)
            if mat.use_nodes:
                for node in mat.node_tree.nodes:
                    if node.type == 'MATERIAL' and node.material:
                        slots.extend(node.material.texture_slots)
            texture_slot = [
                t for t in slots if t and t.texture
                and t.texture.type == 'IMAGE' and t.texture.image == u['image']
            ]
            if not texture_slot:
                print("Warning: image %s not found in material %s." %
                      (u['image'].name, mat.name))
                u['filter'] = True
                u['wrap'] = 'R'
            else:
                u['filter'] = texture_slot[0].texture.use_interpolation
                u['wrap'] = 'R' if texture_slot[
                    0].texture.extension == 'REPEAT' else 'C'
            u['size'] = 0
            fpath = bpy.path.abspath(u['image'].filepath)
            if os.path.isfile(fpath):
                u['size'] = os.path.getsize(fpath)
            # u['filepath'] is only used in old versions of the engine
            u['filepath'] = u['image'].name + '.' + u['image'].get(
                'exported_extension',
                fpath.split('.').pop())
            u['image'] = u['image'].name
            tex_sizes[u['image']] = u['size']
        if 'texpixels' in u:
            # Minimum shadow buffer is 128x128
            if u['texsize'] > 16000:
                # This is a shadow buffer
                u['type'] = gpu.GPU_DYNAMIC_SAMPLER_2DSHADOW
                del u['texpixels']  # we don't need this
                # Assuming a lamp uniform is always sent before this one
                u['lamp'] = last_lamp
                # TODO: send lamp stuff
            else:
                # It's a ramp
                # encode as PNG data URI
                # TODO: Store this in the JSON texture list when the old engine
                # is no longer used
                def png_chunk(ty, data):
                    return struct.pack('>I',len(data)) + ty + data +\
                        struct.pack('>I',zlib.crc32(ty + data))

                u['filepath'] = 'data:image/png;base64,' + base64.b64encode(
                    b'\x89PNG\r\n\x1a\n' +
                    png_chunk(b'IHDR',
                              struct.pack('>IIBBBBB', 256, 1, 8, 6, 0, 0, 0)) +
                    png_chunk(b'IDAT',
                              zlib.compress(b'\x00' + u['texpixels'][:1024])) +
                    png_chunk(b'IEND', b'')
                    #for some reason is 257px?
                ).decode()

                u['image'] = hex(hash(u['filepath']))[-15:]
                u['wrap'] = 'C'  # clamp to edge
                u['type'] = gpu.GPU_DYNAMIC_SAMPLER_2DIMAGE
                u['size'] = 0
                del u['texpixels']
        if 'material' in u:
            param_mats[u['material'].name] = u['material']
            u['material'] = u['material'].name

    # Detect unused varyings (attributes)
    for line in shader['fragment'].split('\n'):
        # len<22 filters varposition/varnormal
        if len(line) < 22 and line.startswith('varying'):
            _, gltype, name = line[:-1].split(' ')
            used = False
            for attr in shader['attributes']:
                if attr['varname'][3:] == name[3:]:
                    used = True
                    break
            if not used:
                shader['attributes'].append(
                    dict(
                        type=-1,  # UNUSED
                        varname=name,
                        gltype=gltype,
                    ))

    # Engine builds its own vertex shader
    del shader['vertex']
    shader['double_sided'] = not mat.game_settings.use_backface_culling
    shader['type'] = 'MATERIAL'
    shader['material_type'] = 'BLENDER_INTERNAL'
    shader['name'] = mat.name
    shader['scene'] = scn.name
    shader['params'] = [{
        'name': m.name,
        'diffuse_color': list(m.diffuse_color),
        'diffuse_intensity': m.diffuse_intensity,
        'specular_color': list(m.specular_color),
        'specular_intensity': m.specular_intensity,
        'specular_hardness': m.specular_hardness,
        'emit': m.emit,
        'alpha': m.alpha,
    } for m in param_mats.values()]

    shader['animation_strips'] = strips

    shader['fragment_hash'] = hash(shader['fragment'])
    ret = loads(dumps(shader))
    # mat['hash'] = hash(ret) % 2147483648
    return ret
Example #22
0
def export_materials(settings, materials, shaders, programs, techniques):
    def export_material(material):
        all_textures = [
            ts for ts in material.texture_slots
            if ts and ts.texture.type == 'IMAGE'
        ]
        diffuse_textures = [
            'texture_' + t.texture.name for t in all_textures
            if t.use_map_color_diffuse
        ]
        emission_textures = [
            'texture_' + t.texture.name for t in all_textures if t.use_map_emit
        ]
        specular_textures = [
            'texture_' + t.texture.name for t in all_textures
            if t.use_map_color_spec
        ]
        diffuse_color = list(
            (material.diffuse_color *
             material.diffuse_intensity)[:]) + [material.alpha]
        emission_color = list(
            (material.diffuse_color * material.emit)[:]) + [material.alpha]
        specular_color = list(
            (material.specular_color *
             material.specular_intensity)[:]) + [material.specular_alpha]
        technique = 'PHONG'
        if material.use_shadeless:
            technique = 'CONSTANT'
            emission_textures = diffuse_textures
            emission_color = diffuse_color
        elif material.specular_intensity == 0.0:
            technique = 'LAMBERT'
        elif material.specular_shader == 'BLINN':
            technique = 'BLINN'
        return {
            'extensions': {
                'KHR_materials_common': {
                    'technique': technique,
                    'values': {
                        'ambient': ([material.ambient] * 3) + [1.0],
                        'diffuse':
                        diffuse_textures[-1]
                        if diffuse_textures else diffuse_color,
                        'doubleSided':
                        not material.game_settings.use_backface_culling,
                        'emission':
                        emission_textures[-1]
                        if emission_textures else emission_color,
                        'specular':
                        specular_textures[-1]
                        if specular_textures else specular_color,
                        'shininess':
                        material.specular_hardness,
                        'transparency':
                        material.alpha,
                        'transparent':
                        material.use_transparency,
                    }
                }
            },
            'name': material.name,
        }

    exp_materials = {}
    for material in materials:
        if settings['shaders_data_storage'] == 'NONE':
            exp_materials['material_' +
                          material.name] = export_material(material)
        else:
            # Handle shaders
            shader_data = gpu.export_shader(bpy.context.scene, material)
            if settings['asset_profile'] == 'DESKTOP':
                shader_converter.to_130(shader_data)
            else:
                shader_converter.to_web(shader_data)

            fs_name = 'shader_{}_FS'.format(material.name)
            vs_name = 'shader_{}_VS'.format(material.name)
            storage_setting = settings['shaders_data_storage']
            if storage_setting == 'EMBED':
                fs_bytes = shader_data['fragment'].encode()
                fs_uri = 'data:text/plain;base64,' + base64.b64encode(
                    fs_bytes).decode('ascii')
                vs_bytes = shader_data['vertex'].encode()
                vs_uri = 'data:text/plain;base64,' + base64.b64encode(
                    vs_bytes).decode('ascii')
            elif storage_setting == 'EXTERNAL':
                names = [
                    bpy.path.clean_name(name) + '.glsl'
                    for name in (material.name + 'VS', material.name + 'FS')
                ]
                data = (shader_data['vertex'], shader_data['fragment'])
                for name, data in zip(names, data):
                    filename = os.path.join(settings['gltf_output_dir'], name)
                    with open(filename, 'w') as fout:
                        fout.write(data)
                vs_uri, fs_uri = names
            else:
                print(
                    'Encountered unknown option ({}) for shaders_data_storage setting'
                    .format(storage_setting))

            shaders[fs_name] = {'type': 35632, 'uri': fs_uri}
            shaders[vs_name] = {'type': 35633, 'uri': vs_uri}

            # Handle programs
            programs['program_' + material.name] = {
                'attributes':
                [a['varname'] for a in shader_data['attributes']],
                'fragmentShader': 'shader_{}_FS'.format(material.name),
                'vertexShader': 'shader_{}_VS'.format(material.name),
            }

            # Handle parameters/values
            values = {}
            parameters = {}
            for attribute in shader_data['attributes']:
                name = attribute['varname']
                semantic = gpu_luts.TYPE_TO_SEMANTIC[attribute['type']]
                _type = gpu_luts.DATATYPE_TO_GLTF_TYPE[attribute['datatype']]
                parameters[name] = {'semantic': semantic, 'type': _type}

            for uniform in shader_data['uniforms']:
                valname = gpu_luts.TYPE_TO_NAME.get(uniform['type'],
                                                    uniform['varname'])
                rnaname = valname
                semantic = None
                node = None
                value = None

                if uniform['varname'] == 'bl_ModelViewMatrix':
                    semantic = 'MODELVIEW'
                elif uniform['varname'] == 'bl_ProjectionMatrix':
                    semantic = 'PROJECTION'
                elif uniform['varname'] == 'bl_NormalMatrix':
                    semantic = 'MODELVIEWINVERSETRANSPOSE'
                else:
                    if uniform['type'] in gpu_luts.LAMP_TYPES:
                        node = uniform['lamp'].name
                        valname = node + '_' + valname
                        semantic = gpu_luts.TYPE_TO_SEMANTIC.get(
                            uniform['type'], None)
                        if not semantic:
                            lamp_obj = bpy.data.objects[node]
                            value = getattr(lamp_obj.data, rnaname)
                    elif uniform['type'] in gpu_luts.MIST_TYPES:
                        valname = 'mist_' + valname
                        mist_settings = bpy.context.scene.world.mist_settings
                        if valname == 'mist_color':
                            value = bpy.context.scene.world.horizon_color
                        else:
                            value = getattr(mist_settings, rnaname)

                        if valname == 'mist_falloff':
                            value = 0.0 if value == 'QUADRATIC' else 1.0 if 'LINEAR' else 2.0
                    elif uniform['type'] in gpu_luts.WORLD_TYPES:
                        world = bpy.context.scene.world
                        value = getattr(world, rnaname)
                    elif uniform['type'] in gpu_luts.MATERIAL_TYPES:
                        value = gpu_luts.DATATYPE_TO_CONVERTER[
                            uniform['datatype']](getattr(material, rnaname))
                        values[valname] = value
                    elif uniform['type'] == gpu.GPU_DYNAMIC_SAMPLER_2DIMAGE:
                        for ts in [
                                ts for ts in material.texture_slots
                                if ts and ts.texture.type == 'IMAGE'
                        ]:
                            if ts.texture.image.name == uniform['image'].name:
                                value = 'texture_' + ts.texture.name
                                values[uniform['varname']] = value
                    else:
                        print('Unconverted uniform:', uniform)

                parameter = {}
                if semantic:
                    parameter['semantic'] = semantic
                    if node:
                        parameter['node'] = 'node_' + node
                else:
                    parameter['value'] = gpu_luts.DATATYPE_TO_CONVERTER[
                        uniform['datatype']](value)
                if uniform['type'] == gpu.GPU_DYNAMIC_SAMPLER_2DIMAGE:
                    parameter['type'] = 35678  #SAMPLER_2D
                else:
                    parameter['type'] = gpu_luts.DATATYPE_TO_GLTF_TYPE[
                        uniform['datatype']]
                parameters[valname] = parameter
                uniform['valname'] = valname

            # Handle techniques
            tech_name = 'technique_' + material.name
            techniques[tech_name] = {
                'parameters': parameters,
                'program': 'program_' + material.name,
                'attributes': {
                    a['varname']: a['varname']
                    for a in shader_data['attributes']
                },
                'uniforms':
                {u['varname']: u['valname']
                 for u in shader_data['uniforms']},
            }

            exp_materials['material_' + material.name] = {
                'technique': tech_name,
                'values': values
            }
            # exp_materials[material.name] = {}

    return exp_materials
def get_shader_lib(mat_list):
    global SHADER_LIB
    if True:
        ## For GPUs unsupported by PBR branch, put an exported scene json in
        ## /tmp/myou-shader-lib.json and uncomment this code
        # try:
        #     for ob in json.load(open('/tmp/myou-shader-lib.json')):
        #         if ob['type'] == 'SHADER_LIB':
        #             SHADER_LIB = ob['code']
        #             return SHADER_LIB
        # except:
        #     pass
        import bpy, gpu
        mat = bpy.data.materials.new('delete_me')
        fragment = gpu.export_shader(bpy.context.scene, mat)['fragment']
        bpy.data.materials.remove(mat)
        print('Converting shader lib')
        parts = fragment.rsplit('}', 2)
        SHADER_LIB = \
"""
#ifdef GL_ES
#if __VERSION__ < 130
#extension GL_OES_standard_derivatives : enable
#extension GL_EXT_shader_texture_lod : enable
#else
#define texture2D texture
#define texture2DLod textureLod
#define textureCube texture
#define textureCubeLod textureLod
#define texture2DProj textureProj
#define sample sample_
#endif
precision highp float;
precision highp int;
#if __VERSION__ < 130
#ifdef GL_EXT_shader_texture_lod
#define texture2DLod texture2DLodEXT
#define textureCubeLod textureCubeLodEXT
#else
#define texture2DLod texture2D
#define textureCubeLod textureCube
#endif
#endif
#endif
""" \
        +defines+uniforms+(parts[0]+'}').replace('\r','')+'\n'+"""
        #ifdef SS_REFR
        uniform vec2 unfrefract_size_px, unfrefract_px_size;
        void screen_space_refraction(vec4 color, vec3 normal,
                float roughness, float ior, out vec4 out_col){
            vec2 limit = (unfrefract_size_px-.5) * unfrefract_px_size;
            vec2 uv = gl_FragCoord.xy * unfrefract_px_size;
            uv += normal.xy*(ior-1.);
            out_col = texture2D(unfrefract, min(uv, limit));
            out_col.r = srgb_to_linearrgb(out_col.r);
            out_col.g = srgb_to_linearrgb(out_col.g);
            out_col.b = srgb_to_linearrgb(out_col.b);
            // out_col = vec4(uv, texture2D(unfrefract, uv).g, 1.0);
        }
        #endif
        """
        functions = get_patched_functions(SHADER_LIB)
        # remove unused functions
        # we're adding '' and 'material_preview_matcap' to ensure we add
        # the preamble and uniforms/globals, respectively
        used = {
            '', 'material_preview_matcap', 'mul',
            'color_to_blender_normal_new_shading', 'distance_based_roughness'
        }
        call = re.compile(r"\b\w+\s*\(", flags=re.DOTALL)

        def add_used(code):
            for name in call.findall(code):
                name = name[:-1]
                if name in functions and name not in used:
                    used.add(name)
                    add_used(functions[name])

        for m in mat_list:
            add_used(m['fragment'])
        print('GLSL functions: total', len(functions), 'used', len(used))

        SHADER_LIB = ''.join(f for k, f in functions.items()
                             if k in used).encode('ascii', 'ignore').decode()
        # This section below is necessary because something is interpreted as non ascii for some reason
        # despite the line above (which is also necessary, mysteriously...)
        splits = SHADER_LIB.split('BIT_OPERATIONS', 2)
        if len(splits) == 3:
            a, b, c = splits
            SHADER_LIB = a + 'BIT_OPERATIONS\n#endif' + c
        if debug_lib:
            open('/tmp/shader_lib.orig.glsl',
                 'w').write((parts[0] + '}').replace('\r', '') + '\n')
            open('/tmp/shader_lib.glsl', 'w').write(SHADER_LIB)

    return SHADER_LIB