예제 #1
0
def __load_texture(texture):
    name = texture.name_full

    # https://numpy.org/doc/stable/reference/arrays.interface.html
    class ArrayInterface(object):
        def __init__(self, typestr, length, address):
            self.__array_interface__ = {
                'shape': (length, ),
                'data': (address, False),
                'typestr': typestr,
                'version': 3,
            }

    w, h = texture.size
    channels = int(texture.channels)
    size = w * h * channels
    sRGB = texture.colorspace_settings.name == 'sRGB'

    if size == 0:
        return True

    buffer = MaltPipeline.get_bridge().get_texture_buffer(size)

    array_interface = ArrayInterface('<f4', size, ctypes.addressof(buffer))

    #np_view = np.empty(size, dtype=np.float32)
    np_view = np.array(array_interface, copy=False)

    texture.pixels.foreach_get(np_view)

    MaltPipeline.get_bridge().load_texture(name, (w, h), channels, sRGB)

    return True
예제 #2
0
    def render(self, depsgraph):
        scene = depsgraph.scene_eval
        scale = scene.render.resolution_percentage / 100.0

        self.size_x = int(scene.render.resolution_x * scale)
        self.size_y = int(scene.render.resolution_y * scale)
        resolution = (self.size_x, self.size_y)

        overrides = ['Final Render']

        if self.bridge is not MaltPipeline.get_bridge(depsgraph.scene.world):
            self.bridge = MaltPipeline.get_bridge()
            self.bridge_id = self.bridge.get_viewport_id()

        scene = self.get_scene(None, depsgraph, True, overrides)
        MaltPipeline.get_bridge().render(0, resolution, scene, True)

        buffers = None
        finished = False

        import time
        while not finished:
            buffers, finished, read_resolution = MaltPipeline.get_bridge(
            ).render_result(0)
            time.sleep(0.1)
            if finished: break

        size = self.size_x * self.size_y

        result = self.begin_result(0,
                                   0,
                                   self.size_x,
                                   self.size_y,
                                   layer=depsgraph.view_layer.name)
        passes = result.layers[0].passes

        if 'Combined' in passes:
            combined_pass = passes['Combined']
            rect_ptr = CBlenderMalt.get_rect_ptr(combined_pass.as_pointer())
            ctypes.memmove(rect_ptr, buffers['COLOR'], size * 4 * 4)

        if 'Depth' in passes:
            depth_pass = passes['Depth']
            rect_ptr = CBlenderMalt.get_rect_ptr(depth_pass.as_pointer())
            ctypes.memmove(rect_ptr, buffers['DEPTH'], size * 4)

        self.end_result(result)
        # Delete the scene. Otherwise we get memory leaks.
        # Blender never deletes RenderEngine instances ???
        del self.scene
예제 #3
0
 def __init__(self):
     self.display_draw = None
     self.meshes = {}
     self.pipeline = MaltPipeline.get_pipeline().__class__()
     self.view_matrix = None
     self.request_new_frame = False
     self.profiling_data = io.StringIO()
예제 #4
0
 def get_parameters(self):
     if '_RNA_UI' not in self.keys():
         return {}
     rna = self.get_rna()
     parameters = {}
     for key in rna.keys():
         if rna[key]['active'] == False:
             continue
         if rna[key]['type'] in (Type.INT, Type.FLOAT):
             parameters[key] = self[key]
         elif rna[key]['type'] == Type.BOOL:
             parameters[key] = self.bools[key].boolean
         elif rna[key]['type'] == Type.TEXTURE:
             texture = self.textures[key].texture
             if texture:
                 texture.gl_load()
                 parameters[key] = texture.bindcode
             else:
                 parameters[key] = None
         elif rna[key]['type'] == Type.GRADIENT:
             #TODO: Only works for materials
             parameters[key] = get_color_ramp_texture(self.id_data, key)
         elif rna[key]['type'] == Type.MATERIAL:
             material = self.materials[key].material
             extension = self.materials[key].extension
             if material:
                 shader = material.malt.get_shader(extension)
                 if shader:
                     parameters[key] = shader[
                         MaltPipeline.get_pipeline().__class__.__name__]
                     continue
             parameters[key] = None
     return parameters
