fromfunctoolsimportpartialimportosimportfuryfromfury.decoratorsimportwarn_on_args_to_kwargsfromfury.ioimportload_textfromfury.libimport(VTK_OBJECT,Command,DataObject,Shader,calldata_type,numpy_support,)SHADERS_DIR=os.path.join(os.path.dirname(__file__))SHADERS_EXTS=[".glsl",".vert",".tesc",".tese",".geom",".frag",".comp",]SHADERS_TYPE={"vertex":Shader.Vertex,"geometry":Shader.Geometry,"fragment":Shader.Fragment,}REPLACEMENT_SHADERS_TYPES={"vertex":Shader.Vertex,"fragment":Shader.Fragment}SHADERS_BLOCK={"position":"//VTK::PositionVC",# frag position in VC"normal":"//VTK::Normal",# optional normal declaration"light":"//VTK::Light",# extra lighting parameters"tcoord":"//VTK::TCoord",# Texture coordinates"color":"//VTK::Color",# material property values"clip":"//VTK::Clip",# clipping plane vars"camera":"//VTK::Camera",# camera and actor matrix values"prim_id":"//VTK::PrimID",# Apple Bug"valuepass":"//VTK::ValuePass",# Value raster"output":"//VTK::Output",# only for geometry shader"coincident":"//VTK::Coincident",# handle coincident offsets"zbufer":"//VTK::ZBuffer","depth_peeling":"//VTK::DepthPeeling",# Depth Peeling Support"picking":"//VTK::Picking",# picking support}# See [1] for a more extensive list of OpenGL constants# [1] https://docs.factorcode.org/content/vocab-opengl.gl.htmlGL_NUMBERS={"GL_ONE":1,"GL_ZERO":0,"GL_BLEND":3042,"GL_ONE_MINUS_SRC_ALPHA":771,"GL_SRC_ALPHA":770,"GL_DEPTH_TEST":2929,"GL_DST_COLOR":774,"GL_FUNC_SUBTRACT":3277,"GL_CULL_FACE":2884,"GL_ALPHA_TEST":3008,"GL_CW":2304,"GL_CCW":2305,"GL_ONE_MINUS_SRC_COLOR":769,"GL_SRC_COLOR":768,}
[docs]defcompose_shader(glsl_code):"""Merge GLSL shader code from a list of strings. Parameters ---------- glsl_code : list of str (code or filenames). Returns ------- code : str GLSL shader code. """ifnotglsl_code:return""ifnotall(isinstance(i,str)foriinglsl_code):raiseIOError("The only supported format are string.")ifisinstance(glsl_code,str):returnglsl_codecode=""forcontentinglsl_code:code+="\n"code+=contentreturncode
[docs]defimport_fury_shader(shader_file):"""Import a Fury shader. Parameters ---------- shader_file : str Filename of shader. The file must be in the fury/shaders directory and must have the one of the supported extensions specified by the Khronos Group (https://github.com/KhronosGroup/glslang#execution-of-standalone-wrapper). Returns ------- code : str GLSL shader code. """shader_fname=os.path.join(SHADERS_DIR,shader_file)returnload_shader(shader_fname)
[docs]defload_shader(shader_file):"""Load a shader from a file. Parameters ---------- shader_file : str Full path to a shader file ending with one of the file extensions defined by the Khronos Group (https://github.com/KhronosGroup/glslang#execution-of-standalone-wrapper). Returns ------- code : str GLSL shader code. """file_ext=os.path.splitext(os.path.basename(shader_file))[1]iffile_extnotinSHADERS_EXTS:raiseIOError('Shader file "{}" does not have one of the supported '"extensions: {}.".format(shader_file,SHADERS_EXTS))returnload_text(shader_file)
[docs]@warn_on_args_to_kwargs()defshader_to_actor(actor,shader_type,*,impl_code="",decl_code="",block="valuepass",keep_default=True,replace_first=True,replace_all=False,debug=False,):"""Apply your own substitutions to the shader creation process. A set of string replacements is applied to a shader template. This function let's apply custom string replacements. Parameters ---------- actor : vtkActor Fury actor you want to set the shader code to. shader_type : str Shader type: vertex, fragment impl_code : str, optional Shader implementation code, should be a string or filename. Default None. decl_code : str, optional Shader declaration code, should be a string or filename. Default None. block : str, optional Section name to be replaced. VTK use of heavy string replacements to insert shader and make it flexible. Each section of the shader template have a specific name. For more information: https://vtk.org/Wiki/Shaders_In_VTK. The possible values are: position, normal, light, tcoord, color, clip, camera, prim_id, valuepass. by default valuepass keep_default : bool, optional Keep the default block tag to let VTK replace it with its default behavior. Default True. replace_first : bool, optional If True, apply this change before the standard VTK replacements. Default True. replace_all : bool, optional [description], by default False debug : bool, optional Introduce a small error to debug shader code. Default False. """shader_type=shader_type.lower()shader_type=REPLACEMENT_SHADERS_TYPES.get(shader_type,None)ifshader_typeisNone:msg="Invalid Shader Type. Please choose between "msg+=", ".join(REPLACEMENT_SHADERS_TYPES.keys())raiseValueError(msg)block=block.lower()block=SHADERS_BLOCK.get(block,None)ifblockisNone:msg="Invalid Shader Type. Please choose between "msg+=", ".join(SHADERS_BLOCK.keys())raiseValueError(msg)block_dec=block+"::Dec"block_impl=block+"::Impl"ifkeep_default:decl_code=block_dec+"\n"+decl_codeimpl_code=block_impl+"\n"+impl_codeifdebug:fury.enable_warnings()error_msg="\n\n--- DEBUG: THIS LINE GENERATES AN ERROR ---\n\n"impl_code+=error_msgsp=actor.GetShaderProperty()sp.AddShaderReplacement(shader_type,block_dec,replace_first,decl_code,replace_all)sp.AddShaderReplacement(shader_type,block_impl,replace_first,impl_code,replace_all)
[docs]defreplace_shader_in_actor(actor,shader_type,code):"""Set and replace the shader template with a new one. Parameters ---------- actor : vtkActor Fury actor you want to set the shader code to. shader_type : str Shader type: vertex, geometry, fragment. code : str New shader template code. """function_name={"vertex":"SetVertexShaderCode","fragment":"SetFragmentShaderCode","geometry":"SetGeometryShaderCode",}shader_type=shader_type.lower()function=function_name.get(shader_type,None)iffunctionisNone:msg="Invalid Shader Type. Please choose between "msg+=", ".join(function_name.keys())raiseValueError(msg)sp=actor.GetShaderProperty()getattr(sp,function)(code)
[docs]@warn_on_args_to_kwargs()defadd_shader_callback(actor,callback,*,priority=0.0):"""Add a shader callback to the actor. Parameters ---------- actor : vtkActor Fury actor you want to add the callback to. callback : callable Function or class that contains 3 parameters: caller, event, calldata. This callback will be trigger at each `UpdateShaderEvent` event. priority : float, optional Commands with a higher priority are called first. Returns ------- id_observer : int An unsigned Int tag which can be used later to remove the event or retrieve the vtkCommand used in the observer. See more at: https://vtk.org/doc/nightly/html/classvtkObject.html Examples -------- .. code-block:: python add_shader_callback(actor, func_call1) id_observer = add_shader_callback(actor, func_call2) actor.GetMapper().RemoveObserver(id_observer) Priority calls .. code-block:: python test_values = [] def callbackLow(_caller, _event, calldata=None): program = calldata if program is not None: test_values.append(0) def callbackHigh(_caller, _event, calldata=None): program = calldata if program is not None: test_values.append(999) def callbackMean(_caller, _event, calldata=None): program = calldata if program is not None: test_values.append(500) fs.add_shader_callback( actor, callbackHigh, priority=999) fs.add_shader_callback( actor, callbackLow, priority=0) id_mean = fs.add_shader_callback( actor, callbackMean, priority=500) showm.start() # test_values = [999, 500, 0, 999, 500, 0, ...] """ifnotisinstance(priority,(float,int)):raiseValueError(""" add_shader_callback priority argument should be a float/int""")# @warn_on_args_to_kwargs()@calldata_type(VTK_OBJECT)defcbk(caller,event,calldata=None):callback(caller,event,calldata=calldata)mapper=actor.GetMapper()id_observer=mapper.AddObserver(Command.UpdateShaderEvent,cbk,priority)returnid_observer
[docs]@warn_on_args_to_kwargs()defshader_apply_effects(window,actor,effects,*,priority=0):"""This applies a specific opengl state (effect) or a list of effects just before the actor's shader is executed. Parameters ---------- window : RenderWindow For example, this is provided by the ShowManager.window attribute. actor : actor effects : a function or a list of functions priority : float, optional Related with the shader callback command. Effects with a higher priority are applied first and can be override by the others. Returns ------- id_observer : int An unsigned Int tag which can be used later to remove the event or retrieve the vtkCommand used in the observer. See more at: https://vtk.org/doc/nightly/html/classvtkObject.html """ifnotisinstance(effects,list):effects=[effects]@warn_on_args_to_kwargs()defcallback(_caller,_event,*,calldata=None,effects=None,window=None):program=calldataglState=window.GetState()ifprogramisnotNone:forfuncineffects:func(glState)id_observer=add_shader_callback(actor,partial(callback,effects=effects,window=window),priority=priority)returnid_observer
[docs]@warn_on_args_to_kwargs()defattribute_to_actor(actor,arr,attr_name,*,deep=True):"""Link a numpy array with vertex attribute. Parameters ---------- actor : vtkActor Fury actor you want to add the vertex attribute to. arr : ndarray Array to link to vertices. attr_name : str Vertex attribute name. The vtk array will take the same name as the attribute. deep : bool, optional If True a deep copy is applied, otherwise a shallow copy is applied. Default True. """nb_components=arr.shape[1]ifarr.ndim>1elsearr.ndimvtk_array=numpy_support.numpy_to_vtk(arr,deep=deep)vtk_array.SetNumberOfComponents(nb_components)vtk_array.SetName(attr_name)actor.GetMapper().GetInput().GetPointData().AddArray(vtk_array)mapper=actor.GetMapper()mapper.MapDataArrayToVertexAttribute(attr_name,attr_name,DataObject.FIELD_ASSOCIATION_POINTS,-1)