├── .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 |
2 |
3 | # lirith-engine
4 | [](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 |
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 |
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 |
--------------------------------------------------------------------------------