예제 #5
0
파일: MaltNodeTree.py 프로젝트: bnpr/Malt
 def get_pipeline_graph(self, graph_type=None):
     if graph_type is None: 
         graph_type = self.graph_type
     bridge = MaltPipeline.get_bridge()
     if bridge and graph_type in bridge.graphs:
         return bridge.graphs[graph_type]
     return None
예제 #6
0
    def update_source(self, context):
        #TODO: Store source locally (for linked data and deleted files)
        global SHADERS
        self.compiler_error = ''
        uniforms = {}

        if self.shader_source != '':
            path = bpy.path.abspath(self.shader_source)
            if os.path.exists(path):
                pipeline_material = {}
                SHADERS[self.shader_source] = pipeline_material

                pipelines = [MaltPipeline.get_pipeline()
                             ]  #TODO: get all active pipelines
                for pipeline in pipelines:
                    pipeline_name = pipeline.__class__.__name__
                    pipeline_material[
                        pipeline_name] = pipeline.compile_material(path)

                    for pass_name, shader in pipeline_material[
                            pipeline_name].items():
                        for uniform_name, uniform in shader.uniforms.items():
                            uniforms[uniform_name] = uniform
                        if shader.error:
                            self.compiler_error += pipeline_name + " : " + pass_name + " : " + shader.error
                        if shader.validator:
                            self.compiler_error += pipeline_name + " : " + pass_name + " : " + shader.validator
            else:
                self.compiler_error = 'Invalid file path'

        self.parameters.setup(uniforms)
예제 #7
0
 def execute(self, context):
     new_pass = context.scene.world.malt_graph_types[
         self.graph_type].custom_passes.add()
     new_pass.name = self.name
     for name in MaltPipeline.get_bridge().graphs[
             self.graph_type].graph_io.keys():
         new_pass.io.add().name = name
     return {'FINISHED'}
예제 #8
0
def get_gradient(color_ramp, material_name, name):
    full_name = material_name + '_' + name
    pixels = []

    if material_name not in __GRADIENTS:
        __GRADIENTS[material_name] = {}

    gradients = __GRADIENTS[material_name]

    if name not in gradients:
        for i in range(0, __GRADIENT_RESOLUTION):
            pixel = color_ramp.evaluate(i * (1.0 / __GRADIENT_RESOLUTION))
            pixels.extend(pixel)
        nearest = color_ramp.interpolation == 'CONSTANT'
        MaltPipeline.get_bridge().load_gradient(full_name, pixels, nearest)
        gradients[name] = full_name

    return full_name
예제 #9
0
 def __init__(self):
     self.display_draw = None
     self.scene = Scene.Scene()
     self.view_matrix = None
     self.request_new_frame = True
     self.request_scene_update = True
     self.profiling_data = io.StringIO()
     self.bridge = MaltPipeline.get_bridge()
     self.bridge_id = self.bridge.get_viewport_id()
예제 #10
0
    def update_source(self, context):
        #TODO: Store source locally (for linked data and deleted files)
        global SHADERS
        self.compiler_error = ''
        parameters = {}

        if self.shader_source != '':
            path = find_shader_path(self.shader_source)
            if path:
                compiled_material = MaltPipeline.get_pipeline(
                ).compile_material(path)
                if compiled_material is None:
                    self.compiler_error = 'Invalid file'
                elif isinstance(compiled_material, str):
                    self.compiler_error = compiled_material
                else:
                    pipeline_material = {}
                    SHADERS[self.shader_source] = pipeline_material

                    pipelines = [MaltPipeline.get_pipeline()
                                 ]  #TODO: get all active pipelines
                    for pipeline in pipelines:
                        pipeline_name = pipeline.__class__.__name__
                        pipeline_material[pipeline_name] = compiled_material

                        for pass_name, shader in pipeline_material[
                                pipeline_name].items():
                            for uniform_name, uniform in shader.uniforms.items(
                            ):
                                parameters[
                                    uniform_name] = Parameter.from_uniform(
                                        uniform)
                            if shader.error:
                                self.compiler_error += pipeline_name + " : " + pass_name + " : " + shader.error
                            if shader.validator:
                                self.compiler_error += pipeline_name + " : " + pass_name + " : " + shader.validator
            else:
                self.compiler_error = 'Invalid file path'

        if self.compiler_error != '':
            SHADERS[self.shader_source] = None
            self.parameters.setup({})
        else:
            self.parameters.setup(parameters)
