├── .editorconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── media ├── icon-192px.png ├── icon-32px.png ├── icon-48px.png ├── icon-512px.png ├── icon.psd ├── icon.svg ├── logo.png ├── logo.psd └── logo.svg ├── shard.lock ├── shard.yml ├── spec ├── lirith_spec.cr └── spec_helper.cr └── src ├── lirith.cr └── lirith ├── application.cr ├── camera.cr ├── clocks.cr ├── clocks ├── base.cr └── game.cr ├── core.cr ├── drawable.cr ├── events.cr ├── events ├── application │ ├── exit.cr │ └── initialized.cr ├── base.cr ├── console │ ├── request_command.cr │ └── toggle_log.cr ├── input │ └── key_events.cr ├── render │ └── render.cr └── window │ └── opened.cr ├── face.cr ├── input.cr ├── loaders.cr ├── loaders ├── base.cr ├── json_loader.cr └── obj_loader.cr ├── managers.cr ├── managers ├── asset.cr └── system.cr ├── materials.cr ├── materials ├── base.cr └── basic.cr ├── math.cr ├── math ├── base.cr ├── color.cr ├── matrix4.cr ├── quaternion.cr ├── vector2.cr └── vector3.cr ├── objects.cr ├── objects ├── base.cr ├── mesh.cr └── plane.cr ├── renderable.cr ├── renderer.cr ├── renderer └── attributes.cr ├── renderers.cr ├── renderers ├── base.cr ├── opengl.cr └── opengl │ ├── attribute.cr │ ├── attribute │ └── buffer.cr │ ├── attributes.cr │ ├── attributes │ ├── object.cr │ └── texture.cr │ ├── core.cr │ ├── elements.cr │ ├── elements │ ├── material.cr │ ├── object.cr │ └── texture.cr │ ├── program.cr │ ├── shader.cr │ ├── shader_lib.cr │ └── shaders │ ├── basic.fs.glsl │ └── basic.vs.glsl ├── scene.cr ├── singleton.cr ├── systems.cr ├── systems ├── application.cr ├── base.cr └── console.cr ├── texture.cr ├── version.cr ├── vertex.cr └── window.cr /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cr] 2 | charset = utf-8 3 | end_of_line = lf 4 | insert_final_newline = true 5 | indent_style = space 6 | indent_size = 2 7 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /doc/ 2 | /lib/ 3 | /bin/ 4 | /.shards/ 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: crystal 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Max Berends 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | lirith-engine 2 | 3 | # lirith-engine 4 | [![Build Status](https://travis-ci.org/lirith-engine/lirith.svg?branch=master)](https://travis-ci.org/lirith-engine/lirith) 5 | 6 | Draw super-awsome graphics on screen with this engine. Fully built written in Crystal! 7 | 8 | ## Installation 9 | 10 | 1. Clone this repo 11 | 2. Run `crystal deps` 12 | 3. Install GLEW and GLFW trough homebrew `brew install glfw3 glew` 13 | 14 | ## Usage 15 | 16 | Run `lirith.cr` within the `src` folder 17 | 18 | ## Contributing 19 | 20 | 1. Fork it ( https://github.com/lirith-engine/lirith/fork ) 21 | 2. Create your feature branch (git checkout -b my-new-feature) 22 | 3. Commit your changes (git commit -am 'Add some feature') 23 | 4. Push to the branch (git push origin my-new-feature) 24 | 5. Create a new Pull Request 25 | 26 | ## Contributors 27 | 28 | - [Rinkana](https://github.com/Rinkana) Max Berends - creator, maintainer 29 | -------------------------------------------------------------------------------- /media/icon-192px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eltanin-engine/lirith/04a63f2e0d2ef1483bc78f9c61a233c90c343482/media/icon-192px.png -------------------------------------------------------------------------------- /media/icon-32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eltanin-engine/lirith/04a63f2e0d2ef1483bc78f9c61a233c90c343482/media/icon-32px.png -------------------------------------------------------------------------------- /media/icon-48px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eltanin-engine/lirith/04a63f2e0d2ef1483bc78f9c61a233c90c343482/media/icon-48px.png -------------------------------------------------------------------------------- /media/icon-512px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eltanin-engine/lirith/04a63f2e0d2ef1483bc78f9c61a233c90c343482/media/icon-512px.png -------------------------------------------------------------------------------- /media/icon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eltanin-engine/lirith/04a63f2e0d2ef1483bc78f9c61a233c90c343482/media/icon.psd -------------------------------------------------------------------------------- /media/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /media/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eltanin-engine/lirith/04a63f2e0d2ef1483bc78f9c61a233c90c343482/media/logo.png -------------------------------------------------------------------------------- /media/logo.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eltanin-engine/lirith/04a63f2e0d2ef1483bc78f9c61a233c90c343482/media/logo.psd -------------------------------------------------------------------------------- /media/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /shard.lock: -------------------------------------------------------------------------------- 1 | version: 1.0 2 | shards: 3 | glew: 4 | path: ../glew 5 | version: 0.1.0 6 | 7 | lib_gl: 8 | path: ../lib_gl 9 | version: 0.1.1 10 | 11 | lib_glew: 12 | github: lirith-engine/lib_glew 13 | commit: 50773a1cd412a9f82f84a79b9663e284f27b6666 14 | 15 | lib_glfw: 16 | path: ../lib_glfw 17 | version: 0.1.1 18 | 19 | opengl: 20 | path: ../opengl 21 | version: 0.1.0 22 | 23 | -------------------------------------------------------------------------------- /shard.yml: -------------------------------------------------------------------------------- 1 | name: lirith 2 | version: 0.1.0 3 | 4 | authors: 5 | - Max Berends 6 | 7 | targets: 8 | lirith: 9 | main: src/lirith.cr 10 | 11 | dependencies: 12 | opengl: 13 | path: ../opengl 14 | #github: lirith-engine/opengl 15 | lib_glfw: 16 | path: ../lib_glfw 17 | #github: lirith-engine/glfw 18 | glew: 19 | path: ../glew 20 | #github: lirith-engine/glew 21 | r_image: 22 | path: ../r_image 23 | #github: lirith-engine/r_image 24 | 25 | crystal: 0.23.1 26 | 27 | license: MIT 28 | -------------------------------------------------------------------------------- /spec/lirith_spec.cr: -------------------------------------------------------------------------------- 1 | require "./spec_helper" 2 | 3 | describe Lirith do 4 | # TODO: Write tests 5 | 6 | it "works" do 7 | true.should eq(true) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/spec_helper.cr: -------------------------------------------------------------------------------- 1 | require "spec" 2 | require "../src/lirith" 3 | -------------------------------------------------------------------------------- /src/lirith.cr: -------------------------------------------------------------------------------- 1 | # require "lib_glew" 2 | # require "glfw" 3 | require "lib_glfw" 4 | require "lib_gl" 5 | require "r_image" 6 | 7 | require "./lirith/*" 8 | 9 | module Lirith 10 | class_property! application : Lirith::Application 11 | end 12 | -------------------------------------------------------------------------------- /src/lirith/application.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | abstract class Application 3 | getter window = Window.new(960,540, fullscreen: true) 4 | getter game_clock = Clocks::Game.new 5 | getter scene = Scene.new 6 | getter camera = Camera.new 7 | getter renderer = Renderers::OpenGL::Core.new(@scene, @camera) 8 | 9 | def initialize 10 | Lirith.application = self 11 | @camera.width = Float32.new(@window.width) 12 | @camera.height = Float32.new(@window.height) 13 | 14 | Lirith::Managers::System.instance.register(Systems::Application.new) 15 | 16 | setup 17 | 18 | Managers::System.trigger_direct_event(Events::Application::Initialized) 19 | 20 | Managers::System.instance.run 21 | end 22 | 23 | abstract def setup 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /src/lirith/camera.cr: -------------------------------------------------------------------------------- 1 | require "./drawable" 2 | 3 | module Lirith 4 | class Camera 5 | include Drawable 6 | 7 | property projection 8 | 9 | property fov = 45_f32 10 | 11 | property width = 1024_f32 # 1920_f32 # 1024_f32 # Theise are the same as in the window, might want to leave it there 12 | property height = 768_f32 # 1200_f32 # 768_f32 13 | 14 | property near = 1_f32 15 | property far = 2000_f32 16 | 17 | def initialize 18 | @position.z = Float32.new(10) 19 | 20 | @projection = Math::Matrix4.perspectiveFov(@fov, @width / @height, @near, @far) 21 | update_view 22 | end 23 | 24 | def update_projection 25 | Math::Matrix4.perspectiveFov(@fov, @width / @height, @near, @far) 26 | end 27 | 28 | def look_at(vector : Vector3) 29 | matrix = Math::Matrix4.look_at(@position, vector, Math::Vector3.up) 30 | quaternion.setFromRotation(matrix) 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /src/lirith/clocks.cr: -------------------------------------------------------------------------------- 1 | require "./clocks/*" 2 | 3 | module Lirith 4 | module Clocks 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /src/lirith/clocks/base.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Clocks 3 | abstract class Base 4 | property? auto_start 5 | getter? running = false 6 | 7 | getter start_time = 0_i64 8 | getter last_time = 0_i64 9 | getter elapsed_time = 0_f64 10 | 11 | def initialize(@auto_start = true); end 12 | 13 | def start 14 | @start_time = @last_time = Time.now.epoch_ms 15 | @running = true 16 | end 17 | 18 | def stop 19 | elapsed_time 20 | @running = false 21 | end 22 | 23 | def elapsed_time 24 | # get_delta 25 | @elapsed_time 26 | end 27 | 28 | def delta 29 | diff = 0 30 | 31 | start if !running? && @auto_start 32 | 33 | if running? 34 | current_time = Time.now.epoch_ms 35 | diff = (current_time - @last_time) / 1000.0 36 | @last_time = current_time 37 | @elapsed_time += diff 38 | end 39 | 40 | diff 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /src/lirith/clocks/game.cr: -------------------------------------------------------------------------------- 1 | require "./base" 2 | 3 | module Lirith 4 | module Clocks 5 | class Game < Base 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/lirith/core.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | class Core 3 | getter window 4 | getter game_clock 5 | getter scene 6 | getter renderer 7 | getter camera 8 | 9 | def initialize 10 | #@window = Window.new 11 | #@game_clock = Clocks::Game.new 12 | #@scene = Scene.new 13 | #@renderer = Renderers::OpenGL.new 14 | #@camera = Camera.new 15 | 16 | # Managers::System.register(self) 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /src/lirith/drawable.cr: -------------------------------------------------------------------------------- 1 | require "./renderable" 2 | 3 | module Lirith 4 | module Drawable 5 | include Renderable 6 | # Move this to base object? 7 | 8 | property position = Math::Vector3.zero 9 | property quaternion = Math::Quaternion.new(0, 0, 0) 10 | property scale = Math::Vector3.new(1, 1, 1) 11 | 12 | property view = Math::Matrix4.identity # modelView? 13 | property worldView = Math::Matrix4.identity 14 | 15 | def update_view 16 | @quaternion.normalize! 17 | view.rotate(@quaternion) 18 | view.scale(@scale) 19 | view.position(@position) 20 | end 21 | 22 | def translate(axis : Math::Vector3, distance) 23 | axis.apply(@quaternion) 24 | @position = @position + (axis * distance) 25 | end 26 | 27 | def translate_x(distance) 28 | translate(Math::Vector3.x_axis, distance) 29 | end 30 | 31 | def translate_y(distance) 32 | translate(Math::Vector3.y_axis, distance) 33 | end 34 | 35 | def translate_z(distance) 36 | translate(Math::Vector3.z_axis, distance) 37 | end 38 | 39 | def rotate(axis, angle) 40 | q = Math::Quaternion.new(0, 0, 0, 1) 41 | q.apply_axis_angle!(axis, angle) 42 | @quaternion = @quaternion * q 43 | end 44 | 45 | def rotate_x(angle) 46 | rotate(Math::Vector3.x_axis, angle) 47 | end 48 | 49 | def rotate_y(angle) 50 | rotate(Math::Vector3.y_axis, angle) 51 | end 52 | 53 | def rotate_z(angle) 54 | rotate(Math::Vector3.z_axis, angle) 55 | end 56 | 57 | def clone 58 | obj = self.class.new 59 | 60 | obj.position = @position.clone 61 | obj.quaternion = @quaternion.clone 62 | obj.scale = @scale.clone 63 | obj.view = @view.clone 64 | obj.worldView = @worldView.clone 65 | 66 | obj 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /src/lirith/events.cr: -------------------------------------------------------------------------------- 1 | require "./events/*" 2 | 3 | module Lirith 4 | module Events 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /src/lirith/events/application/exit.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Events 3 | module Application 4 | struct Exit < Events::Base 5 | end 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/lirith/events/application/initialized.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Events 3 | module Application 4 | struct Initialized < Events::Base 5 | end 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/lirith/events/base.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Events 3 | abstract struct Base 4 | def info 5 | self.class.to_s 6 | end 7 | end 8 | end 9 | end 10 | 11 | require "./application/*" 12 | require "./input/*" 13 | require "./window/*" 14 | require "./render/*" 15 | require "./console/*" 16 | -------------------------------------------------------------------------------- /src/lirith/events/console/request_command.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Events 3 | module Console 4 | struct RequestCommand < Lirith::Events::Base 5 | end 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/lirith/events/console/toggle_log.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Events 3 | module Console 4 | struct ToggleLog < Lirith::Events::Base 5 | end 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/lirith/events/input/key_events.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Events 3 | module Input 4 | abstract struct KeyEvent < Base 5 | getter key : Lirith::Input::Keys 6 | 7 | def initialize(@key) 8 | end 9 | 10 | def info 11 | "#{self.class} (#{@key})" 12 | end 13 | end 14 | 15 | struct KeyPressed < KeyEvent 16 | end 17 | 18 | struct KeyReleased < KeyEvent 19 | end 20 | 21 | struct KeyRepeated < KeyEvent 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /src/lirith/events/render/render.cr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eltanin-engine/lirith/04a63f2e0d2ef1483bc78f9c61a233c90c343482/src/lirith/events/render/render.cr -------------------------------------------------------------------------------- /src/lirith/events/window/opened.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Events 3 | module Window 4 | struct Opened < Events::Base 5 | end 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/lirith/face.cr: -------------------------------------------------------------------------------- 1 | require "./vertex" 2 | 3 | module Lirith 4 | class Face 5 | property a : Vertex 6 | property b : Vertex 7 | property c : Vertex 8 | 9 | def initialize(@a, @b, @c) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /src/lirith/input.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | class Input 3 | enum Keys 4 | Key0 = LibGLFW::KEY_0 5 | Key1 = LibGLFW::KEY_1 6 | Key2 = LibGLFW::KEY_2 7 | Key3 = LibGLFW::KEY_3 8 | Key4 = LibGLFW::KEY_4 9 | Key5 = LibGLFW::KEY_5 10 | Key6 = LibGLFW::KEY_6 11 | Key7 = LibGLFW::KEY_7 12 | Key8 = LibGLFW::KEY_8 13 | Key9 = LibGLFW::KEY_9 14 | A = LibGLFW::KEY_A 15 | B = LibGLFW::KEY_B 16 | C = LibGLFW::KEY_C 17 | D = LibGLFW::KEY_D 18 | E = LibGLFW::KEY_E 19 | F = LibGLFW::KEY_F 20 | G = LibGLFW::KEY_G 21 | H = LibGLFW::KEY_H 22 | I = LibGLFW::KEY_I 23 | J = LibGLFW::KEY_J 24 | K = LibGLFW::KEY_K 25 | L = LibGLFW::KEY_L 26 | M = LibGLFW::KEY_M 27 | N = LibGLFW::KEY_N 28 | O = LibGLFW::KEY_O 29 | P = LibGLFW::KEY_P 30 | Q = LibGLFW::KEY_Q 31 | R = LibGLFW::KEY_R 32 | S = LibGLFW::KEY_S 33 | T = LibGLFW::KEY_T 34 | U = LibGLFW::KEY_U 35 | V = LibGLFW::KEY_V 36 | W = LibGLFW::KEY_W 37 | X = LibGLFW::KEY_X 38 | Y = LibGLFW::KEY_Y 39 | Z = LibGLFW::KEY_Z 40 | LeftShift = LibGLFW::KEY_LEFT_SHIFT 41 | LeftControl = LibGLFW::KEY_LEFT_CONTROL 42 | KeyGraveAccent = LibGLFW::KEY_GRAVE_ACCENT 43 | KeyEscape = LibGLFW::KEY_ESCAPE 44 | PageUp = LibGLFW::KEY_PAGE_UP 45 | PageDown = LibGLFW::KEY_PAGE_DOWN 46 | Left = LibGLFW::KEY_LEFT 47 | Right = LibGLFW::KEY_RIGHT 48 | end 49 | 50 | def self.handle_key(window : LibGLFW::Window, key : Int32, scancode : Int32, action : Int32, mods : Int32) : Void 51 | case action 52 | when LibGLFW::PRESS ; Managers::System.trigger_event(Events::Input::KeyPressed.new(Keys.new(key))) 53 | when LibGLFW::RELEASE; Managers::System.trigger_event(Events::Input::KeyReleased.new(Keys.new(key))) 54 | when LibGLFW::REPEAT ; Managers::System.trigger_event(Events::Input::KeyPressed.new(Keys.new(key))) 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /src/lirith/loaders.cr: -------------------------------------------------------------------------------- 1 | require "./loaders/*" 2 | 3 | module Lirith 4 | module Loaders 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /src/lirith/loaders/base.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Loaders 3 | abstract class Base 4 | def load(*args) 5 | raise "Load is not implemented for #{self.class}" 6 | end 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /src/lirith/loaders/json_loader.cr: -------------------------------------------------------------------------------- 1 | require "./base" 2 | require "json" 3 | 4 | module Lirith 5 | module Loaders 6 | class JsonLoader < Base 7 | enum Formats 8 | Basic 9 | end 10 | 11 | def load(file : File, format = Formats::Basic) : Objects::Mesh 12 | case format 13 | when Formats::Basic; load_basic(file) 14 | else raise "Format unknown" 15 | end 16 | end 17 | 18 | def load_basic(file) 19 | object = BasicFormat.from_json(file.gets_to_end) 20 | 21 | mesh = Objects::Mesh.new 22 | 23 | vector_count = object.vertices.size / 3 24 | 25 | 0.upto(vector_count - 1) do |i| 26 | mesh.vertices << Vertex.new( 27 | Math::Vector3.new( 28 | object.vertices[i * 3], 29 | object.vertices[(i * 3) + 1], 30 | object.vertices[(i * 3) + 2] 31 | ), 32 | Math::Vector2.new( 33 | object.uv[i * 2], 34 | object.uv[(i * 2) + 1] 35 | ) 36 | ) 37 | 38 | mesh.colors << Math::Color.new( 39 | object.colors[i * 3], 40 | object.colors[(i * 3) + 1], 41 | object.colors[(i * 3) + 2] 42 | ) if object.colors.any? 43 | end 44 | # mesh.material = Materials::Base.new 45 | mesh 46 | end 47 | 48 | struct BasicFormat 49 | JSON.mapping( 50 | vertices: {type: Array(Float32), setter: false}, 51 | colors: {type: Array(Float32), setter: false}, 52 | uv: {type: Array(Float32), setter: false} 53 | ) 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /src/lirith/loaders/obj_loader.cr: -------------------------------------------------------------------------------- 1 | require "./base" 2 | 3 | module Lirith 4 | module Loaders 5 | class ObjLoader < Base 6 | @vector_cache = [] of Math::Vector3 7 | @uv_cache = [] of Math::Vector2 8 | 9 | def load(file : File) 10 | mesh = Objects::Mesh.new 11 | 12 | @vector_cache = [] of Math::Vector3 13 | @uv_cache = [] of Math::Vector2 14 | 15 | face_count = 0 16 | while line = file.gets 17 | case line 18 | when .starts_with?("v ") ; @vector_cache << parse_vector(line) 19 | when .starts_with?("vt "); @uv_cache << parse_uv(line) 20 | when .starts_with?("f ") 21 | parse_face(line).each do |v| 22 | mesh.vertices << v 23 | end 24 | end 25 | end 26 | 27 | mesh 28 | end 29 | 30 | private def line_params_f(line : String) 31 | line.scan(/[-+]?([0-9]*\.[0-9]+|[0-9]+)/).map(&.[0].to_f) 32 | end 33 | 34 | private def line_params_i(line : String) 35 | line.scan(/\d+/).map(&.[0].to_i) 36 | end 37 | 38 | private def parse_vector(line : String) 39 | params = line_params_f(line) 40 | Math::Vector3.new( 41 | params[0], 42 | params[1], 43 | params[2] 44 | ) 45 | end 46 | 47 | private def parse_uv(line : String) 48 | params = line_params_f(line) 49 | Math::Vector2.new( 50 | params[0], 51 | params[1] 52 | ) 53 | end 54 | 55 | private def parse_face(line : String) 56 | # params = line_params_i(line).in_groups_of(1) 57 | vertices = [] of Vertex 58 | 59 | params = line.scan(/[\d\/]+/).map(&.[0].split('/').map(&.to_i)) 60 | faces = [] of Array(Array(Int32)) 61 | 62 | v1 = params[0] 63 | (1..(params.size - 2)).each do |i| 64 | v2 = params[i] 65 | v3 = params[i + 1] 66 | 67 | faces << [v1, v2, v3] 68 | end 69 | 70 | faces.each do |face| 71 | face.each do |vertex| 72 | vector = @vector_cache[vertex[0].not_nil! - 1].clone 73 | 74 | uv = if (vertex.size > 1) 75 | @uv_cache[vertex[1].not_nil! - 1].clone 76 | else 77 | Math::Vector2.new(0, 0) 78 | end 79 | 80 | #color = Math::Color.black 81 | 82 | vertices << Vertex.new(vector, uv) 83 | end 84 | end 85 | 86 | vertices 87 | end 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /src/lirith/managers.cr: -------------------------------------------------------------------------------- 1 | require "./managers/*" 2 | 3 | module Lirith 4 | module Managers 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /src/lirith/managers/asset.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Managers 3 | class Asset 4 | end 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /src/lirith/managers/system.cr: -------------------------------------------------------------------------------- 1 | require "../singleton" 2 | 3 | module Lirith 4 | module Managers 5 | class System 6 | include Singleton 7 | 8 | @systems = Array(Systems::Base).new 9 | @event_queue = Deque(Events::Base).new 10 | 11 | @event_channel = Channel(Nil).new 12 | @halt = false 13 | 14 | def initialize 15 | # register(Systems::Application.new) 16 | end 17 | 18 | def register(system : Systems::Base) 19 | @systems << system 20 | end 21 | 22 | def get(system_type : T.class) forall T 23 | @systems.each do |system| 24 | return system.as(T) if system.class == system_type 25 | end 26 | 27 | raise "System not found" 28 | end 29 | 30 | def each 31 | @systems.each do |system| 32 | yield system 33 | end 34 | end 35 | 36 | def run 37 | spawn_event_handler 38 | 39 | @event_channel.receive 40 | end 41 | 42 | def stop 43 | @halt = true 44 | end 45 | 46 | def stop! 47 | @halt = true 48 | @event_channel.send(nil) 49 | end 50 | 51 | def spawn_event_handler 52 | spawn do 53 | loop do 54 | if event = @event_queue.shift? 55 | each { |system| system.handle_event(event) } 56 | end 57 | 58 | @event_channel.send(nil) if @halt && @event_queue.empty? 59 | end 60 | end 61 | end 62 | 63 | def trigger_event(event : Events::Base) 64 | @event_queue << event 65 | end 66 | 67 | def trigger_event(event_class : Class) 68 | trigger_event(event_class.new) 69 | end 70 | 71 | def trigger_direct_event(event : Events::Base) 72 | each { |system| system.handle_event(event) } 73 | end 74 | 75 | def trigger_direct_event(event_class : Class) 76 | trigger_direct_event(event_class.new) 77 | end 78 | 79 | def self.register(system : Systems::Base) 80 | instance.register(system) 81 | end 82 | 83 | def self.trigger_event(event : Events::Base) 84 | instance.trigger_event(event) 85 | end 86 | 87 | def self.trigger_event(event_class : Class) 88 | instance.trigger_event(event_class) 89 | end 90 | 91 | def self.trigger_direct_event(event : Events::Base) 92 | instance.trigger_direct_event(event) 93 | end 94 | 95 | def self.trigger_direct_event(event_class : Class) 96 | instance.trigger_direct_event(event_class) 97 | end 98 | end 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /src/lirith/materials.cr: -------------------------------------------------------------------------------- 1 | require "./materials/*" 2 | 3 | module Lirith 4 | module Materials 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /src/lirith/materials/base.cr: -------------------------------------------------------------------------------- 1 | require "./../texture" 2 | 3 | module Lirith 4 | module Materials 5 | abstract class Base 6 | include Renderable 7 | 8 | property color = Math::Color.black 9 | property texture : Texture? 10 | 11 | def clone 12 | material = self.class.new 13 | material.color = color.clone 14 | material 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /src/lirith/materials/basic.cr: -------------------------------------------------------------------------------- 1 | require "./base" 2 | 3 | module Lirith 4 | module Materials 5 | class Basic < Base 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/lirith/math.cr: -------------------------------------------------------------------------------- 1 | require "./math/*" 2 | 3 | module Lirith 4 | module Math 5 | extend self 6 | 7 | def deg2rad(angle : Float) 8 | angle * {{::Math::PI / 180}} 9 | end 10 | 11 | def rad2deg(angle : Float) 12 | angle * {{180 / ::Math::PI}} 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /src/lirith/math/base.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Math 3 | abstract struct Base 4 | macro buffer(type, properties) 5 | @buffer = Pointer({{type}}).malloc({{properties.size}}) 6 | 7 | {% for property, index in properties %} 8 | def {{property.id}} 9 | @buffer[{{index}}] 10 | end 11 | 12 | def {{property.id}}=(value) 13 | @buffer[{{index}}] = {{type}}.new(value) 14 | end 15 | {% end %} 16 | 17 | def to_unsafe 18 | @buffer 19 | end 20 | 21 | def to_slice 22 | @buffer.as(UInt8*).to_slice(buffer_size) 23 | end 24 | 25 | def property_size 26 | self.class.property_size 27 | end 28 | 29 | def self.property_size 30 | {{properties.size}} 31 | end 32 | 33 | def buffer_size 34 | self.class.buffer_size 35 | end 36 | 37 | def self.buffer_size 38 | sizeof({{type}}) * {{properties.size}} 39 | end 40 | 41 | def inspect 42 | "#{self.class}({{properties.map { |p| ("\#{" + p.stringify + "}").id }.splat}})" 43 | end 44 | end 45 | 46 | macro simple_initialize(name, *parameters) 47 | def self.{{name.id}} 48 | new({{parameters.splat}}) 49 | end 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /src/lirith/math/color.cr: -------------------------------------------------------------------------------- 1 | require "./base" 2 | 3 | module Lirith 4 | module Math 5 | struct Color < Base 6 | buffer Float32, [red, green, blue, alpha] 7 | 8 | def initialize(red, green, blue, alpha = Float32.new(1)) 9 | @buffer[0] = red 10 | @buffer[1] = green 11 | @buffer[2] = blue 12 | @buffer[3] = alpha 13 | end 14 | 15 | def clone 16 | Color.new(red, green, blue, alpha) 17 | end 18 | 19 | def self.from_hex(hex : Int32) 20 | hex = hex.floor 21 | 22 | new( 23 | Float32.new(hex >> 16 & 255) / 255, 24 | Float32.new(hex >> 8 & 255) / 255, 25 | Float32.new(hex >> 0 & 255) / 255 26 | ) 27 | end 28 | 29 | def self.random 30 | new(Random.rand.to_f32, Random.rand.to_f32, Random.rand.to_f32) 31 | end 32 | 33 | simple_initialize(black, 0f32, 0f32, 0f32) 34 | simple_initialize(white, 1f32, 1f32, 1f32) 35 | simple_initialize(transparent, 0f32, 0f32, 0f32, 0f32) 36 | simple_initialize(red, 1f32, 0f32, 0f32) 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /src/lirith/math/matrix4.cr: -------------------------------------------------------------------------------- 1 | require "./base" 2 | 3 | module Lirith 4 | module Math 5 | struct Matrix4 < Base 6 | buffer Float32, [ 7 | m00, m01, m02, m03, 8 | m10, m11, m12, m13, 9 | m20, m21, m22, m23, 10 | m30, m31, m32, m33, 11 | ] 12 | 13 | def [](i : Int32) 14 | @buffer[i] 15 | end 16 | 17 | def []=(i : Int32, value) 18 | @buffer[i] = Float32.new(value) 19 | end 20 | 21 | def [](row : Int32, col : Int32) 22 | @buffer[row + (col * 4)] 23 | end 24 | 25 | def []=(row : Int32, col : Int32, value) 26 | @buffer[row + (col * 4)] = Float32.new(value) 27 | end 28 | 29 | def *(matrix : Matrix4) 30 | new_matrix = self.class.zero 31 | 32 | 0.upto(3) do |row| 33 | 0.upto(3) do |col| 34 | p1 = @buffer + row 35 | p2 = matrix.to_unsafe + 4 * col 36 | new_matrix[row, col] = p1[0] * p2[0] + p1[4] * p2[1] + p1[8] * p2[2] + p1[12] * p2[3] 37 | end 38 | end 39 | 40 | new_matrix 41 | end 42 | 43 | def determinant 44 | m03*m12*m21*m30 - m02*m13*m21*m30 - m03*m11*m22*m30 + m01*m13*m22*m30 + 45 | m02*m11*m23*m30 - m01*m12*m23*m30 - m03*m12*m20*m31 + m02*m13*m20*m31 + 46 | m03*m10*m22*m31 - m00*m13*m22*m31 - m02*m10*m23*m31 + m00*m12*m23*m31 + 47 | m03*m11*m20*m32 - m01*m13*m20*m32 - m03*m10*m21*m32 + m00*m13*m21*m32 + 48 | m01*m10*m23*m32 - m00*m11*m23*m32 - m02*m11*m20*m33 + m01*m12*m20*m33 + 49 | m02*m10*m21*m33 - m00*m12*m21*m33 - m01*m10*m22*m33 + m00*m11*m22*m33 50 | end 51 | 52 | def rotate(q : Quaternion) 53 | self.m00 = 1 - ((q.y * (q.y + q.y)) + (q.z * (q.z + q.z))) 54 | self.m01 = (q.x * (q.y + q.y)) + (q.w * (q.z + q.z)) 55 | self.m02 = (q.x * (q.z + q.z)) - (q.w * (q.y + q.y)) 56 | self.m03 = 0 57 | 58 | self.m10 = (q.x * (q.y + q.y)) - (q.w * (q.z + q.z)) 59 | self.m11 = 1 - ((q.x * (q.x + q.x)) + (q.z * (q.z + q.z))) 60 | self.m12 = (q.y * (q.z + q.z)) + (q.w * (q.x + q.x)) 61 | self.m13 = 0 62 | 63 | self.m20 = (q.x * (q.z + q.z)) + (q.w * (q.y + q.y)) 64 | self.m21 = (q.y * (q.z + q.z)) - (q.w * (q.x + q.x)) 65 | self.m22 = 1 - ((q.x * (q.x + q.x)) + (q.y * (q.y + q.y))) 66 | self.m23 = 0 67 | 68 | self.m30 = 0 69 | self.m31 = 0 70 | self.m32 = 0 71 | self.m33 = 1 72 | end 73 | 74 | def position(position : Vector3) 75 | self.m30 = position.x 76 | self.m31 = position.y 77 | self.m32 = position.z 78 | end 79 | 80 | def scale(scale : Vector3) 81 | self.m00 *= scale.x 82 | self.m01 *= scale.x 83 | self.m02 *= scale.x 84 | self.m03 *= scale.x 85 | 86 | self.m10 *= scale.y 87 | self.m11 *= scale.y 88 | self.m12 *= scale.y 89 | self.m13 *= scale.y 90 | 91 | self.m20 *= scale.z 92 | self.m21 *= scale.z 93 | self.m22 *= scale.z 94 | self.m23 *= scale.z 95 | end 96 | 97 | def inverse 98 | Matrix4.inverse(self) 99 | end 100 | 101 | def clone 102 | Matrix4.new { |i| @buffer[i].clone } 103 | end 104 | 105 | def self.new(&block : Int32 -> Float32) 106 | matrix = new 107 | 108 | 0.upto(15) do |i| 109 | matrix[i] = yield i 110 | end 111 | 112 | matrix 113 | end 114 | 115 | def self.new(matrix : Array(Float32)) 116 | raise ArgumentError.new("Given matrix array is not the correct size") unless matrix.size == 16 117 | 118 | self.new { |i| matrix[i] } 119 | end 120 | 121 | def self.zero 122 | new { Float32.zero } 123 | end 124 | 125 | def self.identity : Matrix4 126 | matrix = self.zero 127 | 128 | matrix[0, 0] = Float32.new(1) 129 | matrix[1, 1] = Float32.new(1) 130 | matrix[2, 2] = Float32.new(1) 131 | matrix[3, 3] = Float32.new(1) 132 | 133 | matrix 134 | end 135 | 136 | def self.perspective(left, right, top, bottom, near, far) : Matrix4 137 | matrix = self.zero 138 | 139 | matrix[0] = 2 * near / (right - left) 140 | matrix[5] = 2 * near / (top - bottom) 141 | matrix[8] = (right + left) / (right - left) 142 | matrix[9] = (top + bottom) / (top - bottom) 143 | matrix[10] = -((far + near) / (far - near)) 144 | matrix[10] = Float32.new(-1) 145 | matrix[14] = -2 * far * near / (far - near) 146 | 147 | matrix 148 | end 149 | 150 | def self.perspectiveFov(fov_y, aspect, near, far) : Matrix4 151 | raise ArgumentError.new("Aspect ratio can not be zero") if aspect == 0 152 | raise ArgumentError.new("Near and Far can not be equal") if near == far 153 | rad = (fov_y / 180.0 * ::Math::PI) 154 | tan_half_fov = Float32.new(::Math.tan(rad / 2)) 155 | 156 | matrix = self.zero 157 | 158 | matrix[0, 0] = 1 / (aspect * tan_half_fov) 159 | matrix[1, 1] = 1 / tan_half_fov 160 | matrix[2, 2] = -(far + near) / (far - near) 161 | matrix[3, 2] = -1_f32 162 | matrix[2, 3] = -(2_f32 * far * near) / (far - near) 163 | 164 | matrix 165 | end 166 | 167 | def self.look_at(eye, center, up) 168 | matrix = self.identity 169 | 170 | z = (center - eye).normalize 171 | x = z.cross(up).normalize 172 | y = x.cross(z) 173 | 174 | matrix[0, 0] = x.x 175 | matrix[0, 1] = x.y 176 | matrix[0, 2] = x.z 177 | matrix[1, 0] = y.x 178 | matrix[1, 1] = y.y 179 | matrix[1, 2] = y.z 180 | matrix[2, 0] = -z.x 181 | matrix[2, 1] = -z.y 182 | matrix[2, 2] = -z.z 183 | matrix[0, 3] = -x.dot(eye) 184 | matrix[1, 3] = -y.dot(eye) 185 | matrix[2, 3] = z.dot(eye) 186 | 187 | matrix 188 | end 189 | 190 | def self.inverse(m) 191 | matrix = self.zero 192 | 193 | matrix.m00 = m.m12*m.m23*m.m31 - m.m13*m.m22*m.m31 + m.m13*m.m21*m.m32 - m.m11*m.m23*m.m32 - m.m12*m.m21*m.m33 + m.m11*m.m22*m.m33 194 | matrix.m01 = m.m03*m.m22*m.m31 - m.m02*m.m23*m.m31 - m.m03*m.m21*m.m32 + m.m01*m.m23*m.m32 + m.m02*m.m21*m.m33 - m.m01*m.m22*m.m33 195 | matrix.m02 = m.m02*m.m13*m.m31 - m.m03*m.m12*m.m31 + m.m03*m.m11*m.m32 - m.m01*m.m13*m.m32 - m.m02*m.m11*m.m33 + m.m01*m.m12*m.m33 196 | matrix.m03 = m.m03*m.m12*m.m21 - m.m02*m.m13*m.m21 - m.m03*m.m11*m.m22 + m.m01*m.m13*m.m22 + m.m02*m.m11*m.m23 - m.m01*m.m12*m.m23 197 | 198 | matrix.m10 = m.m13*m.m22*m.m30 - m.m12*m.m23*m.m30 - m.m13*m.m20*m.m32 + m.m10*m.m23*m.m32 + m.m12*m.m20*m.m33 - m.m10*m.m22*m.m33 199 | matrix.m11 = m.m02*m.m23*m.m30 - m.m03*m.m22*m.m30 + m.m03*m.m20*m.m32 - m.m00*m.m23*m.m32 - m.m02*m.m20*m.m33 + m.m00*m.m22*m.m33 200 | matrix.m12 = m.m03*m.m12*m.m30 - m.m02*m.m13*m.m30 - m.m03*m.m10*m.m32 + m.m00*m.m13*m.m32 + m.m02*m.m10*m.m33 - m.m00*m.m12*m.m33 201 | matrix.m13 = m.m02*m.m13*m.m20 - m.m03*m.m12*m.m20 + m.m03*m.m10*m.m22 - m.m00*m.m13*m.m22 - m.m02*m.m10*m.m23 + m.m00*m.m12*m.m23 202 | 203 | matrix.m20 = m.m11*m.m23*m.m30 - m.m13*m.m21*m.m30 + m.m13*m.m20*m.m31 - m.m10*m.m23*m.m31 - m.m11*m.m20*m.m33 + m.m10*m.m21*m.m33 204 | matrix.m21 = m.m03*m.m21*m.m30 - m.m01*m.m23*m.m30 - m.m03*m.m20*m.m31 + m.m00*m.m23*m.m31 + m.m01*m.m20*m.m33 - m.m00*m.m21*m.m33 205 | matrix.m22 = m.m01*m.m13*m.m30 - m.m03*m.m11*m.m30 + m.m03*m.m10*m.m31 - m.m00*m.m13*m.m31 - m.m01*m.m10*m.m33 + m.m00*m.m11*m.m33 206 | matrix.m23 = m.m03*m.m11*m.m20 - m.m01*m.m13*m.m20 - m.m03*m.m10*m.m21 + m.m00*m.m13*m.m21 + m.m01*m.m10*m.m23 - m.m00*m.m11*m.m23 207 | 208 | matrix.m30 = m.m12*m.m21*m.m30 - m.m11*m.m22*m.m30 - m.m12*m.m20*m.m31 + m.m10*m.m22*m.m31 + m.m11*m.m20*m.m32 - m.m10*m.m21*m.m32 209 | matrix.m31 = m.m01*m.m22*m.m30 - m.m02*m.m21*m.m30 + m.m02*m.m20*m.m31 - m.m00*m.m22*m.m31 - m.m01*m.m20*m.m32 + m.m00*m.m21*m.m32 210 | matrix.m32 = m.m02*m.m11*m.m30 - m.m01*m.m12*m.m30 - m.m02*m.m10*m.m31 + m.m00*m.m12*m.m31 + m.m01*m.m10*m.m32 - m.m00*m.m11*m.m32 211 | matrix.m33 = m.m01*m.m12*m.m20 - m.m02*m.m11*m.m20 + m.m02*m.m10*m.m21 - m.m00*m.m12*m.m21 - m.m01*m.m10*m.m22 + m.m00*m.m11*m.m22 212 | 213 | matrix 214 | end 215 | end 216 | end 217 | end 218 | -------------------------------------------------------------------------------- /src/lirith/math/quaternion.cr: -------------------------------------------------------------------------------- 1 | require "./base" 2 | 3 | module Lirith 4 | module Math 5 | struct Quaternion < Base 6 | buffer Float32, [w, x, y, z] 7 | 8 | def initialize(x, y, z, w = 1_f32) 9 | self.x = x 10 | self.y = y 11 | self.z = z 12 | self.w = w 13 | end 14 | 15 | def *(q) 16 | Quaternion.new( 17 | self.x * q.w + self.w * q.x + self.y * q.z - self.z * q.y, 18 | self.y * q.w + self.w * q.y + self.z * q.x - self.x * q.z, 19 | self.z * q.w + self.w * q.z + self.x * q.y - self.y * q.x, 20 | self.w * q.w - self.x * q.x - self.y * q.y - self.z * q.z 21 | ) 22 | end 23 | 24 | def length 25 | ::Math.sqrt(x * x + y * y + z * z + w * w) 26 | end 27 | 28 | def normalize! 29 | if length === 0 30 | self.x = 0 31 | self.y = 0 32 | self.z = 0 33 | self.w = 1 34 | else 35 | self.x = x * (1 / length) 36 | self.y = y * (1 / length) 37 | self.z = z * (1 / length) 38 | self.w = w * (1 / length) 39 | end 40 | end 41 | 42 | def apply_axis_angle!(axis : Vector3, angle : Float) 43 | sin_half_angle = ::Math.sin(angle / 2) 44 | 45 | self.x = axis.x * sin_half_angle 46 | self.y = axis.y * sin_half_angle 47 | self.z = axis.z * sin_half_angle 48 | self.w = ::Math.cos(angle / 2) 49 | end 50 | 51 | def clone 52 | Quaternion.new(x, y, z, w) 53 | end 54 | 55 | def setFromRotation(matrix : Matrix4) 56 | trace = matrix[0, 0] + matrix[1, 1] + matrix[2, 2] 57 | 58 | if (trace > 0) 59 | num = 0.5 / ::Math.sqrt(trace + 1.0) 60 | 61 | self.w = 0.25 / num 62 | self.x = (matrix[2, 1] - matrix[1, 2]) * num 63 | self.y = (matrix[0, 2] - matrix[2, 0]) * num 64 | self.z = (matrix[1, 0] - matrix[0, 1]) * num 65 | elsif (matrix[0, 0] > matrix[1, 1] && matrix[0, 0] > matrix[2, 2]) 66 | num = 2.0 * ::Math.sqrt(1.0 + matrix[0, 0] - matrix[1, 1] - matrix[2, 2]) 67 | 68 | self.w = (matrix[2, 1] - matrix[1, 2]) / num 69 | self.x = 0.25 * num 70 | self.y = (matrix[0, 1] + matrix[1, 0]) / num 71 | self.z = (matrix[0, 2] + matrix[2, 0]) / num 72 | elsif (matrix[1, 1] > matrix[2, 2]) 73 | num = 2.0 * ::Math.sqrt(1.0 + matrix[1, 1] - matrix[0, 0] - matrix[2, 2]) 74 | 75 | self.w = (matrix[0, 2] - matrix[2, 0]) / num 76 | self.x = (matrix[0, 1] + matrix[1, 0]) / num 77 | self.y = 0.25 * num 78 | self.z = (matrix[1, 2] + matrix[2, 1]) / num 79 | else 80 | num = 2.0 * ::Math.sqrt(1.0 + matrix[2, 2] - matrix[0, 0] - matrix[1, 1]) 81 | 82 | self.w = (matrix[1, 0] - matrix[0, 1]) / num 83 | self.x = (matrix[0, 2] + matrix[2, 0]) / num 84 | self.y = (matrix[1, 2] + matrix[2, 1]) / num 85 | self.z = 0.25 * num 86 | end 87 | 88 | p self 89 | end 90 | end 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /src/lirith/math/vector2.cr: -------------------------------------------------------------------------------- 1 | require "./base" 2 | 3 | module Lirith 4 | module Math 5 | struct Vector2 < Base 6 | buffer Float32, [x, y] 7 | 8 | def initialize(x, y) 9 | self.x = x 10 | self.y = y 11 | end 12 | 13 | def [](i : Int32) 14 | @buffer[i] 15 | end 16 | 17 | def []=(i : Int32, value) 18 | @buffer[i] = Float32.new(value) 19 | end 20 | 21 | def +(v : Vector2) 22 | Vector2.new(x + v.x, y + v.y) 23 | end 24 | 25 | def -(v : Vector2) 26 | Vector2.new(x - v.x, y - v.y) 27 | end 28 | 29 | def dot(v : Vector2) 30 | x * v.x + y * v.y 31 | end 32 | 33 | def *(a) 34 | Vector2.new(x * a, y * a) 35 | end 36 | 37 | def /(a) 38 | Vector2.new(x / a, y / a) 39 | end 40 | 41 | def length 42 | ::Math.sqrt(x * x + y * y) 43 | end 44 | 45 | def normalize 46 | self / length 47 | end 48 | 49 | def clone 50 | Vector2.new(x, y) 51 | end 52 | 53 | simple_initialize(zero, 0f32, 0f32) 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /src/lirith/math/vector3.cr: -------------------------------------------------------------------------------- 1 | require "./base" 2 | 3 | module Lirith 4 | module Math 5 | struct Vector3 < Base 6 | buffer Float32, [x, y, z] 7 | 8 | def initialize(x, y, z) 9 | self.x = x 10 | self.y = y 11 | self.z = z 12 | end 13 | 14 | # def set(x, y, z) 15 | # self.x = Float32.new(x) 16 | # self.y = Float32.new(y) 17 | # self.z = Float32.new(z) 18 | # end 19 | 20 | def [](i : Int32) 21 | @buffer[i] 22 | end 23 | 24 | def []=(i : Int32, value) 25 | @buffer[i] = Float32.new(value) 26 | end 27 | 28 | def +(v : Vector3) 29 | Vector3.new(x + v.x, y + v.y, z + v.z) 30 | end 31 | 32 | def -(v : Vector3) 33 | Vector3.new(x - v.x, y - v.y, z - v.z) 34 | end 35 | 36 | def *(a) 37 | Vector3.new(x * a, y * a, z * a) 38 | end 39 | 40 | def /(a) 41 | Vector3.new(x / a, y / a, z / a) 42 | end 43 | 44 | def length 45 | ::Math.sqrt(x * x + y * y + z * z) 46 | end 47 | 48 | def normalize 49 | self / length 50 | end 51 | 52 | def apply(q : Quaternion) 53 | ix = q.w * x + q.y * z - q.z * y 54 | iy = q.w * y + q.z * x - q.x * z 55 | iz = q.w * z + q.x * y - q.y * x 56 | iw = -q.x * x - q.y * y - q.z * z 57 | 58 | self.x = ix * q.w + iw * -q.x + iy * -q.z - iz * -q.y 59 | self.y = iy * q.w + iw * -q.y + iz * -q.x - ix * -q.z 60 | self.z = iz * q.w + iw * -q.z + ix * -q.y - iy * -q.x 61 | end 62 | 63 | def cross(v : Vector3) 64 | Vector3.new( 65 | y * v.z - v.y * z, 66 | z * v.x - v.z * x, 67 | x * v.y - v.x * y 68 | ) 69 | end 70 | 71 | def dot(v : Vector3) 72 | x * v.x + y * v.y + z * v.z 73 | end 74 | 75 | def clone 76 | Vector3.new(x, y, z) 77 | end 78 | 79 | simple_initialize(zero, 0f32, 0f32, 0f32) 80 | 81 | simple_initialize(up, 0f32, 1f32, 0f32) 82 | simple_initialize(down, 0f32, -1f32, 0f32) 83 | 84 | simple_initialize(x_axis, 1f32, 0f32, 0f32) 85 | simple_initialize(y_axis, 0f32, 1f32, 0f32) 86 | simple_initialize(z_axis, 0f32, 0f32, 1f32) 87 | end 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /src/lirith/objects.cr: -------------------------------------------------------------------------------- 1 | require "./objects/*" 2 | 3 | module Lirith 4 | module Objects 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /src/lirith/objects/base.cr: -------------------------------------------------------------------------------- 1 | require "../renderer" 2 | 3 | module Lirith 4 | module Objects 5 | abstract class Base 6 | include Drawable 7 | 8 | property children = [] of self 9 | 10 | # property render_attributes : Renderer::Attributes? 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /src/lirith/objects/mesh.cr: -------------------------------------------------------------------------------- 1 | require "./base" 2 | 3 | module Lirith 4 | module Objects 5 | class Mesh < Base 6 | property vertices = Array(Vertex).new 7 | property colors = Array(Math::Color).new 8 | property faces = Array(Face).new 9 | 10 | property material = Materials::Basic.new 11 | 12 | def initialize 13 | end 14 | 15 | def clone 16 | mesh = super 17 | 18 | mesh.vertices = @vertices.clone 19 | mesh.colors = @colors.clone 20 | mesh.material = @material.clone 21 | 22 | mesh 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /src/lirith/objects/plane.cr: -------------------------------------------------------------------------------- 1 | require "./mesh" 2 | 3 | module Lirith 4 | module Objects 5 | module Plane 6 | def self.create(width = 1, height = 1, width_segments = 1, height_segments = 1) 7 | segment_width = width.to_f32 / width_segments 8 | segment_height = height.to_f32 / height_segments 9 | 10 | mesh = Objects::Mesh.new 11 | 12 | 0.upto(width_segments - 1) do |ix| 13 | 0.upto(height_segments - 1) do |iy| 14 | # Face 1 15 | mesh.vertices << Vertex.new( 16 | Math::Vector3.new(segment_width * ix, segment_height * iy, 0), 17 | Math::Vector2.new(0, 0) 18 | ) 19 | mesh.vertices << Vertex.new( 20 | Math::Vector3.new(segment_width * ix, segment_height * (iy + 1), 0), 21 | Math::Vector2.new(0, 1) 22 | ) 23 | mesh.vertices << Vertex.new( 24 | Math::Vector3.new(segment_width * (ix + 1), segment_height * iy, 0), 25 | Math::Vector2.new(1, 0) 26 | ) 27 | mesh.colors << Math::Color.black 28 | mesh.colors << Math::Color.black 29 | mesh.colors << Math::Color.black 30 | 31 | # Face 2 32 | mesh.vertices << Vertex.new( 33 | Math::Vector3.new(segment_width * ix, segment_height * (iy + 1), 0), 34 | Math::Vector2.new(0, 1) 35 | ) 36 | mesh.vertices << Vertex.new( 37 | Math::Vector3.new(segment_width * (ix + 1), segment_height * (iy + 1), 0), 38 | Math::Vector2.new(1, 1) 39 | ) 40 | mesh.vertices << Vertex.new( 41 | Math::Vector3.new(segment_width * (ix + 1), segment_height * iy, 0), 42 | Math::Vector2.new(1, 0) 43 | ) 44 | 45 | mesh.colors << Math::Color.red 46 | mesh.colors << Math::Color.red 47 | mesh.colors << Math::Color.red 48 | end 49 | end 50 | 51 | # mesh.material = Materials::Base.new 52 | mesh 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /src/lirith/renderable.cr: -------------------------------------------------------------------------------- 1 | require "./renderer/attributes" 2 | 3 | module Lirith 4 | module Renderable 5 | property render_attributes : Renderer::Attributes? 6 | property? needs_update = true 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/lirith/renderer.cr: -------------------------------------------------------------------------------- 1 | require "./renderer/*" 2 | 3 | module Lirith 4 | module Renderer 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /src/lirith/renderer/attributes.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Renderer 3 | abstract class Attributes 4 | end 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /src/lirith/renderers.cr: -------------------------------------------------------------------------------- 1 | require "./renderers/opengl" 2 | 3 | module Lirith 4 | module Renderers 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /src/lirith/renderers/base.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Renderers 3 | abstract class Base 4 | abstract def render(*args) 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /src/lirith/renderers/opengl.cr: -------------------------------------------------------------------------------- 1 | require "./base" 2 | require "./opengl/*" 3 | 4 | module Lirith 5 | module Renderers 6 | module OpenGL 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /src/lirith/renderers/opengl/attribute.cr: -------------------------------------------------------------------------------- 1 | require "./attribute/*" 2 | 3 | module Lirith 4 | module Renderers 5 | module OpenGL 6 | module Attribute 7 | end 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /src/lirith/renderers/opengl/attribute/buffer.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Renderers 3 | module OpenGL 4 | module Attribute 5 | class Buffer 6 | enum IndexType 7 | # MUST be the same as defined in the vertex shader 8 | Position = 0 9 | Color = 1 10 | UV = 2 11 | end 12 | 13 | getter buffer_id 14 | getter data = [] of UInt8 15 | getter index_type : IndexType 16 | getter property_size = 0 17 | getter? build = false 18 | 19 | def initialize(@index_type) 20 | LibGL.gen_buffers 1, out @buffer_id 21 | end 22 | 23 | def finalize 24 | LibGL.delete_buffers 1, pointerof(@buffer_id) 25 | end 26 | 27 | def set(elements : Array(Math::Base)) 28 | # retreive bytes and store them 29 | elements.each do |e| 30 | e.to_slice.each do |byte| 31 | @data << byte 32 | end 33 | end 34 | 35 | @property_size = typeof(elements[0]).property_size 36 | end 37 | 38 | def size 39 | @data.size * sizeof(UInt8) 40 | end 41 | 42 | def use 43 | LibGL.bind_buffer(LibGL::E_ARRAY_BUFFER, @buffer_id) 44 | 45 | build! 46 | end 47 | 48 | def close 49 | LibGL.disable_vertex_attrib_array 0 50 | end 51 | 52 | def build! 53 | return if @build 54 | 55 | LibGL.buffer_data( 56 | LibGL::E_ARRAY_BUFFER, 57 | size, 58 | @data.to_unsafe.as(Pointer(Void)), 59 | LibGL::E_STATIC_DRAW 60 | ) 61 | 62 | LibGL.enable_vertex_attrib_array @index_type.value 63 | LibGL.bind_buffer LibGL::E_ARRAY_BUFFER, @buffer_id 64 | LibGL.vertex_attrib_pointer @index_type.value, @property_size, LibGL::E_FLOAT, 0_u8, 0, nil 65 | 66 | # @build = true 67 | end 68 | end 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /src/lirith/renderers/opengl/attributes.cr: -------------------------------------------------------------------------------- 1 | require "./attribute" 2 | require "./attributes/*" 3 | 4 | module Lirith 5 | module Renderers 6 | module OpenGL 7 | module Attributes 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /src/lirith/renderers/opengl/attributes/object.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Renderers 3 | module OpenGL 4 | module Attributes 5 | class Object < Renderer::Attributes 6 | property buffers = {} of Symbol => Attribute::Buffer 7 | getter vertex_array_id 8 | 9 | def initialize 10 | LibGL.gen_vertex_arrays 1, out @vertex_array_id 11 | end 12 | 13 | def finalize 14 | LibGL.delete_vertex_arrays 1, pointerof(@vertex_array_id) 15 | end 16 | 17 | def use 18 | LibGL.bind_vertex_array @vertex_array_id 19 | buffers.values.each(&.use) 20 | end 21 | 22 | def close 23 | buffers.values.each(&.close) 24 | LibGL.bind_vertex_array 0_u32 25 | end 26 | end 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /src/lirith/renderers/opengl/attributes/texture.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Renderers 3 | module OpenGL 4 | module Attributes 5 | class Texture < Renderer::Attributes 6 | getter texture_id 7 | 8 | def initialize 9 | LibGL.gen_textures 1, out @texture_id 10 | end 11 | 12 | def finalize 13 | LibGL.delete_textures 1, pointerof(@texture_id) 14 | end 15 | 16 | def use 17 | LibGL.bind_texture LibGL::E_TEXTURE_2D, @texture_id 18 | end 19 | 20 | def close 21 | LibGL.bind_texture LibGL::E_TEXTURE_2D, 0_u32 22 | end 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /src/lirith/renderers/opengl/core.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Renderers 3 | module OpenGL 4 | class Core 5 | def initialize(@scene : Scene, @camera : Camera) 6 | end 7 | 8 | def render 9 | LibGL.clear LibGL::E_COLOR_BUFFER_BIT | LibGL::E_DEPTH_BUFFER_BIT 10 | 11 | LibGL.enable LibGL::E_DEPTH_TEST 12 | LibGL.depth_func LibGL::E_LESS 13 | 14 | set_background 15 | 16 | @scene.children.each do |object| 17 | case object 18 | when Objects::Mesh; render(object) 19 | # update buffers 20 | # Use program 21 | # set uniforms 22 | # bind buffers 23 | # render triangles 24 | end 25 | end 26 | end 27 | 28 | def set_background 29 | LibGL.clear_color( 30 | @scene.background_color.red, 31 | @scene.background_color.green, 32 | @scene.background_color.blue, 33 | @scene.background_color.alpha 34 | ) 35 | end 36 | 37 | def bind_program(material : Materials::Base) 38 | program = ShaderLib.find(:basic) 39 | program.use 40 | 41 | Elements::Material.update(material) if material.needs_update? 42 | program.uniform("color", material.color) 43 | 44 | program.uniform("view", @camera.view.inverse) 45 | program.uniform("projection", @camera.projection) 46 | 47 | program.uniform("use_texture", 0_u8) 48 | if texture = material.texture 49 | texture.render_attributes.try(&.use) 50 | Elements::Texture.use(texture) 51 | program.uniform("use_texture", 1_u8) 52 | end 53 | 54 | program 55 | end 56 | 57 | def render(object : Objects::Mesh) 58 | program = bind_program(object.material) 59 | 60 | Elements::Object.update(object) if object.needs_update? 61 | 62 | program.uniform("world", object.view) 63 | 64 | object.render_attributes.try(&.use) # try or not_nil! ? 65 | 66 | LibGL.draw_arrays LibGL::E_TRIANGLES, 0, object.vertices.size 67 | 68 | object.render_attributes.try(&.close) 69 | 70 | if texture = object.material.texture 71 | texture.render_attributes.try(&.close) 72 | end 73 | end 74 | end 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /src/lirith/renderers/opengl/elements.cr: -------------------------------------------------------------------------------- 1 | require "./elements/*" 2 | 3 | module Lirith 4 | module Renderers 5 | module OpenGL 6 | module Elements 7 | end 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /src/lirith/renderers/opengl/elements/material.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Renderers 3 | module OpenGL 4 | module Elements 5 | module Material 6 | extend self 7 | 8 | def update(material) 9 | Elements::Texture.update(material.texture.not_nil!) unless material.texture.nil? 10 | end 11 | end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /src/lirith/renderers/opengl/elements/object.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Renderers 3 | module OpenGL 4 | module Elements 5 | module Object 6 | extend self 7 | 8 | def update(object) 9 | attributes = Attributes::Object.new 10 | 11 | # Vertex buffer 12 | vertex_buffer = Attribute::Buffer.new(Attribute::Buffer::IndexType::Position) 13 | vertex_buffer.set(object.vertices.map(&.position)) 14 | attributes.buffers[:vertex] = vertex_buffer 15 | 16 | # Color Buffer 17 | if object.colors.any? 18 | color_buffer = Attribute::Buffer.new(Attribute::Buffer::IndexType::Color) 19 | color_buffer.set(object.colors.compact) 20 | attributes.buffers[:colors] = color_buffer 21 | end 22 | 23 | # UV buffer 24 | uv_buffer = Attribute::Buffer.new(Attribute::Buffer::IndexType::UV) 25 | uv_buffer.set(object.vertices.map(&.uv)) 26 | attributes.buffers[:uv] = uv_buffer 27 | 28 | object.render_attributes = attributes 29 | object.needs_update = false 30 | end 31 | end 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /src/lirith/renderers/opengl/elements/texture.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Renderers 3 | module OpenGL 4 | module Elements 5 | module Texture 6 | extend self 7 | 8 | def update(texture) 9 | return unless texture.needs_update? 10 | 11 | attributes = Attributes::Texture.new 12 | texture.render_attributes = attributes 13 | texture.needs_update = false 14 | end 15 | 16 | def use(texture) 17 | LibGL.tex_image2d(LibGL::E_TEXTURE_2D, 0, LibGL::E_RGB, texture.image.not_nil!.width, texture.image.not_nil!.height, 0, LibGL::E_BGR, LibGL::E_UNSIGNED_BYTE, texture.image.not_nil!.data) 18 | LibGL.tex_parameteri(LibGL::E_TEXTURE_2D, LibGL::E_TEXTURE_MAG_FILTER, LibGL::E_NEAREST) 19 | LibGL.tex_parameteri(LibGL::E_TEXTURE_2D, LibGL::E_TEXTURE_MIN_FILTER, LibGL::E_NEAREST) 20 | end 21 | end 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /src/lirith/renderers/opengl/program.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Renderers 3 | module OpenGL 4 | class Program 5 | getter :program_id 6 | 7 | def initialize 8 | @program_id = LibGL.create_program 9 | end 10 | 11 | def attach(shader) 12 | shader.compile 13 | LibGL.attach_shader @program_id, shader.id 14 | end 15 | 16 | def link 17 | LibGL.link_program @program_id 18 | 19 | LibGL.get_programiv @program_id, LibGL::E_LINK_STATUS, out result 20 | 21 | if result == 0 22 | LibGL.get_programiv @program_id, LibGL::E_INFO_LOG_LENGTH, out info_log_length 23 | 24 | info_log = String.new(info_log_length) do |buffer| 25 | LibGL.get_program_info_log @program_id, info_log_length, nil, buffer 26 | {info_log_length, info_log_length} 27 | end 28 | 29 | raise "Error linking program: #{info_log}" 30 | end 31 | end 32 | 33 | def use 34 | LibGL.use_program @program_id 35 | end 36 | 37 | def uniform_location(name) 38 | LibGL.get_uniform_location @program_id, name 39 | end 40 | 41 | def uniform(name : String, value : UInt8) 42 | LibGL.uniform1i(uniform_location(name), value) 43 | end 44 | 45 | def uniform(name : String, value : Math::Matrix4) 46 | LibGL.uniform_matrix4fv(uniform_location(name), 1, 0_u8, value) 47 | end 48 | 49 | def uniform(name : String, value : Math::Color) 50 | LibGL.uniform4f( 51 | uniform_location(name), 52 | value.red, 53 | value.green, 54 | value.blue, 55 | value.alpha 56 | ) 57 | end 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /src/lirith/renderers/opengl/shader.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Renderers 3 | module OpenGL 4 | class Shader 5 | getter :id 6 | getter :type 7 | 8 | def initialize(@type : UInt32, source) 9 | @id = LibGL.create_shader(@type) 10 | 11 | # https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glShaderSource.xhtml 12 | source_ptr = source.to_unsafe 13 | LibGL.shader_source @id, 1, pointerof(source_ptr), nil 14 | end 15 | 16 | def compile 17 | LibGL.compile_shader @id 18 | 19 | LibGL.get_shaderiv @id, LibGL::E_COMPILE_STATUS, out result 20 | 21 | if result == 0 22 | LibGL.get_shaderiv @id, LibGL::E_INFO_LOG_LENGTH, out info_log_length 23 | 24 | info_log = String.new(info_log_length) do |buffer| 25 | LibGL.get_shader_info_log @id, info_log_length, nil, buffer 26 | {info_log_length, info_log_length} 27 | end 28 | 29 | raise "Error compiling shader: #{info_log}" 30 | end 31 | end 32 | 33 | def destroy 34 | LibGL.delete_shader @id 35 | end 36 | 37 | def self.from_file(type, file_path) 38 | search_paths = [ 39 | file_path, 40 | __DIR__ + file_path, 41 | __DIR__ + "/shaders/" + file_path, 42 | ] 43 | 44 | search_paths.each do |path| 45 | if File.exists? path 46 | source = File.read(path) 47 | return new type, source 48 | end 49 | end 50 | 51 | raise "Unable to load shader file '#{file_path}'" 52 | end 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /src/lirith/renderers/opengl/shader_lib.cr: -------------------------------------------------------------------------------- 1 | require "./program" 2 | 3 | module Lirith 4 | module Renderers 5 | module OpenGL 6 | class ShaderLib 7 | include Singleton 8 | 9 | getter programs = {} of Symbol => Program 10 | 11 | def build_program(name : Symbol) 12 | program = Program.new 13 | program.attach Shader.from_file(LibGL::E_VERTEX_SHADER, "#{name}.vs.glsl") 14 | program.attach Shader.from_file(LibGL::E_FRAGMENT_SHADER, "#{name}.fs.glsl") 15 | program.link 16 | # Call shader delete after linking 17 | 18 | program 19 | end 20 | 21 | def find(name : Symbol) 22 | return @programs[name] if @programs.has_key?(name) 23 | 24 | @programs[name] = build_program(name) 25 | end 26 | 27 | def self.find(name : Symbol) 28 | instance.find(name) 29 | end 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /src/lirith/renderers/opengl/shaders/basic.fs.glsl: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | in vec3 fragmentColor; 4 | in vec2 fragmentUV; 5 | 6 | // output color value 7 | out vec3 color; 8 | 9 | uniform sampler2D textureSampler; 10 | uniform bool use_texture; 11 | 12 | void main() { 13 | if(use_texture == true){ 14 | color = texture( textureSampler, fragmentUV ).rgb; 15 | //color = vec4(1,0,0,1).rgb; 16 | }else{ 17 | color = fragmentColor; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/lirith/renderers/opengl/shaders/basic.vs.glsl: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | // vertex input data 4 | layout(location = 0) in vec3 position; 5 | layout(location = 1) in vec4 colors; 6 | layout(location = 2) in vec2 uv; 7 | 8 | out vec3 fragmentColor; 9 | out vec2 fragmentUV; 10 | 11 | // model/view/projection matrix 12 | 13 | uniform vec4 color; 14 | uniform mat4 world; 15 | uniform mat4 view; 16 | uniform mat4 projection; 17 | 18 | void main() { 19 | vec4 worldPos = world * vec4(position, 1); 20 | vec4 viewPos = view * worldPos; 21 | 22 | gl_Position = projection * viewPos; 23 | 24 | fragmentColor = color.rgb; 25 | //fragmentColor = colors.rgb; 26 | fragmentUV = uv; 27 | } 28 | -------------------------------------------------------------------------------- /src/lirith/scene.cr: -------------------------------------------------------------------------------- 1 | require "./objects" 2 | require "./renderable" 3 | 4 | module Lirith 5 | class Scene 6 | include Renderable 7 | 8 | property background_color = Math::Color.from_hex(0x7ec0ee) # 0x282828 9 | property children = [] of Objects::Base 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /src/lirith/singleton.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Singleton 3 | macro included 4 | def self.instance 5 | @@instance ||= new 6 | end 7 | end 8 | 9 | def dup 10 | raise "Singletons can't be duped" 11 | end 12 | 13 | def clone 14 | raise "Singletons can't be cloned" 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /src/lirith/systems.cr: -------------------------------------------------------------------------------- 1 | require "./systems/*" 2 | 3 | module Lirith 4 | module Systems 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /src/lirith/systems/application.cr: -------------------------------------------------------------------------------- 1 | require "./base" 2 | 3 | module Lirith 4 | module Systems 5 | class Application < Base 6 | def run 7 | end 8 | 9 | def shutdown 10 | Managers::System.instance.stop 11 | end 12 | 13 | def handle_event(event) 14 | case event 15 | when Events::Application::Initialized; run 16 | when Events::Application::Exit ; shutdown 17 | end 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /src/lirith/systems/base.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | module Systems 3 | abstract class Base 4 | def handle_event(event : Events::Base) 5 | end 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /src/lirith/systems/console.cr: -------------------------------------------------------------------------------- 1 | require "./base" 2 | 3 | module Lirith 4 | module Systems 5 | class Console < Lirith::Systems::Base 6 | # property polling = false 7 | property log = true 8 | 9 | def initialize(@ignore_events = [] of Events::Base.class) 10 | end 11 | 12 | def get_command 13 | print "Command: " 14 | line = gets.to_s 15 | 16 | command_data = line.split(' ') 17 | 18 | handle_command(command_data.shift, command_data) 19 | end 20 | 21 | def handle_command(command : String, args = [] of String) 22 | case command 23 | when "exit" ; Managers::System.trigger_event(Events::Application::Exit) 24 | when "listMesh" ; listMesh 25 | when "randomColor"; randomColor 26 | else 27 | p "Command '#{command}' was not found" 28 | end 29 | end 30 | 31 | def handle_event(event) 32 | p event.info if @log && !@ignore_events.includes?(event.class) 33 | case event 34 | when Events::Console::RequestCommand; get_command 35 | when Events::Console::ToggleLog ; @log = !@log 36 | end 37 | end 38 | 39 | def listMesh 40 | Lirith.application.scene.children.each_with_index do |obj, index| 41 | next unless obj.is_a?(Lirith::Objects::Mesh) 42 | 43 | p "#{index} | #{obj.class}" 44 | end 45 | end 46 | 47 | def randomColor 48 | Lirith.application.scene.children.each_with_index do |obj, index| 49 | next unless obj.is_a?(Lirith::Objects::Mesh) 50 | if material = obj.material 51 | material.color = Math::Color.random 52 | obj.needs_update = true 53 | end 54 | end 55 | end 56 | 57 | def updateMesh 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /src/lirith/texture.cr: -------------------------------------------------------------------------------- 1 | require "./renderable" 2 | 3 | module Lirith 4 | class Texture 5 | include Renderable 6 | getter image : RImage::Image? 7 | 8 | def initialize(image = nil) 9 | @image = image 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /src/lirith/version.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | VERSION = "0.1.0" 3 | end 4 | -------------------------------------------------------------------------------- /src/lirith/vertex.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | class Vertex 3 | property position : Math::Vector3 4 | property uv = Math::Vector2.zero 5 | 6 | def initialize(@position : Math::Vector3) 7 | end 8 | 9 | def initialize(@position : Math::Vector3) 10 | end 11 | 12 | def initialize(@position : Math::Vector3, @uv : Math::Vector2) 13 | end 14 | 15 | def initialize(@position : Math::Vector3, @uv : Math::Vector2) 16 | end 17 | 18 | def clone 19 | Vertex.new(@position.clone, @uv.clone) 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /src/lirith/window.cr: -------------------------------------------------------------------------------- 1 | module Lirith 2 | class Window 3 | property height 4 | property width 5 | property title 6 | 7 | def initialize(@width = 1024, @height = 768, @title = "", fullscreen = false) 8 | raise "Failed to initialize GLFW" unless LibGLFW.init 9 | 10 | LibGLFW.window_hint LibGLFW::SAMPLES, 4 11 | LibGLFW.window_hint LibGLFW::CONTEXT_VERSION_MAJOR, 3 12 | LibGLFW.window_hint LibGLFW::CONTEXT_VERSION_MINOR, 3 13 | LibGLFW.window_hint LibGLFW::OPENGL_FORWARD_COMPAT, 1 14 | LibGLFW.window_hint LibGLFW::OPENGL_PROFILE, LibGLFW::OPENGL_CORE_PROFILE 15 | 16 | 17 | 18 | @handle = if fullscreen 19 | monitor = LibGLFW.primary_monitor 20 | mode = LibGLFW.video_mode(monitor).value 21 | 22 | LibGLFW.window_hint(LibGLFW::RED_BITS, mode.red_bits); 23 | LibGLFW.window_hint(LibGLFW::GREEN_BITS, mode.green_bits); 24 | LibGLFW.window_hint(LibGLFW::BLUE_BITS, mode.blue_bits); 25 | LibGLFW.window_hint(LibGLFW::REFRESH_RATE, mode.refresh_rate); 26 | 27 | @width = mode.width 28 | @height = mode.height 29 | 30 | LibGLFW.create_window(mode.width, mode.height, title, monitor, nil) 31 | #LibGLFW.create_window(width, height, title, nil, nil) 32 | else 33 | LibGLFW.create_window(width, height, title, nil, nil) 34 | end 35 | 36 | set_current_context 37 | 38 | LibGLFW.set_input_mode @handle, LibGLFW::STICKY_KEYS, 1 39 | # LibGLFW.set_input_mode @handle, LibGLFW::CURSOR, LibGLFW::CURSOR_DISABLED 40 | 41 | set_callbacks 42 | 43 | Managers::System.trigger_event(Events::Window::Opened) 44 | puts "OpenGL version: " + String.new(LibGL.get_string(LibGL::E_VERSION)) 45 | puts "Window init #{@width}x#{@height}" 46 | end 47 | 48 | def handle : LibGLFW::Window 49 | if handle = @handle 50 | return handle 51 | else 52 | raise "No handle is set" 53 | end 54 | end 55 | 56 | def set_callbacks 57 | self.class.key_callback(handle, ->Input.handle_key(LibGLFW::Window, Int32, Int32, Int32, Int32)) 58 | end 59 | 60 | def swap_buffers 61 | LibGLFW.swap_buffers handle 62 | end 63 | 64 | protected def set_current_context 65 | @handle.try do |handle| 66 | LibGLFW.make_context_current handle 67 | end 68 | end 69 | 70 | def self.key_callback(handle : LibGLFW::Window, callback : Proc(LibGLFW::Window, Int32, Int32, Int32, Int32, Void)) 71 | LibGLFW.set_key_callback(handle, callback) 72 | end 73 | end 74 | end 75 | --------------------------------------------------------------------------------