forked from LeenkxTeam/LNXSDK
		
	
		
			
	
	
		
			129 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			129 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								from enum import IntEnum, unique
							 | 
						||
| 
								 | 
							
								from typing import List, Set, Tuple, Union, Optional
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import bpy
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import lnx
							 | 
						||
| 
								 | 
							
								from lnx.material.shader import Shader, ShaderContext, vec3str, floatstr
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if lnx.is_reload(__name__):
							 | 
						||
| 
								 | 
							
								    lnx.material.shader = lnx.reload_module(lnx.material.shader)
							 | 
						||
| 
								 | 
							
								    from lnx.material.shader import Shader, ShaderContext, vec3str, floatstr
							 | 
						||
| 
								 | 
							
								else:
							 | 
						||
| 
								 | 
							
								    lnx.enable_reload(__name__)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @unique
							 | 
						||
| 
								 | 
							
								    class ParserContext(IntEnum):
							 | 
						||
| 
								 | 
							
								        """Describes which kind of node tree is parsed."""
							 | 
						||
| 
								 | 
							
								        OBJECT = 0
							 | 
						||
| 
								 | 
							
								        # Texture node trees are not supported yet
							 | 
						||
| 
								 | 
							
								        # TEXTURE = 1
							 | 
						||
| 
								 | 
							
								        WORLD = 2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @unique
							 | 
						||
| 
								 | 
							
								    class ParserPass(IntEnum):
							 | 
						||
| 
								 | 
							
								        """In some situations, a node tree (or a subtree of that) needs
							 | 
						||
| 
								 | 
							
								        to be parsed multiple times in different contexts called _passes_.
							 | 
						||
| 
								 | 
							
								        Nodes can output different code in reaction to the parser state's
							 | 
						||
| 
								 | 
							
								        current pass; for more information on the individual passes
							 | 
						||
| 
								 | 
							
								        please refer to below enum items.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        REGULAR = 0
							 | 
						||
| 
								 | 
							
								        """The tree is parsed to generate regular shader code."""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        DX_SCREEN_SPACE = 1
							 | 
						||
| 
								 | 
							
								        """The tree is parsed to output shader code to compute
							 | 
						||
| 
								 | 
							
								        the derivative of a value with respect to the screen's x coordinate."""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        DY_SCREEN_SPACE = 2
							 | 
						||
| 
								 | 
							
								        """The tree is parsed to output shader code to compute
							 | 
						||
| 
								 | 
							
								        the derivative of a value with respect to the screen's y coordinate."""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ParserState:
							 | 
						||
| 
								 | 
							
								    """Dataclass to keep track of the current state while parsing a shader tree."""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, context: ParserContext, tree_name: str, world: Optional[bpy.types.World] = None):
							 | 
						||
| 
								 | 
							
								        self.context = context
							 | 
						||
| 
								 | 
							
								        self.tree_name = tree_name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.current_pass = ParserPass.REGULAR
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # The current world, if parsing a world node tree
							 | 
						||
| 
								 | 
							
								        self.world = world
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Active shader - frag for surface / tese for displacement
							 | 
						||
| 
								 | 
							
								        self.curshader: Shader = None
							 | 
						||
| 
								 | 
							
								        self.con: ShaderContext = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.vert: Shader = None
							 | 
						||
| 
								 | 
							
								        self.frag: Shader = None
							 | 
						||
| 
								 | 
							
								        self.geom: Shader = None
							 | 
						||
| 
								 | 
							
								        self.tesc: Shader = None
							 | 
						||
| 
								 | 
							
								        self.tese: Shader = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Group stack (last in the list = innermost group)
							 | 
						||
| 
								 | 
							
								        self.parents: List[bpy.types.Node] = []
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Cache for computing nodes only once
							 | 
						||
| 
								 | 
							
								        self.parsed: Set[str] = set()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # What to parse from the node tree
							 | 
						||
| 
								 | 
							
								        self.parse_surface = True
							 | 
						||
| 
								 | 
							
								        self.parse_opacity = True
							 | 
						||
| 
								 | 
							
								        self.parse_displacement = True
							 | 
						||
| 
								 | 
							
								        self.basecol_only = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.procedurals_written: set[Shader] = set()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Already exported radiance/irradiance (currently we can only convert
							 | 
						||
| 
								 | 
							
								        # an already existing texture as radiance/irradiance)
							 | 
						||
| 
								 | 
							
								        self.radiance_written = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.normal_parsed = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.dxdy_varying_input_value = False
							 | 
						||
| 
								 | 
							
								        """Whether the result of the previously parsed node differs
							 | 
						||
| 
								 | 
							
								        between fragments and represents an input value to which to apply
							 | 
						||
| 
								 | 
							
								        dx/dy offsets (if required by the parser pass).
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Shader output values
							 | 
						||
| 
								 | 
							
								        self.out_basecol: vec3str = 'vec3(0.8)'
							 | 
						||
| 
								 | 
							
								        self.out_roughness: floatstr = '0.0'
							 | 
						||
| 
								 | 
							
								        self.out_metallic: floatstr = '0.0'
							 | 
						||
| 
								 | 
							
								        self.out_occlusion: floatstr = '1.0'
							 | 
						||
| 
								 | 
							
								        self.out_specular: floatstr = '1.0'
							 | 
						||
| 
								 | 
							
								        self.out_opacity: floatstr = '1.0'
							 | 
						||
| 
								 | 
							
								        self.out_ior: floatstr = '1.450'
							 | 
						||
| 
								 | 
							
								        self.out_emission_col: vec3str = 'vec3(0.0)'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def reset_outs(self):
							 | 
						||
| 
								 | 
							
								        """Reset the shader output values to their default values."""
							 | 
						||
| 
								 | 
							
								        self.out_basecol = 'vec3(0.8)'
							 | 
						||
| 
								 | 
							
								        self.out_roughness = '0.0'
							 | 
						||
| 
								 | 
							
								        self.out_metallic = '0.0'
							 | 
						||
| 
								 | 
							
								        self.out_occlusion = '1.0'
							 | 
						||
| 
								 | 
							
								        self.out_specular = '1.0'
							 | 
						||
| 
								 | 
							
								        self.out_opacity = '1.0'
							 | 
						||
| 
								 | 
							
								        self.out_ior = '1.450'
							 | 
						||
| 
								 | 
							
								        self.out_emission_col = 'vec3(0.0)'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def get_outs(self) -> Tuple[vec3str, floatstr, floatstr, floatstr, floatstr, floatstr, floatstr, vec3str]:
							 | 
						||
| 
								 | 
							
								        """Return the shader output values as a tuple."""
							 | 
						||
| 
								 | 
							
								        return (self.out_basecol, self.out_roughness, self.out_metallic, self.out_occlusion, self.out_specular,
							 | 
						||
| 
								 | 
							
								                self.out_opacity, self.out_ior, self.out_emission_col)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def get_parser_pass_suffix(self) -> str:
							 | 
						||
| 
								 | 
							
								        """Return a suffix for the current parser pass that can be appended
							 | 
						||
| 
								 | 
							
								        to shader variables to avoid compilation errors due to redefinitions.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        if self.current_pass == ParserPass.DX_SCREEN_SPACE:
							 | 
						||
| 
								 | 
							
								            return '_dx'
							 | 
						||
| 
								 | 
							
								        elif self.current_pass == ParserPass.DY_SCREEN_SPACE:
							 | 
						||
| 
								 | 
							
								            return '_dy'
							 | 
						||
| 
								 | 
							
								        return ''
							 |