예제 #11
0
 def get_parameter_enums(self, context=None):
     types = ['None']
     bridge = MaltPipeline.get_bridge()
     if bridge and self.graph_type in bridge.graphs:
         graph = bridge.graphs[self.graph_type]
         if self.io_type in graph.graph_io.keys():
             if self.is_output:
                 types = graph.graph_io[self.io_type].dynamic_output_types
             else:
                 types = graph.graph_io[self.io_type].dynamic_input_types
     return [(type, type, type) for type in types]
예제 #12
0
파일: MaltNodeTree.py 프로젝트: bnpr/Malt
def setup_node_trees():
    graphs = MaltPipeline.get_bridge().graphs

    for name, graph in graphs.items():
        preload_menus(graph.structs, graph.functions)
    
    track_library_changes(force_update=True, is_initial_setup=True)
    
    for tree in bpy.data.node_groups:
        if tree.bl_idname == 'MaltTree':
            tree.reload_nodes()
            tree.update_ext(force_track_shader_changes=False)
    from BlenderMalt import MaltMaterial
    MaltMaterial.track_shader_changes()
예제 #13
0
 def update_source(self, context):
     #print('UPDATE SOURCE')
     global _SHADER_PATHS
     if str(self.shader_source) not in _SHADER_PATHS:
         _SHADER_PATHS.append(str(self.shader_source))
     self.compiler_error = ''
     if self.shader_source != '':
         path = bpy.path.abspath(self.shader_source)
         compiled_material = MaltPipeline.get_bridge().compile_material(
             path)
         self.compiler_error = compiled_material.compiler_error
         self.parameters.setup(compiled_material.parameters)
     else:
         self.parameters.setup({})
예제 #14
0
def track_shader_changes(force_update=False):
    if bpy.context.scene.render.engine != 'MALT' and force_update == False:
        return 1

    global INITIALIZED
    global __TIMESTAMP
    global _SHADER_PATHS
    try:
        start_time = time.time()

        #print('TRACK UPDATES')

        needs_update = []

        for material in bpy.data.materials:
            path = bpy.path.abspath(material.malt.shader_source,
                                    library=material.library)
            if path not in needs_update:
                if os.path.exists(path):
                    stats = os.stat(path)
                    if path not in _SHADER_PATHS or stats.st_mtime > __TIMESTAMP:
                        if path not in _SHADER_PATHS:
                            _SHADER_PATHS.append(path)
                        needs_update.append(path)

        #print(needs_update)

        if len(needs_update) > 0:
            compiled_materials = MaltPipeline.get_bridge().compile_materials(
                needs_update)
            for material in bpy.data.materials:
                path = bpy.path.abspath(material.malt.shader_source,
                                        library=material.library)
                if path in compiled_materials.keys():
                    material.malt.compiler_error = compiled_materials[
                        path].compiler_error
                    material.malt.parameters.setup(
                        compiled_materials[path].parameters)
            for screen in bpy.data.screens:
                for area in screen.areas:
                    area.tag_redraw()

        __TIMESTAMP = start_time
        INITIALIZED = True
    except:
        import traceback, logging as log
        log.error(traceback.format_exc())
    return 1  #Track again in 1 second
예제 #15
0
 def get_parameters(self):
     if '_RNA_UI' not in self.keys():
         return {}
     rna = self.get_rna()
     parameters = {}
     for key in rna.keys():
         if rna[key]['active'] == False:
             continue
         if rna[key]['type'] == 'SHADER':
             shader = self.shaders[key].get_shader()
             if shader:
                 shader = shader[MaltPipeline.get_pipeline().__class__.
                                 __name__]['SHADER']
             parameters[key] = shader
         elif rna[key]['type'] == GL.GL_BOOL:
             parameters[key] = self.bools[key].boolean
         else:
             parameters[key] = self[key]
     return parameters
예제 #16
0
 def draw(self, context):
     import pprint
     from . import MaltPipeline
     stats = MaltPipeline.get_bridge().get_stats()
     for line in stats.splitlines():
         self.layout.label(text=line)
예제 #17
0
 def get_pipeline(self):
     if self.pipeline is None or self.pipeline.__class__ != MaltPipeline.get_pipeline(
     ).__class__:
         self.pipeline = MaltPipeline.get_pipeline().__class__()
     return self.pipeline
