polyfox/moon

View on GitHub
modules/graphics/mrblib/screen.rb

Summary

Maintainability
A
0 mins
Test Coverage
module Moon
  # Screen represents the actual game window.
  class Screen
    # Screen width
    attr_reader :w
    # Screen height
    attr_reader :h
    attr_reader :rect
    # Scale of the screen coordinates.
    # By default, we map 1:1, each unit is 1px.
    attr_reader :scale
    # The color with which the screen is cleared at the start of every frame.
    # Black by default.
    attr_reader :clear_color
    # Underlying window implementation (in default case GLFW).
    attr_reader :window
    # @return [Moon::ContextLogger, ILogger] any logger like interface
    attr_accessor :logger

    # Creates a new game window with the given width and height.
    #
    # @param [Integer] w
    # @param [Integer] h
    def initialize(w, h)
      @scale = 1.0
      @logger = Moon::ContextLogger.new(STDERR, 'Screen')
      create_window w, h
      initialize_renderer
      initialize_clear_color
      initialize_screen_size
      @vsync = true
    end

    # Creates the internal window
    #
    # @param [Integer] w  width of the window
    # @param [Integer] h  height of the window
    def create_window(w, h)
      @logger.debug "Creating Window: w=#{w} h=#{h}"
      GLFW.window_hint GLFW::RESIZABLE, GL2::GL_FALSE
      GLFW.window_hint GLFW::CONTEXT_VERSION_MAJOR, 3
      GLFW.window_hint GLFW::CONTEXT_VERSION_MINOR, 3
      GLFW.window_hint GLFW::OPENGL_FORWARD_COMPAT, GL2::GL_TRUE # for 3.0
      GLFW.window_hint GLFW::OPENGL_PROFILE, GLFW::OPENGL_CORE_PROFILE # for 3.0 and on
      Moon::Shader.is_legacy = false

      title = 'Moon Player'
      begin
        @window = GLFW::Window.new w, h, title
        @logger.warn "3.3 Window Created"
      rescue GLFWError
        @logger.warn "Failed to obtain 3.3 context, falling back to 2.1"
        GLFW.default_window_hints
        GLFW.window_hint GLFW::CONTEXT_VERSION_MAJOR, 2
        GLFW.window_hint GLFW::CONTEXT_VERSION_MINOR, 1
        Moon::Shader.is_legacy = true

        @window = GLFW::Window.new w, h, title
        @logger.warn "2.1 Window Created"
      end
    end

    # Called by the engine to setup the screen after all the horrible, horrible
    # backend stuff is initialized
    def setup
      OpenGL.reset_flags
    end

    # Returns the OpenGL context version
    #
    # @return [String]
    def gl_version
      "#{@window.window_attrib(GLFW::CONTEXT_VERSION_MAJOR).to_s}.#{@window.window_attrib(GLFW::CONTEXT_VERSION_MINOR).to_s}"
    end

    # Returns the guesstimated version of GLSL supported
    #
    # @return [String]
    def glsl_version
      OpenGL.glsl_version
    end

    # Prints all the available version strings to the @log
    def print_versions
      @logger.info "OpenGL v" + gl_version
      @logger.info "GLSL v" + glsl_version
      @logger.info "GLFW v" + GLFW.version_string
    end

    # Make the screen the current context
    def make_current
      @window.make_current
      Screen.current = self
      self.vsync = @vsync
    end

    # Should the screen close?
    def should_close?
      @window.should_close?
    end

    # Swaps buffers, call this once per frame
    def swap
      @window.swap_buffers
    end

    # Closes the underlying window and cleans up
    def shutdown
      @logger.debug "Shutting Down"
      if @window
        @logger.debug "Closing Window"
        @window.should_close = true
        GLFW.wait_events
        @window.destroy
      end
      @window = nil
      @logger.debug "Shutdown"
      self
    end

    private def initialize_clear_color
      @clear_color = Vector4.new 0, 0, 0, 0
      self.clear_color = @clear_color
    end

    private def initialize_screen_size
      resize(*@window.window_size)
    end

    private def initialize_renderer
      Moon::Renderer.instance = Moon::Renderer.new
    end

    # @param [Vector4] color
    def clear_color=(color)
      @clear_color = Vector4[color]
      OpenGL.clear_color = @clear_color.to_a
    end

    # Updates the Renderer's projection_matrix to match the screen resolution.
    # This is called any time the window is resized.
    def update_projection
      Renderer.instance.projection_matrix = Matrix4.ortho 0.0, w / scale, h / scale, 0.0, -1.0, 1.0
    end

    # Resize the game window to a certain size.
    #
    # @param [Integer] w
    # @param [Integer] h
    def resize(w, h)
      @logger.debug "Resizing Screen to w=#{w} h=#{h}"
      @window.window_size = [w, h]
      @w, @h = *@window.window_size
      @rect = Rect.new(0, 0, @w, @h)
      update_projection
    end

    # @param [Float] s
    def scale=(s)
      @scale = s
      update_projection
    end

    attr_reader :title
    # Sets the window title.
    # @param [String] title
    def title=(title)
      @title = title
      @window.title = @title
    end

    attr_reader :vsync
    # Sets the vsync state on / off
    # @param [Boolean] bool
    def vsync=(bool)
      @vsync = bool
      GLFW.swap_interval = @vsync ? 1 : 0
    end

    class << self
      # Represents the currently active Screen.
      # (we can technically create more than one window with GLFW and toggle
      # between them)
      attr_accessor :current
    end
  end
end