Class: Engine::Shader

Inherits:
Object
  • Object
show all
Includes:
Serializable
Defined in:
lib/engine/shader.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Serializable

allowed_class?, get_class, included, register_class, #uuid

Instance Attribute Details

#sourceObject (readonly)

Returns the value of attribute source.



5
6
7
# File 'lib/engine/shader.rb', line 5

def source
  @source
end

Class Method Details

.colourObject



62
63
64
# File 'lib/engine/shader.rb', line 62

def self.colour
  @colour ||= Engine::Shader.for('colour_vertex.glsl', 'colour_frag.glsl', source: :engine)
end

.defaultObject



26
27
28
# File 'lib/engine/shader.rb', line 26

def self.default
  @default ||= Shader.for('mesh_vertex.glsl', 'mesh_frag.glsl', source: :engine)
end

.for(vertex_path, fragment_path, source: :game) ⇒ Object



9
10
11
12
# File 'lib/engine/shader.rb', line 9

def self.for(vertex_path, fragment_path, source: :game)
  key = [vertex_path, fragment_path, source]
  @cache[key] ||= create(vertex_path: vertex_path, fragment_path: fragment_path, source: source)
end

.from_file(vertex_path, fragment_path, source: :game) ⇒ Object



14
15
16
# File 'lib/engine/shader.rb', line 14

def self.from_file(vertex_path, fragment_path, source: :game)
  self.for(vertex_path, fragment_path, source: source)
end

.from_serializable_data(data) ⇒ Object



18
19
20
# File 'lib/engine/shader.rb', line 18

def self.from_serializable_data(data)
  self.for(data[:vertex_path], data[:fragment_path], source: (data[:source] || :game).to_sym)
end

.fullscreenObject



58
59
60
# File 'lib/engine/shader.rb', line 58

def self.fullscreen
  @fullscreen ||= Engine::Shader.for('fullscreen_vertex.glsl', 'fullscreen_frag.glsl', source: :engine)
end

.instanced_spriteObject



42
43
44
# File 'lib/engine/shader.rb', line 42

def self.instanced_sprite
  @instanced_sprite ||= Engine::Shader.for('instanced_sprite_vertex.glsl', 'instanced_sprite_frag.glsl', source: :engine)
end

.point_shadowObject



70
71
72
# File 'lib/engine/shader.rb', line 70

def self.point_shadow
  @point_shadow ||= Engine::Shader.for('point_shadow_vertex.glsl', 'point_shadow_frag.glsl', source: :engine)
end

.shadowObject



66
67
68
# File 'lib/engine/shader.rb', line 66

def self.shadow
  @shadow ||= Engine::Shader.for('shadow_vertex.glsl', 'shadow_frag.glsl', source: :engine)
end

.skybox_cubemapObject



34
35
36
# File 'lib/engine/shader.rb', line 34

def self.skybox_cubemap
  @skybox_cubemap ||= Shader.for('fullscreen_vertex.glsl', 'skybox_cubemap_frag.glsl', source: :engine)
end

.spriteObject



38
39
40
# File 'lib/engine/shader.rb', line 38

def self.sprite
  @sprite ||= Engine::Shader.for('sprite_vertex.glsl', 'sprite_frag.glsl', source: :engine)
end

.textObject



46
47
48
# File 'lib/engine/shader.rb', line 46

def self.text
  @text ||= Engine::Shader.for('text_vertex.glsl', 'text_frag.glsl', source: :engine)
end

.ui_spriteObject



54
55
56
# File 'lib/engine/shader.rb', line 54

def self.ui_sprite
  @ui_sprite ||= Engine::Shader.for('ui_sprite_vertex.glsl', 'ui_sprite_frag.glsl', source: :engine)
end

.ui_textObject



50
51
52
# File 'lib/engine/shader.rb', line 50

def self.ui_text
  @ui_text ||= Engine::Shader.for('text_vertex.glsl', 'text_frag.glsl', source: :engine)
end

.vertex_litObject



30
31
32
# File 'lib/engine/shader.rb', line 30

def self.vertex_lit
  @vertex_lit ||= Engine::Shader.for('vertex_lit_vertex.glsl', 'vertex_lit_frag.glsl', source: :engine)
end

Instance Method Details

#awakeObject



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/engine/shader.rb', line 74

def awake
  @texture_fallbacks = {}
  @cubemap_fallbacks = {}
  @vertex_shader = compile_shader(@vertex_path, Engine::GL::VERTEX_SHADER)
  @fragment_shader = compile_shader(@fragment_path, Engine::GL::FRAGMENT_SHADER)
  @program = Engine::GL.CreateProgram
  Engine::GL.AttachShader(@program, @vertex_shader)
  Engine::GL.AttachShader(@program, @fragment_shader)
  Engine::GL.LinkProgram(@program)

  linked_buf = ' ' * 4
  Engine::GL.GetProgramiv(@program, Engine::GL::LINK_STATUS, linked_buf)
  linked = linked_buf.unpack('L')[0]
  if linked == 0
    compile_log = ' ' * 1024
    Engine::GL.GetProgramInfoLog(@program, 1023, nil, compile_log)
    vertex_log = ' ' * 1024
    Engine::GL.GetShaderInfoLog(@vertex_shader, 1023, nil, vertex_log)
    fragment_log = ' ' * 1024
    Engine::GL.GetShaderInfoLog(@fragment_shader, 1023, nil, fragment_log)
    puts "Shader program failed to link"
    puts compile_log.strip
    puts vertex_log.strip
    puts fragment_log.strip
  end
  @uniform_cache = {}
  @uniform_locations = {}
