danini-the-panini/mittsu-opengl

View on GitHub
lib/mittsu/opengl/shader/rbsl_loader.rb

Summary

Maintainability
A
1 hr
Test Coverage
B
88%
module Mittsu
  class OpenGL::Shader
    module RBSLLoader
      UNIFORM_RGX = /uniform\s+(\S+)\s+(\w+)(|\s+\=\s+(.+));/
      COLOR_RGX = /color\(([^\)]*)\)/
  
      class << self
        def load_shader(shader, chunks)
          shader.lines.flat_map(&:chomp).map{ |line|
            if line =~ /(\s*)#include\s+(\w+)/
                indentation = $1
                chunk_name = $2.to_sym
    
                chunks[chunk_name].lines.map(&:chomp).map{ |l|
                "#{indentation}#{l}"
                }
            else
                line
            end
          }.join("\n") + "\n"
        end
    
        def extract_array_contents(str)
          /\[([^\]]+)\]/.match(str)[1]
        end
    
        def parse_int(str)
          str.to_i
        end
    
        (2..4).each do |n|
          define_method("parse_ivec#{n}") do |str|
            str =~ /ivec#{n}\(([^\)]+)\)/
            $1.split(',').map(&:strip).map(&:to_i).take(n)
          end
        end
    
        def parse_float(str)
          str.to_f
        end
    
        def parse_single_color(values)
          if values.length == 1
            values = values.map(&:to_i)
          else
            values = values.map(&:to_f)
          end
          Color.new(*values)
        end
    
        def parse_color(str)
          str =~ COLOR_RGX
          values = $1.split(',').map(&:strip)
          parse_single_color(values)
        end
    
        def parse_color_array(str)
          str = extract_array_contents(str)
          str.scan(COLOR_RGX).map{ |m|
            values = m.first.split(',').map(&:strip)
            parse_single_color(values)
          }
        end
    
        def parse_int_array(str)
          str = extract_array_contents(str)
          str.split(',').map(&:strip).map(&:to_i)
        end
    
        def parse_float_array(str)
          str = extract_array_contents(str)
          str.split(',').map(&:strip).map(&:to_f)
        end
    
        [Vector2, Vector3, Vector4].each do |vectorClass|
          define_method("parse_vec#{vectorClass::DIMENSIONS}") do |str|
            str =~ /vec#{vectorClass::DIMENSIONS}\(([^\)]+)\)/
            values = $1.split(',').map(&:strip).map(&:to_f).take(vectorClass::DIMENSIONS)
            vectorClass.new(*values)
          end
        end
    
        (2..4).each do |n|
          define_method("parse_ivec#{n}_array") do |str|
            str = extract_array_contents(str)
            str.scan(/ivec#{n}\(([^\)]+)\)/).map{ |m| m.first.split(',').map(&:strip).map(&:to_i).take(n) }
          end
        end
    
        [Vector2, Vector3, Vector4].each do |vectorClass|
          define_method("parse_vec#{vectorClass::DIMENSIONS}_array") do |str|
            str = extract_array_contents(str)
            str.scan(/vec#{vectorClass::DIMENSIONS}\(([^\)]+)\)/).map{ |m|
              values = m.first.split(',').map(&:strip).map(&:to_f).take(vectorClass::DIMENSIONS)
              vectorClass.new(*values)
            }
          end
        end
    
        [Matrix3, Matrix4].each do |matrixClass|
          define_method("parse_mat#{matrixClass::DIMENSIONS}") do |str|
            str =~ /mat#{matrixClass::DIMENSIONS}\(([^\)]+)\)/
            values = $1.split(',').map(&:strip).map(&:to_f).take(matrixClass::DIMENSIONS * matrixClass::DIMENSIONS)
            matrixClass.new().tap { |mat| mat.set(*values) }
          end
        end
    
        [Matrix3, Matrix4].each do |matrixClass|
          define_method("parse_mat#{matrixClass::DIMENSIONS}_array") do |str|
            str = extract_array_contents(str)
            str.scan(/mat#{matrixClass::DIMENSIONS}\(([^\)]+)\)/).map{ |m|
              values = m.first.split(',').map(&:strip).map(&:to_f).take(matrixClass::DIMENSIONS * matrixClass::DIMENSIONS)
              matrixClass.new().tap { |mat| mat.set(*values) }
            }
          end
        end
    
        def parse_texture(_str)
          nil
        end
    
        def parse_texture_array(_str)
          []
        end
    
        def parse_uniform(uniform)
          uniform =~ UNIFORM_RGX
          type_str = $1
          type = type_str.to_sym
          is_array = type_str.end_with?('[]')
          name = $2
          value_str = $4
          value = is_array ? [] : nil
          if value_str && !value_str.empty?
            value = send("parse_#{type.to_s.gsub(/\[\]/, '_array')}".to_s, value_str)
          end
          [name, Uniform.new(type, value)]
        end
    
        def load_uniforms(uniforms, uniforms_lib)
          uniform_strings = nil;
          in_uniform = false
    
          uniforms.lines.map(&:strip).each_with_object({}) { |line, hash|
            if in_uniform
              uniform_strings << line
              if line.end_with?(';')
                in_uniform = false
                name, value = parse_uniform(uniform_strings.join(' '))
                hash[name] = value
              end
            elsif line =~ /#include\s+(\w+)/
              uniforms_lib[$1.to_sym].map { |(k, v)| hash[k] = v.clone }
            elsif line.start_with?('uniform')
              if line.end_with?(';')
                name, value = parse_uniform(line)
                hash[name] = value
              else
                in_uniform = true
                uniform_strings = [line]
              end
            end
          }
        end
      end
    end
  end
end