예제 #18
0
    def view_draw(self, context, depsgraph):
        profiler = cProfile.Profile()
        global PROFILE
        if PROFILE:
            profiler.enable()
            if self.request_new_frame:
                self.profiling_data = io.StringIO()
        
        if self.bridge is not MaltPipeline.get_bridge():
            #The Bridge has been reset
            self.bridge.free_viewport_id(self.bridge_id)
            self.bridge = MaltPipeline.get_bridge()
            self.bridge_id = self.bridge.get_viewport_id()
            self.request_new_frame = True
            self.request_scene_update = True
        
        overrides = []
        if context.space_data.shading.type == 'MATERIAL':
            overrides.append('Preview')

        scene = self.get_scene(context, depsgraph, self.request_scene_update, overrides)
        resolution = context.region.width, context.region.height

        if self.request_new_frame:
            self.bridge.render(self.bridge_id, resolution, scene, self.request_scene_update)
            self.request_new_frame = False
            self.request_scene_update = False

        buffers, finished, read_resolution = self.bridge.render_result(self.bridge_id)
        pixels = buffers['COLOR']

        if not finished:
            self.tag_redraw()
        if pixels is None or resolution != read_resolution:
            # Only render if resolution is the same as read_resolution.
            # This avoids visual glitches when the viewport is resizing.
            # The alternative would be locking when writing/reading the pixel buffer.
            return

        fbo = GL.gl_buffer(GL.GL_INT, 1)
        GL.glGetIntegerv(GL.GL_FRAMEBUFFER_BINDING, fbo)
        
        render_texture = Texture(resolution, GL.GL_RGBA32F, GL.GL_FLOAT, pixels)
        
        self.bind_display_space_shader(depsgraph.scene_eval)
        if self.display_draw is None or self.display_draw.resolution != resolution:
            if self.display_draw:
                self.display_draw.gl_delete()
            self.display_draw = DisplayDraw(resolution)
        self.display_draw.draw(fbo, render_texture)
        self.unbind_display_space_shader()

        if PROFILE:
            profiler.disable()
            stats = pstats.Stats(profiler, stream=self.profiling_data)
            stats.strip_dirs()
            stats.sort_stats(pstats.SortKey.CUMULATIVE)
            stats.print_stats()
            print('PROFILE BEGIN--------------------------------------')
            print(self.profiling_data.getvalue())
            print('PROFILE END--------------------------------------')
예제 #19
0
def load_mesh(object, name):
    use_split_faces = False  #Use split_faces instead of calc_normals_split (Slightly faster)

    m = object.data
    if object.type != 'MESH' or bpy.context.mode == 'EDIT_MESH':
        m = object.to_mesh()

    if m is None or len(m.polygons) == 0:
        return None

    m.calc_loop_triangles()

    polys_ptr = ctypes.c_void_p(m.polygons[0].as_pointer())
    has_flat_polys = CBlenderMalt.has_flat_polys(polys_ptr, len(m.polygons))

    needs_split_normals = m.use_auto_smooth or m.has_custom_normals or has_flat_polys
    if needs_split_normals:
        if use_split_faces:
            m.split_faces()
        else:
            m.calc_normals_split()

    verts_ptr = ctypes.c_void_p(m.vertices[0].as_pointer())
    loops_ptr = ctypes.c_void_p(m.loops[0].as_pointer())
    loop_tris_ptr = ctypes.c_void_p(m.loop_triangles[0].as_pointer())

    loop_count = len(m.loops)
    loop_tri_count = len(m.loop_triangles)
    material_count = max(1, len(m.materials))

    positions = get_load_buffer('positions', ctypes.c_float, (loop_count * 3))
    normals = get_load_buffer('normals', ctypes.c_int16, (loop_count * 3))
    indices = []
    indices_ptrs = (ctypes.c_void_p * material_count)()
    for i in range(material_count):
        indices.append(
            get_load_buffer('indices' + str(i), ctypes.c_uint32,
                            (loop_tri_count * 3)))
        indices_ptrs[i] = ctypes.cast(indices[i], ctypes.c_void_p)

    #Create a new one each time so we don't have to care about zeroing the previous results
    indices_lengths = (ctypes.c_uint32 * material_count)()

    CBlenderMalt.retrieve_mesh_data(verts_ptr, loops_ptr, loop_count,
                                    loop_tris_ptr, loop_tri_count, polys_ptr,
                                    positions, normals, indices_ptrs,
                                    indices_lengths)

    if needs_split_normals and use_split_faces == False:
        #TODO: Find a way to get a direct pointer to custom normals
        normals = retrieve_array(m.loops, 'normal', 'f', ctypes.c_float,
                                 [0.0, 0.0, 0.0])

    uvs = []
    tangents = []
    for i, uv_layer in enumerate(m.uv_layers):
        uv_ptr = ctypes.c_void_p(uv_layer.data[0].as_pointer())
        uv = get_load_buffer('uv' + str(i), ctypes.c_float, loop_count * 2)
        CBlenderMalt.retrieve_mesh_uv(uv_ptr, loop_count, uv)
        uvs.append(uv)

        if (object.original.data.malt_parameters.bools['precomputed_tangents'].
                boolean):
            m.calc_tangents(uvmap=uv_layer.name)
            #calc_tangents is so slow there's no point in optimizing this
            packed_tangents = [
                e for l in m.loops for e in (*l.tangent, l.bitangent_sign)
            ]
            tangents.append(packed_tangents)

    colors = []
    for i, vertex_color in enumerate(m.vertex_colors):
        #Colors are already contiguous in memory, so we pass them directly to OpenGL
        color = (ctypes.c_uint8 * (loop_count * 4)).from_address(
            vertex_color.data[0].as_pointer())
        colors.append(color)

    #TODO: Optimize. Create load buffers from bytearrays and retrieve them later
    mesh_data = {
        'positions': bytearray(positions),
        'indices': [bytearray(i) for i in indices],
        'indices_lengths': [l for l in indices_lengths],
        'normals': bytearray(normals),
        'uvs': [bytearray(u) for u in uvs],
        'tangents': [bytearray(t) for t in tangents],
        'colors': [bytearray(c) for c in colors],
    }

    MaltPipeline.get_bridge().load_mesh(name, mesh_data)

    return [name for i in range(material_count)]