end

#compile_shader(shader, type) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
# File 'lib/engine/shader.rb', line 103

def compile_shader(shader, type)
  handle = Engine::GL.CreateShader(type)
  path = resolve_shader_path(shader)
  source = preprocess_shader(path)
  parse_texture_fallbacks(source)
  s_srcs = [source].pack('p')
  s_lens = [source.bytesize].pack('I')
  Engine::GL.ShaderSource(handle, 1, s_srcs, s_lens)
  Engine::GL.CompileShader(handle)
  handle
end

#cubemap_fallback(name) ⇒ Object



131
132
133
# File 'lib/engine/shader.rb', line 131

def cubemap_fallback(name)
  @cubemap_fallbacks[name]
end

#expected_texturesObject



127
128
129
# File 'lib/engine/shader.rb', line 127

def expected_textures
  @texture_fallbacks.keys
end

#preprocess_shader(path, included = []) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/engine/shader.rb', line 135

def preprocess_shader(path, included = [])
  return "" if included.include?(path)
  included << path

  source = File.read(path)
  dir = File.dirname(path)

  source.gsub(/#include\s+"([^"]+)"/) do
    include_path = File.join(dir, $1)
    preprocess_shader(include_path, included)
  end
end

#resolve_shader_path(shader) ⇒ Object



115
116
117
118
119
120
121
# File 'lib/engine/shader.rb', line 115

def resolve_shader_path(shader)
  if @source == :engine
    File.join(ENGINE_DIR, "shaders", shader)
  else
    File.join(GAME_DIR, "shaders", shader)
  end
end

#serializable_dataObject



22
23
24
# File 'lib/engine/shader.rb', line 22

def serializable_data
  { vertex_path: @vertex_path, fragment_path: @fragment_path, source: @source }
end

#set_float(name, float) ⇒ Object



194
195
196
197
198
# File 'lib/engine/shader.rb', line 194

def set_float(name, float)
  return if @uniform_cache[name] == float
  @uniform_cache[name] = float
  Engine::GL.Uniform1f(uniform_location(name), float)
end

#set_int(name, int) ⇒ Object



188
189
190
191
192
# File 'lib/engine/shader.rb', line 188

def set_int(name, int)
  return if @uniform_cache[name] == int
  @uniform_cache[name] = int
  Engine::GL.Uniform1i(uniform_location(name), int)
end

#set_mat4(name, mat) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
# File 'lib/engine/shader.rb', line 176

def set_mat4(name, mat)
  return if @uniform_cache[name] == mat
  @uniform_cache[name] = mat
  mat_array = [
    mat[0, 0], mat[0, 1], mat[0, 2], mat[0, 3],
    mat[1, 0], mat[1, 1], mat[1, 2], mat[1, 3],
    mat[2, 0], mat[2, 1], mat[2, 2], mat[2, 3],
    mat[3, 0], mat[3, 1], mat[3, 2], mat[3, 3]
  ]
  Engine::GL.UniformMatrix4fv(uniform_location(name), 1, Engine::GL::FALSE, mat_array.pack('F*'))
end

#set_vec2(name, vec) ⇒ Object



152
153
154
155
156
# File 'lib/engine/shader.rb', line 152

def set_vec2(name, vec)
  return if @uniform_cache[name] == vec
  @uniform_cache[name] = vec
  Engine::GL.Uniform2f(uniform_location(name), vec[0], vec[1])
end

#set_vec3(name, vec) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
# File 'lib/engine/shader.rb', line 158

def set_vec3(name, vec)
  vector = if vec.is_a?(Vector)
             vec
           else
             Vector[vec[:r], vec[:g], vec[:b]]
           end
  cache_key = [vector[0], vector[1], vector[2]]
  return if @uniform_cache[name] == cache_key
  @uniform_cache[name] = cache_key
  Engine::GL.Uniform3f(uniform_location(name), vector[0], vector[1], vector[2])
end

#set_vec4(name, vec) ⇒ Object



170
171
172
173
174
# File 'lib/engine/shader.rb', line 170

def set_vec4(name, vec)
  return if @uniform_cache[name] == vec
  @uniform_cache[name] = vec
  Engine::GL.Uniform4f(uniform_location(name), vec[0], vec[1], vec[2], vec[3])
end

#texture_fallback(name) ⇒ Object



123
124
125
# File 'lib/engine/shader.rb', line 123

def texture_fallback(name)
  @texture_fallbacks[name] || :white
end

#useObject



148
149
150
# File 'lib/engine/shader.rb', line 148

def use
  Engine::GL.UseProgram(@program)
end