예제 #20
0
파일: MaltNodeTree.py 프로젝트: bnpr/Malt
def track_library_changes(force_update=False, is_initial_setup=False):
    if bpy.context.scene.render.engine != 'MALT' and force_update == False:
        return 1
    
    bridge = MaltPipeline.get_bridge()
    graphs = MaltPipeline.get_bridge().graphs
    updated_graphs = []
    if is_initial_setup == False:
        for name, graph in graphs.items():
            if graph.needs_reload():
                updated_graphs.append(name)
        if len(updated_graphs) > 0:        
            bridge.reload_graphs(updated_graphs)
            for graph_name in updated_graphs:
                graph = graphs[graph_name]
                preload_menus(graph.structs, graph.functions)

    global __LIBRARIES
    global __TIMESTAMP
    start_time = time.time()

    #purge unused libraries
    new_dic = {}
    for tree in bpy.data.node_groups:
        if isinstance(tree, MaltTree):
            src_path = tree.get_library_path()
            if src_path:
                if src_path in __LIBRARIES:
                    new_dic[src_path] = __LIBRARIES[src_path]
                else:
                    new_dic[src_path] = None
    __LIBRARIES = new_dic

    needs_update = set()
    for path, library in __LIBRARIES.items():
        root_dir = os.path.dirname(path)
        if os.path.exists(path):
            if library is None:
                needs_update.add(path)
            else:
                for sub_path in library['paths']:
                    sub_path = os.path.join(root_dir, sub_path)
                    if os.path.exists(sub_path):
                        # Don't track individual files granularly since macros can completely change them
                        if os.stat(sub_path).st_mtime > __TIMESTAMP:
                            needs_update.add(path)
                            break
    
    if len(needs_update) > 0:
        results = MaltPipeline.get_bridge().reflect_source_libraries(needs_update)
        for path, reflection in results.items():
            __LIBRARIES[path] = reflection
            preload_menus(reflection['structs'], reflection['functions'])
        
    if is_initial_setup == False and max(len(needs_update), len(updated_graphs)) > 0:
        for tree in bpy.data.node_groups:
            if isinstance(tree, MaltTree):
                src_path = tree.get_library_path()
                if tree.graph_type in updated_graphs or (src_path and src_path in needs_update):
                    tree.reload_nodes()
                    tree.update_ext(force_track_shader_changes=False)
        from BlenderMalt import MaltMaterial
        MaltMaterial.track_shader_changes()
    
    __TIMESTAMP = start_time
    return 0.1