├── .gitattributes
├── .gitmodules
├── CHANGELOG.MD
├── LICENSE.MD
├── README.MD
├── assets
├── fonts
│ ├── Muli-Bold.ttf
│ └── Muli-Regular.ttf
├── html
│ └── index.html
└── shaders
│ ├── colored.frag.glsl
│ ├── colored.vert.glsl
│ ├── multitextured.vert.glsl
│ ├── multitextured16.frag.glsl
│ ├── multitextured8.frag.glsl
│ ├── textured.frag.glsl
│ └── textured.vert.glsl
├── clay
├── App.hx
├── Audio.hx
├── Clay.hx
├── Display.hx
├── Graphics.hx
├── Input.hx
├── Resources.hx
├── Window.hx
├── audio
│ ├── AudioChannel.hx
│ ├── AudioEffect.hx
│ ├── AudioGroup.hx
│ ├── Sound.hx
│ ├── dsp
│ │ ├── AllPassFilter.hx
│ │ ├── CombFilter.hx
│ │ ├── Delay.hx
│ │ ├── FloatRingBuffer.hx
│ │ ├── HighPassFilter.hx
│ │ └── LowPassFilter.hx
│ └── effects
│ │ ├── Compressor.hx
│ │ ├── Distortion.hx
│ │ ├── Filter.hx
│ │ ├── MultiDelay.hx
│ │ └── Reverb.hx
├── events
│ ├── AppEvent.hx
│ ├── GamepadEvent.hx
│ ├── IEvent.hx
│ ├── InputEvent.hx
│ ├── KeyEvent.hx
│ ├── MouseEvent.hx
│ ├── PenEvent.hx
│ ├── RenderEvent.hx
│ └── TouchEvent.hx
├── graphics
│ ├── Camera.hx
│ ├── Color.hx
│ ├── Font.hx
│ ├── Polygon.hx
│ ├── Texture.hx
│ ├── Vertex.hx
│ ├── Video.hx
│ ├── batchers
│ │ ├── PolygonBatch.hx
│ │ ├── PolygonCache.hx
│ │ ├── SpriteBatch.hx
│ │ └── SpriteCache.hx
│ ├── render
│ │ ├── IndexBuffer.hx
│ │ ├── Pipeline.hx
│ │ ├── Shaders.hx
│ │ ├── Uniforms.hx
│ │ ├── VertexBuffer.hx
│ │ └── VertexStructure.hx
│ ├── slice
│ │ ├── NineSlice.hx
│ │ └── ThreeSlice.hx
│ └── utils
│ │ ├── ImmediateColoredRenderer.hx
│ │ ├── ImmediateTexturedRenderer.hx
│ │ └── ShapeRenderer.hx
├── input
│ ├── Bindings.hx
│ ├── Gamepad.hx
│ ├── Key.hx
│ ├── Keyboard.hx
│ ├── Mouse.hx
│ ├── Pen.hx
│ └── Touch.hx
├── math
│ ├── FastMatrix3.hx
│ ├── FastMatrix4.hx
│ ├── FastVector2.hx
│ ├── FastVector3.hx
│ ├── FastVector4.hx
│ ├── Matrix.hx
│ ├── Rectangle.hx
│ ├── RectangleCallback.hx
│ ├── Transform.hx
│ ├── Vector2.hx
│ └── Vector2Callback.hx
├── resources
│ ├── AudioResource.hx
│ ├── BytesResource.hx
│ ├── JsonResource.hx
│ ├── Resource.hx
│ └── TextResource.hx
└── utils
│ ├── Align.hx
│ ├── ArrayTools.hx
│ ├── Atlas.hx
│ ├── BitVector.hx
│ ├── Bits.hx
│ ├── Common.hx
│ ├── DSP.hx
│ ├── DynamicPool.hx
│ ├── Emitter.hx
│ ├── EventType.hx
│ ├── Events.hx
│ ├── FPSCounter.hx
│ ├── FastFloat.hx
│ ├── Float32Array.hx
│ ├── IdGenerator.hx
│ ├── IntRingBuffer.hx
│ ├── Log.hx
│ ├── Math.hx
│ ├── PowerOfTwo.hx
│ ├── Random.hx
│ ├── ScissorStack.hx
│ ├── Signal.hx
│ ├── SparseSet.hx
│ ├── StrokeAlign.hx
│ ├── Timer.hx
│ ├── UUID.hx
│ ├── Uint32Array.hx
│ ├── Viewport.hx
│ └── macro
│ ├── EventMacro.hx
│ └── MacroUtils.hx
├── haxelib.json
├── run.n
├── templates
└── empty
│ ├── assets
│ └── .keep
│ ├── project.yml
│ └── src
│ ├── Game.hx
│ └── Main.hx
└── tools
└── cli
├── CLI.hx
├── Command.hx
├── Config.hx
├── cli.hxml
└── commands
├── Build.hx
├── Clear.hx
├── Collect.hx
├── Help.hx
├── Init.hx
├── Launch.hx
├── Run.hx
└── Server.hx
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "backend/Kha"]
2 | path = backend/Kha
3 | url = https://github.com/Kode/Kha
4 |
--------------------------------------------------------------------------------
/CHANGELOG.MD:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | ## [0.0.1] - 2020-11-29
6 |
7 | ### Added
8 |
9 | - Sprite, Polygon Batchers and Cache.
10 | Multitexture rendering
11 | Camera and Viewport
12 | Immediate renderers
13 | ShapeRenderer for drawing thick lines and shapes
14 |
15 | ### Changed
16 |
17 | - This is third iteration of framework, basicaly i rewrite all rendering system from scratch.
18 |
19 | ### Removed
20 |
21 | - Scene graph.
22 | Particle System.
23 | Tweening.
24 |
25 |
26 | [0.0.1]: https://github.com/clay2d/clay/releases/tag/v0.0.1
--------------------------------------------------------------------------------
/LICENSE.MD:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Andrei Rudenko
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 all
13 | 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 THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | # Clay
2 | a cross-platform, 2d game framework.
3 | Built on top of [Kha](https://github.com/Kode/Kha) framework using [Haxe](https://haxe.org/) language.
4 |
5 | ## Installation
6 | ```bash
7 | git clone --recursive https://github.com/clay2d/clay.git
8 | haxelib dev clay2d path/to/clay2d
9 | ```
10 |
11 | ### Command Line Interface
12 | - haxelib run clay2d init [ -t template ]: *initialize a new project*.
13 | - haxelib run clay2d build < target > [ --debug ]: *build the current project*.
14 | - haxelib run clay2d run < target > [ --debug ]: *build and run the current project*.
15 | - haxelib run clay2d server : *launch server for html5 build*.
16 | - haxelib run clay2d launch: *launch build*.
17 | - haxelib run clay2d collect [--clear, --verbose]: *copy all libraries to project folder*.
18 | - haxelib run clay2d clear < target >: *remove project build files*.
19 |
20 |
--------------------------------------------------------------------------------
/assets/fonts/Muli-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndreiRudenko/clay/f3936e625e027a7b45552946a94a43971af5a9ef/assets/fonts/Muli-Bold.ttf
--------------------------------------------------------------------------------
/assets/fonts/Muli-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndreiRudenko/clay/f3936e625e027a7b45552946a94a43971af5a9ef/assets/fonts/Muli-Regular.ttf
--------------------------------------------------------------------------------
/assets/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {name}
6 |
7 |
8 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/assets/shaders/colored.frag.glsl:
--------------------------------------------------------------------------------
1 | #version 450
2 |
3 | in vec4 outColor;
4 | out vec4 FragColor;
5 |
6 | void main() {
7 | FragColor = outColor;
8 | }
9 |
--------------------------------------------------------------------------------
/assets/shaders/colored.vert.glsl:
--------------------------------------------------------------------------------
1 | #version 450
2 |
3 | in vec2 position;
4 | in vec4 color;
5 |
6 | uniform mat3 projectionMatrix;
7 |
8 | out vec4 outColor;
9 |
10 | void main() {
11 | gl_Position = vec4((projectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0);
12 | outColor = color;
13 | }
14 |
--------------------------------------------------------------------------------
/assets/shaders/multitextured.vert.glsl:
--------------------------------------------------------------------------------
1 | #version 450
2 |
3 | in vec2 position;
4 | in vec4 color;
5 | in vec2 texCoord;
6 | in float texId;
7 | in float texFormat;
8 |
9 | uniform mat3 projectionMatrix;
10 |
11 | out vec4 outColor;
12 | out vec2 outTexCoord;
13 | out float outTexId;
14 | out float outTexFormat;
15 |
16 | void main() {
17 | gl_Position = vec4((projectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0);
18 | outColor = color;
19 | outTexCoord = texCoord;
20 | outTexId = texId;
21 | outTexFormat = texFormat;
22 | }
--------------------------------------------------------------------------------
/assets/shaders/multitextured16.frag.glsl:
--------------------------------------------------------------------------------
1 | #version 450
2 |
3 | in vec4 outColor;
4 | in vec2 outTexCoord;
5 | in float outTexId;
6 | in float outTexFormat;
7 |
8 | uniform sampler2D tex[16];
9 |
10 | out vec4 FragColor;
11 |
12 | void main(){
13 | vec4 texColor;
14 |
15 | if(outTexId < 0.5) {
16 | texColor = texture(tex[0], outTexCoord);
17 | } else if(outTexId < 1.5) {
18 | texColor = texture(tex[1], outTexCoord);
19 | } else if(outTexId < 2.5) {
20 | texColor = texture(tex[2], outTexCoord);
21 | } else if(outTexId < 3.5) {
22 | texColor = texture(tex[3], outTexCoord);
23 | } else if(outTexId < 4.5) {
24 | texColor = texture(tex[4], outTexCoord);
25 | } else if(outTexId < 5.5) {
26 | texColor = texture(tex[5], outTexCoord);
27 | } else if(outTexId < 6.5) {
28 | texColor = texture(tex[6], outTexCoord);
29 | } else if(outTexId < 7.5) {
30 | texColor = texture(tex[7], outTexCoord);
31 | } else if(outTexId < 8.5) {
32 | texColor = texture(tex[8], outTexCoord);
33 | } else if(outTexId < 9.5) {
34 | texColor = texture(tex[9], outTexCoord);
35 | } else if(outTexId < 10.5) {
36 | texColor = texture(tex[10], outTexCoord);
37 | } else if(outTexId < 11.5) {
38 | texColor = texture(tex[11], outTexCoord);
39 | } else if(outTexId < 12.5) {
40 | texColor = texture(tex[12], outTexCoord);
41 | } else if(outTexId < 13.5) {
42 | texColor = texture(tex[13], outTexCoord);
43 | } else if(outTexId < 14.5) {
44 | texColor = texture(tex[14], outTexCoord);
45 | } else {
46 | texColor = texture(tex[15], outTexCoord);
47 | }
48 |
49 | if(outTexFormat == 2) { //L8
50 | texColor = texColor.rrrr;
51 | }
52 |
53 | texColor *= outColor;
54 | texColor.rgb *= outColor.a;
55 |
56 | FragColor = texColor;
57 | }
--------------------------------------------------------------------------------
/assets/shaders/multitextured8.frag.glsl:
--------------------------------------------------------------------------------
1 | #version 450
2 |
3 | in vec4 outColor;
4 | in vec2 outTexCoord;
5 | in float outTexId;
6 | in float outTexFormat;
7 |
8 | uniform sampler2D tex[8];
9 |
10 | out vec4 FragColor;
11 |
12 | void main(){
13 | vec4 texColor;
14 |
15 | if(outTexId < 0.5) {
16 | texColor = texture(tex[0], outTexCoord);
17 | } else if(outTexId < 1.5) {
18 | texColor = texture(tex[1], outTexCoord);
19 | } else if(outTexId < 2.5) {
20 | texColor = texture(tex[2], outTexCoord);
21 | } else if(outTexId < 3.5) {
22 | texColor = texture(tex[3], outTexCoord);
23 | } else if(outTexId < 4.5) {
24 | texColor = texture(tex[4], outTexCoord);
25 | } else if(outTexId < 5.5) {
26 | texColor = texture(tex[5], outTexCoord);
27 | } else if(outTexId < 6.5) {
28 | texColor = texture(tex[6], outTexCoord);
29 | } else {
30 | texColor = texture(tex[7], outTexCoord);
31 | }
32 |
33 | if(outTexFormat == 2) { //L8
34 | texColor = texColor.rrrr;
35 | }
36 |
37 | texColor *= outColor;
38 | texColor.rgb *= outColor.a;
39 |
40 | FragColor = texColor;
41 | }
--------------------------------------------------------------------------------
/assets/shaders/textured.frag.glsl:
--------------------------------------------------------------------------------
1 | #version 450
2 |
3 | in vec4 outColor;
4 | in vec2 outTexCoord;
5 |
6 | uniform sampler2D tex;
7 |
8 | out vec4 FragColor;
9 |
10 | void main() {
11 | vec4 texColor = texture(tex, outTexCoord) * outColor;
12 | texColor.rgb *= outColor.a;
13 | FragColor = texColor;
14 | }
15 |
--------------------------------------------------------------------------------
/assets/shaders/textured.vert.glsl:
--------------------------------------------------------------------------------
1 | #version 450
2 |
3 | in vec2 position;
4 | in vec4 color;
5 | in vec2 texCoord;
6 |
7 | uniform mat3 projectionMatrix;
8 |
9 | out vec4 outColor;
10 | out vec2 outTexCoord;
11 |
12 | void main() {
13 | gl_Position = vec4((projectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0);
14 | outColor = color;
15 | outTexCoord = texCoord;
16 | }
--------------------------------------------------------------------------------
/clay/Audio.hx:
--------------------------------------------------------------------------------
1 | package clay;
2 |
3 | import kha.audio2.Buffer;
4 | import kha.arrays.Float32Array;
5 | import clay.resources.AudioResource;
6 | import clay.utils.Math;
7 | import clay.utils.Log;
8 | import clay.audio.AudioGroup;
9 | import clay.audio.Sound;
10 |
11 | #if cpp
12 | import sys.thread.Mutex;
13 | #end
14 |
15 | class Audio extends AudioGroup {
16 |
17 | #if cpp
18 | static var mutex:Mutex;
19 | static var mutexLocked:Bool = false;
20 | #end
21 |
22 | static public inline function mutexLock() {
23 | #if cpp
24 | Log.assert(!mutexLocked, 'Audio: unlock mutex before locking');
25 | mutex.acquire();
26 | mutexLocked = true;
27 | #end
28 | }
29 | static public inline function mutexUnlock() {
30 | #if cpp
31 | Log.assert(mutexLocked, 'Audio: lock mutex before unlocking');
32 | mutex.release();
33 | mutexLocked = false;
34 | #end
35 | }
36 |
37 | public var sampleRate(get, never):Int;
38 | var _data:Float32Array;
39 |
40 | @:allow(clay.App)
41 | function new() {
42 | super();
43 | #if cpp
44 | mutex = new Mutex();
45 | #end
46 |
47 | _data = new Float32Array(512);
48 |
49 | kha.audio2.Audio.audioCallback = mix;
50 | }
51 |
52 | public function play(res:AudioResource, volume:Float = 1, pan:Float = 0, pitch:Float = 1, loop:Bool = false, output:AudioGroup = null):Sound {
53 | var sound = new Sound(res, output);
54 | sound.volume = volume;
55 | sound.pan = pan;
56 | sound.pitch = pitch;
57 | sound.loop = loop;
58 | sound.play();
59 |
60 | return sound;
61 | }
62 |
63 | public function stream(res:AudioResource, volume:Float = 1, pan:Float = 0, pitch:Float = 1, loop:Bool = false, output:AudioGroup = null):Sound {
64 | // var sound = new Sound(res, output);
65 | // sound.volume = volume;
66 | // sound.pan = pan;
67 | // sound.pitch = pitch;
68 | // sound.loop = loop;
69 | // // sound.stream = true;
70 | // sound.play();
71 |
72 | return null;
73 | }
74 |
75 | public function stop(res:AudioResource) {
76 |
77 | }
78 |
79 | function mix(samplesbox:kha.internal.IntBox, buffer:Buffer) {
80 | var samples = samplesbox.value;
81 |
82 | if (_data.length < samples) {
83 | _data = new Float32Array(samples);
84 | }
85 |
86 | for (i in 0...samples) {
87 | _data[i] = 0;
88 | }
89 |
90 | if(!_mute) {
91 | process(_data, samples);
92 | }
93 |
94 | for (i in 0...samples) {
95 | buffer.data.set(buffer.writeLocation, Math.clamp(_data[i], -1.0, 1.0) * _volume);
96 | buffer.writeLocation += 1;
97 | if (buffer.writeLocation >= buffer.size) {
98 | buffer.writeLocation = 0;
99 | }
100 | }
101 | }
102 |
103 | inline function get_sampleRate():Int {
104 | return kha.audio2.Audio.samplesPerSecond;
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/clay/Clay.hx:
--------------------------------------------------------------------------------
1 | package clay;
2 |
3 | import clay.App;
4 |
5 | class Clay {
6 |
7 | @:allow(clay.App)
8 | public static var app(default, null):App;
9 |
10 | public static var display(get, never):clay.Display;
11 | public static var window(get, never):clay.Window;
12 | public static var graphics(get, never):clay.Graphics;
13 | public static var input(get, never):clay.Input;
14 | public static var resources(get, never):clay.Resources;
15 | public static var audio(get, never):clay.Audio;
16 |
17 | // TODO: remove timer and random
18 | // public static var timer(get, never):clay.utils.Timer.TimerManager;
19 | public static var random(get, never):clay.utils.Random;
20 |
21 | public static var dt(get, never):Float;
22 | public static var time(get, never):Float;
23 | public static var timescale(get, set):Float;
24 |
25 | static var inited:Bool = false;
26 |
27 |
28 | public static function init(options:ClayOptions, onReady:()->Void) {
29 | clay.utils.Log.assert(!inited, "app is already inited");
30 | inited = true;
31 | new App(options, onReady);
32 | }
33 |
34 | public static inline function on(event:clay.utils.EventType, handler:T->Void, priority:Int = 0) {
35 | app.emitter.on(event, handler, priority);
36 | }
37 |
38 | public static inline function off(event:clay.utils.EventType, handler:T->Void):Bool {
39 | return app.emitter.off(event, handler);
40 | }
41 |
42 | public static inline function next(func:()->Void) app.next(func);
43 | public static inline function defer(func:()->Void) app.defer(func);
44 |
45 | static inline function get_display() return Display.primary;
46 | static inline function get_window() return app.window;
47 | static inline function get_graphics() return app.graphics;
48 | static inline function get_input() return app.input;
49 | static inline function get_resources() return app.resources;
50 | static inline function get_audio() return app.audio;
51 | // static inline function get_timer() return app.timer;
52 | static inline function get_random() return app.random;
53 |
54 | static inline function get_dt() return app.dt;
55 | static inline function get_time() return app.time;
56 | static inline function get_timescale() return app.timescale;
57 | static inline function set_timescale(v) return app.timescale = v;
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/clay/Display.hx:
--------------------------------------------------------------------------------
1 | package clay;
2 |
3 | typedef DisplayMode = kha.DisplayMode;
4 | typedef Display = kha.Display;
--------------------------------------------------------------------------------
/clay/Input.hx:
--------------------------------------------------------------------------------
1 | package clay;
2 |
3 | import clay.input.Mouse;
4 | import clay.input.Keyboard;
5 | import clay.input.Touch;
6 | import clay.input.Gamepad;
7 | import clay.input.Pen;
8 | import clay.input.Bindings;
9 | import clay.App;
10 |
11 | @:allow(clay.App)
12 | class Input {
13 |
14 | public var binding(default, null):Bindings;
15 |
16 | public var mouse(default, null):Mouse;
17 | public var keyboard(default, null):Keyboard;
18 | public var touch(default, null):Touch;
19 | public var gamepad(default, null):Gamepads;
20 | public var pen(default, null):Pen;
21 |
22 | function new() {
23 | binding = new Bindings();
24 | }
25 |
26 | function init() {
27 | mouse = new Mouse();
28 | keyboard = new Keyboard();
29 | touch = new Touch();
30 | gamepad = new Gamepads();
31 | pen = new Pen();
32 | }
33 |
34 | function destroy() {
35 | binding = null;
36 |
37 | mouse = null;
38 | keyboard = null;
39 | touch = null;
40 | gamepad = null;
41 | pen = null;
42 | }
43 |
44 | function enable() {
45 | binding.enable();
46 |
47 | mouse.enable();
48 | keyboard.enable();
49 | touch.enable();
50 | gamepad.enable();
51 | pen.enable();
52 | }
53 |
54 | function disable() {
55 | binding.disable();
56 |
57 | mouse.disable();
58 | keyboard.disable();
59 | touch.disable();
60 | gamepad.disable();
61 | pen.disable();
62 | }
63 |
64 | function reset() {
65 | binding.reset();
66 |
67 | mouse.reset();
68 | keyboard.reset();
69 | touch.reset();
70 | gamepad.reset();
71 | pen.reset();
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/clay/Window.hx:
--------------------------------------------------------------------------------
1 | package clay;
2 |
3 | import kha.System;
4 | import kha.WindowMode;
5 |
6 | import clay.Graphics;
7 | import clay.graphics.Texture;
8 | import clay.graphics.Color;
9 | import clay.math.Vector2;
10 | import clay.utils.Signal;
11 | import clay.utils.Log;
12 |
13 | typedef ScreenRotation = kha.ScreenRotation;
14 | typedef WindowMode = kha.WindowMode;
15 |
16 | @:allow(clay.App)
17 | class Window {
18 |
19 | public var width(get, null):Int;
20 | public var height(get, null):Int;
21 | public var mid(default, null):Vector2;
22 |
23 | public var buffer(default, null):Texture;
24 | public var antialiasing(get, set):Int;
25 |
26 | public var ppi(get, null):Int;
27 | public var rotation(get, null):ScreenRotation;
28 | public var fullscreen(get, set):Bool;
29 |
30 | public var cursor(default, null):Cursor;
31 | public var onResize(default, null):Signal<(w:Int, h:Int)->Void>;
32 | public var color:Color;
33 |
34 | var _antialiasing:Int = 1;
35 |
36 | var _windowId:Int = 0;
37 | var _window:kha.Window; // TODO: multiple windows ?
38 |
39 | function new(antialiasing:Int) {
40 | _antialiasing = antialiasing;
41 |
42 | cursor = new Cursor();
43 | _window = kha.Window.get(_windowId);
44 | mid = new Vector2();
45 | onResize = new Signal();
46 | color = new Color();
47 | }
48 |
49 | public function resize(w:Int, h:Int) {
50 | _window.resize(w, h);
51 | onResize.emit(w, h);
52 | updateSize();
53 | }
54 |
55 | function init() {
56 | updateSize();
57 | }
58 |
59 | function render() {
60 | Graphics.blit(buffer);
61 | }
62 |
63 | function updateSize() {
64 | if(buffer != null) {
65 | buffer.unload();
66 | Clay.resources.remove(buffer);
67 | }
68 |
69 | buffer = Texture.createRenderTarget(
70 | width,
71 | height,
72 | TextureFormat.RGBA32,
73 | DepthStencilFormat.NoDepthAndStencil,
74 | _antialiasing
75 | );
76 |
77 | buffer.name = 'windowbuffer';
78 | buffer.ref();
79 | Clay.resources.add(buffer);
80 |
81 | mid.set(width*0.5, height*0.5);
82 | }
83 |
84 |
85 | function get_ppi():Int {
86 | return kha.Display.primary.pixelsPerInch;
87 | }
88 |
89 | function get_rotation():ScreenRotation {
90 | return System.screenRotation;
91 | }
92 |
93 | function get_width():Int {
94 | return _window.width;
95 | }
96 |
97 | function get_height():Int {
98 | return _window.height;
99 | }
100 |
101 | function get_fullscreen():Bool {
102 | return _window.mode == WindowMode.Fullscreen;
103 | }
104 |
105 | function set_fullscreen(v:Bool):Bool {
106 | if(v) {
107 | if(!fullscreen) {
108 | _window.mode = WindowMode.Fullscreen;
109 | }
110 | } else if(fullscreen) {
111 | _window.mode = WindowMode.Windowed;
112 | }
113 |
114 | return v;
115 | }
116 |
117 | inline function get_antialiasing():Int {
118 | return _antialiasing;
119 | }
120 |
121 | function set_antialiasing(v:Int):Int {
122 | // Log.assert(!Clay.renderer.isRendering, 'you cant change antialiasing while rendering');
123 | _antialiasing = v;
124 |
125 | updateSize();
126 |
127 | return v;
128 | }
129 |
130 | }
131 |
132 | class Cursor {
133 |
134 | public var x(default, null):Float;
135 | public var y(default, null):Float;
136 | public var dx(default, null):Float;
137 | public var dy(default, null):Float;
138 |
139 | public var visible(get, set):Bool;
140 | var _visible:Bool = true;
141 |
142 | @:allow(clay.Window)
143 | function new() {
144 | x = 0;
145 | y = 0;
146 | dx = 0;
147 | dy = 0;
148 | var m = kha.input.Mouse.get();
149 | if(m != null) {
150 | m.notify(null, null, onMove, null);
151 | }
152 | }
153 |
154 | public function lock() {
155 | var m = kha.input.Mouse.get();
156 | if(m != null) {
157 | m.lock();
158 | }
159 | }
160 |
161 | public function unlock() {
162 | var m = kha.input.Mouse.get();
163 | if(m != null) {
164 | m.unlock();
165 | }
166 | }
167 |
168 | function get_visible():Bool {
169 | return _visible;
170 | }
171 |
172 | function onMove(x:Int, y:Int, dx:Int, dy:Int) {
173 | this.x = x;
174 | this.y = y;
175 | this.dx = dx;
176 | this.dy = dy;
177 | }
178 |
179 | function set_visible(v:Bool):Bool {
180 | var m = kha.input.Mouse.get();
181 | if(m != null) {
182 | if(v) {
183 | m.showSystemCursor();
184 | } else {
185 | m.hideSystemCursor();
186 | }
187 | _visible = v;
188 | }
189 |
190 | return _visible;
191 | }
192 |
193 | }
194 |
--------------------------------------------------------------------------------
/clay/audio/AudioChannel.hx:
--------------------------------------------------------------------------------
1 | package clay.audio;
2 |
3 | import clay.utils.Math;
4 | import kha.arrays.Float32Array;
5 | import clay.audio.AudioEffect;
6 | import clay.Audio;
7 | import clay.utils.ArrayTools;
8 | import haxe.ds.Vector;
9 | import clay.utils.Log;
10 |
11 | class AudioChannel {
12 |
13 | static inline var maxEffects:Int = 8;
14 |
15 | public var mute(get, set):Bool;
16 | public var volume(get, set):Float;
17 | public var pan(get, set):Float;
18 | public var output(get, null):AudioGroup;
19 |
20 | public var effects(get, null):Array;
21 | public var effectsCount(get, null):Int;
22 |
23 | var _mute:Bool;
24 | var _volume:Float;
25 | var _pan:Float;
26 | var _output:AudioGroup;
27 |
28 | var _l:Float;
29 | var _r:Float;
30 |
31 | var _effectsCount:Int;
32 | var _maxEffects:Int;
33 | var _dirtyEffects:Bool;
34 |
35 | var _effects:Vector;
36 | var _effectsInternal:Vector;
37 |
38 | public function new(maxEffects:Int = 8) {
39 | _mute = false;
40 | _volume = 1;
41 | _pan = 0;
42 |
43 | _l = 0.7071;
44 | _r = 0.7071;
45 |
46 | _effectsCount = 0;
47 | _maxEffects = maxEffects;
48 | _dirtyEffects = true;
49 |
50 | _effects = new Vector(_maxEffects);
51 | _effectsInternal = new Vector(_maxEffects);
52 | }
53 |
54 | public function addEffect(effect:AudioEffect) {
55 | Audio.mutexLock();
56 |
57 | if(_effectsCount >= _maxEffects) {
58 | Log.warning("cant add effect, max effects: " + _maxEffects);
59 | return;
60 | }
61 |
62 | if(effect._parent != null) {
63 | Log.warning("audio effect already in another channel");
64 | return;
65 | }
66 |
67 | effect._parent = this;
68 | _effects[_effectsCount++] = effect;
69 | _dirtyEffects = true;
70 |
71 | Audio.mutexUnlock();
72 | }
73 |
74 | public function removeEffect(effect:AudioEffect) {
75 | Audio.mutexLock();
76 |
77 | for (i in 0..._effectsCount) {
78 | if(_effects[i] == effect) { // todo: remove rest from _effectsCount and effect
79 | _effects[i] = _effects[--_effectsCount];
80 | break;
81 | }
82 | }
83 | _dirtyEffects = true;
84 |
85 | Audio.mutexUnlock();
86 | }
87 |
88 | public function removeAllEffects() {
89 | Audio.mutexLock();
90 |
91 | _effectsCount = 0;
92 | _dirtyEffects = true;
93 |
94 | Audio.mutexUnlock();
95 | }
96 |
97 | @:noCompletion public function updateProps() {}
98 | @:noCompletion public function process(data:Float32Array, samples:Int) {}
99 |
100 | inline function processEffects(data:Float32Array, samples:Int) {
101 | Audio.mutexLock();
102 |
103 | if(_dirtyEffects) {
104 | var j:Int = 0;
105 | while (j < _effectsCount) {
106 | _effectsInternal[j] = _effects[j];
107 | j++;
108 | }
109 | _dirtyEffects = false;
110 | }
111 | var count = _effectsCount;
112 |
113 | Audio.mutexUnlock();
114 |
115 | var i:Int = 0;
116 | var e:AudioEffect;
117 | while (i < count) {
118 | e = _effectsInternal[i];
119 | if(!e._mute) {
120 | e.process(samples, data, Clay.audio.sampleRate);
121 | }
122 | i++;
123 | }
124 | }
125 |
126 | function get_mute():Bool {
127 | Audio.mutexLock();
128 | var v = _mute;
129 | Audio.mutexUnlock();
130 |
131 | return v;
132 | }
133 |
134 | function set_mute(v:Bool):Bool {
135 | Audio.mutexLock();
136 | _mute = v;
137 | Audio.mutexUnlock();
138 |
139 | return v;
140 | }
141 |
142 | function get_volume():Float {
143 | Audio.mutexLock();
144 | var v = _volume;
145 | Audio.mutexUnlock();
146 |
147 | return v;
148 | }
149 |
150 | function set_volume(v:Float):Float {
151 | Audio.mutexLock();
152 | _volume = Math.clamp(v, 0, 1);
153 | v = _volume;
154 | Audio.mutexUnlock();
155 |
156 | return v;
157 | }
158 |
159 | function get_pan():Float {
160 | Audio.mutexLock();
161 | var v = _pan;
162 | Audio.mutexUnlock();
163 |
164 | return v;
165 | }
166 |
167 | function set_pan(v:Float):Float {
168 | Audio.mutexLock();
169 |
170 | _pan = Math.clamp(v, -1, 1);
171 | _l = Math.cos((_pan + 1) * Math.PI / 4);
172 | _r = Math.sin((_pan + 1) * Math.PI / 4);
173 | v = _pan;
174 |
175 | Audio.mutexUnlock();
176 |
177 | return v;
178 | }
179 |
180 | function get_output():AudioGroup {
181 | Audio.mutexLock();
182 | var v = _output;
183 | Audio.mutexUnlock();
184 |
185 | return v;
186 | }
187 |
188 | function get_effects():Array {
189 | Audio.mutexLock();
190 | var v = [];
191 | for (i in 0..._effectsCount) {
192 | v.push(_effects[i]);
193 | }
194 | Audio.mutexUnlock();
195 |
196 | return v;
197 | }
198 |
199 | function get_effectsCount():Int {
200 | Audio.mutexLock();
201 | var v = _effectsCount;
202 | Audio.mutexUnlock();
203 |
204 | return v;
205 | }
206 |
207 | }
208 |
209 |
--------------------------------------------------------------------------------
/clay/audio/AudioEffect.hx:
--------------------------------------------------------------------------------
1 | package clay.audio;
2 |
3 |
4 | import clay.utils.Math;
5 | import clay.Audio;
6 |
7 | @:allow(clay.audio.AudioChannel)
8 | class AudioEffect {
9 |
10 | public var mute(get, set):Bool;
11 | public var parent(get, null):AudioChannel;
12 |
13 | var _mute:Bool;
14 | var _parent:AudioChannel;
15 |
16 | public function new() {
17 | _mute = false;
18 | }
19 |
20 | public function process(samples:Int, buffer:kha.arrays.Float32Array, sampleRate:Int) {}
21 |
22 | function get_mute():Bool {
23 | Audio.mutexLock();
24 | var v = _mute;
25 | Audio.mutexUnlock();
26 |
27 | return v;
28 | }
29 |
30 | function set_mute(v:Bool):Bool {
31 | Audio.mutexLock();
32 | _mute = v;
33 | Audio.mutexUnlock();
34 |
35 | return v;
36 | }
37 |
38 | function get_parent():AudioChannel {
39 | Audio.mutexLock();
40 | var v = _parent;
41 | Audio.mutexUnlock();
42 |
43 | return v;
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/clay/audio/AudioGroup.hx:
--------------------------------------------------------------------------------
1 | package clay.audio;
2 |
3 | import clay.utils.Math;
4 | import clay.audio.AudioEffect;
5 | import clay.Audio;
6 | import clay.utils.ArrayTools;
7 | import kha.arrays.Float32Array;
8 | import haxe.ds.Vector;
9 | import clay.utils.Log;
10 |
11 | class AudioGroup extends AudioChannel {
12 |
13 | public var channels(get, null):Array;
14 | public var channelsCount(get, null):Int;
15 |
16 | var _channels:Vector;
17 | var _channelsCount:Int;
18 | var _dirtyChannels:Bool;
19 |
20 | var _maxChannels:Int;
21 | var _cache:Float32Array;
22 |
23 | var _channelsInternal:Vector;
24 |
25 | public function new(maxChannels:Int = 32) {
26 | super();
27 |
28 | _maxChannels = maxChannels;
29 | _channelsCount = 0;
30 | _cache = new Float32Array(512);
31 | _dirtyChannels = true;
32 |
33 | _channels = new Vector(_maxChannels);
34 | _channelsInternal = new Vector(_maxChannels);
35 | }
36 |
37 | public function add(channel:AudioChannel) {
38 | Audio.mutexLock();
39 |
40 | if(channel == this) {
41 | Audio.mutexUnlock();
42 | Log.warning('can`t add channel to itself');
43 | return;
44 | }
45 |
46 | if(channel._output == this) {
47 | Audio.mutexUnlock();
48 | Log.warning('channel already added to group');
49 | return;
50 | }
51 |
52 | if(_channelsCount >= _maxChannels) {
53 | Audio.mutexUnlock();
54 | Log.warning('can`t add channel, max channels: ${_maxChannels}');
55 | return;
56 | }
57 |
58 | if(channel._output != null) {
59 | channel._output.remove(channel);
60 | }
61 |
62 | channel._output = this;
63 | _channels[_channelsCount++] = channel;
64 | _dirtyChannels = true;
65 |
66 | Audio.mutexUnlock();
67 | }
68 |
69 | public function remove(channel:AudioChannel) {
70 | Audio.mutexLock();
71 |
72 | if(channel._output == this) {
73 | channel._output = null;
74 | var found = false;
75 | for (i in 0..._channelsCount) {
76 | if(_channels[i] == channel) { // TODO: remove rest from _channelsInternal and channels
77 | _channelsCount--;
78 | _channels[i] = _channels[_channelsCount];
79 | _channels[_channelsCount] = null;
80 | found = true;
81 | break;
82 | }
83 | }
84 | if(!found) {
85 | Log.warning('can`t remove channel ?');
86 | }
87 | _dirtyChannels = true;
88 | } else {
89 | Log.warning('can`t remove channel, it not belong to this group');
90 | }
91 |
92 | Audio.mutexUnlock();
93 | }
94 |
95 | public function empty() {
96 | Audio.mutexLock();
97 |
98 | var j:Int = 0;
99 | while(j < _maxChannels) {
100 | _channels[j]._output = null;
101 | _channels[j] = null;
102 | j++;
103 | }
104 | _channelsCount = 0;
105 | _dirtyChannels = true;
106 |
107 | Audio.mutexUnlock();
108 | }
109 |
110 | override function process(data:Float32Array, bufferSamples:Int) {
111 | if (_cache.length < bufferSamples) {
112 | Audio.mutexLock();
113 | Log.warning('Allocation request in audio thread. cache: ${_cache.length}, samples: $bufferSamples');
114 | Audio.mutexUnlock();
115 | _cache = new Float32Array(bufferSamples);
116 | }
117 |
118 | var i:Int = 0;
119 | while(i < bufferSamples) {
120 | _cache[i] = 0;
121 | i++;
122 | }
123 |
124 | Audio.mutexLock();
125 |
126 | if(_dirtyChannels) {
127 | var j:Int = 0;
128 | while(j < _channelsCount) {
129 | _channelsInternal[j] = _channels[j];
130 | j++;
131 | }
132 | while(j < _maxChannels && _channelsInternal[j] != null) {
133 | _channelsInternal[j] = null;
134 | j++;
135 | }
136 | _dirtyChannels = false;
137 | }
138 |
139 | var count = _channelsCount;
140 |
141 | Audio.mutexUnlock();
142 |
143 | i = 0;
144 | while(i < count) {
145 | if(!_channelsInternal[i]._mute) {
146 | _channelsInternal[i].process(_cache, bufferSamples);
147 | }
148 | i++;
149 | }
150 |
151 | processEffects(_cache, bufferSamples);
152 |
153 | i = 0;
154 | while(i < bufferSamples) {
155 | data[i] += _cache[i] * _volume * _l;
156 | data[i+1] += _cache[i+1] * _volume * _r;
157 | i +=2;
158 | }
159 | }
160 |
161 | function get_channels():Array {
162 | Audio.mutexLock();
163 | var v = [];
164 | for (i in 0..._channelsCount) {
165 | v.push(_channels[i]);
166 | }
167 | Audio.mutexUnlock();
168 |
169 | return v;
170 | }
171 |
172 | function get_channelsCount():Int {
173 | Audio.mutexLock();
174 | var v = _channelsCount;
175 | Audio.mutexUnlock();
176 |
177 | return v;
178 | }
179 |
180 | }
--------------------------------------------------------------------------------
/clay/audio/dsp/AllPassFilter.hx:
--------------------------------------------------------------------------------
1 | package clay.audio.dsp;
2 |
3 | import clay.Clay;
4 | import clay.utils.Math;
5 |
6 | class AllPassFilter {
7 |
8 | public var feedback:Float;
9 | var _buffer:FloatRingBuffer;
10 |
11 | public function new(length:Int, feedback:Float = 0) {
12 | this.feedback = feedback;
13 | _buffer = new FloatRingBuffer(length);
14 | }
15 |
16 | public function process(input:Float):Float {
17 | var delayed = _buffer.read();
18 | var output = -input + delayed;
19 | _buffer.insert(input + delayed * feedback);
20 |
21 | return output;
22 | }
23 |
24 | }
25 |
26 |
27 |
--------------------------------------------------------------------------------
/clay/audio/dsp/CombFilter.hx:
--------------------------------------------------------------------------------
1 | package clay.audio.dsp;
2 |
3 | import clay.utils.Math;
4 |
5 | class CombFilter {
6 |
7 | public var feedback:Float;
8 | public var damping(default, set):Float;
9 | public var length(get, never):Int;
10 |
11 | var _buffer:FloatRingBuffer;
12 | var _filter:Float;
13 | var _dampingInv:Float;
14 |
15 | public function new(length:Int, feedback:Float = 0, damping:Float = 0) {
16 | _filter = 0;
17 | _dampingInv = 0;
18 | this.feedback = feedback;
19 | this.damping = damping;
20 | _buffer = new FloatRingBuffer(length);
21 | }
22 |
23 | public function process(input:Float):Float {
24 | var output = _buffer.read();
25 | _filter = output * _dampingInv + _filter * damping;
26 | _buffer.insert(input + _filter * feedback);
27 |
28 | return output;
29 | }
30 |
31 | function set_damping(v:Float):Float {
32 | damping = Math.clamp(v, 0, 1);
33 | _dampingInv = 1.0 - damping;
34 |
35 | return damping;
36 | }
37 |
38 | inline function get_length():Int {
39 | return _buffer.length;
40 | }
41 |
42 | }
43 |
44 |
45 |
--------------------------------------------------------------------------------
/clay/audio/dsp/Delay.hx:
--------------------------------------------------------------------------------
1 | package clay.audio.dsp;
2 |
3 | import clay.Clay;
4 | import clay.utils.Math;
5 |
6 | // Simple delay
7 | class Delay {
8 |
9 | public var feedback:Float;
10 | var _buffer:FloatRingBuffer;
11 |
12 | public function new(length:Int, feedback:Float = 0) {
13 | this.feedback = feedback;
14 | _buffer = new FloatRingBuffer(length);
15 | }
16 |
17 | public function process(input:Float):Float {
18 | var output = _buffer.read();
19 | _buffer.insert(input * feedback);
20 |
21 | return output;
22 | }
23 |
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/clay/audio/dsp/FloatRingBuffer.hx:
--------------------------------------------------------------------------------
1 | package clay.audio.dsp;
2 |
3 | import kha.arrays.Float32Array;
4 |
5 | class FloatRingBuffer {
6 |
7 | public var length(get, never):Int;
8 |
9 | var _buffer:Float32Array;
10 | var _pos:Int;
11 |
12 | public function new(length:Int) {
13 | _pos = 0;
14 | _buffer = new Float32Array(length);
15 |
16 | for (i in 0...length) {
17 | _buffer[i] = 0; // cpp target not 0 ?
18 | }
19 | }
20 |
21 | public inline function insert(v:Float) {
22 | _buffer[_pos] = v;
23 | shift(1);
24 | }
25 |
26 | public inline function get(i:Int):Float {
27 | return _buffer[modLength(getIndex(i))];
28 | }
29 |
30 | public inline function set(i:Int, v:Float) {
31 | _buffer[modLength(getIndex(i))] = v;
32 | }
33 |
34 | public inline function read():Float {
35 | return _buffer[_pos];
36 | }
37 |
38 | public inline function shift(n:Int) {
39 | _pos = modLength(_pos+n);
40 | }
41 |
42 | public function clear() {
43 | for (i in 0..._buffer.length) {
44 | _buffer[i] = 0;
45 | }
46 |
47 | _pos = 0;
48 | }
49 |
50 | inline function modLength(i:Int):Int {
51 | return clay.utils.Math.mod(i, _buffer.length);
52 | }
53 |
54 | inline function getIndex(i:Int):Int {
55 | return _pos + i;
56 | }
57 |
58 | inline function get_length():Int {
59 | return _buffer.length;
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/clay/audio/dsp/HighPassFilter.hx:
--------------------------------------------------------------------------------
1 | package clay.audio.dsp;
2 |
3 | import clay.Clay;
4 | import clay.utils.Math;
5 |
6 | // First Order Digital HighPass Filter from FreeVerb3
7 | class HighPassFilter {
8 |
9 | public var freq(default, set):Float;
10 |
11 | var y1:Float;
12 | var a2:Float;
13 | var b1:Float;
14 | var b2:Float;
15 |
16 | var sampleRate:Int;
17 |
18 | public function new(freq:Float, sampleRate:Int) {
19 | y1 = 0;
20 | a2 = 0;
21 | b1 = 0;
22 | b2 = 0;
23 |
24 | this.sampleRate = sampleRate;
25 | this.freq = freq;
26 | }
27 |
28 | public function process(input:Float):Float {
29 | var output = input * b1 + y1;
30 | y1 = output * a2 + input * b2;
31 |
32 | return output;
33 | }
34 |
35 | function set_freq(v:Float):Float {
36 | freq = Math.max(v, 0);
37 |
38 | a2 = Math.exp(-1 * Math.PI * freq / (sampleRate / 2));
39 |
40 | b1 = 1.0;
41 | b2 = -1;
42 |
43 | var norm = (1 + a2) / 2;
44 |
45 | b1 *= norm;
46 | b2 *= norm;
47 |
48 | return freq;
49 | }
50 |
51 | }
52 |
53 |
54 |
--------------------------------------------------------------------------------
/clay/audio/dsp/LowPassFilter.hx:
--------------------------------------------------------------------------------
1 | package clay.audio.dsp;
2 |
3 | import clay.Clay;
4 | import clay.utils.Math;
5 |
6 | // First Order Digital LowPass Filter from FreeVerb3
7 | class LowPassFilter {
8 |
9 | public var freq(default, set):Float;
10 |
11 | var y1:Float;
12 | var a2:Float;
13 | var b1:Float;
14 | var b2:Float;
15 |
16 | var sampleRate:Int;
17 |
18 | public function new(freq:Float, sampleRate:Int) {
19 | y1 = 0;
20 | a2 = 0;
21 | b1 = 0;
22 | b2 = 0;
23 |
24 | this.sampleRate = sampleRate;
25 | this.freq = freq;
26 | }
27 |
28 | public function process(input:Float):Float {
29 | var output = input * b1 + y1;
30 | y1 = output * a2 + input * b2;
31 |
32 | return output;
33 | }
34 |
35 | function set_freq(v:Float):Float {
36 | freq = Math.max(v, 0);
37 |
38 | a2 = Math.exp(-1 * Math.PI * freq / (sampleRate / 2));
39 |
40 | b1 = 1.0;
41 | b2 = 0.1;
42 |
43 | var norm = (1 - a2) / (b1 + b2);
44 |
45 | b1 *= norm;
46 | b2 *= norm;
47 |
48 | return freq;
49 | }
50 |
51 | }
52 |
53 |
54 |
--------------------------------------------------------------------------------
/clay/audio/effects/Distortion.hx:
--------------------------------------------------------------------------------
1 | package clay.audio.effects;
2 |
3 | import clay.Audio;
4 |
5 | class Distortion extends AudioEffect {
6 |
7 | public var gain(get, set):Float;
8 | var _gain:Float;
9 |
10 | public function new(gain:Float = 1) {
11 | super();
12 | _gain = gain;
13 | }
14 |
15 | override function process(samples:Int, buffer:kha.arrays.Float32Array, sampleRate:Int) {
16 | var x:Float = 0;
17 |
18 | for (i in 0...samples) {
19 | x = buffer[i] * gain;
20 | if(x > 0) {
21 | buffer[i] = 1 - Math.exp(-x);
22 | } else {
23 | buffer[i] = -1 + Math.exp(x);
24 | }
25 | }
26 | }
27 |
28 | function get_gain():Float {
29 | Audio.mutexLock();
30 | var v = _gain;
31 | Audio.mutexUnlock();
32 |
33 | return v;
34 | }
35 |
36 | function set_gain(v:Float):Float {
37 | Audio.mutexLock();
38 | _gain = v;
39 | Audio.mutexUnlock();
40 |
41 | return v;
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/clay/audio/effects/Filter.hx:
--------------------------------------------------------------------------------
1 | package clay.audio.effects;
2 |
3 | import clay.utils.Math;
4 | import clay.audio.Sound;
5 | import clay.Audio;
6 |
7 | class Filter extends AudioEffect {
8 |
9 | public var cutoff(default, set):Float;
10 | public var resonance(default, set):Float;
11 | public var filterType:FilterType;
12 |
13 | var freq:Float;
14 | var damp:Float;
15 |
16 | var sampleRate:Float;
17 |
18 | var f:Array;
19 |
20 | public function new(type:FilterType, cutoff:Float, resonance:Float, sampleRate:Int = 44100) {
21 | filterType = type;
22 |
23 | f = [];
24 |
25 | f[0] = 0;
26 | f[1] = 0;
27 | f[2] = 0;
28 | f[3] = 0;
29 |
30 | freq = 0;
31 | damp = 0;
32 |
33 | this.sampleRate = sampleRate;
34 |
35 | this.cutoff = cutoff;
36 | this.resonance = resonance;
37 |
38 | calcCoef();
39 | }
40 |
41 | override function process(samples:Int, buffer:kha.arrays.Float32Array, sampleRate:Int) {
42 | var input:Float = 0;
43 | var output:Float = 0;
44 | for (i in 0...samples) {
45 | input = buffer[i];
46 | // first pass
47 | f[3] = input - damp * f[2];
48 | f[0] = f[0] + freq * f[2];
49 | f[1] = f[3] - f[0];
50 | f[2] = freq * f[1] + f[2];
51 | output = 0.5 * f[filterType];
52 |
53 | // second pass
54 | f[3] = input - damp * f[2];
55 | f[0] = f[0] + freq * f[2];
56 | f[1] = f[3] - f[0];
57 | f[2] = freq * f[1] + f[2];
58 | output += 0.5 * f[filterType];
59 | buffer[i] = output;
60 | }
61 | }
62 |
63 | function calcCoef() {
64 | freq = 2 * Math.sin(Math.PI * Math.min(0.25, cutoff/(sampleRate*2)));
65 | damp = Math.min(2 * (1 - Math.pow(resonance, 0.25)), Math.min(2, 2/freq - freq*0.5));
66 | }
67 |
68 | function set_cutoff(v:Float):Float {
69 | Audio.mutexLock();
70 | cutoff = v;
71 | calcCoef();
72 | Audio.mutexUnlock();
73 |
74 | return cutoff;
75 | }
76 |
77 | function set_resonance(v:Float):Float {
78 | Audio.mutexLock();
79 | resonance = Math.clamp(v, 0, 1);
80 | calcCoef();
81 | Audio.mutexUnlock();
82 |
83 | return resonance;
84 | }
85 |
86 | }
87 |
88 | enum abstract FilterType(Int){
89 | var LOWPASS;
90 | var HIGHPASS;
91 | var BANDPASS;
92 | var NOTCH;
93 | }
--------------------------------------------------------------------------------
/clay/audio/effects/MultiDelay.hx:
--------------------------------------------------------------------------------
1 | package clay.audio.effects;
2 |
3 | import clay.audio.Sound;
4 |
5 | // https://github.com/corbanbrook/dsp.js
6 |
7 | class MultiDelay extends AudioEffect {
8 |
9 | public var delaySamples(default, set):Int;
10 |
11 | var delayBufferSamples:kha.arrays.Float32Array;
12 |
13 | var delayInputPointer:Int;
14 | var delayOutputPointer:Int;
15 | var delayVolume:Float;
16 | var masterVolume:Float;
17 |
18 | var sampleRate:Float;
19 |
20 | public function new(delaySamples:Int, masterVolume:Float, delayVolume:Float) {
21 | delayBufferSamples = new kha.arrays.Float32Array(delaySamples); // The maximum size of delay
22 | delayInputPointer = delaySamples;
23 | delayOutputPointer = 0;
24 |
25 | this.delaySamples = delaySamples;
26 | this.delayVolume = delayVolume;
27 | this.masterVolume = masterVolume;
28 |
29 | sampleRate = Clay.audio.sampleRate;
30 | }
31 |
32 | override function process(samples:Int, buffer:kha.arrays.Float32Array, sampleRate:Int) {
33 | for (i in 0...Std.int(samples/2)) {
34 | buffer[i*2] = getDelayed(buffer[i*2]) * masterVolume;
35 | buffer[i*2+1] = getDelayed(buffer[i*2+1]) * masterVolume;
36 | }
37 | }
38 |
39 | function getDelayed(input:Float) {
40 | var delaySample = delayBufferSamples.get(delayOutputPointer);
41 |
42 | // Mix normal audio data with delayed audio
43 | var sample = (delaySample * delayVolume) + input;
44 |
45 | // Add audio data with the delay in the delay buffer
46 | delayBufferSamples.set(delayInputPointer, sample);
47 |
48 | // Manage circulair delay buffer pointers
49 | delayInputPointer++;
50 |
51 | if (delayInputPointer >= delayBufferSamples.length-1) {
52 | delayInputPointer = 0;
53 | }
54 |
55 | delayOutputPointer++;
56 |
57 | if (delayOutputPointer >= delayBufferSamples.length-1) {
58 | delayOutputPointer = 0;
59 | }
60 |
61 | return sample;
62 | }
63 |
64 | function set_delaySamples(v:Int):Int {
65 | delaySamples = v;
66 | delayInputPointer = delayOutputPointer + delaySamples;
67 |
68 | if (delayInputPointer >= delayBufferSamples.length-1) {
69 | delayInputPointer = delayInputPointer - delayBufferSamples.length;
70 | }
71 |
72 | return delaySamples;
73 | }
74 |
75 |
76 | }
--------------------------------------------------------------------------------
/clay/events/AppEvent.hx:
--------------------------------------------------------------------------------
1 | package clay.events;
2 |
3 | import clay.utils.EventType;
4 |
5 | @:allow(clay.App)
6 | class AppEvent implements IEvent {
7 |
8 | static public inline var TICKSTART:EventType;
9 | static public inline var TICKEND:EventType;
10 |
11 | static public inline var UPDATE:EventType;
12 | static public inline var FIXEDUPDATE:EventType;
13 |
14 | static public inline var TIMESCALE:EventType;
15 |
16 | static public inline var FOREGROUND:EventType;
17 | static public inline var BACKGROUND:EventType;
18 | static public inline var PAUSE:EventType;
19 | static public inline var RESUME:EventType;
20 |
21 | public function new() {}
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/clay/events/GamepadEvent.hx:
--------------------------------------------------------------------------------
1 | package clay.events;
2 |
3 | import clay.utils.EventType;
4 |
5 | @:allow(clay.input.Gamepad)
6 | class GamepadEvent implements IEvent {
7 |
8 | static public inline var BUTTON_UP:EventType;
9 | static public inline var BUTTON_DOWN:EventType;
10 | static public inline var AXIS:EventType;
11 | static public inline var DEVICE_ADDED:EventType;
12 | static public inline var DEVICE_REMOVED:EventType;
13 |
14 | public var id(default, null):String;
15 | public var gamepad(default, null):Int;
16 |
17 | public var button(default, null):Int;
18 | public var axis(default, null):Int;
19 | public var value(default, null):Float;
20 |
21 | public var state(default, null):EventType;
22 |
23 | public function new() {}
24 |
25 | inline function set(gamepad:Int, id:String, button:Int, axisID:Int, value:Float, state:EventType) {
26 | this.id = id;
27 | this.gamepad = gamepad;
28 | this.button = button;
29 | this.axis = axisID;
30 | this.value = value;
31 | this.state = state;
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/clay/events/IEvent.hx:
--------------------------------------------------------------------------------
1 | package clay.events;
2 |
3 | #if !macro
4 | @:autoBuild(clay.utils.macro.EventMacro.build())
5 | #end
6 |
7 | interface IEvent {}
8 |
--------------------------------------------------------------------------------
/clay/events/InputEvent.hx:
--------------------------------------------------------------------------------
1 | package clay.events;
2 |
3 | import clay.utils.EventType;
4 |
5 | @:allow(clay.input.Bindings)
6 | class InputEvent implements IEvent {
7 |
8 | static public inline var INPUT_UP:EventType;
9 | static public inline var INPUT_DOWN:EventType;
10 |
11 | public var name(default, null):String;
12 | public var type(default, null):InputType;
13 |
14 | public var mouse(default, null):MouseEvent;
15 | public var keyboard(default, null):KeyEvent;
16 | public var gamepad(default, null):GamepadEvent;
17 | public var touch(default, null):TouchEvent;
18 | public var pen(default, null):PenEvent;
19 |
20 | function new() {
21 | name = "";
22 | type = InputType.NONE;
23 | }
24 |
25 | inline function set(mouse:MouseEvent, keyboard:KeyEvent, gamepad:GamepadEvent, touch:TouchEvent, pen:PenEvent) {
26 | this.mouse = mouse;
27 | this.keyboard = keyboard;
28 | this.gamepad = gamepad;
29 | this.touch = touch;
30 | this.pen = pen;
31 | }
32 |
33 | @:noCompletion public function setMouse(name:String, mouse:MouseEvent) {
34 | this.name = name;
35 | type = InputType.MOUSE;
36 | set(mouse, null, null, null, null);
37 | }
38 |
39 | @:noCompletion public function setKey(name:String, keyboard:KeyEvent) {
40 | this.name = name;
41 | type = InputType.KEYBOARD;
42 | set(null, keyboard, null, null, null);
43 | }
44 |
45 | @:noCompletion public function setGamepad(name:String, gamepad:GamepadEvent) {
46 | this.name = name;
47 | type = InputType.GAMEPAD;
48 | set(null, null, gamepad, null, null);
49 | }
50 |
51 | @:noCompletion public function setTouch(name:String, touch:TouchEvent) {
52 | this.name = name;
53 | type = InputType.TOUCH;
54 | set(null, null, null, touch, null);
55 | }
56 |
57 | @:noCompletion public function setPen(name:String, pen:PenEvent) {
58 | this.name = name;
59 | type = InputType.PEN;
60 | set(null, null, null, null, pen);
61 | }
62 |
63 | }
64 |
65 | enum abstract InputType(Int){
66 | var NONE;
67 | var MOUSE;
68 | var KEYBOARD;
69 | var GAMEPAD;
70 | var TOUCH;
71 | var PEN;
72 | }
73 |
74 |
--------------------------------------------------------------------------------
/clay/events/KeyEvent.hx:
--------------------------------------------------------------------------------
1 | package clay.events;
2 |
3 | import clay.utils.EventType;
4 |
5 | @:allow(clay.input.Keyboard)
6 | class KeyEvent implements IEvent {
7 |
8 | static public inline var KEY_UP:EventType;
9 | static public inline var KEY_DOWN:EventType;
10 | static public inline var TEXT_INPUT:EventType;
11 |
12 | public var key(default, null):Int;
13 | public var state(default, null):EventType = KeyEvent.KEY_UP;
14 |
15 | public function new() {}
16 |
17 | inline function set(key:Int, state:EventType) {
18 | this.key = key;
19 | this.state = state;
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/clay/events/MouseEvent.hx:
--------------------------------------------------------------------------------
1 | package clay.events;
2 |
3 | import clay.utils.EventType;
4 |
5 | @:allow(clay.input.Mouse)
6 | class MouseEvent implements IEvent {
7 |
8 | static public inline var MOUSE_UP:EventType;
9 | static public inline var MOUSE_DOWN:EventType;
10 | static public inline var MOUSE_MOVE:EventType;
11 | static public inline var MOUSE_WHEEL:EventType;
12 |
13 | public var x(default, null):Int = 0;
14 | public var y(default, null):Int = 0;
15 | public var dx(default, null):Int = 0;
16 | public var dy(default, null):Int = 0;
17 | public var wheel(default, null):Int = 0;
18 |
19 | public var button(default, null):MouseButton = MouseButton.NONE;
20 | public var state(default, null):EventType = MouseEvent.MOUSE_UP;
21 |
22 | public function new() {}
23 |
24 | public inline function set(x:Int, y:Int, dx:Int, dy:Int, wheel:Int, state:EventType, button:MouseButton) {
25 | this.x = x;
26 | this.y = y;
27 | this.dx = dx;
28 | this.dy = dy;
29 | this.wheel = wheel;
30 | this.state = state;
31 | this.button = button;
32 | }
33 |
34 | }
35 |
36 | enum abstract MouseButton(Int) from Int to Int {
37 | var NONE = -1;
38 | var LEFT = 0;
39 | var RIGHT = 1;
40 | var MIDDLE = 2;
41 | var EXTRA1 = 3;
42 | var EXTRA2 = 4;
43 | var EXTRA3 = 5;
44 | var EXTRA4 = 6;
45 | }
46 |
--------------------------------------------------------------------------------
/clay/events/PenEvent.hx:
--------------------------------------------------------------------------------
1 | package clay.events;
2 |
3 | import clay.utils.EventType;
4 |
5 | @:allow(clay.input.Pen)
6 | class PenEvent implements IEvent{
7 |
8 | static public inline var PEN_UP:EventType;
9 | static public inline var PEN_DOWN:EventType;
10 | static public inline var PEN_MOVE:EventType;
11 |
12 | public var x(default, null):Int = 0;
13 | public var y(default, null):Int = 0;
14 | public var dx(default, null):Int = 0;
15 | public var dy(default, null):Int = 0;
16 |
17 | public var pressure(default, null):Float = 0;
18 | public var state(default, null):EventType = PenEvent.PEN_UP;
19 |
20 | public function new() {}
21 |
22 | inline function set(x:Int, y:Int, dx:Int, dy:Int, state:EventType, pressure:Float) {
23 | this.x = x;
24 | this.y = y;
25 | this.dx = dx;
26 | this.dy = dy;
27 | this.state = state;
28 | this.pressure = pressure;
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/clay/events/RenderEvent.hx:
--------------------------------------------------------------------------------
1 | package clay.events;
2 |
3 | import clay.Graphics;
4 | import clay.utils.EventType;
5 | import kha.Framebuffer;
6 |
7 | @:allow(clay.App)
8 | class RenderEvent implements IEvent {
9 |
10 | static public inline var PRERENDER:EventType;
11 | static public inline var POSTRENDER:EventType;
12 | static public inline var RENDER:EventType;
13 |
14 | public var g(default, null):Graphics;
15 | public var g2(default, null):kha.graphics2.Graphics;
16 | public var g4(default, null):kha.graphics4.Graphics;
17 |
18 | public function new() {}
19 |
20 | inline function set(g:Graphics, g2:kha.graphics2.Graphics, g4:kha.graphics4.Graphics) {
21 | this.g = g;
22 | this.g2 = g2;
23 | this.g4 = g4;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/clay/events/TouchEvent.hx:
--------------------------------------------------------------------------------
1 | package clay.events;
2 |
3 | import clay.utils.EventType;
4 |
5 | @:allow(clay.input.Touch)
6 | class TouchEvent implements IEvent{
7 |
8 | static public inline var TOUCH_UP:EventType;
9 | static public inline var TOUCH_DOWN:EventType;
10 | static public inline var TOUCH_MOVE:EventType;
11 |
12 | public var id(default, null):Int = 0;
13 |
14 | public var x(default, null):Int = 0;
15 | public var y(default, null):Int = 0;
16 | public var dx(default, null):Int = 0;
17 | public var dy(default, null):Int = 0;
18 |
19 | public var state(default, null):EventType = TouchEvent.TOUCH_UP;
20 |
21 | public function new(id:Int) {
22 | this.id = id;
23 | }
24 |
25 | inline function set(x:Int, y:Int, dx:Int, dy:Int, state:EventType) {
26 | this.x = x;
27 | this.y = y;
28 | this.dx = dx;
29 | this.dy = dy;
30 | this.state = state;
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/clay/graphics/Camera.hx:
--------------------------------------------------------------------------------
1 | package clay.graphics;
2 |
3 | import clay.math.Vector2;
4 | import clay.math.FastMatrix3;
5 | import clay.utils.Math;
6 | import clay.graphics.Texture;
7 |
8 | class Camera {
9 |
10 | public var x:Float = 0;
11 | public var y:Float = 0;
12 |
13 | public var width:Float = 0;
14 | public var height:Float = 0;
15 |
16 | public var rotation:Float = 0;
17 |
18 | public var originX:Float = 0;
19 | public var originY:Float = 0;
20 |
21 | public var offsetX:Float = 0;
22 | public var offsetY:Float = 0;
23 |
24 | public var scaleX:Float = 1;
25 | public var scaleY:Float = 1;
26 |
27 | public var zoom:Float = 1;
28 |
29 | public var projection:FastMatrix3 = new FastMatrix3();
30 | public var view:FastMatrix3 = new FastMatrix3();
31 | public var invProjectionView:FastMatrix3 = new FastMatrix3();
32 |
33 | public function new(x:Float = 0, y:Float = 0, width:Float = 0, height:Float = 0) {
34 | this.x = x;
35 | this.y = y;
36 | this.width = width > 0 ? width : Clay.window.width;
37 | this.height = height > 0 ? height : Clay.window.height;
38 | update();
39 | }
40 |
41 | public function update() {
42 | if (Texture.renderTargetsInvertedY) {
43 | projection.orto(0, width, 0, height);
44 | } else {
45 | projection.orto(0, width, height, 0);
46 | }
47 |
48 | view.setTransform(x+offsetX, y+offsetY, Math.radians(rotation), scaleX*zoom, scaleY*zoom, originX, originY, 0, 0);
49 |
50 | invProjectionView.copyFrom(view);
51 | invProjectionView.invert();
52 |
53 | projection.append(invProjectionView);
54 | }
55 |
56 | public function screenToWorld(v:Vector2, ?into:Vector2):Vector2 {
57 | if(into == null) into = new Vector2();
58 | into.set(view.getTransformX(v.x, v.y), view.getTransformY(v.x, v.y));
59 | return into;
60 | }
61 |
62 | public function worldToScreen(v:Vector2, ?into:Vector2):Vector2 {
63 | if(into == null) into = new Vector2();
64 | into.set(invProjectionView.getTransformX(v.x, v.y), invProjectionView.getTransformY(v.x, v.y));
65 | return into;
66 | }
67 |
68 | public function pointInside(px:Float, py:Float) {
69 | // convert world to screen coords
70 | var tmp:Float = px;
71 | px = view.getTransformX(px, py);
72 | py = view.getTransformY(tmp, py);
73 |
74 | if(px < 0 || px > width) return false;
75 | if(py < 0 || py > height) return false;
76 |
77 | return true;
78 | }
79 |
80 | public function rectangleInside(rx:Float, ry:Float, rw:Float, rh:Float) {
81 | // convert world to screen coords
82 | var minX = view.getTransformX(rx, ry);
83 | var minY = view.getTransformY(rx, ry);
84 |
85 | var maxX = view.getTransformX(rx+rw, ry+rh);
86 | var maxY = view.getTransformY(rx+rw, ry+rh);
87 |
88 | if (x + width < minX || x > maxX) return false;
89 | if (y + height < minY || y > maxY) return false;
90 |
91 | return true;
92 | }
93 |
94 | public function circleInside(cx:Float, cy:Float, cr:Float) {
95 | // convert world to screen coords
96 | cr = view.getTransformY(0, cr); // TODO: probably wrong
97 | var tmp:Float = cx;
98 | cx = view.getTransformX(cx, cy);
99 | cy = view.getTransformY(tmp, cy);
100 |
101 | var cpX:Float = cx;
102 | var cpY:Float = cy;
103 |
104 | if (cpX < x) cpX = x;
105 | if (cpX > x + width) cpX = x + width;
106 | if (cpY < y) cpY = y;
107 | if (cpY > y + height) cpY = y + height;
108 |
109 | var diffX:Float = cx - cpX;
110 | var diffY:Float = cy - cpY;
111 |
112 | return (diffX * diffX + diffY * diffY < cr * cr);
113 | }
114 |
115 |
116 | }
--------------------------------------------------------------------------------
/clay/graphics/Color.hx:
--------------------------------------------------------------------------------
1 | package clay.graphics;
2 |
3 | import clay.utils.Math;
4 |
5 | abstract Color(Int) from kha.Color to kha.Color {
6 |
7 | static public function random(?includeAlpha:Bool = false):Color {
8 | return new Color(Math.random(), Math.random(), Math.random(), includeAlpha ? Math.random() : 1.0 );
9 | }
10 |
11 | static public function fromValue(value:Int):Color {
12 | var c = new Color();
13 | c.value = value;
14 | return c;
15 | }
16 |
17 | static public inline var TRANSPARENT:Color = 0x00000000;
18 | static public inline var BLACK:Color = 0xff000000;
19 | static public inline var WHITE:Color = 0xffffffff;
20 | static public inline var RED:Color = 0xffff0000;
21 | static public inline var BLUE:Color = 0xff0000ff;
22 | static public inline var GREEN:Color = 0xff00ff00;
23 | static public inline var MAGENTA:Color = 0xffff00ff;
24 | static public inline var YELLOW:Color = 0xffffff00;
25 | static public inline var CYAN:Color = 0xff00ffff;
26 | static public inline var PURPLE:Color = 0xff800080;
27 | static public inline var PINK:Color = 0xffffc0cb;
28 | static public inline var ORANGE:Color = 0xffffa500;
29 |
30 | static inline var invMaxChannelValue: Float = 1 / 255;
31 |
32 | public var r(get, set):Float;
33 | public var g(get, set):Float;
34 | public var b(get, set):Float;
35 | public var a(get, set):Float;
36 |
37 | public var rB(get, set):Int;
38 | public var gB(get, set):Int;
39 | public var bB(get, set):Int;
40 | public var aB(get, set):Int;
41 |
42 | public var value(get, set):Int;
43 |
44 | public inline function new(r:Float = 1, g:Float = 1, b:Float = 1, a:Float = 1) {
45 | this = (Std.int(a * 255) << 24) | (Std.int(r * 255) << 16) | (Std.int(g * 255) << 8) | Std.int(b * 255);
46 | }
47 |
48 | public inline function set(r:Float, g:Float, b:Float, a:Float):Color {
49 | this = (Std.int(a * 255) << 24) | (Std.int(r * 255) << 16) | (Std.int(g * 255) << 8) | Std.int(b * 255);
50 | return this;
51 | }
52 |
53 | public inline function setBytes(rB:Int, gB:Int, bB:Int, aB:Int):Color {
54 | this = (aB << 24) | (rB << 16) | (gB << 8) | bB;
55 | return this;
56 | }
57 |
58 | public inline function lerp(to:Color, t:Float):Color {
59 | t = Math.clamp(t, 0, 1);
60 | return set(
61 | r + t * (to.r - r),
62 | g + t * (to.g - g),
63 | b + t * (to.b - b),
64 | a + t * (to.a - a)
65 | );
66 | }
67 |
68 | public inline function setHSB(hue:Float, saturation:Float, brightness:Float):Color {
69 | var chroma = brightness * saturation;
70 | var match = brightness - chroma;
71 | return setHCM(hue, chroma, match);
72 | }
73 |
74 | public inline function setHSL(hue:Float, saturation:Float, lightness:Float):Color {
75 | var chroma = (1 - Math.abs(2 * lightness - 1)) * saturation;
76 | var match = lightness - chroma / 2;
77 | return setHCM(hue, chroma, match);
78 | }
79 |
80 | inline function setHCM(hue:Float, chroma:Float, match:Float):Color {
81 | hue %= 360;
82 | var hueD = hue / 60;
83 | var mid = chroma * (1 - Math.abs(hueD % 2 - 1)) + match;
84 | chroma += match;
85 |
86 | switch (Std.int(hueD)) {
87 | case 0: setRGB(chroma, mid, match);
88 | case 1: setRGB(mid, chroma, match);
89 | case 2: setRGB(match, chroma, mid);
90 | case 3: setRGB(match, mid, chroma);
91 | case 4: setRGB(mid, match, chroma);
92 | case 5: setRGB(chroma, match, mid);
93 | }
94 |
95 | return this;
96 | }
97 |
98 | inline function setRGB(r:Float, g:Float, b:Float) {
99 | this = (get_aB() << 24) | (Std.int(r * 255) << 16) | (Std.int(g * 255) << 8) | Std.int(b * 255);
100 | }
101 |
102 | inline function get_r():Float {
103 | return get_rB() * invMaxChannelValue;
104 | }
105 |
106 | inline function get_g():Float {
107 | return get_gB() * invMaxChannelValue;
108 | }
109 |
110 | inline function get_b():Float {
111 | return get_bB() * invMaxChannelValue;
112 | }
113 |
114 | inline function get_a():Float {
115 | return get_aB() * invMaxChannelValue;
116 | }
117 |
118 | inline function set_r(f:Float):Float {
119 | this = (Std.int(a * 255) << 24) | (Std.int(f * 255) << 16) | (Std.int(g * 255) << 8) | Std.int(b * 255);
120 | return f;
121 | }
122 |
123 | inline function set_g(f:Float):Float {
124 | this = (Std.int(a * 255) << 24) | (Std.int(r * 255) << 16) | (Std.int(f * 255) << 8) | Std.int(b * 255);
125 | return f;
126 | }
127 |
128 | inline function set_b(f:Float):Float {
129 | this = (Std.int(a * 255) << 24) | (Std.int(r * 255) << 16) | (Std.int(g * 255) << 8) | Std.int(f * 255);
130 | return f;
131 | }
132 |
133 | inline function set_a(f:Float):Float {
134 | this = (Std.int(f * 255) << 24) | (Std.int(r * 255) << 16) | (Std.int(g * 255) << 8) | Std.int(b * 255);
135 | return f;
136 | }
137 |
138 | inline function get_rB():Int {
139 | return (this & 0x00ff0000) >>> 16;
140 | }
141 |
142 | inline function get_gB():Int {
143 | return (this & 0x0000ff00) >>> 8;
144 | }
145 |
146 | inline function get_bB():Int {
147 | return this & 0x000000ff;
148 | }
149 |
150 | inline function get_aB():Int {
151 | return this >>> 24;
152 | }
153 |
154 | inline function set_rB(i:Int):Int {
155 | this = (aB << 24) | (i << 16) | (gB << 8) | bB;
156 | return i;
157 | }
158 |
159 | inline function set_gB(i:Int):Int {
160 | this = (aB << 24) | (rB << 16) | (i << 8) | bB;
161 | return i;
162 | }
163 |
164 | inline function set_bB(i:Int):Int {
165 | this = (aB << 24) | (rB << 16) | (gB << 8) | i;
166 | return i;
167 | }
168 |
169 | inline function set_aB(i:Int):Int {
170 | this = (i << 24) | (rB << 16) | (gB << 8) | bB;
171 | return i;
172 | }
173 |
174 | inline function get_value():Int {
175 | return this;
176 | }
177 |
178 | inline function set_value(value:Int):Int {
179 | this = value;
180 | return this;
181 | }
182 |
183 | }
--------------------------------------------------------------------------------
/clay/graphics/Font.hx:
--------------------------------------------------------------------------------
1 | package clay.graphics;
2 |
3 | import kha.Kravur;
4 | import kha.Kravur.KravurImage;
5 | import clay.resources.Resource;
6 | import clay.Resources;
7 | using StringTools;
8 |
9 | @:access(kha.Kravur)
10 | @:access(kha.KravurImage)
11 | class Font extends Resource {
12 |
13 | public var font:kha.Font;
14 | public var textures(default, null):Map;
15 |
16 | public function new(font:kha.Font) {
17 | this.font = font;
18 | textures = new Map();
19 |
20 | resourceType = ResourceType.FONT;
21 | }
22 |
23 | override function unload() {
24 | font.unload();
25 | font = null;
26 | textures = null;
27 | }
28 |
29 | override function memoryUse() {
30 | return font.blob.length;
31 | }
32 |
33 | public function getTexture(fontSize:Int):Texture {
34 | var t = textures.get(fontSize);
35 |
36 | if(t == null) {
37 | var k = font._get(fontSize);
38 | t = new Texture(k.getTexture());
39 | t.name = name + "_" + fontSize;
40 | textures.set(fontSize, t);
41 | }
42 |
43 | return t;
44 | }
45 |
46 | public function height(fontSize:Int):Float {
47 | return font._get(fontSize).getHeight();
48 | }
49 |
50 | public function width(fontSize:Int, str:String):Float {
51 | return font._get(fontSize).stringWidth(str);
52 | }
53 |
54 | public function charWidth(fontSize:Int, charCode:Int):Float {
55 | return font._get(fontSize).getCharWidth(charCode);
56 | }
57 |
58 | public function charactersWidth(fontSize:Int, characters:Array, start:Int, length:Int):Float {
59 | return font._get(fontSize).charactersWidth(characters, start, length);
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/clay/graphics/Polygon.hx:
--------------------------------------------------------------------------------
1 | package clay.graphics;
2 |
3 | import clay.graphics.Texture;
4 | import clay.graphics.Vertex;
5 |
6 | class Polygon {
7 |
8 | public var texture(get, set):Texture;
9 | var _texture:Texture;
10 | inline function get_texture() return _texture;
11 | function set_texture(value:Texture) return _texture = value;
12 |
13 | public var vertices:Array;
14 | public var indices:Array;
15 |
16 | public function new(texture:Texture, vertices:Array, indices:Array) {
17 | _texture = texture;
18 | this.vertices = vertices;
19 | this.indices = indices;
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/clay/graphics/Texture.hx:
--------------------------------------------------------------------------------
1 | package clay.graphics;
2 |
3 | import haxe.io.Bytes;
4 | import clay.resources.Resource;
5 | import clay.Resources;
6 | import clay.utils.IdGenerator;
7 | import clay.utils.Log;
8 |
9 | typedef TextureFormat = kha.graphics4.TextureFormat;
10 | typedef DepthStencilFormat = kha.graphics4.DepthStencilFormat;
11 |
12 | typedef TextureFilter = kha.graphics4.TextureFilter;
13 | typedef MipMapFilter = kha.graphics4.MipMapFilter;
14 | typedef TextureAddressing = kha.graphics4.TextureAddressing;
15 |
16 | class Texture extends Resource {
17 |
18 | static public var maxTextures:Int = 4096; // TODO: get from sortkey
19 | static var ids:IdGenerator = new IdGenerator();
20 |
21 | static function getId():Int {
22 | var id = ids.get();
23 | Log.assert(id < maxTextures, 'Texture: Cant create more than ${maxTextures} textures');
24 | return id;
25 | }
26 | static inline function putId(id:Int) ids.put(id);
27 |
28 | static public var maxSize(get, never):Int;
29 | static inline function get_maxSize() return kha.Image.maxSize;
30 |
31 | static public var renderTargetsInvertedY(get, never):Bool;
32 | static inline function get_renderTargetsInvertedY() return kha.Image.renderTargetsInvertedY();
33 |
34 | static public var nonPow2Supported(get, never):Bool;
35 | static inline function get_nonPow2Supported() return kha.Image.nonPow2Supported;
36 |
37 | static public function create(width:Int, height:Int, ?format:TextureFormat) {
38 | var img = kha.Image.create(width, height, format);
39 | return new Texture(img);
40 | }
41 |
42 | static public function createFromBytes(bytes:Bytes, width:Int, height:Int, ?format:TextureFormat) {
43 | var img = kha.Image.fromBytes(bytes, width, height, format);
44 | return new Texture(img);
45 | }
46 |
47 | static public function createRenderTarget(width:Int, height:Int, ?format:TextureFormat, ?depthStencil:DepthStencilFormat, ?antialiasing:Int, ?contextID:Int) {
48 | var img = kha.Image.createRenderTarget(width, height, format, depthStencil, antialiasing, contextID);
49 | var t = new Texture(img, true);
50 | t.resourceType = ResourceType.RENDERTEXTURE;
51 | return t;
52 | }
53 |
54 | public var widthActual(get, never):Int;
55 | inline function get_widthActual() return image.realWidth;
56 |
57 | public var heightActual(get, never):Int;
58 | inline function get_heightActual() return image.realHeight;
59 |
60 | public var width(get, never):Int;
61 | inline function get_width() return image.width;
62 |
63 | public var height(get, never):Int;
64 | inline function get_height() return image.height;
65 |
66 | public var format(get, never):TextureFormat;
67 | inline function get_format() return image.format;
68 |
69 | public var isRenderTarget(default, null):Bool;
70 |
71 | public var image:kha.Image;
72 |
73 | public function new(image:kha.Image, renderTarget:Bool = false) {
74 | // can be used for texture sorting, and packing to int
75 | this.id = Texture.getId();
76 | this.image = image;
77 | isRenderTarget = renderTarget;
78 |
79 | resourceType = ResourceType.TEXTURE;
80 | }
81 |
82 | public inline function generateMipmaps(levels:Int) {
83 | image.generateMipmaps(levels);
84 | }
85 |
86 | public inline function lock(level:Int = 0):Bytes {
87 | return image.lock(level);
88 | }
89 |
90 | public inline function unlock() {
91 | image.unlock();
92 | }
93 |
94 | public inline function get_bytes():Bytes {
95 | return image.getPixels();
96 | }
97 |
98 | override function unload() {
99 | image.unload();
100 | image = null;
101 | Texture.putId(id);
102 | }
103 |
104 | override function memoryUse() {
105 | return (widthActual * heightActual * image.depth);
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/clay/graphics/Vertex.hx:
--------------------------------------------------------------------------------
1 | package clay.graphics;
2 |
3 | import clay.graphics.Color;
4 |
5 | class Vertex {
6 |
7 | public var x:Float;
8 | public var y:Float;
9 | public var color:Color;
10 | public var u:Float;
11 | public var v:Float;
12 |
13 | public function new(x:Float, y:Float, color:Color = Color.WHITE, u:Float = 0, v:Float = 0) {
14 | this.x = x;
15 | this.y = y;
16 | this.color = color;
17 | this.u = u;
18 | this.v = v;
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/clay/graphics/Video.hx:
--------------------------------------------------------------------------------
1 | package clay.graphics;
2 |
3 | import clay.Resources;
4 | import clay.resources.Resource;
5 |
6 | class Video extends Resource {
7 |
8 | public var video:kha.Video;
9 |
10 | public function new(video:kha.Video) {
11 | this.video = video;
12 | resourceType = ResourceType.VIDEO;
13 | }
14 |
15 | override function unload() {
16 | video.unload();
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/clay/graphics/render/IndexBuffer.hx:
--------------------------------------------------------------------------------
1 | package clay.graphics.render;
2 |
3 |
4 | typedef IndexBuffer = kha.graphics4.IndexBuffer;
--------------------------------------------------------------------------------
/clay/graphics/render/Shaders.hx:
--------------------------------------------------------------------------------
1 | package clay.graphics.render;
2 |
3 |
4 | typedef Shaders = kha.Shaders;
--------------------------------------------------------------------------------
/clay/graphics/render/VertexBuffer.hx:
--------------------------------------------------------------------------------
1 | package clay.graphics.render;
2 |
3 | typedef VertexBuffer = kha.graphics4.VertexBuffer;
4 | typedef Usage = kha.graphics4.Usage;
5 |
--------------------------------------------------------------------------------
/clay/graphics/render/VertexStructure.hx:
--------------------------------------------------------------------------------
1 | package clay.graphics.render;
2 |
3 |
4 | typedef VertexStructure = kha.graphics4.VertexStructure;
5 | typedef VertexData = kha.graphics4.VertexData;
--------------------------------------------------------------------------------
/clay/graphics/slice/NineSlice.hx:
--------------------------------------------------------------------------------
1 | package clay.graphics.slice;
2 |
3 | import clay.graphics.Texture;
4 | import clay.graphics.Vertex;
5 |
6 | /*
7 | 0---1---2---3
8 | | | | |
9 | 4---5---6---7
10 | | | | |
11 | 8---9---10--11
12 | | | | |
13 | 12--13--14--15
14 | */
15 |
16 | class NineSlice extends Polygon {
17 |
18 | public var width(get, set):Float;
19 | var _width:Float;
20 | inline function get_width() return _width;
21 | function set_width(v:Float):Float {
22 | _width = v;
23 | updateWidth();
24 | return _width;
25 | }
26 |
27 | public var height(get, set):Float;
28 | var _height:Float;
29 | inline function get_height() return _height;
30 | function set_height(v:Float):Float {
31 | _height = v;
32 | updateHeight();
33 | return _height;
34 | }
35 |
36 | public var top(get, set):Float;
37 | var _top:Float;
38 | inline function get_top():Float return _top;
39 | function set_top(v:Float):Float {
40 | _top = v;
41 | updateHeight();
42 | return _top;
43 | }
44 |
45 | public var bottom(get, set):Float;
46 | var _bottom:Float;
47 | inline function get_bottom() return _bottom;
48 | function set_bottom(v:Float):Float {
49 | _bottom = v;
50 | updateHeight();
51 | return _bottom;
52 | }
53 |
54 | public var left(get, set):Float;
55 | var _left:Float;
56 | inline function get_left()return _left;
57 | function set_left(v:Float):Float {
58 | _left = v;
59 | updateWidth();
60 | return _left;
61 | }
62 |
63 | public var right(get, set):Float;
64 | var _right:Float;
65 | inline function get_right()return _right;
66 | function set_right(v:Float):Float {
67 | _right = v;
68 | updateWidth();
69 | return _right;
70 | }
71 |
72 | public var edgeScale(get, set):Float;
73 | var _edgeScale:Float;
74 | inline function get_edgeScale() return _edgeScale;
75 | function set_edgeScale(v:Float) {
76 | _edgeScale = v;
77 | updateWidth();
78 | updateHeight();
79 | return _edgeScale;
80 | }
81 |
82 | public var drawCender(get, set):Bool;
83 | var _drawCender:Bool = true;
84 | inline function get_drawCender() return _drawCender;
85 | function set_drawCender(v:Bool):Bool {
86 | _drawCender = v;
87 | indices = getIndices();
88 | return v;
89 | }
90 |
91 | public function new(texture:Texture, top:Float, left:Float, right:Float, bottom:Float) {
92 | var vertices = [];
93 | for (i in 0...16) {
94 | vertices.push(new Vertex(0, 0));
95 | }
96 |
97 | super(texture, vertices, getIndices());
98 |
99 | _top = top;
100 | _bottom = bottom;
101 | _left = left;
102 | _right = right;
103 |
104 | _width = 128;
105 | _height = 128;
106 | _edgeScale = 1;
107 |
108 | _drawCender = true;
109 |
110 | updateWidth();
111 | updateHeight();
112 | }
113 |
114 | override function set_texture(v:Texture):Texture {
115 | super.set_texture(v);
116 |
117 | updateWidth();
118 | updateHeight();
119 |
120 | return v;
121 | }
122 |
123 | function updateWidth() {
124 | var tw:Float = texture.widthActual;
125 |
126 | vertices[0].x = vertices[4].x = vertices[8].x = vertices[12].x = 0;
127 | vertices[1].x = vertices[5].x = vertices[9].x = vertices[13].x = _left * _edgeScale;
128 | vertices[2].x = vertices[6].x = vertices[10].x = vertices[14].x = _width - _right * _edgeScale;
129 | vertices[3].x = vertices[7].x = vertices[11].x = vertices[15].x = _width;
130 |
131 | vertices[0].u = vertices[4].u = vertices[8].u = vertices[12].u = 0;
132 | vertices[1].u = vertices[5].u = vertices[9].u = vertices[13].u = _left / tw;
133 | vertices[2].u = vertices[6].u = vertices[10].u = vertices[14].u = 1 - _right / tw;
134 | vertices[3].u = vertices[7].u = vertices[11].u = vertices[15].u = 1;
135 | }
136 |
137 | function updateHeight() {
138 | var th:Float = texture.heightActual;
139 |
140 | vertices[0].y = vertices[1].y = vertices[2].y = vertices[3].y = 0;
141 | vertices[4].y = vertices[5].y = vertices[6].y = vertices[7].y = _top * _edgeScale;
142 | vertices[8].y = vertices[9].y = vertices[10].y = vertices[11].y = _height - _bottom * _edgeScale;
143 | vertices[12].y = vertices[13].y = vertices[14].y = vertices[15].y = _height;
144 |
145 | vertices[0].v = vertices[1].v = vertices[2].v = vertices[3].v = 0;
146 | vertices[4].v = vertices[5].v = vertices[6].v = vertices[7].v = _top / th;
147 | vertices[8].v = vertices[9].v = vertices[10].v = vertices[11].v = 1 - _bottom / th;
148 | vertices[12].v = vertices[13].v = vertices[14].v = vertices[15].v = 1;
149 | }
150 |
151 | function getIndices() {
152 | if(_drawCender) {
153 | return [
154 | 0, 1, 5, 5, 4, 0, // 0
155 | 1, 2, 6, 6, 5, 1, // 1
156 | 2, 3, 7, 7, 6, 2, // 2
157 | 4, 5, 9, 9, 8, 4, // 3
158 | 5, 6, 10, 10, 9, 5, // 4
159 | 6, 7, 11, 11, 10, 6, // 5
160 | 8, 9, 13, 13, 12, 8, // 6
161 | 9, 10, 14, 14, 13, 9, // 7
162 | 10, 11, 15, 15, 14, 10, // 8
163 | ];
164 | }
165 |
166 | return [
167 | 0, 1, 5, 5, 4, 0, // 0
168 | 1, 2, 6, 6, 5, 1, // 1
169 | 2, 3, 7, 7, 6, 2, // 2
170 | 4, 5, 9, 9, 8, 4, // 3
171 | // 5, 6, 10, 10, 9, 5, // 4
172 | 6, 7, 11, 11, 10, 6, // 5
173 | 8, 9, 13, 13, 12, 8, // 6
174 | 9, 10, 14, 14, 13, 9, // 7
175 | 10, 11, 15, 15, 14, 10, // 8
176 | ];
177 |
178 | }
179 |
180 | }
181 |
--------------------------------------------------------------------------------
/clay/graphics/slice/ThreeSlice.hx:
--------------------------------------------------------------------------------
1 | package clay.graphics.slice;
2 |
3 | import clay.graphics.Texture;
4 | import clay.graphics.Vertex;
5 |
6 | /*
7 | 0---1---2---3
8 | | | | |
9 | 4---5---6---7
10 |
11 | 4---0
12 | | |
13 | 5---1
14 | | |
15 | 6---2
16 | | |
17 | 7---3
18 | */
19 |
20 | // TODO: vertical/horizontal switch, instead of auto
21 | class ThreeSlice extends Polygon {
22 |
23 | public var width(get, set):Float;
24 | var _width:Float;
25 | inline function get_width() return _width;
26 | function set_width(v:Float):Float {
27 | _width = v;
28 | updateVertices();
29 | return _width;
30 | }
31 |
32 | public var height(get, set):Float;
33 | var _height:Float;
34 | inline function get_height() return _height;
35 | function set_height(v:Float):Float {
36 | _height = v;
37 | updateVertices();
38 | return _height;
39 | }
40 |
41 | public var left(get, set):Float;
42 | var _left:Float;
43 | inline function get_left()return _left;
44 | function set_left(v:Float):Float {
45 | _left = v;
46 | updateVertices();
47 | return _left;
48 | }
49 |
50 | public var right(get, set):Float;
51 | var _right:Float;
52 | inline function get_right()return _right;
53 | function set_right(v:Float):Float {
54 | _right = v;
55 | updateVertices();
56 | return _right;
57 | }
58 |
59 | public function new(texture:Texture, left:Float, right:Float) {
60 | var vertices = [];
61 | for (i in 0...8) {
62 | vertices.push(new Vertex(0, 0));
63 | }
64 |
65 | var indices = [
66 | 0, 1, 5, 5, 4, 0, // 0
67 | 1, 2, 6, 6, 5, 1, // 1
68 | 2, 3, 7, 7, 6, 2, // 2
69 | ];
70 |
71 | super(texture, vertices, indices);
72 |
73 | _left = left;
74 | _right = right;
75 |
76 | _width = 128;
77 | _height = 128;
78 |
79 | updateVertices();
80 | }
81 |
82 | override function set_texture(v:Texture):Texture {
83 | super.set_texture(v);
84 |
85 | updateVertices();
86 |
87 | return v;
88 | }
89 |
90 | function updateVertices() {
91 | var tw:Float = texture.widthActual;
92 | var th:Float = texture.heightActual;
93 |
94 | if(width > height) {
95 | var leftScale = (_height / _left) * (_left / th);
96 | var rightScale = (_height / _right) * (_right / th);
97 | vertices[0].x = vertices[4].x = 0;
98 | vertices[1].x = vertices[5].x = _left * leftScale;
99 | vertices[2].x = vertices[6].x = _width - _right * rightScale;
100 | vertices[3].x = vertices[7].x = _width;
101 |
102 | vertices[0].y = vertices[1].y = vertices[2].y = vertices[3].y = 0;
103 | vertices[4].y = vertices[5].y = vertices[6].y = vertices[7].y = _height;
104 | } else {
105 | var leftScale = (_width / _left) * (_left / th);
106 | var rightScale = (_width / _right) * (_right / th);
107 | vertices[0].y = vertices[4].y = 0;
108 | vertices[1].y = vertices[5].y = _left * leftScale;
109 | vertices[2].y = vertices[6].y = _height - _right * rightScale;
110 | vertices[3].y = vertices[7].y = _height;
111 |
112 | vertices[0].x = vertices[1].x = vertices[2].x = vertices[3].x = _width;
113 | vertices[4].x = vertices[5].x = vertices[6].x = vertices[7].x = 0;
114 | }
115 |
116 | vertices[0].u = vertices[4].u = 0;
117 | vertices[1].u = vertices[5].u = _left / tw;
118 | vertices[2].u = vertices[6].u = 1 - _right / tw;
119 | vertices[3].u = vertices[7].u = 1;
120 |
121 | vertices[0].v = vertices[1].v = vertices[2].v = vertices[3].v = 0;
122 | vertices[4].v = vertices[5].v = vertices[6].v = vertices[7].v = 1;
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/clay/graphics/utils/ImmediateColoredRenderer.hx:
--------------------------------------------------------------------------------
1 | package clay.graphics.utils;
2 |
3 | import clay.math.Vector2;
4 | import clay.math.FastMatrix3;
5 | import clay.math.FastVector2;
6 | import clay.graphics.Color;
7 | import clay.graphics.Texture;
8 | import clay.graphics.render.Pipeline;
9 | import clay.graphics.render.VertexBuffer;
10 | import clay.graphics.render.IndexBuffer;
11 | import clay.utils.StrokeAlign;
12 | import clay.utils.Math;
13 | import clay.utils.DynamicPool;
14 | import clay.utils.FastFloat;
15 | import clay.utils.Float32Array;
16 | import clay.utils.Uint32Array;
17 | import clay.utils.Log;
18 | import clay.Graphics;
19 |
20 | class ImmediateColoredRenderer {
21 |
22 | public var projection(get, set):FastMatrix3;
23 | var _projection:FastMatrix3 = new FastMatrix3();
24 | inline function get_projection() return _projection;
25 | function set_projection(v:FastMatrix3) {
26 | Log.assert(!_inGeometryMode, 'ImmediateColoredRenderer.endGeometry must be called before changing projection');
27 | if(isDrawing) flush();
28 | return _projection = v;
29 | }
30 |
31 | public var pipeline(get, set):Pipeline;
32 | var _pipeline:Pipeline;
33 | inline function get_pipeline() return _pipeline;
34 | function set_pipeline(v:Pipeline) {
35 | Log.assert(!_inGeometryMode, 'ImmediateColoredRenderer.endGeometry must be called before changing pipeline');
36 | if(isDrawing) flush();
37 | _pipeline = v;
38 | _currentPipeline = _pipeline != null ? _pipeline : _pipelineColored;
39 | return _pipeline;
40 | }
41 |
42 | /** true if currently between begin and end. */
43 | public var isDrawing(default, null):Bool = false;
44 |
45 | /** Number of render calls since the last {@link #begin()}. **/
46 | public var renderCalls(default, null):Int = 0;
47 |
48 | /** Number of rendering calls, ever. Will not be reset unless set manually. **/
49 | public var renderCallsTotal:Int = 0;
50 |
51 | /** The maximum number of vertices rendered in one batch so far. **/
52 | public var maxVerticesInBatch:Int = 0;
53 |
54 | var _currentPipeline:Pipeline;
55 | var _pipelineColored:Pipeline;
56 |
57 | var _vertexBuffer:VertexBuffer;
58 | var _indexBuffer:IndexBuffer;
59 | var _vertices:Float32Array;
60 | var _indices:Uint32Array;
61 |
62 | var _verticesMax:Int = 0;
63 | var _indicesMax:Int = 0;
64 |
65 | var _vertsDraw:Int = 0;
66 | var _indicesDraw:Int = 0;
67 |
68 | var _vertStartPos:Int = 0;
69 | var _vertexIdx:Int = 0;
70 | var _vertPos:Int = 0;
71 | var _indPos:Int = 0;
72 |
73 | var _inGeometryMode:Bool = false;
74 |
75 | var _graphics:Graphics;
76 |
77 | public function new(verticesMax:Int = 8192, indicesMax:Int = 16384) {
78 | _graphics = Clay.graphics;
79 | _verticesMax = verticesMax;
80 | _indicesMax = indicesMax;
81 |
82 | _pipelineColored = Graphics.pipelineColored;
83 | _currentPipeline = _pipelineColored;
84 |
85 | _vertexBuffer = new VertexBuffer(_verticesMax, _pipelineColored.inputLayout[0], Usage.DynamicUsage);
86 | _vertices = _vertexBuffer.lock();
87 |
88 | _indexBuffer = new IndexBuffer(_indicesMax, Usage.DynamicUsage);
89 | _indices = _indexBuffer.lock();
90 |
91 | if(Texture.renderTargetsInvertedY) {
92 | _projection.orto(0, Clay.window.width, 0, Clay.window.height);
93 | } else {
94 | _projection.orto(0, Clay.window.width, Clay.window.height, 0);
95 | }
96 | }
97 |
98 | public function dispose() {
99 | _vertexBuffer.delete();
100 | _indexBuffer.delete();
101 | _vertices = null;
102 | _indices = null;
103 | }
104 |
105 | public function begin() {
106 | Log.assert(!isDrawing, 'ImmediateColoredRenderer.end must be called before begin');
107 | isDrawing = true;
108 | renderCalls = 0;
109 | }
110 |
111 | public function end() {
112 | Log.assert(isDrawing, 'ImmediateColoredRenderer.begin must be called before end');
113 | flush();
114 | isDrawing = false;
115 | }
116 |
117 | public function flush() {
118 | if(_vertsDraw == 0) return;
119 |
120 | renderCalls++;
121 | renderCallsTotal++;
122 | if(_vertsDraw > maxVerticesInBatch) maxVerticesInBatch = _vertsDraw;
123 |
124 | _currentPipeline.setMatrix3('projectionMatrix', _projection);
125 |
126 | _graphics.setPipeline(_currentPipeline);
127 | _graphics.applyUniforms(_currentPipeline);
128 |
129 | _vertexBuffer.unlock(_vertsDraw);
130 | _vertices = _vertexBuffer.lock();
131 | _graphics.setVertexBuffer(_vertexBuffer);
132 |
133 | _indexBuffer.unlock(_indicesDraw);
134 | _indices = _indexBuffer.lock();
135 | _graphics.setIndexBuffer(_indexBuffer);
136 |
137 | _graphics.draw(0, _indicesDraw);
138 |
139 | _vertsDraw = 0;
140 | _indicesDraw = 0;
141 | }
142 |
143 | public function beginGeometry(verticesCount:Int, indicesCount:Int) {
144 | Log.assert(isDrawing, 'ImmediateColoredRenderer.begin must be called before beginGeometry');
145 | Log.assert(!_inGeometryMode, 'ImmediateColoredRenderer.endGeometry must be called before beginGeometry');
146 | _inGeometryMode = true;
147 |
148 | if(verticesCount >= _verticesMax || indicesCount >= _indicesMax) {
149 | throw('can`t batch geometry with vertices(${verticesCount}/$_verticesMax), indices($indicesCount/$_indicesMax)');
150 | } else if(_vertPos + verticesCount >= _verticesMax || _indPos + indicesCount >= _indicesMax) {
151 | flush();
152 | }
153 |
154 | _vertStartPos = _vertsDraw;
155 | _vertPos = _vertsDraw;
156 | _indPos = _indicesDraw;
157 |
158 | _vertsDraw += verticesCount;
159 | _indicesDraw += indicesCount;
160 | }
161 |
162 | public function endGeometry() {
163 | Log.assert(_inGeometryMode, 'ImmediateColoredRenderer.beginGeometry must be called before endGeometry');
164 | Log.assert(_vertPos == _vertsDraw, 'ImmediateColoredRenderer: added vertices($_vertPos) not equals of requested($_vertsDraw) in beginGeometry');
165 | Log.assert(_indPos == _indicesDraw, 'ImmediateColoredRenderer: added indicies($_indPos) is not equals of requested($_indicesDraw) in beginGeometry');
166 | _inGeometryMode = false;
167 | }
168 |
169 | public function addVertex(x:FastFloat, y:FastFloat, c:Color) {
170 | _vertexIdx = _vertPos * Graphics.vertexSizeColored;
171 |
172 | _vertices[_vertexIdx + 0] = x;
173 | _vertices[_vertexIdx + 1] = y;
174 |
175 | var a = c.a;
176 | _vertices[_vertexIdx + 2] = c.r * a;
177 | _vertices[_vertexIdx + 3] = c.g * a;
178 | _vertices[_vertexIdx + 4] = c.b * a;
179 | _vertices[_vertexIdx + 5] = a;
180 |
181 | _vertPos++;
182 | }
183 |
184 | public function addIndex(i:Int) {
185 | _indices[_indPos++] = _vertStartPos + i;
186 | }
187 |
188 | }
189 |
--------------------------------------------------------------------------------
/clay/input/Bindings.hx:
--------------------------------------------------------------------------------
1 | package clay.input;
2 |
3 | import clay.input.Mouse;
4 | import clay.input.Keyboard;
5 | import clay.input.Gamepad;
6 | import clay.input.Touch;
7 | import clay.input.Pen;
8 | import clay.utils.Log;
9 | import clay.events.*;
10 |
11 | @:allow(clay.Input)
12 | class Bindings {
13 |
14 | public var active(default, null):Bool = false;
15 |
16 | @:noCompletion public var inputEvent:InputEvent;
17 |
18 | var _inputPressed:Map;
19 | var _inputReleased:Map;
20 | var _inputDown:Map;
21 |
22 | var _dirty:Bool = false;
23 |
24 | public function new() {}
25 |
26 | public function enable() {
27 | if(active) {
28 | return;
29 | }
30 |
31 | _inputPressed = new Map();
32 | _inputReleased = new Map();
33 | _inputDown = new Map();
34 |
35 | inputEvent = new InputEvent();
36 |
37 | active = true;
38 | }
39 |
40 | public function disable() {
41 | if(!active) {
42 | return;
43 | }
44 |
45 | _inputPressed = null;
46 | _inputReleased = null;
47 | _inputDown = null;
48 |
49 | inputEvent = null;
50 |
51 | active = false;
52 | }
53 |
54 | public function pressed(_key:String):Bool {
55 | return _inputPressed.exists(_key);
56 | }
57 |
58 | public function released(_key:String):Bool {
59 | return _inputReleased.exists(_key);
60 | }
61 |
62 | public function down(_key:String):Bool {
63 | return _inputDown.exists(_key);
64 | }
65 |
66 | function reset() {
67 | if(_dirty) {
68 | Log.debug("reset");
69 | for (k in _inputPressed.keys()) {
70 | _inputPressed.remove(k);
71 | }
72 | for (k in _inputReleased.keys()) {
73 | _inputReleased.remove(k);
74 | }
75 | _dirty = false;
76 | }
77 | }
78 |
79 | inline function addPressed(_name:String) {
80 | var n:Int = 0;
81 | if(_inputPressed.exists(_name)) {
82 | n = _inputPressed.get(_name);
83 | }
84 | _inputPressed.set(_name, ++n);
85 | }
86 |
87 | inline function addDown(_name:String) {
88 | var n:Int = 0;
89 | if(_inputDown.exists(_name)) {
90 | n = _inputDown.get(_name);
91 | }
92 | _inputDown.set(_name, ++n);
93 | }
94 |
95 | inline function addReleased(_name:String) {
96 | var n:Int = 0;
97 | if(_inputReleased.exists(_name)) {
98 | n = _inputReleased.get(_name);
99 | }
100 | _inputReleased.set(_name, ++n);
101 | }
102 |
103 | inline function removePressed(_name:String) {
104 | if(_inputPressed.exists(_name)) {
105 | var n = _inputPressed.get(_name);
106 | if(--n <= 0) {
107 | _inputPressed.remove(_name);
108 | }
109 | }
110 | }
111 |
112 | inline function removeDown(_name:String) {
113 | if(_inputDown.exists(_name)) {
114 | var n = _inputDown.get(_name);
115 | if(--n <= 0) {
116 | _inputDown.remove(_name);
117 | }
118 | }
119 | }
120 |
121 | inline function removeReleased(_name:String) {
122 | if(_inputReleased.exists(_name)) {
123 | var n = _inputReleased.get(_name);
124 | if(--n <= 0) {
125 | _inputReleased.remove(_name);
126 | }
127 | }
128 | }
129 |
130 | @:noCompletion public function removeAll(_name:String) {
131 | removePressed(_name);
132 | removeDown(_name);
133 | removeReleased(_name);
134 | }
135 |
136 | @:noCompletion public function inputPressed() {
137 | Log.debug('inputPressed');
138 |
139 | _dirty = true;
140 |
141 | addPressed(inputEvent.name);
142 | addDown(inputEvent.name);
143 |
144 | Clay.app.emitter.emit(InputEvent.INPUT_DOWN, inputEvent);
145 | }
146 |
147 | @:noCompletion public function inputReleased() {
148 | Log.debug('inputReleased');
149 |
150 | _dirty = true;
151 |
152 | addReleased(inputEvent.name);
153 | removePressed(inputEvent.name);
154 | removeDown(inputEvent.name);
155 |
156 | Clay.app.emitter.emit(InputEvent.INPUT_UP, inputEvent);
157 | }
158 |
159 | }
160 |
--------------------------------------------------------------------------------
/clay/input/Key.hx:
--------------------------------------------------------------------------------
1 | package clay.input;
2 |
3 | import kha.input.KeyCode;
4 |
5 | enum abstract Key(Int) to Int {
6 | var UNKNOWN = KeyCode.Unknown;
7 | var BACK = KeyCode.Back; // Android
8 | var CANCEL = KeyCode.Cancel;
9 | var HELP = KeyCode.Help;
10 | var BACKSPACE = KeyCode.Backspace;
11 | var TAB = KeyCode.Tab;
12 | var CLEAR = KeyCode.Clear;
13 | var RETURN = KeyCode.Return;
14 | var SHIFT = KeyCode.Shift;
15 | var CONTROL = KeyCode.Control;
16 | var ALT = KeyCode.Alt;
17 | var PAUSE = KeyCode.Pause;
18 | var CAPSLOCK = KeyCode.CapsLock;
19 | var KANA = KeyCode.Kana;
20 | var HANGUL = KeyCode.Hangul;
21 | var EISU = KeyCode.Eisu;
22 | var JUNJA = KeyCode.Junja;
23 | var FINAL = KeyCode.Final;
24 | var HANJA = KeyCode.Hanja;
25 | var KANJI = KeyCode.Kanji;
26 | var ESCAPE = KeyCode.Escape;
27 | var CONVERT = KeyCode.Convert;
28 | var NONCONVERT = KeyCode.NonConvert;
29 | var ACCEPT = KeyCode.Accept;
30 | var MODECHANGE = KeyCode.ModeChange;
31 | var SPACE = KeyCode.Space;
32 | var PAGEUP = KeyCode.PageUp;
33 | var PAGEDOWN = KeyCode.PageDown;
34 | var END = KeyCode.End;
35 | var HOME = KeyCode.Home;
36 | var LEFT = KeyCode.Left;
37 | var UP = KeyCode.Up;
38 | var RIGHT = KeyCode.Right;
39 | var DOWN = KeyCode.Down;
40 | var SELECT = KeyCode.Select;
41 | var PRINT = KeyCode.Print;
42 | var EXECUTE = KeyCode.Execute;
43 | var PRINTSCREEN = KeyCode.PrintScreen;
44 | var INSERT = KeyCode.Insert;
45 | var DELETE = KeyCode.Delete;
46 | var ZERO = KeyCode.Zero;
47 | var ONE = KeyCode.One;
48 | var TWO = KeyCode.Two;
49 | var THREE = KeyCode.Three;
50 | var FOUR = KeyCode.Four;
51 | var FIVE = KeyCode.Five;
52 | var SIX = KeyCode.Six;
53 | var SEVEN = KeyCode.Seven;
54 | var EIGHT = KeyCode.Eight;
55 | var NINE = KeyCode.Nine;
56 | var COLON = KeyCode.Colon;
57 | var SEMICOLON = KeyCode.Semicolon;
58 | var LESSTHAN = KeyCode.LessThan;
59 | var EQUALS = KeyCode.Equals;
60 | var GREATERTHAN = KeyCode.GreaterThan;
61 | var QUESTIONMARK = KeyCode.QuestionMark;
62 | var AT = KeyCode.At;
63 | var A = KeyCode.A;
64 | var B = KeyCode.B;
65 | var C = KeyCode.C;
66 | var D = KeyCode.D;
67 | var E = KeyCode.E;
68 | var F = KeyCode.F;
69 | var G = KeyCode.G;
70 | var H = KeyCode.H;
71 | var I = KeyCode.I;
72 | var J = KeyCode.J;
73 | var K = KeyCode.K;
74 | var L = KeyCode.L;
75 | var M = KeyCode.M;
76 | var N = KeyCode.N;
77 | var O = KeyCode.O;
78 | var P = KeyCode.P;
79 | var Q = KeyCode.Q;
80 | var R = KeyCode.R;
81 | var S = KeyCode.S;
82 | var T = KeyCode.T;
83 | var U = KeyCode.U;
84 | var V = KeyCode.V;
85 | var W = KeyCode.W;
86 | var X = KeyCode.X;
87 | var Y = KeyCode.Y;
88 | var Z = KeyCode.Z;
89 | var WIN = KeyCode.Win;
90 | var CONTEXTMENU = KeyCode.ContextMenu;
91 | var SLEEP = KeyCode.Sleep;
92 | var NUMPAD0 = KeyCode.Numpad0;
93 | var NUMPAD1 = KeyCode.Numpad1;
94 | var NUMPAD2 = KeyCode.Numpad2;
95 | var NUMPAD3 = KeyCode.Numpad3;
96 | var NUMPAD4 = KeyCode.Numpad4;
97 | var NUMPAD5 = KeyCode.Numpad5;
98 | var NUMPAD6 = KeyCode.Numpad6;
99 | var NUMPAD7 = KeyCode.Numpad7;
100 | var NUMPAD8 = KeyCode.Numpad8;
101 | var NUMPAD9 = KeyCode.Numpad9;
102 | var MULTIPLY = KeyCode.Multiply;
103 | var ADD = KeyCode.Add;
104 | var SEPARATOR = KeyCode.Separator;
105 | var SUBTRACT = KeyCode.Subtract;
106 | var DECIMAL = KeyCode.Decimal;
107 | var DIVIDE = KeyCode.Divide;
108 | var F1 = KeyCode.F1;
109 | var F2 = KeyCode.F2;
110 | var F3 = KeyCode.F3;
111 | var F4 = KeyCode.F4;
112 | var F5 = KeyCode.F5;
113 | var F6 = KeyCode.F6;
114 | var F7 = KeyCode.F7;
115 | var F8 = KeyCode.F8;
116 | var F9 = KeyCode.F9;
117 | var F10 = KeyCode.F10;
118 | var F11 = KeyCode.F11;
119 | var F12 = KeyCode.F12;
120 | var F13 = KeyCode.F13;
121 | var F14 = KeyCode.F14;
122 | var F15 = KeyCode.F15;
123 | var F16 = KeyCode.F16;
124 | var F17 = KeyCode.F17;
125 | var F18 = KeyCode.F18;
126 | var F19 = KeyCode.F19;
127 | var F20 = KeyCode.F20;
128 | var F21 = KeyCode.F21;
129 | var F22 = KeyCode.F22;
130 | var F23 = KeyCode.F23;
131 | var F24 = KeyCode.F24;
132 | var NUMLOCK = KeyCode.NumLock;
133 | var SCROLLLOCK = KeyCode.ScrollLock;
134 | var WINOEMFJJISHO = KeyCode.WinOemFjJisho;
135 | var WINOEMFJMASSHOU = KeyCode.WinOemFjMasshou;
136 | var WINOEMFJTOUROKU = KeyCode.WinOemFjTouroku;
137 | var WINOEMFJLOYA = KeyCode.WinOemFjLoya;
138 | var WINOEMFJROYA = KeyCode.WinOemFjRoya;
139 | var CIRCUMFLEX = KeyCode.Circumflex;
140 | var EXCLAMATION = KeyCode.Exclamation;
141 | var DOUBLEQUOTE = KeyCode.DoubleQuote;
142 | var HASH = KeyCode.Hash;
143 | var DOLLAR = KeyCode.Dollar;
144 | var PERCENT = KeyCode.Percent;
145 | var AMPERSAND = KeyCode.Ampersand;
146 | var UNDERSCORE = KeyCode.Underscore;
147 | var OPENPAREN = KeyCode.OpenParen;
148 | var CLOSEPAREN = KeyCode.CloseParen;
149 | var ASTERISK = KeyCode.Asterisk;
150 | var PLUS = KeyCode.Plus;
151 | var PIPE = KeyCode.Pipe;
152 | var HYPHENMINUS = KeyCode.HyphenMinus;
153 | var OPENCURLYBRACKET = KeyCode.OpenCurlyBracket;
154 | var CLOSECURLYBRACKET = KeyCode.CloseCurlyBracket;
155 | var TILDE = KeyCode.Tilde;
156 | var VOLUMEMUTE = KeyCode.VolumeMute;
157 | var VOLUMEDOWN = KeyCode.VolumeDown;
158 | var VOLUMEUP = KeyCode.VolumeUp;
159 | var COMMA = KeyCode.Comma;
160 | var PERIOD = KeyCode.Period;
161 | var SLASH = KeyCode.Slash;
162 | var BACKQUOTE = KeyCode.BackQuote;
163 | var OPENBRACKET = KeyCode.OpenBracket;
164 | var BACKSLASH = KeyCode.BackSlash;
165 | var CLOSEBRACKET = KeyCode.CloseBracket;
166 | var QUOTE = KeyCode.Quote;
167 | var META = KeyCode.Meta;
168 | var ALTGR = KeyCode.AltGr;
169 | var WINICOHELP = KeyCode.WinIcoHelp;
170 | var WINICO00 = KeyCode.WinIco00;
171 | var WINICOCLEAR = KeyCode.WinIcoClear;
172 | var WINOEMRESET = KeyCode.WinOemReset;
173 | var WINOEMJUMP = KeyCode.WinOemJump;
174 | var WINOEMPA1 = KeyCode.WinOemPA1;
175 | var WINOEMPA2 = KeyCode.WinOemPA2;
176 | var WINOEMPA3 = KeyCode.WinOemPA3;
177 | var WINOEMWSCTRL = KeyCode.WinOemWSCTRL;
178 | var WINOEMCUSEL = KeyCode.WinOemCUSEL;
179 | var WINOEMATTN = KeyCode.WinOemATTN;
180 | var WINOEMFINISH = KeyCode.WinOemFinish;
181 | var WINOEMCOPY = KeyCode.WinOemCopy;
182 | var WINOEMAUTO = KeyCode.WinOemAuto;
183 | var WINOEMENLW = KeyCode.WinOemENLW;
184 | var WINOEMBACKTAB = KeyCode.WinOemBackTab;
185 | var ATTN = KeyCode.ATTN;
186 | var CRSEL = KeyCode.CRSEL;
187 | var EXSEL = KeyCode.EXSEL;
188 | var EREOF = KeyCode.EREOF;
189 | var PLAY = KeyCode.Play;
190 | var ZOOM = KeyCode.Zoom;
191 | var PA1 = KeyCode.PA1;
192 | var WINOEMCLEAR = KeyCode.WinOemClear;
193 | }
194 |
--------------------------------------------------------------------------------
/clay/input/Keyboard.hx:
--------------------------------------------------------------------------------
1 | package clay.input;
2 |
3 | import clay.utils.BitVector;
4 | import clay.input.Key;
5 | import clay.utils.Log;
6 | import clay.events.KeyEvent;
7 | import clay.App;
8 |
9 | @:allow(clay.Input)
10 | @:access(clay.App)
11 | class Keyboard {
12 |
13 | public var active(default, null):Bool = false;
14 |
15 | var _keyCodePressed:BitVector;
16 | var _keyCodeReleased:BitVector;
17 | var _keyCodeDown:BitVector;
18 |
19 | var _keyEvent:KeyEvent;
20 | var _dirty:Bool = false;
21 |
22 | var _keyBindings:Map>;
23 | var _binding:Bindings;
24 |
25 | public function new() {
26 | _keyBindings = new Map();
27 | _binding = Clay.input.binding;
28 | }
29 |
30 | public function enable() {
31 | if(active) {
32 | return;
33 | }
34 |
35 | #if use_keyboard_input
36 |
37 | var k = kha.input.Keyboard.get();
38 | if(k != null) {
39 | k.notify(onKeyPressed, onKeyReleased, onTextInput);
40 | }
41 |
42 | #end
43 |
44 | _keyCodePressed = new BitVector(256);
45 | _keyCodeReleased = new BitVector(256);
46 | _keyCodeDown = new BitVector(256);
47 |
48 | _keyEvent = new KeyEvent();
49 |
50 | active = true;
51 | }
52 |
53 | public function disable() {
54 | if(!active) {
55 | return;
56 | }
57 |
58 | #if use_keyboard_input
59 |
60 | var k = kha.input.Keyboard.get();
61 | if(k != null) {
62 | k.remove(onKeyPressed, onKeyReleased, onTextInput);
63 | }
64 |
65 | #end
66 |
67 | _keyCodePressed = null;
68 | _keyCodeReleased = null;
69 | _keyCodeDown = null;
70 |
71 | _keyEvent = null;
72 |
73 | active = true;
74 | }
75 |
76 | public function pressed(key:Key):Bool {
77 | return _keyCodePressed.get(key);
78 | }
79 |
80 | public function released(key:Key):Bool {
81 | return _keyCodeReleased.get(key);
82 | }
83 |
84 | public function down(key:Key):Bool {
85 | return _keyCodeDown.get(key);
86 | }
87 |
88 | public function bind(name:String, key:Key) {
89 | var b = _keyBindings.get(name);
90 | if(b == null) {
91 | b = new Map();
92 | _keyBindings.set(name, b);
93 | }
94 | b.set(key, true);
95 | }
96 |
97 | public function unbind(name:String) {
98 | if(_keyBindings.exists(name)) {
99 | _keyBindings.remove(name);
100 | _binding.removeAll(name);
101 | }
102 | }
103 |
104 | function checkBinding(key:Int, pressed:Bool) {
105 | for (k in _keyBindings.keys()) {
106 | if(_keyBindings.get(k).exists(key)) {
107 | _binding.inputEvent.setKey(k, _keyEvent);
108 | if(pressed) {
109 | _binding.inputPressed();
110 | } else {
111 | _binding.inputReleased();
112 | }
113 | return;
114 | }
115 | }
116 | }
117 |
118 | function reset() {
119 | #if use_keyboard_input
120 |
121 | if(_dirty) {
122 | Log.debug("reset");
123 | _keyCodePressed.disableAll();
124 | _keyCodeReleased.disableAll();
125 | _dirty = false;
126 | }
127 | #end
128 | }
129 |
130 | function onKeyPressed(key:Int) {
131 | Log.debug('onKeyPressed: $key');
132 |
133 | _dirty = true;
134 |
135 | _keyCodePressed.enable(key);
136 | _keyCodeDown.enable(key);
137 |
138 | _keyEvent.set(key, KeyEvent.KEY_DOWN);
139 |
140 | checkBinding(key, true);
141 |
142 | Clay.app.emitter.emit(KeyEvent.KEY_DOWN, _keyEvent);
143 | }
144 |
145 | function onKeyReleased(key:Int) {
146 | Log.debug('onKeyReleased: $key');
147 |
148 | _dirty = true;
149 |
150 | _keyCodeReleased.enable(key);
151 | _keyCodePressed.disable(key);
152 | _keyCodeDown.disable(key);
153 | _keyEvent.set(key, KeyEvent.KEY_UP);
154 |
155 | checkBinding(key, false);
156 |
157 | Clay.app.emitter.emit(KeyEvent.KEY_UP, _keyEvent);
158 | }
159 |
160 | function onTextInput(char:String) {
161 | Log.debug('onTextInput: $char');
162 | Clay.app.emitter.emit(KeyEvent.TEXT_INPUT, char);
163 | }
164 |
165 | }
166 |
--------------------------------------------------------------------------------
/clay/input/Mouse.hx:
--------------------------------------------------------------------------------
1 | package clay.input;
2 |
3 | import clay.App;
4 | import clay.utils.Log;
5 | import clay.utils.Bits;
6 |
7 | import clay.events.MouseEvent;
8 |
9 | @:allow(clay.Input)
10 | @:access(clay.App)
11 | class Mouse {
12 |
13 | public var active(default, null):Bool = false;
14 |
15 | public var x(default, null):Int = 0;
16 | public var y(default, null):Int = 0;
17 |
18 | var _buttonsPressed:UInt = 0;
19 | var _buttonsReleased:UInt = 0;
20 | var _buttonsDown:UInt = 0;
21 | var _mouseEvent:MouseEvent;
22 |
23 | var _mouseBindings:Map;
24 | var _binding:Bindings;
25 |
26 | public function new() {
27 | _mouseBindings = new Map();
28 | _binding = Clay.input.binding;
29 | }
30 |
31 | public function enable() {
32 | if(active) {
33 | return;
34 | }
35 |
36 | _mouseEvent = new MouseEvent();
37 |
38 | #if use_mouse_input
39 |
40 | var m = kha.input.Mouse.get();
41 | if(m != null) {
42 | m.notify(_onPressed, _onReleased, _onMove, _onWheel);
43 | }
44 |
45 | #end
46 |
47 | active = true;
48 | }
49 |
50 | public function disable() {
51 | if(!active) {
52 | return;
53 | }
54 |
55 | #if use_mouse_input
56 |
57 | var m = kha.input.Mouse.get();
58 | if(m != null) {
59 | m.remove(_onPressed, _onReleased, _onMove, _onWheel);
60 | }
61 |
62 | #end
63 |
64 | _mouseEvent = null;
65 |
66 | active = false;
67 | }
68 |
69 | public inline function pressed(button:Int):Bool {
70 | return Bits.check(_buttonsPressed, button);
71 | }
72 |
73 | public inline function released(button:Int):Bool {
74 | return Bits.check(_buttonsReleased, button);
75 | }
76 |
77 | public inline function down(button:Int):Bool {
78 | return Bits.check(_buttonsDown, button);
79 | }
80 |
81 | public function bind(name:String, key:UInt) {
82 | var n:Int = 0;
83 |
84 | if(_mouseBindings.exists(name)) {
85 | n = _mouseBindings.get(name);
86 | }
87 |
88 | _mouseBindings.set(name, Bits.set(n, key));
89 | }
90 |
91 | public function unbind(name:String) {
92 | if(_mouseBindings.exists(name)) {
93 | _mouseBindings.remove(name);
94 | _binding.removeAll(name);
95 | }
96 | }
97 |
98 | function checkBinding(key:Int, pressed:Bool) {
99 | for (k in _mouseBindings.keys()) { // todo: using this is broke hashlink build, ftw?
100 | if(_mouseBindings.exists(k)) {
101 | var n = _mouseBindings.get(k);
102 | if(Bits.check(n, key)) {
103 | _binding.inputEvent.setMouse(k, _mouseEvent);
104 | if(pressed) {
105 | _binding.inputPressed();
106 | } else {
107 | _binding.inputReleased();
108 | }
109 | return;
110 | }
111 | }
112 | }
113 | }
114 |
115 | function reset() {
116 | #if use_mouse_input
117 | _buttonsPressed = 0;
118 | _buttonsReleased = 0;
119 | #end
120 | }
121 |
122 | function _onPressed(button:Int, x:Int, y:Int) {
123 | Log.debug('_onPressed x:$x, y$y, button:$button');
124 |
125 | this.x = x;
126 | this.y = y;
127 |
128 | _buttonsPressed = Bits.set(_buttonsPressed, button);
129 | _buttonsDown = Bits.set(_buttonsDown, button);
130 |
131 | _mouseEvent.set(x, y, 0, 0, 0, MouseEvent.MOUSE_DOWN, button);
132 |
133 | checkBinding(button, true);
134 |
135 | Clay.app.emitter.emit(MouseEvent.MOUSE_DOWN, _mouseEvent);
136 | }
137 |
138 | function _onReleased(button:Int, x:Int, y:Int) {
139 | Log.debug('_onPressed x:$x, y$y, button:$button');
140 |
141 | this.x = x;
142 | this.y = y;
143 |
144 | _buttonsPressed = Bits.clear(_buttonsPressed, button);
145 | _buttonsDown = Bits.clear(_buttonsDown, button);
146 | _buttonsReleased = Bits.set(_buttonsReleased, button);
147 |
148 | _mouseEvent.set(x, y, 0, 0, 0, MouseEvent.MOUSE_UP, button);
149 |
150 | checkBinding(button, false);
151 |
152 | Clay.app.emitter.emit(MouseEvent.MOUSE_UP, _mouseEvent);
153 | }
154 |
155 | function _onWheel(d:Int) {
156 | Log.debug('_onWheel delta:$d');
157 |
158 | _mouseEvent.set(x, y, 0, 0, d, MouseEvent.MOUSE_WHEEL, MouseButton.NONE);
159 |
160 | checkBinding(MouseButton.NONE, false); // todo: check this
161 |
162 | Clay.app.emitter.emit(MouseEvent.MOUSE_WHEEL, _mouseEvent);
163 | }
164 |
165 | function _onMove(x:Int, y:Int, dx:Int, dy:Int) {
166 | Log.verbose('_onMove x:$x, y$y, dx:$dx, dy:$dy');
167 |
168 | this.x = x;
169 | this.y = y;
170 |
171 | _mouseEvent.set(x, y, dx, dy, 0, MouseEvent.MOUSE_MOVE, MouseButton.NONE);
172 |
173 | Clay.app.emitter.emit(MouseEvent.MOUSE_MOVE, _mouseEvent);
174 | }
175 |
176 | }
177 |
--------------------------------------------------------------------------------
/clay/input/Pen.hx:
--------------------------------------------------------------------------------
1 | package clay.input;
2 |
3 | import clay.App;
4 | import clay.utils.Log;
5 | import clay.utils.Bits;
6 | import clay.events.PenEvent;
7 |
8 | @:allow(clay.Input)
9 | @:access(clay.App)
10 | class Pen {
11 |
12 | public var active(default, null):Bool = false;
13 |
14 | public var x(default, null):Int = 0;
15 | public var y(default, null):Int = 0;
16 | public var dx(default, null):Int = 0;
17 | public var dy(default, null):Int = 0;
18 | public var pressure(default, null):Float = 0;
19 |
20 | var _penPressed:Bool = false;
21 | var _penReleased:Bool = false;
22 | var _penDown:Bool = false;
23 |
24 | var _penEvent:PenEvent;
25 |
26 | public function new() {
27 |
28 | }
29 |
30 | public function enable() {
31 | if(active) {
32 | return;
33 | }
34 |
35 | _penEvent = new PenEvent();
36 |
37 | #if use_pen_input
38 | var p = kha.input.Pen.get();
39 | if(p != null) {
40 | p.notify(onPressed, onReleased, onMove);
41 | }
42 | #end
43 |
44 | active = true;
45 | }
46 |
47 | public function disable() {
48 | if(!active) {
49 | return;
50 | }
51 |
52 | #if use_pen_input
53 | var p = kha.input.Pen.get();
54 | if(p != null) {
55 | p.remove(onPressed, onReleased, onMove);
56 | }
57 | #end
58 |
59 | _penEvent = null;
60 |
61 | active = false;
62 | }
63 |
64 | function reset() {
65 | #if use_pen_input
66 | _penPressed = false;
67 | _penReleased = false;
68 | dx = 0;
69 | dy = 0;
70 | #end
71 | }
72 |
73 | function onPressed(x:Int, y:Int, pressure:Float) {
74 | Log.debug('onPressed x:$x, y$y, button:$pressure');
75 |
76 | this.x = x;
77 | this.y = y;
78 | this.pressure = pressure;
79 |
80 | _penPressed = true;
81 | _penReleased = false;
82 | _penDown = true;
83 |
84 | _penEvent.set(x, y, 0, 0, PenEvent.PEN_DOWN, pressure);
85 |
86 | Clay.app.emitter.emit(PenEvent.PEN_DOWN, _penEvent);
87 | }
88 |
89 | function onReleased(x:Int, y:Int, pressure:Float) {
90 | Log.debug('onReleased x:$x, y$y, button:$pressure');
91 |
92 | this.x = x;
93 | this.y = y;
94 | this.pressure = pressure;
95 |
96 | _penPressed = false;
97 | _penReleased = true;
98 | _penDown = false;
99 |
100 | _penEvent.set(x, y, 0, 0, PenEvent.PEN_UP, pressure);
101 |
102 | Clay.app.emitter.emit(PenEvent.PEN_UP, _penEvent);
103 | }
104 |
105 | function onMove(x:Int, y:Int, pressure:Float) {
106 | dx = x - this.x;
107 | dy = y - this.y;
108 | this.x = x;
109 | this.y = y;
110 | this.pressure = pressure;
111 |
112 | Log.verbose('onMove x:$x, y$y, dx:$dx, dy:$dy');
113 |
114 | _penEvent.set(x, y, dx, dy, PenEvent.PEN_MOVE, pressure);
115 |
116 | Clay.app.emitter.emit(PenEvent.PEN_MOVE, _penEvent);
117 | }
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/clay/input/Touch.hx:
--------------------------------------------------------------------------------
1 | package clay.input;
2 |
3 | import clay.App;
4 | import clay.utils.Log;
5 | import clay.utils.Bits;
6 | import clay.events.TouchEvent;
7 |
8 | @:allow(clay.Input)
9 | @:access(clay.App)
10 | class Touch {
11 |
12 | public var active(default, null):Bool = false;
13 |
14 | public var count(default, null):Int = 0;
15 | public var touches(default, null):Array;
16 |
17 | var _touches:Array;
18 |
19 | public function new() {
20 |
21 | }
22 |
23 | public function enable() {
24 | if(active) {
25 | return;
26 | }
27 |
28 | Log.debug('enable');
29 |
30 | _touches = [];
31 | touches = [];
32 |
33 | for (i in 0...10) {
34 | _touches.push(new TouchEvent(i));
35 | }
36 |
37 | #if use_touch_input
38 |
39 | var t = kha.input.Surface.get();
40 | if(t != null) {
41 | t.notify(onPressed, onReleased, onMove);
42 | }
43 |
44 | #end
45 |
46 | active = true;
47 | }
48 |
49 | public function disable() {
50 | if(!active) {
51 | return;
52 | }
53 |
54 | Log.debug('disable');
55 |
56 | #if use_touch_input
57 |
58 | var t = kha.input.Surface.get();
59 | if(t != null) {
60 | t.remove(onPressed, onReleased, onMove);
61 | }
62 |
63 | #end
64 |
65 | _touches = null;
66 |
67 | active = false;
68 | }
69 |
70 | function reset() {}
71 |
72 | function onPressed(id:Int, x:Int, y:Int) {
73 | Log.debug('onPressed id:$id, x:$x, y$y');
74 |
75 | count++;
76 |
77 | var t = _touches[id];
78 | t.set(x, y, 0, 0, TouchEvent.TOUCH_DOWN);
79 |
80 | touches.push(t);
81 |
82 | Clay.app.emitter.emit(TouchEvent.TOUCH_DOWN, t);
83 | }
84 |
85 | function onReleased(id:Int, x:Int, y:Int) {
86 | Log.debug('onPressed id:$id, x:$x, y$y');
87 |
88 | count--;
89 |
90 | var t = _touches[id];
91 | t.set(x, y, 0, 0, TouchEvent.TOUCH_UP);
92 |
93 | Clay.app.emitter.emit(TouchEvent.TOUCH_UP, t);
94 |
95 | touches.remove(t);
96 | }
97 |
98 | function onMove(id:Int, x:Int, y:Int) {
99 | Log.verbose('onMove id:$id, x:$x, y$y');
100 |
101 | var t = _touches[id];
102 | t.set(x, y, x - t.x, y - t.y, TouchEvent.TOUCH_MOVE);
103 |
104 | Clay.app.emitter.emit(TouchEvent.TOUCH_MOVE, t);
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/clay/math/FastMatrix3.hx:
--------------------------------------------------------------------------------
1 | package clay.math;
2 |
3 | import clay.math.Transform;
4 | import kha.math.FastMatrix3;
5 | import kha.FastFloat;
6 |
7 | /*
8 | | a | c | tx |
9 | | b | d | ty |
10 | | 0 | 0 | 1 |
11 | */
12 |
13 | abstract FastMatrix3(kha.math.FastMatrix3) from kha.math.FastMatrix3 to kha.math.FastMatrix3 {
14 |
15 | public var a(get, set):FastFloat;
16 | inline function get_a() return this._00;
17 | inline function set_a(v:FastFloat) return this._00 = v;
18 |
19 | public var b(get, set):FastFloat;
20 | inline function get_b() return this._01;
21 | inline function set_b(v:FastFloat) return this._01 = v;
22 |
23 | public var c(get, set):FastFloat;
24 | inline function get_c() return this._10;
25 | inline function set_c(v:FastFloat) return this._10 = v;
26 |
27 | public var d(get, set):FastFloat;
28 | inline function get_d() return this._11;
29 | inline function set_d(v:FastFloat) return this._11 = v;
30 |
31 | public var tx(get, set):FastFloat;
32 | inline function get_tx() return this._20;
33 | inline function set_tx(v:FastFloat) return this._20 = v;
34 |
35 | public var ty(get, set):FastFloat;
36 | inline function get_ty() return this._21;
37 | inline function set_ty(v:FastFloat) return this._21 = v;
38 |
39 | public function new(a:FastFloat = 1, b:FastFloat = 0, c:FastFloat = 0, d:FastFloat = 1, tx:FastFloat = 0, ty:FastFloat = 0) {
40 | this = kha.math.FastMatrix3.identity();
41 | set(a, b, c, d, tx, ty);
42 | }
43 |
44 | public inline function identity():FastMatrix3 {
45 | set(
46 | 1, 0,
47 | 0, 1,
48 | 0, 0
49 | );
50 |
51 | return this;
52 | }
53 |
54 | public inline function set(a:FastFloat, b:FastFloat, c:FastFloat, d:FastFloat, tx:FastFloat, ty:FastFloat):FastMatrix3 {
55 | set_a(a);
56 | set_b(b);
57 | set_c(c);
58 | set_d(d);
59 | set_tx(tx);
60 | set_ty(ty);
61 |
62 | return this;
63 | }
64 |
65 | public inline function translate(x:FastFloat, y:FastFloat):FastMatrix3 {
66 | tx += x;
67 | ty += y;
68 |
69 | return this;
70 | }
71 |
72 | public inline function prependTranslate(x:FastFloat, y:FastFloat):FastMatrix3 {
73 | tx = a * x + c * y + tx;
74 | ty = b * x + d * y + ty;
75 |
76 | return this;
77 | }
78 |
79 | public inline function scale(x:FastFloat, y:FastFloat):FastMatrix3 {
80 | a *= x;
81 | b *= x;
82 | c *= y;
83 | d *= y;
84 |
85 | return this;
86 | }
87 |
88 | public inline function rotate(radians:FastFloat):FastMatrix3 {
89 | var sin:FastFloat = Math.sin(radians);
90 | var cos:FastFloat = Math.cos(radians);
91 |
92 | var a1:FastFloat = a;
93 | var b1:FastFloat = b;
94 | var c1:FastFloat = c;
95 | var d1:FastFloat = d;
96 |
97 | a = a1 * cos + b1 * sin;
98 | b = a1 * -sin + b1 * cos;
99 | c = c1 * cos + d1 * sin;
100 | d = c1 * -sin + d1 * cos;
101 |
102 | return this;
103 | }
104 |
105 | public inline function append(m:FastMatrix3):FastMatrix3 {
106 | var a1 = a;
107 | var b1 = b;
108 | var c1 = c;
109 | var d1 = d;
110 |
111 | a = m.a * a1 + m.b * c1;
112 | b = m.a * b1 + m.b * d1;
113 | c = m.c * a1 + m.d * c1;
114 | d = m.c * b1 + m.d * d1;
115 |
116 | tx = m.tx * a1 + m.ty * c1 + tx;
117 | ty = m.tx * b1 + m.ty * d1 + ty;
118 |
119 | return this;
120 | }
121 |
122 | public inline function prepend(m:FastMatrix3):FastMatrix3 {
123 | var tx1 = tx;
124 |
125 | if (m.a != 1 || m.b != 0 || m.c != 0 || m.d != 1) {
126 | var a1 = a;
127 | var c1 = c;
128 |
129 | a = a1 * m.a + b * m.c;
130 | b = a1 * m.b + b * m.d;
131 | c = c1 * m.a + d * m.c;
132 | d = c1 * m.b + d * m.d;
133 | }
134 |
135 | tx = tx1 * m.a + ty * m.c + m.tx;
136 | ty = tx1 * m.b + ty * m.d + m.ty;
137 |
138 | return this;
139 | }
140 |
141 | public inline function orto(left:FastFloat, right:FastFloat, bottom:FastFloat, top:FastFloat):FastMatrix3 {
142 | var sx:FastFloat = 1.0 / (right - left);
143 | var sy:FastFloat = 1.0 / (top - bottom);
144 |
145 | set(
146 | 2.0*sx, 0,
147 | 0, 2.0*sy,
148 | -(right+left)*sx, -(top+bottom)*sy
149 | );
150 |
151 | return this;
152 | }
153 |
154 | public inline function invert():FastMatrix3 {
155 | var a1:FastFloat = a;
156 | var b1:FastFloat = b;
157 | var c1:FastFloat = c;
158 | var d1:FastFloat = d;
159 | var tx1:FastFloat = tx;
160 | var n:FastFloat = a1 * d1 - b1 * c1;
161 |
162 | a = d1 / n;
163 | b = -b1 / n;
164 | c = -c1 / n;
165 | d = a1 / n;
166 | tx = (c1 * ty - d1 * tx1) / n;
167 | ty = -(a1 * ty - b1 * tx1) / n;
168 |
169 | return this;
170 | }
171 |
172 | public inline function copyFrom(other:FastMatrix3):FastMatrix3 {
173 | set(
174 | other.a, other.b,
175 | other.c, other.d,
176 | other.tx, other.ty
177 | );
178 |
179 | return this;
180 | }
181 |
182 | public inline function clone():FastMatrix3 {
183 | return new FastMatrix3(a, b, c, d, tx, ty);
184 | }
185 |
186 | public inline function decompose(into:Spatial) {
187 | var determ:FastFloat = a * d - b * c;
188 |
189 | into.pos.set(tx, ty);
190 |
191 | if(a != 0 || b != 0) {
192 | var r:FastFloat = Math.sqrt(a * a + b * b);
193 | into.rotation = (b > 0) ? Math.acos(a / r) : -Math.acos(a / r);
194 | into.scale.set(r, determ / r);
195 | } else if(c != 0 || d != 0) {
196 | var s:FastFloat = Math.sqrt(c * c + d * d);
197 | into.rotation = Math.PI * 0.5 - (d > 0 ? Math.acos(-c / s) : -Math.acos(c / s));
198 | into.scale.set(determ / s, s);
199 | } else {
200 | into.rotation = 0;
201 | into.scale.set(0,0);
202 | }
203 | }
204 |
205 | public inline function fromMatrix(m:Matrix):FastMatrix3 {
206 | set(m.a, m.b, m.c, m.d, m.tx, m.ty);
207 |
208 | return this;
209 | }
210 |
211 | public inline function getTransformX(x:FastFloat, y:FastFloat):FastFloat {
212 | return a * x + c * y + tx;
213 | }
214 |
215 | public inline function getTransformY(x:FastFloat, y:FastFloat):FastFloat {
216 | return b * x + d * y + ty;
217 | }
218 |
219 | public function setTransform(x:FastFloat, y:FastFloat, angle:FastFloat, sx:FastFloat, sy:FastFloat, ox:FastFloat, oy:FastFloat, kx:FastFloat, ky:FastFloat):FastMatrix3 {
220 | var sin:FastFloat = Math.sin(angle);
221 | var cos:FastFloat = Math.cos(angle);
222 |
223 | a = cos * sx - ky * sin * sy;
224 | b = sin * sx + ky * cos * sy;
225 | c = kx * cos * sx - sin * sy;
226 | d = kx * sin * sx + cos * sy;
227 | tx = x - ox * a - oy * c;
228 | ty = y - ox * b - oy * d;
229 |
230 | return this;
231 | }
232 |
233 | }
234 |
--------------------------------------------------------------------------------
/clay/math/FastMatrix4.hx:
--------------------------------------------------------------------------------
1 | package clay.math;
2 |
3 | typedef FastMatrix4 = kha.math.FastMatrix4;
--------------------------------------------------------------------------------
/clay/math/FastVector2.hx:
--------------------------------------------------------------------------------
1 | package clay.math;
2 |
3 | import kha.FastFloat;
4 |
5 | abstract FastVector2(kha.math.FastVector2) from kha.math.FastVector2 to kha.math.FastVector2 {
6 |
7 | public var x(get, set):FastFloat;
8 | inline function get_x() return this.x;
9 | inline function set_x(v:FastFloat) return this.x = v;
10 |
11 | public var y(get, set):FastFloat;
12 | inline function get_y() return this.y;
13 | inline function set_y(v:FastFloat) return this.y = v;
14 |
15 | public var length(get, set):FastFloat;
16 | inline function get_length() return this.length;
17 | inline function set_length(v:FastFloat) return this.length = v;
18 |
19 | public var lengthSq(get, never):FastFloat;
20 | inline function get_lengthSq() return x * x + y * y;
21 |
22 | public inline function new(x:FastFloat, y:FastFloat) {
23 | this = new kha.math.FastVector2(x, y);
24 | }
25 |
26 | public inline function set(x:FastFloat, y:FastFloat) {
27 | this.x = x;
28 | this.y = y;
29 | }
30 |
31 | public inline function copyFrom(other:FastVector2) {
32 | x = other.x;
33 | y = other.y;
34 | }
35 |
36 | public inline function fromVector2(other:Vector2) {
37 | x = other.x;
38 | y = other.y;
39 | }
40 |
41 | public inline function equals(other:FastVector2):Bool {
42 | return x == other.x && y == other.y;
43 | }
44 |
45 | public inline function clone() {
46 | return new FastVector2(x, y);
47 | }
48 |
49 | public inline function perpendicular(clockwise:Bool = false) {
50 | var tmp:FastFloat = x;
51 | if(clockwise) {
52 | x = y;
53 | y = -tmp;
54 | } else {
55 | x = -y;
56 | y = tmp;
57 | }
58 | }
59 |
60 | public inline function normalize() {
61 | return divideScalar(length);
62 | }
63 |
64 | public inline function dot(other:FastVector2) {
65 | return x * other.x + y * other.y;
66 | }
67 |
68 | public inline function cross(other:FastVector2) {
69 | return x * other.y - y * other.x;
70 | }
71 |
72 | public inline function distance(other:FastVector2) {
73 | return Math.sqrt((other.y - y) * (other.y - y) + (other.x - x) * (other.x - x));
74 | }
75 |
76 | public inline function invert() {
77 | x = -x;
78 | y = -y;
79 | }
80 |
81 | public inline function add(other:FastVector2) {
82 | x += other.x;
83 | y += other.y;
84 | }
85 |
86 | public inline function addXY(x:FastFloat, y:FastFloat) {
87 | this.x += x;
88 | this.y += y;
89 | }
90 |
91 | public inline function addScalar(v:FastFloat) {
92 | x += v;
93 | y += v;
94 | }
95 |
96 | public inline function subtract(other:FastVector2) {
97 | x -= other.x;
98 | y -= other.y;
99 | }
100 |
101 | public inline function subtractXY(x:FastFloat, y:FastFloat) {
102 | this.x -= x;
103 | this.y -= y;
104 | }
105 |
106 | public inline function subtractScalar(v:FastFloat) {
107 | x -= v;
108 | y -= v;
109 | }
110 |
111 | public inline function multiply(other:FastVector2) {
112 | x *= other.x;
113 | y *= other.y;
114 | }
115 |
116 | public inline function multiplyXY(x:FastFloat, y:FastFloat) {
117 | this.x *= x;
118 | this.y *= y;
119 | }
120 |
121 | public inline function multiplyScalar(v:FastFloat) {
122 | x *= v;
123 | y *= v;
124 | }
125 |
126 | public inline function divide(other:FastVector2) {
127 | x /= other.x;
128 | y /= other.y;
129 | }
130 |
131 | public inline function divideXY(x:FastFloat, y:FastFloat) {
132 | this.x /= x;
133 | this.y /= y;
134 | }
135 |
136 | public inline function divideScalar(v:FastFloat) {
137 | x /= v;
138 | y /= v;
139 | }
140 |
141 | public inline function rotate(radians:FastFloat) {
142 | var ca = Math.cos(radians);
143 | var sa = Math.sin(radians);
144 | var tmp = x;
145 | this.x = ca * x - sa * y;
146 | this.y = sa * tmp + ca * y;
147 | }
148 |
149 | public inline function transform(a:FastFloat, b:FastFloat, c:FastFloat, d:FastFloat, tx:FastFloat, ty:FastFloat) {
150 | var tmp = x;
151 | x = a * x + c * y + tx;
152 | y = b * tmp + d * y + ty;
153 | }
154 |
155 | // return angle in radians
156 | public inline function angle2D(other:FastVector2):FastFloat {
157 | return Math.atan2(other.y - y, other.x - x);
158 | }
159 |
160 | static public inline function Add(a:FastVector2, b:FastVector2) {
161 | return new FastVector2(a.x + b.x, a.y + b.y);
162 | }
163 |
164 | static public inline function AddScalar(a:FastVector2, v:FastFloat) {
165 | return new FastVector2(a.x + v, a.y + v);
166 | }
167 |
168 | static public inline function Subtract(a:FastVector2, b:FastVector2) {
169 | return new FastVector2(a.x - b.x, a.y - b.y);
170 | }
171 |
172 | static public inline function SubtractScalar(a:FastVector2, v:FastFloat) {
173 | return new FastVector2(a.x - v, a.y - v);
174 | }
175 |
176 | static public inline function Multiply(a:FastVector2, b:FastVector2) {
177 | return new FastVector2(a.x * b.x, a.y * b.y);
178 | }
179 |
180 | static public inline function MultiplyScalar(a:FastVector2, v:FastFloat) {
181 | return new FastVector2(a.x * v, a.y * v);
182 | }
183 |
184 | static public inline function Divide(a:FastVector2, b:FastVector2) {
185 | return new FastVector2(a.x / b.x, a.y / b.y);
186 | }
187 |
188 | static public inline function DivideScalar(a:FastVector2, v:FastFloat) {
189 | return new FastVector2(a.x / v, a.y / v);
190 | }
191 |
192 | static public inline function Distance(a:FastVector2, v:FastVector2) {
193 | return a.distance(v);
194 | }
195 |
196 | }
197 |
198 |
--------------------------------------------------------------------------------
/clay/math/FastVector3.hx:
--------------------------------------------------------------------------------
1 | package clay.math;
2 |
3 | import kha.FastFloat;
4 |
5 | abstract FastVector3(kha.math.FastVector3) from kha.math.FastVector3 to kha.math.FastVector3 {
6 |
7 | public var x(get, set):FastFloat;
8 | inline function get_x() return this.x;
9 | inline function set_x(v:FastFloat) return this.x = v;
10 |
11 | public var y(get, set):FastFloat;
12 | inline function get_y() return this.y;
13 | inline function set_y(v:FastFloat) return this.y = v;
14 |
15 | public var z(get, set):FastFloat;
16 | inline function get_z() return this.z;
17 | inline function set_z(v:FastFloat) return this.z = v;
18 |
19 | public var length(get, set):FastFloat;
20 | inline function get_length() return this.length;
21 | inline function set_length(v:FastFloat) return this.length = v;
22 |
23 | public var lengthSq(get, never):FastFloat;
24 | inline function get_lengthSq() return x * x + y * y + z * z;
25 |
26 | public inline function new(x:FastFloat, y:FastFloat, z:FastFloat) {
27 | this = new kha.math.FastVector3(x, y, z);
28 | }
29 |
30 | public inline function set(x:FastFloat, y:FastFloat, z:FastFloat) {
31 | this.x = x;
32 | this.y = y;
33 | this.z = z;
34 | }
35 |
36 | public inline function copyFrom(other:FastVector3) {
37 | this.x = other.x;
38 | this.y = other.y;
39 | this.z = other.z;
40 | }
41 |
42 | public inline function equals(other:FastVector3):Bool {
43 | return x == other.x && y == other.y && z == other.z;
44 | }
45 |
46 | public inline function clone() {
47 | return new FastVector3(x, y, z);
48 | }
49 |
50 | public inline function normalize() {
51 | return divideScalar(length);
52 | }
53 |
54 | public inline function dot(other:FastVector3) {
55 | return x * other.x + y * other.y + z * other.z;
56 | }
57 |
58 | public inline function cross(other:FastVector3) {
59 | set(
60 | y * other.z - z * other.y,
61 | z * other.x - x * other.z,
62 | x * other.y - y * other.x
63 | );
64 | }
65 |
66 | public inline function distance(other:FastVector3) {
67 | var dx = x - other.x;
68 | var dy = y - other.y;
69 | var dz = z - other.z;
70 | return Math.sqrt(dx * dx + dy * dy + dz * dz);
71 | }
72 |
73 | public inline function invert() {
74 | x = -x;
75 | y = -y;
76 | z = -z;
77 | }
78 |
79 | public inline function add(other:FastVector3) {
80 | this.x += other.x;
81 | this.y += other.y;
82 | this.z += other.z;
83 | }
84 |
85 | public inline function addXYZ(x:FastFloat, y:FastFloat, z:FastFloat) {
86 | this.x += x;
87 | this.y += y;
88 | this.z += z;
89 | }
90 |
91 | public inline function addScalar(v:FastFloat) {
92 | this.x += v;
93 | this.y += v;
94 | this.z += v;
95 | }
96 |
97 | public inline function subtract(other:FastVector3) {
98 | this.x -= other.x;
99 | this.y -= other.y;
100 | this.z -= other.z;
101 | }
102 |
103 | public inline function subtractXYZ(x:FastFloat, y:FastFloat, z:FastFloat) {
104 | this.x -= x;
105 | this.y -= y;
106 | this.z -= z;
107 | }
108 |
109 | public inline function subtractScalar(v:FastFloat) {
110 | this.x -= v;
111 | this.y -= v;
112 | this.z -= v;
113 | }
114 |
115 | public inline function multiply(other:FastVector3) {
116 | this.x *= other.x;
117 | this.y *= other.y;
118 | this.z *= other.z;
119 | }
120 |
121 | public inline function multiplyXYZ(x:FastFloat, y:FastFloat, z:FastFloat) {
122 | this.x *= x;
123 | this.y *= y;
124 | this.z *= z;
125 | }
126 |
127 | public inline function multiplyScalar(v:FastFloat) {
128 | this.x *= v;
129 | this.y *= v;
130 | this.z *= v;
131 | }
132 |
133 | public inline function divide(other:FastVector3) {
134 | this.x /= other.x;
135 | this.y /= other.y;
136 | this.z /= other.z;
137 | }
138 |
139 | public inline function divideXYZ(x:FastFloat, y:FastFloat, z:FastFloat) {
140 | this.x /= x;
141 | this.y /= y;
142 | this.z /= z;
143 | }
144 |
145 | public inline function divideScalar(v:FastFloat) {
146 | this.x /= v;
147 | this.y /= v;
148 | this.z /= v;
149 | }
150 |
151 | // public inline function rotate(quat:Quaternion) {
152 |
153 | // }
154 |
155 |
156 | static public inline function Add(a:FastVector3, b:FastVector3) {
157 | return new FastVector3(a.x + b.x, a.y + b.y, a.z + b.z);
158 | }
159 |
160 | static public inline function AddScalar(a:FastVector3, v:FastFloat) {
161 | return new FastVector3(a.x + v, a.y + v, a.z + v);
162 | }
163 |
164 | static public inline function Subtract(a:FastVector3, b:FastVector3) {
165 | return new FastVector3(a.x - b.x, a.y - b.y, a.z - b.z);
166 | }
167 |
168 | static public inline function SubtractScalar(a:FastVector3, v:FastFloat) {
169 | return new FastVector3(a.x - v, a.y - v, a.z - v);
170 | }
171 |
172 | static public inline function Multiply(a:FastVector3, b:FastVector3) {
173 | return new FastVector3(a.x * b.x, a.y * b.y, a.z * b.z);
174 | }
175 |
176 | static public inline function MultiplyScalar(a:FastVector3, v:FastFloat) {
177 | return new FastVector3(a.x * v, a.y * v, a.z * v);
178 | }
179 |
180 | static public inline function Divide(a:FastVector3, b:FastVector3) {
181 | return new FastVector3(a.x / b.x, a.y / b.y, a.z / b.z);
182 | }
183 |
184 | static public inline function DivideScalar(a:FastVector3, v:FastFloat) {
185 | return new FastVector3(a.x / v, a.y / v, a.z / v);
186 | }
187 |
188 | static public inline function Distance(a:FastVector3, v:FastVector3) {
189 | return a.distance(v);
190 | }
191 |
192 | }
193 |
194 |
--------------------------------------------------------------------------------
/clay/math/FastVector4.hx:
--------------------------------------------------------------------------------
1 | package clay.math;
2 |
3 | import kha.FastFloat;
4 |
5 | abstract FastVector4(kha.math.FastVector4) from kha.math.FastVector4 to kha.math.FastVector4 {
6 |
7 | public var x(get, set):FastFloat;
8 | inline function get_x() return this.x;
9 | inline function set_x(v:FastFloat) return this.x = v;
10 |
11 | public var y(get, set):FastFloat;
12 | inline function get_y() return this.y;
13 | inline function set_y(v:FastFloat) return this.y = v;
14 |
15 | public var z(get, set):FastFloat;
16 | inline function get_z() return this.z;
17 | inline function set_z(v:FastFloat) return this.z = v;
18 |
19 | public var w(get, set):FastFloat;
20 | inline function get_w() return this.w;
21 | inline function set_w(v:FastFloat) return this.w = v;
22 |
23 | public var length(get, set):FastFloat;
24 | inline function get_length() return this.length;
25 | inline function set_length(v:FastFloat) return this.length = v;
26 |
27 | public var lengthSq(get, never):FastFloat;
28 | inline function get_lengthSq() return x * x + y * y + z * z + w * w;
29 |
30 | public inline function new(x:FastFloat, y:FastFloat, z:FastFloat, w:FastFloat) {
31 | this = new kha.math.FastVector4(x, y, z, w);
32 | }
33 |
34 | public inline function set(x:FastFloat, y:FastFloat, z:FastFloat, w:FastFloat) {
35 | this.x = x;
36 | this.y = y;
37 | this.z = z;
38 | this.w = w;
39 | }
40 |
41 | public inline function copyFrom(other:FastVector4) {
42 | this.x = other.x;
43 | this.y = other.y;
44 | this.z = other.z;
45 | this.w = other.w;
46 | }
47 |
48 | public inline function equals(other:FastVector4):Bool {
49 | return x == other.x && y == other.y && z == other.z && w == other.w;
50 | }
51 |
52 | public inline function clone() {
53 | return new FastVector4(x, y, z, w);
54 | }
55 |
56 | public inline function normalize() {
57 | return divideScalar(length);
58 | }
59 |
60 | public inline function invert() {
61 | x = -x;
62 | y = -y;
63 | z = -z;
64 | w = -w;
65 | }
66 |
67 | public inline function add(other:FastVector4) {
68 | this.x += other.x;
69 | this.y += other.y;
70 | this.z += other.z;
71 | this.w += other.w;
72 | }
73 |
74 | public inline function addXYZW(x:FastFloat, y:FastFloat, z:FastFloat, w:FastFloat) {
75 | this.x += x;
76 | this.y += y;
77 | this.z += z;
78 | this.w += w;
79 | }
80 |
81 | public inline function addScalar(v:FastFloat) {
82 | this.x += v;
83 | this.y += v;
84 | this.z += v;
85 | this.w += v;
86 | }
87 |
88 | public inline function subtract(other:FastVector4) {
89 | this.x -= other.x;
90 | this.y -= other.y;
91 | this.z -= other.z;
92 | this.w -= other.w;
93 | }
94 |
95 | public inline function subtractXYZW(x:FastFloat, y:FastFloat, z:FastFloat, w:FastFloat) {
96 | this.x -= x;
97 | this.y -= y;
98 | this.z -= z;
99 | this.w -= w;
100 | }
101 |
102 | public inline function subtractScalar(v:FastFloat) {
103 | this.x -= v;
104 | this.y -= v;
105 | this.z -= v;
106 | this.w -= v;
107 | }
108 |
109 | public inline function multiply(other:FastVector4) {
110 | this.x *= other.x;
111 | this.y *= other.y;
112 | this.z *= other.z;
113 | this.w *= other.w;
114 | }
115 |
116 | public inline function multiplyXYZW(x:FastFloat, y:FastFloat, z:FastFloat, w:FastFloat) {
117 | this.x *= x;
118 | this.y *= y;
119 | this.z *= z;
120 | this.w *= w;
121 | }
122 |
123 | public inline function multiplyScalar(v:FastFloat) {
124 | this.x *= v;
125 | this.y *= v;
126 | this.z *= v;
127 | this.w *= v;
128 | }
129 |
130 | public inline function divide(other:FastVector4) {
131 | this.x /= other.x;
132 | this.y /= other.y;
133 | this.z /= other.z;
134 | this.w /= other.w;
135 | }
136 |
137 | public inline function divideXYZW(x:FastFloat, y:FastFloat, z:FastFloat, w:FastFloat) {
138 | this.x /= x;
139 | this.y /= y;
140 | this.z /= z;
141 | this.w /= w;
142 | }
143 |
144 | public inline function divideScalar(v:FastFloat) {
145 | this.x /= v;
146 | this.y /= v;
147 | this.z /= v;
148 | this.w /= v;
149 | }
150 |
151 | static public inline function Add(a:FastVector4, b:FastVector4) {
152 | return new FastVector4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
153 | }
154 |
155 | static public inline function AddScalar(a:FastVector4, v:FastFloat) {
156 | return new FastVector4(a.x + v, a.y + v, a.z + v, a.w + v);
157 | }
158 |
159 | static public inline function Subtract(a:FastVector4, b:FastVector4) {
160 | return new FastVector4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
161 | }
162 |
163 | static public inline function SubtractScalar(a:FastVector4, v:FastFloat) {
164 | return new FastVector4(a.x - v, a.y - v, a.z - v, a.w - v);
165 | }
166 |
167 | static public inline function Multiply(a:FastVector4, b:FastVector4) {
168 | return new FastVector4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
169 | }
170 |
171 | static public inline function MultiplyScalar(a:FastVector4, v:FastFloat) {
172 | return new FastVector4(a.x * v, a.y * v, a.z * v, a.w * v);
173 | }
174 |
175 | static public inline function Divide(a:FastVector4, b:FastVector4) {
176 | return new FastVector4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
177 | }
178 |
179 | static public inline function DivideScalar(a:FastVector4, v:FastFloat) {
180 | return new FastVector4(a.x / v, a.y / v, a.z / v, a.w / v);
181 | }
182 |
183 | }
184 |
185 |
--------------------------------------------------------------------------------
/clay/math/Matrix.hx:
--------------------------------------------------------------------------------
1 | package clay.math;
2 |
3 | import clay.math.Transform;
4 |
5 | /*
6 | | a | c | tx |
7 | | b | d | ty |
8 | | 0 | 0 | 1 |
9 | */
10 |
11 | class Matrix {
12 |
13 | public var a:Float;
14 | public var b:Float;
15 | public var c:Float;
16 | public var d:Float;
17 | public var tx:Float;
18 | public var ty:Float;
19 |
20 | public function new(a:Float = 1, b:Float = 0, c:Float = 0, d:Float = 1, tx:Float = 0, ty:Float = 0) {
21 | set(a, b, c, d, tx, ty);
22 | }
23 |
24 | public inline function identity():Matrix {
25 | set(
26 | 1, 0,
27 | 0, 1,
28 | 0, 0
29 | );
30 |
31 | return this;
32 | }
33 |
34 | public inline function set(a:Float, b:Float, c:Float, d:Float, tx:Float, ty:Float):Matrix {
35 | this.a = a;
36 | this.b = b;
37 | this.c = c;
38 | this.d = d;
39 | this.tx = tx;
40 | this.ty = ty;
41 |
42 | return this;
43 | }
44 |
45 | public inline function translate(x:Float, y:Float):Matrix {
46 | tx += x;
47 | ty += y;
48 |
49 | return this;
50 | }
51 |
52 | public inline function prependTranslate(x:Float, y:Float):Matrix {
53 | tx = a * x + c * y + tx;
54 | ty = b * x + d * y + ty;
55 |
56 | return this;
57 | }
58 |
59 | public inline function scale(x:Float, y:Float):Matrix {
60 | a *= x;
61 | b *= x;
62 | c *= y;
63 | d *= y;
64 |
65 | return this;
66 | }
67 |
68 | // https://github.com/yoshihitofujiwara/INKjs/blob/master/src/class_geometry/Matrix2.js
69 | public inline function skew(x:Float, y:Float):Matrix {
70 | var cy:Float = Math.cos(y);
71 | var sy:Float = Math.sin(y);
72 | var sx:Float = -Math.sin(x);
73 | var cx:Float = Math.cos(x);
74 |
75 | var a1:Float = a;
76 | var b1:Float = b;
77 | var c1:Float = c;
78 | var d1:Float = d;
79 |
80 | a = (cy * a1) + (sy * c1);
81 | b = (cy * b1) + (sy * d1);
82 | c = (sx * a1) + (cx * c1);
83 | d = (sx * b1) + (cx * d1);
84 |
85 | return this;
86 | }
87 |
88 | public inline function rotate(radians:Float):Matrix {
89 | var sin:Float = Math.sin(radians);
90 | var cos:Float = Math.cos(radians);
91 |
92 | var a1:Float = a;
93 | var b1:Float = b;
94 | var c1:Float = c;
95 | var d1:Float = d;
96 |
97 | a = a1 * cos + b1 * sin;
98 | b = a1 * -sin + b1 * cos;
99 | c = c1 * cos + d1 * sin;
100 | d = c1 * -sin + d1 * cos;
101 |
102 | return this;
103 | }
104 |
105 | public inline function append(m:Matrix):Matrix {
106 | var a1 = a;
107 | var b1 = b;
108 | var c1 = c;
109 | var d1 = d;
110 |
111 | a = m.a * a1 + m.b * c1;
112 | b = m.a * b1 + m.b * d1;
113 | c = m.c * a1 + m.d * c1;
114 | d = m.c * b1 + m.d * d1;
115 |
116 | tx = m.tx * a1 + m.ty * c1 + tx;
117 | ty = m.tx * b1 + m.ty * d1 + ty;
118 |
119 | return this;
120 | }
121 |
122 | public inline function prepend(m:Matrix):Matrix {
123 | var tx1 = tx;
124 |
125 | if (m.a != 1 || m.b != 0 || m.c != 0 || m.d != 1) {
126 | var a1 = a;
127 | var c1 = c;
128 |
129 | a = a1 * m.a + b * m.c;
130 | b = a1 * m.b + b * m.d;
131 | c = c1 * m.a + d * m.c;
132 | d = c1 * m.b + d * m.d;
133 | }
134 |
135 | tx = tx1 * m.a + ty * m.c + m.tx;
136 | ty = tx1 * m.b + ty * m.d + m.ty;
137 |
138 | return this;
139 | }
140 |
141 | public inline function orto(left:Float, right:Float, bottom:Float, top:Float):Matrix {
142 | var sx:Float = 1.0 / (right - left);
143 | var sy:Float = 1.0 / (top - bottom);
144 |
145 | set(
146 | 2.0*sx, 0,
147 | 0, 2.0*sy,
148 | -(right+left)*sx, -(top+bottom)*sy
149 | );
150 |
151 | return this;
152 | }
153 |
154 | public inline function invert():Matrix {
155 | var a1:Float = a;
156 | var b1:Float = b;
157 | var c1:Float = c;
158 | var d1:Float = d;
159 | var tx1:Float = tx;
160 | var n:Float = a1 * d1 - b1 * c1;
161 |
162 | a = d1 / n;
163 | b = -b1 / n;
164 | c = -c1 / n;
165 | d = a1 / n;
166 | tx = (c1 * ty - d1 * tx1) / n;
167 | ty = -(a1 * ty - b1 * tx1) / n;
168 |
169 | return this;
170 | }
171 |
172 | public inline function copyFrom(other:Matrix):Matrix {
173 | set(
174 | other.a, other.b,
175 | other.c, other.d,
176 | other.tx, other.ty
177 | );
178 |
179 | return this;
180 | }
181 |
182 | public inline function clone():Matrix {
183 | return new Matrix(a, b, c, d, tx, ty);
184 | }
185 |
186 | // public inline function decompose(into:Spatial) {
187 | // var determ = a * d - b * c;
188 |
189 | // into.pos.set(tx, ty);
190 |
191 | // if(a != 0 || b != 0) {
192 | // var r = Math.sqrt(a * a + b * b);
193 | // into.rotation = (b > 0) ? Math.acos(a / r) : -Math.acos(a / r);
194 | // into.scale.set(r, determ / r);
195 | // } else if(c != 0 || d != 0) {
196 | // var s = Math.sqrt(c * c + d * d);
197 | // into.rotation = Math.PI * 0.5 - (d > 0 ? Math.acos(-c / s) : -Math.acos(c / s));
198 | // into.scale.set(determ / s, s);
199 | // } else {
200 | // into.rotation = 0;
201 | // into.scale.set(0,0);
202 | // }
203 | // }
204 | public inline function fromFastMatrix3(m:FastMatrix3):Matrix {
205 | set(m.a, m.b, m.c, m.d, m.tx, m.ty);
206 | return this;
207 | }
208 |
209 | public inline function getTransformX(x:Float, y:Float):Float {
210 | return a * x + c * y + tx;
211 | }
212 |
213 | public inline function getTransformY(x:Float, y:Float):Float {
214 | return b * x + d * y + ty;
215 | }
216 |
217 | public function setTransform(x:Float, y:Float, angle:Float, sx:Float, sy:Float, ox:Float, oy:Float, kx:Float, ky:Float):Matrix {
218 | var sin:Float = Math.sin(angle);
219 | var cos:Float = Math.cos(angle);
220 |
221 | a = cos * sx - ky * sin * sy;
222 | b = sin * sx + ky * cos * sy;
223 | c = kx * cos * sx - sin * sy;
224 | d = kx * sin * sx + cos * sy;
225 | tx = x - ox * a - oy * c;
226 | ty = y - ox * b - oy * d;
227 |
228 | return this;
229 | }
230 |
231 | }
--------------------------------------------------------------------------------
/clay/math/Rectangle.hx:
--------------------------------------------------------------------------------
1 | package clay.math;
2 |
3 | @:structInit
4 | class Rectangle {
5 |
6 | public var x(get, set):Float;
7 | var _x:Float;
8 | inline function get_x() return _x;
9 | function set_x(v:Float) return _x = v;
10 |
11 | public var y(get, set):Float;
12 | var _y:Float;
13 | inline function get_y() return _y;
14 | function set_y(v:Float) return _y = v;
15 |
16 | public var w(get, set):Float;
17 | var _w:Float;
18 | inline function get_w() return _w;
19 | function set_w(v:Float) return _w = v;
20 |
21 | public var h(get, set):Float;
22 | var _h:Float;
23 | inline function get_h() return _h;
24 | function set_h(v:Float) return _h = v;
25 |
26 | public function new(x:Float = 0, y:Float = 0, w:Float = 0, h:Float = 0) {
27 | _x = x;
28 | _y = y;
29 | _w = w;
30 | _h = h;
31 | }
32 |
33 | public function set(x:Float, y:Float, w:Float, h:Float):Rectangle {
34 | _x = x;
35 | _y = y;
36 | _w = w;
37 | _h = h;
38 |
39 | return this;
40 | }
41 |
42 | public function pointInside(px:Float, py:Float) {
43 | if(px < x) return false;
44 | if(py < y) return false;
45 | if(px > x + w) return false;
46 | if(py > y + h) return false;
47 |
48 | return true;
49 | }
50 |
51 | public function overlaps(other:Rectangle) {
52 | if(
53 | x < (other.x + other.w) &&
54 | y < (other.y + other.h) &&
55 | (x + w) > other.x &&
56 | (y + h) > other.y
57 | ) {
58 | return true;
59 | }
60 |
61 | return false;
62 | }
63 |
64 | public function clamp(other:Rectangle) {
65 | if(x < other.x) {
66 | w -= other.x - x;
67 | x = other.x;
68 | }
69 |
70 | if(y < other.y) {
71 | h -= other.y - y;
72 | y = other.y;
73 | }
74 |
75 | if(x + w > other.x + other.w) {
76 | w = other.x + other.w - x;
77 | }
78 |
79 | if(y + h > other.y + other.h) {
80 | h = other.y + other.h - y;
81 | }
82 | }
83 |
84 | public function expand(other:Rectangle) {
85 | if(other.x < x) {
86 | x = other.x;
87 | }
88 |
89 | if(other.y < y) {
90 | y = other.y;
91 | }
92 |
93 | if(x + w < other.x + other.w) {
94 | w = other.x + other.w - x;
95 | }
96 |
97 | if(y + h < other.y + other.h) {
98 | h = other.y + other.h - y;
99 | }
100 | }
101 |
102 | public inline function equals(other:Rectangle):Bool {
103 | return x == other.x &&
104 | y == other.y &&
105 | w == other.w &&
106 | h == other.h;
107 | }
108 |
109 | public inline function copyFrom(other:Rectangle):Rectangle {
110 | return set(other.x, other.y, other.w, other.h);
111 | }
112 |
113 | public inline function clone():Rectangle {
114 | return new Rectangle(x, y, w, h);
115 | }
116 |
117 | @:noCompletion public function toString() {
118 | return '{$x, $y, $w, $h}';
119 | }
120 |
121 | }
--------------------------------------------------------------------------------
/clay/math/RectangleCallback.hx:
--------------------------------------------------------------------------------
1 | package clay.math;
2 |
3 | @:structInit
4 | class RectangleCallback extends Rectangle {
5 |
6 | public var ignoreListeners:Bool = false;
7 | public var listener:(v:Rectangle)->Void;
8 |
9 | public function new(x:Float = 0, y:Float = 0, w:Float = 0, h:Float = 0) {
10 | super(x, y, w, h);
11 | }
12 |
13 | override function set(x:Float, y:Float, w:Float, h:Float) {
14 | _x = x;
15 | _y = y;
16 | _w = w;
17 | _h = h;
18 | callListener();
19 | return this;
20 | }
21 |
22 | override function set_x(v:Float):Float {
23 | super.set_x(v);
24 | callListener();
25 | return v;
26 | }
27 |
28 | override function set_y(v:Float):Float {
29 | super.set_y(v);
30 | callListener();
31 | return v;
32 | }
33 |
34 | override function set_w(v:Float):Float {
35 | super.set_w(v);
36 | callListener();
37 | return v;
38 | }
39 |
40 | override function set_h(v:Float):Float {
41 | super.set_h(v);
42 | callListener();
43 | return v;
44 | }
45 |
46 | public inline function listen(f:(r:Rectangle)->Void):RectangleCallback {
47 | listener = f;
48 | return this;
49 | }
50 |
51 | inline function callListener() {
52 | if(listener != null && !ignoreListeners) {
53 | listener(this);
54 | }
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/clay/math/Transform.hx:
--------------------------------------------------------------------------------
1 | package clay.math;
2 |
3 | import clay.math.Vector2;
4 | import clay.math.Vector2Callback;
5 | import clay.math.Matrix;
6 | import clay.utils.Math;
7 |
8 | class Transform {
9 |
10 | public var parent(default, set):Transform;
11 |
12 | public var local(get, never):Spatial;
13 | inline function get_local() return _local;
14 |
15 | public var world(get, never):Spatial;
16 | inline function get_world() return _world;
17 |
18 | public var pos(get, never):Vector2Callback;
19 | inline function get_pos() return _local.pos;
20 |
21 | public var scale(get, never):Vector2Callback;
22 | inline function get_scale() return _local.scale;
23 |
24 | public var rotation(get, set):Float;
25 | inline function get_rotation() return _local.rotation;
26 | function set_rotation(v:Float):Float {
27 | setDirty();
28 | return _local.rotation = v;
29 | }
30 |
31 | public var origin(default, null):Vector2Callback;
32 |
33 | public var skew(get, never):Vector2Callback;
34 | inline function get_skew() return _local.skew;
35 |
36 | public var manualUpdate:Bool;
37 | public var dirty:Bool;
38 |
39 | var _local:Spatial;
40 | var _world:Spatial;
41 | var _cleaning:Bool;
42 | var _cleanHandlers:Array<(t:Transform)->Void>;
43 |
44 | public function new() {
45 | _local = new Spatial();
46 | _world = new Spatial();
47 | origin = new Vector2Callback();
48 |
49 | manualUpdate = false;
50 | dirty = true;
51 | _cleaning = false;
52 |
53 | _local.pos.listen(setDirtyCallback);
54 | _local.scale.listen(setDirtyCallback);
55 | _local.skew.listen(setDirtyCallback);
56 | origin.listen(setDirtyCallback);
57 | }
58 |
59 | public function update() {
60 | updateParent();
61 |
62 | if(dirty && !_cleaning && !manualUpdate) {
63 | _cleaning = true;
64 |
65 | updateLocalMatrix();
66 | appendWorldMatrix();
67 |
68 | _world.decompose(false);
69 |
70 | _cleaning = false;
71 | dirty = false;
72 |
73 | updateListeners();
74 | }
75 | }
76 |
77 | public function listen(handler:(t:Transform)->Void) {
78 | if(_cleanHandlers == null) {
79 | _cleanHandlers = [];
80 | }
81 | _cleanHandlers.push(handler);
82 | }
83 |
84 | public function unlisten(handler:(t:Transform)->Void) {
85 | if(_cleanHandlers != null) {
86 | _cleanHandlers.remove(handler);
87 | if(_cleanHandlers.length == 0) {
88 | _cleanHandlers = null;
89 | }
90 | }
91 | }
92 |
93 | inline function updateParent() {
94 | if(parent != null) {
95 | parent.update();
96 | }
97 | }
98 |
99 | inline function updateLocalMatrix() {
100 | // _local.matrix.setTransform(pos.x, pos.y, Math.radians(-rotation), scale.x, scale.y, origin.x, origin.y, skew.x, skew.y);
101 | _local.matrix.identity()
102 | .translate(pos.x, pos.y)
103 | .rotate(Math.radians(-rotation))
104 | .scale(scale.x, scale.y)
105 | .prependTranslate(-origin.x, -origin.y);
106 | }
107 |
108 | inline function appendWorldMatrix() {
109 | if(parent != null) {
110 | _world.matrix.copyFrom(parent._world.matrix).append(_local.matrix);
111 | } else {
112 | _world.matrix.copyFrom(_local.matrix);
113 | }
114 | }
115 |
116 | inline function updateListeners() {
117 | if(_cleanHandlers != null && _cleanHandlers.length > 0) {
118 | for(handler in _cleanHandlers) {
119 | handler(this);
120 | }
121 | }
122 | }
123 |
124 | function set_parent(v:Transform):Transform {
125 | if(parent != null) {
126 | parent.unlisten(onParentCleaned);
127 | }
128 | parent = v;
129 | if(parent != null) {
130 | parent.listen(onParentCleaned);
131 | }
132 | return v;
133 | }
134 |
135 | function setDirtyCallback(v:Vector2) {
136 | setDirty();
137 | }
138 |
139 | inline function onParentCleaned(p:Transform) {
140 | setDirty();
141 | }
142 |
143 | inline function setDirty() {
144 | dirty = true;
145 | }
146 |
147 | }
148 |
149 | class Spatial {
150 |
151 | public var pos:Vector2Callback;
152 | public var scale:Vector2Callback;
153 | public var skew:Vector2Callback;
154 | public var rotation:Float;
155 | public var matrix:Matrix;
156 | public var autoDecompose:Bool = false;
157 |
158 | public function new() {
159 | pos = new Vector2Callback();
160 | scale = new Vector2Callback(1,1);
161 | skew = new Vector2Callback();
162 | rotation = 0;
163 | matrix = new Matrix();
164 | }
165 |
166 | //assigns the local values (pos/rotation/scale/skew) according to the matrix
167 | //when called manually, will make sure it happens using force.
168 | //if force is false, autoDecompose will apply
169 | public inline function decompose(force:Bool = true):Spatial {
170 | if(autoDecompose || force) {
171 | var a = matrix.a;
172 | var b = matrix.b;
173 | var c = matrix.c;
174 | var d = matrix.d;
175 |
176 | var skewX = -Math.atan2(-c, d);
177 | var skewY = Math.atan2(b, a);
178 |
179 | var delta = Math.abs(skewX + skewY);
180 |
181 | if(delta < Math.EPSILON || Math.abs(Math.TAU - delta) < Math.EPSILON) {
182 | rotation = skewY;
183 | skew.set(0, 0);
184 | } else {
185 | rotation = 0;
186 | skew.set(skewX, skewY);
187 | }
188 |
189 | scale.set(Math.sqrt((a * a) + (b * b)), Math.sqrt((c * c) + (d * d)));
190 | pos.set(matrix.tx, matrix.ty);
191 | }
192 | return this;
193 | }
194 |
195 | }
196 |
--------------------------------------------------------------------------------
/clay/math/Vector2.hx:
--------------------------------------------------------------------------------
1 | package clay.math;
2 |
3 | @:structInit
4 | class Vector2 {
5 |
6 | public var x(get, set):Float;
7 | var _x:Float;
8 | inline function get_x() return _x;
9 | function set_x(v:Float) return _x = v;
10 |
11 | public var y(get, set):Float;
12 | var _y:Float;
13 | inline function get_y() return _y;
14 | function set_y(v:Float) return _y = v;
15 |
16 | public var length(get, set):Float;
17 | inline function get_length() return Math.sqrt(x * x + y * y);
18 | inline function set_length(v:Float) {
19 | normalize();
20 | multiplyScalar(v);
21 | return v;
22 | }
23 |
24 | public var lengthSq(get, never):Float;
25 | inline function get_lengthSq() return x * x + y * y;
26 |
27 | public inline function new(x:Float = 0, y:Float = 0) {
28 | _x = x;
29 | _y = y;
30 | }
31 |
32 | public function set(x:Float, y:Float) {
33 | _x = x;
34 | _y = y;
35 |
36 | return this;
37 | }
38 |
39 | public inline function copyFrom(other:Vector2) {
40 | set(other.x, other.y);
41 | return this;
42 | }
43 |
44 | public inline function equals(other:Vector2):Bool {
45 | return x == other.x && y == other.y;
46 | }
47 |
48 | public inline function clone() {
49 | return new Vector2(x, y);
50 | }
51 |
52 | public inline function normalize() {
53 | return divideScalar(length);
54 | }
55 |
56 | public inline function dot(other:Vector2) {
57 | return x * other.x + y * other.y;
58 | }
59 |
60 | public inline function cross(other:Vector2) {
61 | return x * other.y - y * other.x;
62 | }
63 |
64 | public inline function distance(other:Vector2) {
65 | return Math.sqrt((other.y - y) * (other.y - y) + (other.x - x) * (other.x - x));
66 | }
67 |
68 | public inline function invert() {
69 | set(-x, -y);
70 | return this;
71 | }
72 |
73 | public inline function add(other:Vector2) {
74 | set(x + other.x, y + other.y);
75 | return this;
76 | }
77 |
78 | public inline function addXY(x:Float, y:Float) {
79 | set(this.x + x, this.y + y);
80 | return this;
81 | }
82 |
83 | public inline function addScalar(v:Float) {
84 | set(x + v, y + v);
85 | return this;
86 | }
87 |
88 | public inline function subtract(other:Vector2) {
89 | set(x - other.x, y - other.y);
90 | return this;
91 | }
92 |
93 | public inline function subtractXY(x:Float, y:Float) {
94 | set(this.x - x, this.y - y);
95 | return this;
96 | }
97 |
98 | public inline function subtractScalar(v:Float) {
99 | set(x - v, y - v);
100 | return this;
101 | }
102 |
103 | public inline function multiply(other:Vector2) {
104 | set(x * other.x, y * other.y);
105 | return this;
106 | }
107 |
108 | public inline function multiplyXY(x:Float, y:Float) {
109 | set(this.x * x, this.y * y);
110 | return this;
111 | }
112 |
113 | public inline function multiplyScalar(v:Float) {
114 | set(x * v, y * v);
115 | return this;
116 | }
117 |
118 | public inline function divide(other:Vector2) {
119 | set(x / other.x, y / other.y);
120 | return this;
121 | }
122 |
123 | public inline function divideXY(x:Float, y:Float) {
124 | set(this.x / x, this.y / y);
125 | return this;
126 | }
127 |
128 | public inline function divideScalar(v:Float) {
129 | set(x / v, y / v);
130 | return this;
131 | }
132 |
133 | public inline function perpendicular(clockwise:Bool = true) {
134 | if(clockwise) {
135 | set(y, -x);
136 | } else {
137 | set(-y, x);
138 | }
139 | return this;
140 | }
141 |
142 | public inline function rotate(radians:Float) {
143 | var ca = Math.cos(radians);
144 | var sa = Math.sin(radians);
145 | set(ca * x - sa * y, sa * x + ca * y);
146 | return this;
147 | }
148 |
149 | public inline function transform(a:Float, b:Float, c:Float, d:Float, tx:Float, ty:Float) {
150 | set(a * x + c * y + tx, b * x + d * y + ty);
151 | return this;
152 | }
153 |
154 | // return angle in radians
155 | public inline function angle2D(other:Vector2):Float {
156 | return Math.atan2(other.y - y, other.x - x);
157 | }
158 |
159 | static public inline function Add(a:Vector2, b:Vector2) {
160 | return new Vector2(a.x + b.x, a.y + b.y);
161 | }
162 |
163 | static public inline function AddScalar(a:Vector2, v:Float) {
164 | return new Vector2(a.x + v, a.y + v);
165 | }
166 |
167 | static public inline function Subtract(a:Vector2, b:Vector2) {
168 | return new Vector2(a.x - b.x, a.y - b.y);
169 | }
170 |
171 | static public inline function SubtractScalar(a:Vector2, v:Float) {
172 | return new Vector2(a.x - v, a.y - v);
173 | }
174 |
175 | static public inline function Multiply(a:Vector2, b:Vector2) {
176 | return new Vector2(a.x * b.x, a.y * b.y);
177 | }
178 |
179 | static public inline function MultiplyScalar(a:Vector2, v:Float) {
180 | return new Vector2(a.x * v, a.y * v);
181 | }
182 |
183 | static public inline function Divide(a:Vector2, b:Vector2) {
184 | return new Vector2(a.x / b.x, a.y / b.y);
185 | }
186 |
187 | static public inline function DivideScalar(a:Vector2, v:Float) {
188 | return new Vector2(a.x / v, a.y / v);
189 | }
190 |
191 | static public inline function Distance(a:Vector2, v:Vector2) {
192 | return a.distance(v);
193 | }
194 |
195 | }
196 |
197 |
--------------------------------------------------------------------------------
/clay/math/Vector2Callback.hx:
--------------------------------------------------------------------------------
1 | package clay.math;
2 |
3 | @:structInit
4 | class Vector2Callback extends Vector2 {
5 |
6 | public var ignoreListeners:Bool = false;
7 | public var listener:(v:Vector2)->Void;
8 |
9 | public inline function new(x:Float = 0, y:Float = 0) {
10 | super(x, y);
11 | }
12 |
13 | override function set(x:Float, y:Float) {
14 | _x = x;
15 | _y = y;
16 | callListener();
17 | return this;
18 | }
19 |
20 | override function set_x(v:Float):Float {
21 | _x = v;
22 | callListener();
23 | return v;
24 | }
25 |
26 | override function set_y(v:Float):Float {
27 | _y = v;
28 | callListener();
29 | return v;
30 | }
31 |
32 | public function listen(f:(v:Vector2)->Void) {
33 | listener = f;
34 | }
35 |
36 | inline function callListener() {
37 | if(listener != null && !ignoreListeners) {
38 | listener(this);
39 | }
40 | }
41 |
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/clay/resources/AudioResource.hx:
--------------------------------------------------------------------------------
1 | package clay.resources;
2 |
3 | import haxe.io.Bytes;
4 | import clay.utils.Float32Array;
5 | import clay.resources.Resource;
6 | import clay.Resources;
7 |
8 | class AudioResource extends Resource {
9 |
10 | public var sound:kha.Sound;
11 |
12 | public var duration(get, never):Float;
13 | inline function get_duration() return sound.length;
14 |
15 | public var channels(get, never):Int;
16 | inline function get_channels() return sound.channels;
17 |
18 | public var compressedData(get, set):Bytes;
19 | inline function get_compressedData() return sound.compressedData;
20 | inline function set_compressedData(v:Bytes) return sound.compressedData = v;
21 |
22 | public var uncompressedData(get, set):Float32Array;
23 | inline function get_uncompressedData() return sound.uncompressedData;
24 | inline function set_uncompressedData(v:Float32Array) return sound.uncompressedData = v;
25 |
26 | public function new(?sound:kha.Sound) {
27 | if(sound == null) {
28 | sound = new kha.Sound();
29 | }
30 |
31 | this.sound = sound;
32 |
33 | resourceType = ResourceType.AUDIO;
34 | }
35 |
36 | override function unload() {
37 | sound.unload();
38 | }
39 |
40 | override function memoryUse() {
41 | return sound.uncompressedData.length;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/clay/resources/BytesResource.hx:
--------------------------------------------------------------------------------
1 | package clay.resources;
2 |
3 | import haxe.io.Bytes;
4 | import clay.Resources;
5 | import clay.resources.Resource;
6 |
7 | class BytesResource extends Resource {
8 |
9 | static public function create(_size:Int):BytesResource {
10 | var b = kha.Blob.alloc(_size);
11 | return new BytesResource(b);
12 | }
13 |
14 | static public function createFromBytes(bytes:Bytes):BytesResource {
15 | var b = kha.Blob.fromBytes(bytes);
16 | return new BytesResource(b);
17 | }
18 |
19 | public var blob:kha.Blob;
20 |
21 | public function new(blob:kha.Blob) {
22 | this.blob = blob;
23 | resourceType = ResourceType.BYTES;
24 | }
25 |
26 | override function unload() {
27 | blob.unload();
28 | }
29 |
30 | override function memoryUse() {
31 | return blob.length;
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/clay/resources/JsonResource.hx:
--------------------------------------------------------------------------------
1 | package clay.resources;
2 |
3 | import clay.Resources;
4 |
5 | class JsonResource extends Resource {
6 |
7 | public var json:Dynamic;
8 |
9 | public function new(json:Dynamic) {
10 | this.json = json;
11 | resourceType = ResourceType.JSON;
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/clay/resources/Resource.hx:
--------------------------------------------------------------------------------
1 | package clay.resources;
2 |
3 | import clay.Resources;
4 |
5 | @:allow(clay.Resources)
6 | class Resource {
7 |
8 | public var name:String;
9 | public var id(default, null):Int = -1;
10 | public var resourceType(default, null):ResourceType;
11 | public var references(default, null):Int = 0;
12 |
13 | public function unload() {}
14 |
15 | public function memoryUse():Int {
16 | return 0;
17 | }
18 |
19 | public function ref() {
20 | references++;
21 | }
22 |
23 | public function unref() {
24 | references--;
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/clay/resources/TextResource.hx:
--------------------------------------------------------------------------------
1 | package clay.resources;
2 |
3 | import clay.Resources;
4 |
5 | class TextResource extends Resource {
6 |
7 | public var text:String;
8 |
9 | public function new(text:String) {
10 | this.text = text;
11 | resourceType = ResourceType.TEXT;
12 | }
13 |
14 | override function memoryUse() {
15 | return text != null ? text.length : 0;
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/clay/utils/Align.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | enum abstract Align(Int) {
4 | var LEFT;
5 | var RIGHT;
6 | var TOP;
7 | var BOTTOM;
8 | var CENTER;
9 | }
10 |
--------------------------------------------------------------------------------
/clay/utils/ArrayTools.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | class ArrayTools {
4 |
5 | static public inline function clear(array:Array) {
6 | #if cpp
7 | // splice causes Array allocation, so prefer pop for most arrays
8 | if (array.length > 256) {
9 | array.splice(0, array.length);
10 | } else {
11 | while (array.length > 0) array.pop();
12 | }
13 | #else
14 | untyped array.length = 0;
15 | #end
16 | }
17 |
18 | static public function shuffle(a:Array) {
19 | var i:Int = a.length, j:Int, t:T;
20 | while (--i > 0) {
21 | t = a[i];
22 | a[i] = a[j = Clay.random.int(i + 1)];
23 | a[j] = t;
24 | }
25 | }
26 |
27 | static public function insertSortedKey(list:Array, key:T, compare:(a:T, b:T)->Int):Void {
28 | var result:Int = 0;
29 | var mid:Int = 0;
30 | var min:Int = 0;
31 | var max:Int = list.length - 1;
32 |
33 | while (max >= min) {
34 | mid = min + Std.int((max - min) / 2);
35 | result = compare(list[mid], key);
36 | if (result > 0) {
37 | max = mid - 1;
38 | } else if (result < 0) {
39 | min = mid + 1;
40 | } else {
41 | return;
42 | }
43 | }
44 |
45 | list.insert(result > 0 ? mid : mid + 1, key);
46 | }
47 |
48 | }
--------------------------------------------------------------------------------
/clay/utils/Atlas.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | import clay.Clay;
4 | import clay.resources.Texture;
5 | import clay.math.Rectangle;
6 | import haxe.io.Path;
7 |
8 | class Atlas {
9 |
10 | static public function loadFromPath(path:String):Atlas {
11 | var jsonRes = Clay.resources.json(path);
12 | var data:AtlasData = jsonRes.json;
13 | var dir = Path.directory(path);
14 | var p = Path.join([dir, data.textureName]);
15 | var texture = Clay.resources.texture(p);
16 | var atlas = new Atlas(texture);
17 | for (r in data.regions) {
18 | atlas.addRegion(r.name, new Rectangle(r.x, r.y, r.w, r.h));
19 | }
20 | return atlas;
21 | }
22 |
23 | public var texture:Texture;
24 | var regions:Map;
25 |
26 | public function new(texture:Texture) {
27 | this.texture = texture;
28 | this.regions = new Map();
29 | }
30 |
31 | public function addRegion(name:String, rect:Rectangle) {
32 | return regions.set(name, rect);
33 | }
34 |
35 | public function getRegion(name:String):Rectangle {
36 | return regions.get(name);
37 | }
38 |
39 | public function removeRegion(name:String) {
40 | return regions.remove(name);
41 | }
42 |
43 | public function toJson():AtlasData {
44 | var regionsData:Array = [];
45 | var rect:Rectangle;
46 | for (k in regions.keys()) {
47 | rect = regions.get(k);
48 | regionsData.push(
49 | {
50 | name: k,
51 | x: rect.x,
52 | y: rect.y,
53 | w: rect.w,
54 | h: rect.h
55 | }
56 | );
57 | }
58 | return {
59 | textureName: Path.withoutDirectory(texture.id),
60 | regions: regionsData,
61 | }
62 | }
63 |
64 | }
65 |
66 | typedef AtlasData = {
67 | textureName:String,
68 | regions:Array
69 | }
70 |
71 | typedef AtlasRegionData = {
72 | name:String,
73 | x:Int,
74 | y:Int,
75 | w:Int,
76 | h:Int
77 | }
--------------------------------------------------------------------------------
/clay/utils/BitVector.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | // https://github.com/eliasku/ecx/blob/develop/src/ecx/ds/CInt32RingBuffer.hx
4 |
5 | #if js
6 | private typedef IntArray = js.lib.Int32Array;
7 | #else
8 | private typedef IntArray = haxe.ds.Vector;
9 | #end
10 |
11 | abstract BitVector(IntArray) from IntArray {
12 |
13 | static public inline var BITS_PER_ELEMENT:Int = 32;
14 | static public inline var BIT_SHIFT:Int = 5;
15 | static public inline var BIT_MASK:Int = 0x1F;
16 |
17 | @:pure
18 | static public inline function address(index:Int):Int {
19 | return index >>> BIT_SHIFT;
20 | }
21 |
22 | @:pure
23 | static public inline function mask(index:Int):Int {
24 | return 0x1 << (index & BIT_MASK);
25 | }
26 |
27 | public inline function new(count:Int) {
28 | this = new IntArray(Math.ceil(count / BITS_PER_ELEMENT));
29 |
30 | #if neko
31 | for(i in 0...this.length) {
32 | this[i] = 0;
33 | }
34 | #end
35 | }
36 |
37 | public inline function enable(index:Int) {
38 | this[address(index)] |= mask(index);
39 | }
40 |
41 | public inline function disable(index:Int) {
42 | this[address(index)] &= ~(mask(index));
43 | }
44 |
45 | @:arrayAccess
46 | public inline function get(index:Int):Bool {
47 | return (this[address(index)] & mask(index)) != 0;
48 | }
49 |
50 | @:arrayAccess
51 | public inline function set(index:Int, value:Bool):Void {
52 | value ? enable(index) : disable(index);
53 | }
54 |
55 | public inline function isFalse(index:Int):Bool {
56 | return (this[address(index)] & mask(index)) == 0;
57 | }
58 |
59 | public inline function enableIfNot(index:Int):Bool {
60 | var a = address(index);
61 | var m = mask(index);
62 | var v = this[a];
63 | if((v & m) == 0) {
64 | this[a] = v | m;
65 | return true;
66 | }
67 | return false;
68 | }
69 |
70 | public inline function enableAll() {
71 | for (i in 0...this.length) {
72 | this[i] = -1;
73 | }
74 | }
75 |
76 | public function disableAll() {
77 | for (i in 0...this.length) {
78 | this[i] = 0;
79 | }
80 | }
81 |
82 | public function forEach(cb:(b:Int)->Void) {
83 | var p:Int = 0;
84 | var bitset:Int = 0;
85 | for (i in 0...this.length) {
86 | p = i * 32;
87 | bitset = this[i];
88 | while (bitset != 0) {
89 | if (bitset & 0x1 == 1) {
90 | cb(p);
91 | }
92 | bitset >>= 1;
93 | p++;
94 | }
95 | }
96 | }
97 |
98 | public inline function size():Int {
99 | return this.length << 2;
100 | }
101 |
102 | inline function toString() {
103 | var _list = [];
104 |
105 | for (i in 0...(this.length << BIT_SHIFT)) {
106 | _list.push(get(i));
107 | }
108 |
109 | return "[" + _list.join(", ") + "]";
110 | }
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/clay/utils/Bits.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | class Bits {
4 |
5 | static public inline function set(v:Int, n:Int):Int {
6 | return v | (1 << n);
7 | }
8 |
9 | static public inline function clear(v:Int, n:Int):Int {
10 | return v & ~(1 << n);
11 | }
12 |
13 | static public inline function toggle(v:Int, n:Int):Int {
14 | return v ^ (1 << n);
15 | }
16 |
17 | static public inline function check(v:Int, n:Int):Bool {
18 | return (v >> n) & 1 == 1;
19 | }
20 |
21 | static public inline function setToPos(v:Int, num:Int, pos:Int):Int {
22 | return v | (num << pos);
23 | }
24 |
25 | static public inline function setRange(v:Int, left:Int, right:Int):Int {
26 | return v | (((1 << (left - 1)) - 1) ^ ((1 << right) - 1));
27 | }
28 |
29 | static public inline function clearRange(v:Int, left:Int, right:Int):Int {
30 | return v & ~(((1 << (left - 1)) - 1) ^ ((1 << right) - 1));
31 | }
32 |
33 | static public inline function toggleRange(v:Int, left:Int, right:Int):Int {
34 | return v ^ (((1 << (left - 1)) - 1) ^ ((1 << right) - 1));
35 | }
36 |
37 | static public inline function extractRange(v:Int, pos:Int, len:Int):Int {
38 | return ((1 << len) - 1) & (v >> (pos - 1));
39 | }
40 |
41 | static public function forEach(v:Int, cb:(b:Int)->Void) {
42 | var i:Int = 0;
43 | while (v != 0) {
44 | if (v & 0x1 == 1) {
45 | cb(i);
46 | }
47 | v >>= 1;
48 | i++;
49 | }
50 | }
51 |
52 | static public function countSinged(n:Int):Int {
53 | return Std.int(Math.pow(2, n)-1);
54 | }
55 |
56 | static public function countUnsinged(n:Int, neg:Bool) {
57 | if(neg) {
58 | return Std.int(Math.pow(-2, n-1));
59 | } else {
60 | return Std.int(Math.pow(2, n-1)-1);
61 | }
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/clay/utils/Common.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | import haxe.macro.Context;
4 | import haxe.macro.Expr;
5 |
6 | class Common {
7 |
8 | macro static public function def(value:Expr, def:Expr):Expr {
9 | return macro @:pos(Context.currentPos()) {
10 | if($value == null) $value = $def;
11 | $value;
12 | }
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/clay/utils/DSP.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | class DSP {
4 |
5 | static public inline function toSamples(ms:Float, sampleRate:Int):Int {
6 | return Math.floor(ms * 0.001 * sampleRate);
7 | }
8 |
9 | static public inline function toMs(samples:Int, sampleRate:Int):Float {
10 | return samples * 1000 / sampleRate;
11 | }
12 |
13 | static public inline function toMono(left:Float, right:Float):Float {
14 | return (left + right) / 2;
15 | }
16 |
17 | }
--------------------------------------------------------------------------------
/clay/utils/DynamicPool.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | import haxe.ds.Vector;
4 |
5 | // dynamic pool
6 | @:generic
7 | class DynamicPool {
8 |
9 | public var items:Vector;
10 | public var createFunc:()->T;
11 | public var sizeLimit:Int;
12 | public var size:Int;
13 |
14 | public function new(sizeLimit:Int, createCallback:()->T, populate:Bool = true){
15 | this.sizeLimit = sizeLimit;
16 | size = 0;
17 |
18 | items = new Vector(this.sizeLimit);
19 |
20 | createFunc = createCallback;
21 |
22 | if(populate) {
23 | for (i in 0...sizeLimit) {
24 | items[i] = createFunc();
25 | }
26 | size= sizeLimit;
27 | }
28 | }
29 |
30 | public inline function get():T {
31 | if(size > 0) {
32 | size--;
33 | var item:T = items[size];
34 | items[size] = null;
35 | return item;
36 | }
37 | return createFunc();
38 | }
39 |
40 | public inline function put(item:T) {
41 | if(size < sizeLimit) {
42 | items[size] = item;
43 | size++;
44 | }
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/clay/utils/Emitter.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | class Emitter {
4 |
5 | @:noCompletion public var bindings:Map, Array>>;
6 |
7 | var _toRemove:Array>;
8 | var _toAdd:Array>;
9 | var _processing:Bool;
10 |
11 | public function new() {
12 | bindings = new Map();
13 |
14 | _toRemove = [];
15 | _toAdd = [];
16 | _processing = false;
17 | }
18 |
19 | public function emit(event:EventType, ?data:T) {
20 | var list = bindings.get(event);
21 |
22 | if(list != null) {
23 | _processing = true;
24 | for (e in list) {
25 | e.callback(data);
26 | }
27 | _processing = false;
28 |
29 | if(_toRemove.length > 0) {
30 | for (e in _toRemove) {
31 | _remove(e.event, e.handler);
32 | }
33 | _toRemove.splice(0, _toRemove.length);
34 | }
35 | if(_toAdd.length > 0) {
36 | for (eh in _toAdd) {
37 | _add(eh);
38 | }
39 | _toAdd.splice(0, _toAdd.length);
40 | }
41 | }
42 | }
43 |
44 | public function on(event:EventType, handler:(e:T)->Void, priority:Int = 0) {
45 | if(_has(event, handler)) {
46 | return;
47 | }
48 |
49 | if(_processing) {
50 | for (e in _toAdd) {
51 | if(e.callback == handler) {
52 | return;
53 | }
54 | }
55 | _toAdd.push(new EmitHandler(event, handler, priority));
56 | } else {
57 | _add(new EmitHandler(event, handler, priority));
58 | }
59 | }
60 |
61 | public function off(event:EventType, handler:(e:T)->Void):Bool {
62 | if(!_has(event, handler)) {
63 | return false;
64 | }
65 |
66 | if(_processing) {
67 | for (e in _toRemove) {
68 | if(e.handler == handler) {
69 | return false;
70 | }
71 | }
72 | _toRemove.push({event:event, handler:handler});
73 | } else {
74 | _remove(event, handler);
75 | }
76 |
77 | return true;
78 | }
79 |
80 | function _has(event:EventType, handler:(e:T)->Void):Bool {
81 | var list = bindings.get(event);
82 |
83 | if(list != null) {
84 | for (eh in list) {
85 | if(eh.callback == handler) {
86 | return true;
87 | }
88 | }
89 | }
90 |
91 | return false;
92 | }
93 |
94 | function _add(emitHandler:EmitHandler) {
95 | var list = bindings.get(emitHandler.event);
96 | if(list == null) {
97 | list = new Array>();
98 | list.push(emitHandler);
99 | bindings.set(emitHandler.event, list);
100 | } else {
101 | var atPos:Int = list.length;
102 | for (i in 0...list.length) {
103 | if (emitHandler.priority < list[i].priority) {
104 | atPos = i;
105 | break;
106 | }
107 | }
108 | list.insert(atPos, emitHandler);
109 | }
110 | }
111 |
112 | function _remove(event:EventType, handler:(e:T)->Void) {
113 | var list = bindings.get(event);
114 | for (i in 0...list.length) {
115 | if(list[i].callback == handler) {
116 | list.splice(i, 1);
117 | }
118 | }
119 |
120 | if(list.length == 0) {
121 | bindings.remove(event);
122 | }
123 | }
124 |
125 | }
126 |
127 | private typedef EmitDef = {event:T, handler:(e:T)->Void}
128 |
129 | private class EmitHandler {
130 |
131 | public var event:EventType;
132 | public var callback:(e:T)->Void;
133 | public var priority:Int;
134 |
135 | public function new(event:EventType, callback:(e:T)->Void, priority:Int) {
136 | this.event = event;
137 | this.callback = callback;
138 | this.priority = priority;
139 | }
140 |
141 | }
--------------------------------------------------------------------------------
/clay/utils/EventType.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | abstract EventType(Int) from Int to Int {}
4 |
--------------------------------------------------------------------------------
/clay/utils/FPSCounter.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | class FPSCounter {
4 |
5 | public static var fps:Int = 0;
6 |
7 | static var deltaTime:Float = 0;
8 | static var totalFrames:Int = 0;
9 | static var elapsedTime:Float = 0;
10 | static var previousTime:Float = 0;
11 | static var currentTime:Float = 0;
12 |
13 | public static function update() {
14 | previousTime = currentTime;
15 |
16 | currentTime = kha.Scheduler.realTime();
17 | deltaTime = (currentTime - previousTime);
18 |
19 | elapsedTime += deltaTime;
20 | if (elapsedTime >= 1.0) {
21 | fps = totalFrames;
22 | totalFrames = 0;
23 | elapsedTime = 0;
24 | }
25 | totalFrames++;
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/clay/utils/FastFloat.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | typedef FastFloat = kha.FastFloat;
--------------------------------------------------------------------------------
/clay/utils/Float32Array.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | typedef Float32Array = kha.arrays.Float32Array;
--------------------------------------------------------------------------------
/clay/utils/IdGenerator.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | class IdGenerator {
4 |
5 | var _id:Int;
6 | var _removedIds:Array;
7 |
8 | public function new() {
9 | reset();
10 | }
11 |
12 | public function get():Int {
13 | if(_removedIds.length > 0) {
14 | return _removedIds.shift();
15 | }
16 | return _id++;
17 | }
18 |
19 | public function put(id:Int) {
20 | _removedIds.push(id);
21 | }
22 |
23 | public function reset() {
24 | _id = 0;
25 | _removedIds = [];
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/clay/utils/IntRingBuffer.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 |
4 | // https://github.com/eliasku/ecx/blob/develop/src/ecx/ds/CInt32RingBuffer.hx
5 | // https://github.com/zeliard/Dispatcher/blob/master/JobDispatcher/ObjectPool.h
6 |
7 | #if js
8 | private typedef IntArray = js.lib.Int32Array;
9 | #else
10 | private typedef IntArray = haxe.ds.Vector;
11 | #end
12 |
13 | class IntRingBuffer {
14 |
15 | public var length(get, never):Int;
16 |
17 | var _buffer:IntArray;
18 | var _mask:Int;
19 | var _head:Int = 0;
20 | var _tail:Int = 0;
21 |
22 | public function new(capacity:Int) {
23 | _mask = capacity - 1;
24 |
25 | if(capacity == 0) throw "non-zero capacity is required";
26 | if((_mask & capacity) != 0) throw "capacity " + capacity + " must be power of two";
27 |
28 | _buffer = new IntArray(capacity);
29 |
30 | for (i in 0...capacity) {
31 | _buffer[i] = i;
32 | }
33 | }
34 |
35 | public inline function set(index:Int, value:Int) {
36 | _buffer[index] = value;
37 | }
38 |
39 | public inline function pop():Int {
40 | var popAt = _head;
41 | _head = popAt + 1;
42 | _head &= _mask;
43 | return _buffer[popAt];
44 | }
45 |
46 | public inline function push(value:Int) {
47 | var placeAt = _tail;
48 | _buffer[placeAt] = value;
49 | ++placeAt;
50 | _tail = placeAt & _mask;
51 | }
52 |
53 | public inline function clear() {
54 | _head = 0;
55 | _tail = 0;
56 | _buffer = new IntArray(_mask + 1);
57 | for (i in 0..._buffer.length) {
58 | _buffer[i] = i;
59 | }
60 | }
61 |
62 | inline function get_length() {
63 | return _buffer.length;
64 | }
65 |
66 | }
--------------------------------------------------------------------------------
/clay/utils/Log.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | import haxe.io.Path;
4 | import haxe.macro.Context;
5 | import haxe.macro.Expr;
6 |
7 | @:enum
8 | private abstract LogLevel(Int) from Int to Int {
9 | var VERBOSE = 0;
10 | var DEBUG = 1;
11 | var INFO = 2;
12 | var WARNING = 3;
13 | var ERROR = 4;
14 | var CRITICAL = 5;
15 | }
16 |
17 | class Log {
18 |
19 | static inline var _spaces:String = ' ';
20 | static inline var _minLevel =
21 | #if (clay_loglevel == 'info') LogLevel.INFO
22 | #elseif (clay_loglevel == 'verbose') LogLevel.VERBOSE
23 | #elseif (clay_loglevel == 'debug') LogLevel.DEBUG
24 | #elseif (clay_loglevel == 'error') LogLevel.ERROR
25 | #elseif (clay_loglevel == 'critical') LogLevel.CRITICAL
26 | #elseif (clay_loglevel == 'verbose') LogLevel.VERBOSE
27 | #else LogLevel.WARNING
28 | #end;
29 |
30 | macro static public function verbose(value:Dynamic):Expr {
31 | #if (clay_debug && !(clay_no_log))
32 | var file = Path.withoutDirectory(Context.getPosInfos(Context.currentPos()).file);
33 | var context = Path.withoutExtension(file);
34 | if(Std.int(LogLevel.VERBOSE) >= Std.int(_minLevel)) {
35 | return macro @:pos(Context.currentPos()) trace('${_spaces}V / $context / ' + $value);
36 | }
37 | #end
38 | return macro null;
39 | }
40 |
41 | macro static public function debug(value:Dynamic):Expr {
42 | #if (clay_debug && !(clay_no_log))
43 | var file = Path.withoutDirectory(Context.getPosInfos(Context.currentPos()).file);
44 | var context = Path.withoutExtension(file);
45 | if(Std.int(LogLevel.DEBUG) >= Std.int(_minLevel)) {
46 | return macro @:pos(Context.currentPos()) trace('${_spaces}D / $context / ' + $value);
47 | }
48 | #end
49 | return macro null;
50 | }
51 |
52 | macro static public function info(value:Dynamic):Expr {
53 | #if (clay_debug && !(clay_no_log))
54 | var file = Path.withoutDirectory(Context.getPosInfos(Context.currentPos()).file);
55 | var context = Path.withoutExtension(file);
56 | if(Std.int(LogLevel.INFO) >= Std.int(_minLevel)) {
57 | return macro @:pos(Context.currentPos()) trace('${_spaces}I / $context / ' + $value);
58 | }
59 | #end
60 | return macro null;
61 | }
62 |
63 | macro static public function warning(value:Dynamic):Expr {
64 | #if (clay_debug && !(clay_no_log))
65 | var file = Path.withoutDirectory(Context.getPosInfos(Context.currentPos()).file);
66 | var context = Path.withoutExtension(file);
67 | if(Std.int(LogLevel.WARNING) >= Std.int(_minLevel)) {
68 | return macro @:pos(Context.currentPos()) trace('${_spaces}W / $context / ' + $value);
69 | }
70 | #end
71 | return macro null;
72 | }
73 |
74 | macro static public function error(value:Dynamic):Expr {
75 | #if (clay_debug && !(clay_no_log))
76 | var file = Path.withoutDirectory(Context.getPosInfos(Context.currentPos()).file);
77 | var context = Path.withoutExtension(file);
78 | if(Std.int(LogLevel.ERROR) >= Std.int(_minLevel)) {
79 | return macro @:pos(Context.currentPos()) trace('${_spaces}E / $context / ' + $value);
80 | }
81 | #end
82 | return macro null;
83 | }
84 |
85 | macro static public function critical(value:Dynamic):Expr {
86 | #if (clay_debug && !(clay_no_log))
87 | var file = Path.withoutDirectory(Context.getPosInfos(Context.currentPos()).file);
88 | var context = Path.withoutExtension(file);
89 | if(Std.int(LogLevel.ERROR) >= Std.int(_minLevel)) {
90 | return macro @:pos(Context.currentPos()) trace('${_spaces}! / $context / ' + $value);
91 | }
92 | #end
93 | return macro null;
94 | }
95 |
96 | macro static public function assert(expr:Expr, ?reason:ExprOf) {
97 | #if !clay_no_assert
98 | var str = haxe.macro.ExprTools.toString(expr);
99 |
100 | reason = switch(reason) {
101 | case macro null: macro '';
102 | case _: macro ' ( ' + $reason + ' )';
103 | }
104 |
105 | return macro @:pos(Context.currentPos()) {
106 | if(!$expr) throw('$str' + $reason);
107 | }
108 | #end
109 | return macro null;
110 | }
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/clay/utils/Math.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | class Math {
4 |
5 | static public inline var TAU = 6.28318530717958648;
6 | static public inline var PI = 3.14159265358979323;
7 | static public inline var DEG2RAD:Float = 6.28318530717958648 / 360;
8 | static public inline var RAD2DEG:Float = 360 / 6.28318530717958648;
9 | static public inline var EPSILON:Float = 1e-10;
10 |
11 | static public var POSITIVE_INFINITY(get, never):Float;
12 | static public var NEGATIVE_INFINITY(get, never):Float;
13 | static public var NaN(get, never):Float;
14 |
15 | static inline function get_POSITIVE_INFINITY() {
16 | return std.Math.POSITIVE_INFINITY;
17 | }
18 |
19 | static inline function get_NEGATIVE_INFINITY() {
20 | return std.Math.NEGATIVE_INFINITY;
21 | }
22 |
23 | static inline function get_NaN() {
24 | return std.Math.NaN;
25 | }
26 |
27 | static public inline function isNaN(v:Float) {
28 | return std.Math.isNaN(v);
29 | }
30 |
31 | static public inline function floor(f:Float) {
32 | return std.Math.floor(f);
33 | }
34 |
35 | static public inline function log(f:Float) {
36 | return std.Math.log(f);
37 | }
38 |
39 | static public inline function random() {
40 | return std.Math.random();
41 | }
42 |
43 | static public inline function ceil(f:Float) {
44 | return std.Math.ceil(f);
45 | }
46 |
47 | static public inline function round(f:Float) {
48 | return std.Math.round(f);
49 | }
50 |
51 | static public inline function pow(v:Float, p:Float) {
52 | return std.Math.pow(v,p);
53 | }
54 |
55 | static public inline function exp(f:Float) {
56 | return std.Math.exp(f);
57 | }
58 |
59 | static public inline function cos(f:Float) {
60 | return std.Math.cos(f);
61 | }
62 |
63 | static public inline function sin(f:Float) {
64 | return std.Math.sin(f);
65 | }
66 |
67 | static public inline function tan(f:Float) {
68 | return std.Math.tan(f);
69 | }
70 |
71 | static public inline function acos(f:Float) {
72 | return std.Math.acos(f);
73 | }
74 |
75 | static public inline function asin(f:Float) {
76 | return std.Math.asin(f);
77 | }
78 |
79 | static public inline function atan(f:Float) {
80 | return std.Math.atan(f);
81 | }
82 |
83 | static public inline function sqrt(f:Float) {
84 | return std.Math.sqrt(f);
85 | }
86 |
87 | static public inline function invSqrt(f:Float) {
88 | return 1.0 / sqrt(f);
89 | }
90 |
91 | static public inline function atan2(dy:Float, dx:Float) {
92 | return std.Math.atan2(dy,dx);
93 | }
94 |
95 | static public inline function abs(f:Float) {
96 | return f < 0 ? -f : f;
97 | }
98 |
99 | static public inline function iabs(i:Int) {
100 | return i < 0 ? -i : i;
101 | }
102 |
103 | static public inline function max(a:Float, b:Float) {
104 | return a < b ? b : a;
105 | }
106 |
107 | static public inline function imax(a:Int, b:Int) {
108 | return a < b ? b : a;
109 | }
110 |
111 | static public inline function min(a:Float, b:Float) {
112 | return a > b ? b : a;
113 | }
114 |
115 | static public inline function imin(a:Int, b:Int) {
116 | return a > b ? b : a;
117 | }
118 |
119 | static public inline function fixed(value:Float, precision:Int):Float {
120 | var n = Math.pow( 10, precision );
121 | return ( Std.int(value * n) / n );
122 | }
123 |
124 | static public inline function lerp(start:Float, end:Float, t:Float):Float {
125 | return start + t * (end - start);
126 | }
127 |
128 | static public inline function inverseLerp(start:Float, end:Float, value:Float):Float {
129 | return end == start ? 0.0 : (value - start) / (end - start);
130 | }
131 |
132 | static public inline function map(istart:Float, iend:Float, ostart:Float, oend:Float, value:Float):Float {
133 | return ostart + (oend - ostart) * ((value - istart) / (iend - istart));
134 | }
135 |
136 | static public inline function imap(istart:Int, iend:Int, ostart:Int, oend:Int, value:Int):Int {
137 | return Std.int(map(istart, iend, ostart, oend, value));
138 | }
139 |
140 | static public inline function clamp(value:Float, a:Float, b:Float):Float {
141 | return ( value < a ) ? a : ( ( value > b ) ? b : value );
142 | }
143 |
144 | static public inline function iclamp(value:Int, a:Int, b:Int):Int {
145 | return ( value < a ) ? a : ( ( value > b ) ? b : value );
146 | }
147 |
148 | static public inline function withinRange(value:Float, startRange:Float, endRange:Float):Bool {
149 | return value >= startRange && value <= endRange;
150 | }
151 |
152 | static public inline function smoothStep(x:Float, min:Float, max:Float):Float {
153 | if (x <= min) {
154 | return 0;
155 | }
156 |
157 | if (x >= max) {
158 | return 1;
159 | }
160 |
161 | x = ( x - min ) / ( max - min );
162 |
163 | return x * x * ( 3 - 2 * x );
164 | }
165 |
166 | /** Return the sign of a number, `1` if >= 0 and `-1` if < 0 */
167 | static public inline function sign(x:Float):Int {
168 | return (x >= 0) ? 1 : -1;
169 | }
170 |
171 | /** Return the sign of a number, `0` is returned as `0`, `1` if > `0` and `-1` if < `0` */
172 | static public inline function sign0(x:Float):Int {
173 | return (x < 0) ? -1 : ((x > 0) ? 1 : 0);
174 | }
175 |
176 | static public inline function fract(v:Float):Float {
177 | return v - Std.int(v);
178 | }
179 |
180 | /** Convert a number from degrees to radians */
181 | static public inline function radians(degrees:Float):Float {
182 | return degrees * DEG2RAD;
183 | }
184 |
185 | /** Convert a number from radians to degrees */
186 | static public inline function degrees(radians:Float):Float {
187 | return radians * RAD2DEG;
188 | }
189 |
190 | /** Computes log base 2 of v */
191 | static public inline function log2(v:Int):Int {
192 | var r;
193 | var shift;
194 |
195 | r = v > 0xFFFF? 1 << 4 : 0; v >>>= r;
196 | shift = v > 0xFF ? 1 << 3 : 0; v >>>= shift; r |= shift;
197 | shift = v > 0xF ? 1 << 2 : 0; v >>>= shift; r |= shift;
198 | shift = v > 0x3 ? 1 << 1 : 0; v >>>= shift; r |= shift;
199 |
200 | return r | (v >> 1);
201 | }
202 |
203 | static public inline function mod(i:Int, n:Int):Int {
204 | return (i % n + n) % n;
205 | }
206 |
207 | static public inline function distanceSq(dx:Float, dy:Float) {
208 | return dx * dx + dy * dy;
209 | }
210 |
211 | static public inline function distance(dx:Float, dy:Float) {
212 | return sqrt(distanceSq(dx,dy));
213 | }
214 |
215 | }
--------------------------------------------------------------------------------
/clay/utils/PowerOfTwo.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | class PowerOfTwo {
4 |
5 | static public inline function next(x:Int):Int {
6 | x |= x >> 1;
7 | x |= x >> 2;
8 | x |= x >> 4;
9 | x |= x >> 8;
10 | x |= x >> 16;
11 |
12 | return x + 1;
13 | }
14 |
15 | static public inline function prev(x:Int):Int {
16 | x |= x >>> 1;
17 | x |= x >>> 2;
18 | x |= x >>> 4;
19 | x |= x >>> 8;
20 | x |= x >>> 16;
21 |
22 | return x - (x>>>1);
23 | }
24 |
25 | static public inline function check(x:Int):Bool {
26 | return x != 0 && (x & (x - 1)) == 0;
27 | }
28 |
29 | static public inline function get(x:Int):Int {
30 | if(x == 0) {
31 | return 1;
32 | }
33 |
34 | --x;
35 | x |= x >> 1;
36 | x |= x >> 2;
37 | x |= x >> 4;
38 | x |= x >> 8;
39 | x |= x >> 16;
40 |
41 | return x + 1;
42 | }
43 |
44 | static public inline function toPowOf2(num:Int):Int {
45 | return Math.round(Math.log(num)/Math.log(2));
46 | }
47 |
48 | static public inline function fromPowOf2(num:Int):Int {
49 | return 1 << num;
50 | }
51 |
52 | }
--------------------------------------------------------------------------------
/clay/utils/Random.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | abstract Random(kha.math.Random) from kha.math.Random to kha.math.Random {
4 |
5 | public inline function new(?seed:Int) {
6 | if(seed == null) seed = Std.random(0x7fffffff);
7 | this = new kha.math.Random(seed);
8 | }
9 |
10 | public inline function get():Int {
11 | return this.Get();
12 | }
13 | /** Returns a float number between [0,1) */
14 | public inline function getFloat():Float {
15 | return this.GetFloat();
16 | }
17 |
18 | /** Returns a number between [min,max).
19 | max is optional, returning a number between [0,min) */
20 | public inline function float(min:Float, ?max:Null):Float {
21 | if(max == null) { max = min; min = 0; }
22 | return getFloat() * (max - min) + min;
23 | }
24 |
25 | /** Return a number between [min, max).
26 | max is optional, returning a number between [0,min) */
27 | public inline function int(min:Int, ?max:Null):Int {
28 | return Math.floor(float(min, max));
29 | }
30 |
31 | /** Returns true or false based on a chance of [0..1] percent.
32 | Given 0.5, 50% chance of true, with 0.9, 90% chance of true and so on. */
33 | public inline function bool(chance:Float = 0.5):Bool {
34 | return (getFloat() < chance);
35 | }
36 |
37 | /** Returns 1 or -1 based on a chance of [0..1] percent.
38 | Given 0.5, 50% chance of 1, with 0.9, 90% chance of 1 and so on. */
39 | public inline function sign(chance:Float = 0.5):Int {
40 | return (getFloat() < chance) ? 1 : -1;
41 | }
42 |
43 | /** Returns 1 or 0 based on a chance of [0..1] percent.
44 | Given 0.5, 50% chance of 1, with 0.9, 90% chance of 1 and so on. */
45 | public inline function bit(chance:Float = 0.5):Int {
46 | return (getFloat() < chance) ? 1 : 0;
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/clay/utils/ScissorStack.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | import clay.math.Rectangle;
4 | import clay.utils.DynamicPool;
5 | import clay.utils.Log;
6 |
7 | class ScissorStack {
8 |
9 | static public var scissor(get, never):Rectangle;
10 | static inline function get_scissor() return _scissors[_scissors.length-1];
11 |
12 | static var _scissors:Array = [];
13 | static var _scissorPool:DynamicPool = new DynamicPool(16, function() {return new Rectangle();});
14 |
15 | public function pushScissor(rect:Rectangle, clipFromLast:Bool = false) {
16 | var s = _scissorPool.get().copyFrom(rect);
17 | if(clipFromLast) {
18 | var lastScissor = scissor;
19 | if(lastScissor != null) {
20 | s.clamp(lastScissor);
21 | }
22 | }
23 | _scissors.push(s);
24 | Clay.graphics.scissor(s.x, s.y, s.w, s.h);
25 | }
26 |
27 | public function popScissor() {
28 | if(_scissors.length > 0) {
29 | _scissorPool.put(_scissors.pop());
30 | var s = scissor;
31 | Clay.graphics.scissor(s.x, s.y, s.w, s.h);
32 | } else {
33 | Log.warning('ScissorStack.popScissor with no scissors left in stack');
34 | }
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/clay/utils/SparseSet.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | import haxe.ds.Vector;
4 |
5 | class SparseSet {
6 |
7 | public var used(default, null):Int = 0;
8 | public final capacity:Int;
9 |
10 | var _sparse:Vector;
11 | var _dense:Vector;
12 |
13 | public function new(capacity:Int) {
14 | this.capacity = capacity;
15 |
16 | _sparse = new Vector(capacity);
17 | _dense = new Vector(capacity);
18 |
19 | for (i in 0...capacity) {
20 | _sparse[i] = -1;
21 | _dense[i] = -1;
22 | }
23 | }
24 |
25 | public function has(num:Int):Bool {
26 | return _dense[_sparse[num]] == num;
27 | }
28 |
29 | public function insert(num:Int) {
30 | _dense[used] = num;
31 | _sparse[num] = used;
32 |
33 | used++;
34 | }
35 |
36 | public function remove(num:Int) {
37 | final temp = _dense[used-1];
38 | _dense[_sparse[num]] = temp;
39 | _sparse[temp] = _sparse[num];
40 |
41 | used--;
42 | }
43 |
44 | public inline function getDense(idx:Int) {
45 | return _dense[idx];
46 | }
47 |
48 | public inline function getSparse(num:Int) {
49 | return _sparse[num];
50 | }
51 |
52 | public function clear() {
53 | while(used > 0) {
54 | used--;
55 | _sparse[_dense[used]] = -1;
56 | _dense[used] = -1;
57 | }
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/clay/utils/StrokeAlign.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | enum abstract StrokeAlign(Int) {
4 | var CENTER;
5 | var INSIDE;
6 | var OUTSIDE;
7 | }
--------------------------------------------------------------------------------
/clay/utils/UUID.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | class UUID {
4 |
5 | static public function get(?val:Null):String {
6 | // https://web.archive.org/web/20190318040344/http://www.anotherchris.net/csharp/friendly-unique-id-generation-part-2/#base62
7 |
8 | if(val == null) val = Std.random(0x7fffffff);
9 |
10 | function toChar(value:Int):String {
11 | if (value > 9) {
12 | var ascii = (65 + (value - 10));
13 | if (ascii > 90) { ascii += 6; }
14 | return String.fromCharCode(ascii);
15 | } else {
16 | return Std.string(value).charAt(0);
17 | }
18 | }
19 |
20 | var r = Std.int(val % 62);
21 | var q = Std.int(val / 62);
22 | return q > 0 ? get(q) + toChar(r) : Std.string(toChar(r));
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/clay/utils/Uint32Array.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | typedef Uint32Array = kha.arrays.Uint32Array;
--------------------------------------------------------------------------------
/clay/utils/Viewport.hx:
--------------------------------------------------------------------------------
1 | package clay.utils;
2 |
3 | import clay.math.Vector2;
4 | import clay.graphics.Camera;
5 | class Viewport {
6 |
7 | public var x:Int = 0;
8 | public var y:Int = 0;
9 | public var width:Int = 0;
10 | public var height:Int = 0;
11 | public var camera:Camera;
12 | public var scaleMode:ScaleMode;
13 | public var centered:Bool = false;
14 |
15 | var _scaledX:Float = 0;
16 | var _scaledY:Float = 0;
17 | var _scaledWidth:Float = 0;
18 | var _scaledHeight:Float = 0;
19 |
20 | public function new(camera:Camera, x:Int = 0, y:Int = 0, width:Int = 0, height:Int = 0) {
21 | this.camera = camera;
22 | this.x = x;
23 | this.y = y;
24 | this.width = width > 0 ? width : Std.int(Clay.window.width);
25 | this.height = height > 0 ? height : Std.int(Clay.window.height);
26 | _scaledX = this.x;
27 | _scaledY = this.y;
28 | _scaledWidth = this.width;
29 | _scaledHeight = this.height;
30 | scaleMode = ScaleMode.NONE;
31 | }
32 |
33 | public function screenToWorld(v:Vector2, ?into:Vector2):Vector2 {
34 | if(into == null) into = new Vector2();
35 |
36 | into.x = (v.x - _scaledX) * (camera.width / _scaledWidth);
37 | into.y = (v.y - _scaledY) * (camera.height / _scaledHeight);
38 | into.set(camera.view.getTransformX(into.x, into.y), camera.view.getTransformY(into.x, into.y));
39 |
40 | return into;
41 | }
42 |
43 | public function worldToScreen(v:Vector2, ?into:Vector2):Vector2 {
44 | if(into == null) into = new Vector2();
45 |
46 | into.copyFrom(v);
47 | into.set(camera.invProjectionView.getTransformX(into.x, into.y), camera.invProjectionView.getTransformY(into.x, into.y));
48 | into.x = (_scaledWidth / camera.width) * into.x + _scaledX;
49 | into.y = (_scaledHeight / camera.height) * into.y + _scaledY;
50 |
51 | return into;
52 | }
53 |
54 | public function apply() {
55 | applyScaledViewport();
56 | camera.update();
57 | }
58 |
59 | inline function applyScaledViewport() {
60 | _scaledX = x;
61 | _scaledY = y;
62 | _scaledWidth = width;
63 | _scaledHeight = height;
64 |
65 | switch (scaleMode) {
66 | case ScaleMode.FIT: {
67 | var sW = width / camera.width;
68 | var sH = height / camera.height;
69 | var scale = sW < sH ? sW : sH;
70 |
71 | _scaledWidth = camera.width * scale;
72 | _scaledHeight = camera.height * scale;
73 | }
74 | case ScaleMode.FILL: {
75 | var sW = width / camera.width;
76 | var sH = height / camera.height;
77 | var scale = sW > sH ? sW : sH;
78 |
79 | _scaledWidth = camera.width * scale;
80 | _scaledHeight = camera.height * scale;
81 | }
82 | case ScaleMode.NONE: {
83 | _scaledWidth = camera.width;
84 | _scaledHeight = camera.height;
85 | }
86 | default:
87 | }
88 |
89 | if(centered) {
90 | _scaledX = (width - _scaledWidth) / 2;
91 | _scaledY = (height - _scaledHeight) / 2;
92 | }
93 |
94 | Clay.graphics.viewport(_scaledX, _scaledY, _scaledWidth, _scaledHeight);
95 | }
96 |
97 | inline function setScissor(x:Float, y:Float, w:Float, h:Float) {
98 | Clay.graphics.scissor(x, y, w, h);
99 | }
100 |
101 | }
102 |
103 | enum abstract ScaleMode(Int){
104 | // Scales the source to fit the target while keeping the same aspect ratio.
105 | // This may cause the source to be smaller than thetarget in one direction.
106 | var FIT;
107 | // Scales the source to fill the target while keeping the same aspect ratio.
108 | // This may cause the source to be larger than the target in one direction.
109 | var FILL;
110 | // Scales the source to fill the target. This may cause the source to not keep the same aspect ratio.
111 | var STRETCH;
112 | // The source is not scaled.
113 | var NONE;
114 | }
--------------------------------------------------------------------------------
/clay/utils/macro/EventMacro.hx:
--------------------------------------------------------------------------------
1 | package clay.utils.macro;
2 |
3 | import haxe.macro.Context;
4 | import haxe.macro.Expr;
5 |
6 | #if macro
7 | class EventMacro {
8 |
9 | static public var eventID:Int = 0;
10 | static public var eventMap:Map = new Map();
11 |
12 | static public function build():Array {
13 | var fields = Context.getBuildFields();
14 |
15 | function getEventName(field:Field):String {
16 | var name:String;
17 | switch (field.kind) {
18 | case FVar(t,_):
19 | switch (t) {
20 | case TPath(p):
21 | switch (p.params[0]) {
22 | case TPType(t1):
23 | switch (t1) {
24 | case TPath(p1):
25 | name = '${p1.name}_${field.name}';
26 | case _:
27 | }
28 | case _:
29 | }
30 | case _:
31 | }
32 | case _:
33 | }
34 | return name;
35 | }
36 |
37 | // todo: check for static field
38 | for(field in fields) {
39 | switch (field.kind) {
40 | case FVar(t,e):
41 | switch (t) {
42 | case TPath(p):
43 | if(p.name == 'EventType') {
44 | if(e != null) {
45 | Context.error("Remove initializer, EventType id will be created at compile-time", field.pos);
46 | }
47 | var name = getEventName(field);
48 | var id:Int = 0;
49 | if(eventMap.exists(name)) {
50 | id = eventMap.get(name);
51 | } else {
52 | id = eventID++;
53 | }
54 | field.kind = FVar(t,macro $v{id});
55 | }
56 | case _:
57 | }
58 | case _:
59 | }
60 | }
61 |
62 | return fields;
63 | }
64 |
65 | }
66 | #end
67 |
--------------------------------------------------------------------------------
/clay/utils/macro/MacroUtils.hx:
--------------------------------------------------------------------------------
1 | package clay.utils.macro;
2 |
3 | import haxe.macro.Context;
4 | import haxe.macro.Expr;
5 | import haxe.macro.Type;
6 |
7 | class MacroUtils {
8 |
9 | static public function createString(types:Array, delimiter:String = '_', sort:Bool = true):String {
10 | var len = types.length;
11 | var typesStrings = [];
12 |
13 | for (i in 0...len) {
14 | var typeName = switch (types[i]) {
15 | case TInst(ref, types): ref.get().name;
16 | default:
17 | throw false;
18 | }
19 | var typePack = switch (types[i]) {
20 | case TInst(ref, types): ref.get().pack;
21 | default:
22 | throw false;
23 | }
24 |
25 | typePack.push(typeName);
26 | var fullType = typePack.join(delimiter);
27 | typesStrings.push(fullType);
28 | // typesStrings.push(typeName);
29 | }
30 |
31 | if(sort) {
32 | typesStrings.sort(function(a:String, b:String):Int {
33 | a = a.toUpperCase();
34 | b = b.toUpperCase();
35 |
36 | if (a < b) {
37 | return -1;
38 | } else if (a > b) {
39 | return 1;
40 | } else {
41 | return 0;
42 | }
43 | });
44 | }
45 |
46 | return typesStrings.join(delimiter);
47 | }
48 |
49 | static public function buildTypeExpr(pack:Array, module:String, name:String):Expr {
50 | var packModule = pack.concat([module, name]);
51 |
52 | var typeExpr = macro $i{packModule[0]};
53 | for (idx in 1...packModule.length){
54 | var field = $i{packModule[idx]};
55 | typeExpr = macro $typeExpr.$field;
56 | }
57 |
58 | return macro $typeExpr;
59 | }
60 |
61 | static public inline function camelCase(name:String):String {
62 | return name.substr(0, 1).toLowerCase() + name.substr(1);
63 | }
64 |
65 | public static macro function getDefine(key:String):Expr {
66 | return macro $v{Context.definedValue(key)};
67 | }
68 |
69 | public static macro function isDefined(key:String):Expr {
70 | return macro $v{Context.defined(key)};
71 | }
72 |
73 | public static macro function getDefines():Expr {
74 | var defines : Map = Context.getDefines();
75 | var map : Array = [];
76 | for (key in defines.keys()) {
77 | map.push(macro $v{key} => $v{Std.string(defines.get(key))});
78 | }
79 | return macro $a{map};
80 | }
81 |
82 | #if macro
83 | static public function buildVar(name:String, access:Array, type:ComplexType, e:Expr = null, m:Metadata = null):Field {
84 | return {
85 | pos: Context.currentPos(),
86 | name: name,
87 | access: access,
88 | kind: FVar(type, e),
89 | meta: m == null ? [] : m
90 | };
91 | }
92 |
93 | static public function buildProp(name:String, access:Array, get:String, set:String, type:ComplexType, e:Expr = null, m:Metadata = null):Field {
94 | return {
95 | pos: Context.currentPos(),
96 | name: name,
97 | access: access,
98 | kind: FProp(get, set, type, e),
99 | meta: m == null ? [] : m
100 | };
101 | }
102 |
103 | static public function buildFunction(name:String, access:Array, args:Array, ret:ComplexType, exprs:Array, m:Metadata = null):Field {
104 | return {
105 | pos: Context.currentPos(),
106 | name: name,
107 | access: access,
108 | kind: FFun({
109 | args: args,
110 | ret: ret,
111 | expr: macro $b{exprs}
112 | }),
113 | meta: m == null ? [] : m
114 | };
115 | }
116 |
117 | static public function buildConstructor(name:String, pack:Array, params:Array, exprs:Array):Expr {
118 | return {
119 | pos: Context.currentPos(),
120 | expr: ENew(
121 | {
122 | name: name,
123 | pack: pack,
124 | params: params
125 | },
126 | exprs
127 | )
128 | }
129 | }
130 | #end
131 |
132 | static public function getPathInfo(type:Type):PathInfo {
133 | var data:PathInfo = {
134 | pack: null,
135 | module: null,
136 | name: null
137 | }
138 |
139 | switch (type) {
140 | case TInst(ref, types):
141 | data.pack = ref.get().pack;
142 | data.module = ref.get().module.split('.').pop();
143 | data.name = ref.get().name;
144 | case TType(ref, types):
145 | data.pack = ref.get().pack;
146 | data.module = ref.get().module.split('.').pop();
147 | data.name = ref.get().name;
148 | default:
149 | throw false;
150 | }
151 |
152 | return data;
153 | }
154 |
155 | static public function getClassTypePath(type:haxe.macro.Type) {
156 | switch (type) {
157 | case TType(ref, types):
158 | var name = ref.get().name;
159 | if(!StringTools.startsWith(name, 'Class')) throw '$name must be Class';
160 | var pack = name.substring(6, name.length-1).split('.');
161 | name = pack.pop();
162 | return {pack: pack, name: name, sub: null, params: []};
163 | default:
164 | throw 'Invalid type';
165 | }
166 | }
167 |
168 | static public function subclasses(type:ClassType, root:String):Bool {
169 | var name = type.module + '.' + type.name;
170 | return (name.substr(0, root.length) == root || type.superClass != null && subclasses(type.superClass.t.get(), root));
171 | }
172 |
173 | }
174 |
175 | typedef PathInfo = {
176 | var pack:Array;
177 | var module:String;
178 | var name:String;
179 | }
--------------------------------------------------------------------------------
/haxelib.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "clay2d",
3 | "url": "https://github.com/clay2d/clay",
4 | "license": "MIT",
5 | "tags": ["engine","cross","clay"],
6 | "description": "engine",
7 | "version": "0.0.1",
8 | "releasenote": "",
9 | "contributors": ["RudenkoArts"]
10 | }
--------------------------------------------------------------------------------
/run.n:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndreiRudenko/clay/f3936e625e027a7b45552946a94a43971af5a9ef/run.n
--------------------------------------------------------------------------------
/templates/empty/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AndreiRudenko/clay/f3936e625e027a7b45552946a94a43971af5a9ef/templates/empty/assets/.keep
--------------------------------------------------------------------------------
/templates/empty/project.yml:
--------------------------------------------------------------------------------
1 | project:
2 | title: project_title
3 | authors: []
4 | description : ''
5 | version: '1.0.0'
6 | sources: ['src']
7 | assets: ['assets']
8 | defines: []
9 | shaders: []
10 | libraries: []
11 |
12 | input:
13 | mouse: true
14 | keyboard: true
15 | gamepad: true
16 | touch: true
17 | pen: false
18 |
19 | app:
20 | name: app_name
21 | icon: ''
22 |
23 | android:
24 | package: com.package.name
25 | orientation: sensor
26 | permissions: []
27 | version_code: 1
28 | installLocation: internalOnly
29 | arch: arm7
30 |
31 | ios:
32 | bundle: tech.kode.$(PRODUCT_NAME:rfc1034identifier)
33 | build: 1
34 | organizationName: ''
35 | developmentTeam: ''
36 |
37 | html5:
38 | webgl: true
39 | canvas: 'kanvas'
40 | script: 'game'
41 | width: 800
42 | height: 600
43 | serverPort: 8080
44 | htmlFile: ''
45 | favicon: ''
46 | minify: false
47 |
48 | windows:
49 | graphics: 'direct3d11'
50 |
51 | compiler:
52 | output: 'build'
53 | parameters: ['-dce full']
54 | options: ['']
55 | haxe: ''
56 | kha: ''
57 | ffmpeg: ''
58 |
--------------------------------------------------------------------------------
/templates/empty/src/Game.hx:
--------------------------------------------------------------------------------
1 | package;
2 |
3 | import clay.Clay;
4 |
5 | class Game {
6 |
7 | public function new() {
8 |
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/templates/empty/src/Main.hx:
--------------------------------------------------------------------------------
1 | package;
2 |
3 | import clay.Clay;
4 |
5 | class Main {
6 |
7 | public static function main() {
8 | Clay.init(
9 | {
10 | title: 'empty',
11 | width: 800,
12 | height: 600,
13 | window: {
14 | resizable: false
15 | }
16 | },
17 | onReady
18 | );
19 | }
20 |
21 | static function onReady() {
22 | new Game();
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/tools/cli/CLI.hx:
--------------------------------------------------------------------------------
1 | package;
2 |
3 | import haxe.io.Path;
4 | import sys.FileSystem;
5 |
6 | class CLI {
7 |
8 | public static final engineName:String = 'clay2d';
9 | public static final templatesPath:String = 'templates';
10 | public static final backendPath:String = 'backend';
11 | public static final khaPath:String = 'Kha';
12 |
13 | public static var commandMap:Map;
14 | public static var userDir:String;
15 | public static var engineDir:String;
16 | public static var khamakePath:String;
17 |
18 | public static var targets:Array = ['html5', 'windows', 'windows-hl', 'osx', 'linux', 'android-native', 'android-hl', 'ios', 'uwp'];
19 |
20 | static function main() {
21 | var args = Sys.args();
22 |
23 | userDir = args.pop();
24 | engineDir = FileSystem.absolutePath(Path.directory(neko.vm.Module.local().name));
25 | khamakePath = Path.join([engineDir, backendPath, khaPath, 'make']);
26 |
27 | init();
28 |
29 | if(args.length == 0) {
30 | for (c in commandMap) {
31 | print('${c.name}\t ${c.usage}');
32 | }
33 | return;
34 | }
35 | processArgs(args);
36 | }
37 |
38 | public static function print(msg:String) {
39 | Sys.println(msg);
40 | }
41 |
42 | public static function error(msg:String) {
43 | Sys.println("error: " + msg);
44 | Sys.exit(1);
45 | }
46 |
47 | public static function execute(cmd:String, args:Array):Int {
48 | var cwd = Sys.getCwd();
49 | Sys.setCwd(userDir);
50 |
51 | var ret = Sys.command(cmd, args);
52 |
53 | Sys.setCwd(cwd);
54 |
55 | return ret;
56 | }
57 |
58 | public static function deleteDir(path:String, verbose:Bool = false) {
59 | if (sys.FileSystem.exists(path) && sys.FileSystem.isDirectory(path)) {
60 | var entries = sys.FileSystem.readDirectory(path);
61 | for (entry in entries) {
62 | if (sys.FileSystem.isDirectory(path + '/' + entry)) {
63 | deleteDir(path + '/' + entry);
64 | if (verbose) CLI.print('delete dir ${path + '/' + entry}');
65 | sys.FileSystem.deleteDirectory(path + '/' + entry);
66 | } else {
67 | if (verbose) CLI.print('delete file ${path + '/' + entry}');
68 | sys.FileSystem.deleteFile(path + '/' + entry);
69 | }
70 | }
71 | }
72 | }
73 |
74 | public static function copyDir(srcDir:String, dstDir:String, verbose:Bool = false, includeGit:Bool = false) {
75 | if (!FileSystem.exists(dstDir)) {
76 | if (verbose) CLI.print('create dir ${dstDir}');
77 | FileSystem.createDirectory(dstDir);
78 | }
79 |
80 | for (name in FileSystem.readDirectory(srcDir)) {
81 | var srcPath = Path.join([srcDir, name]);
82 | var dstPath = Path.join([dstDir, name]);
83 |
84 | if(!includeGit && name =='.git') {
85 | if (verbose) CLI.print('skip copying .git folder');
86 | continue;
87 | }
88 |
89 | if (FileSystem.isDirectory(srcPath)) {
90 | copyDir(srcPath, dstPath, verbose);
91 | } else {
92 | if (verbose) CLI.print('copy $srcPath to $dstPath');
93 | sys.io.File.copy(srcPath, dstPath);
94 | }
95 |
96 | }
97 | }
98 |
99 | static function processArgs(args:Array) {
100 | var cname = args.shift();
101 | runCommand(cname, args);
102 | }
103 |
104 | static function runCommand(cname:String, args:Array) {
105 | var cmd = commandMap.get(cname);
106 | if(cmd == null) {
107 | print('Unknown command');
108 | return;
109 | }
110 | cmd.execute(args);
111 | }
112 |
113 | static function init() {
114 | commandMap = new Map();
115 |
116 | commandMap.set('init', new commands.Init());
117 | commandMap.set('help', new commands.Help());
118 | commandMap.set('build', new commands.Build());
119 | commandMap.set('run', new commands.Run());
120 | commandMap.set('launch', new commands.Launch());
121 | commandMap.set('server', new commands.Server());
122 | commandMap.set('clear', new commands.Clear());
123 | commandMap.set('collect', new commands.Collect());
124 | }
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/tools/cli/Command.hx:
--------------------------------------------------------------------------------
1 | package;
2 |
3 |
4 | class Command {
5 |
6 | public var name:String;
7 | public var usage:String;
8 |
9 | public function new(name:String, usage:String) {
10 | this.name = name;
11 | this.usage = usage;
12 | }
13 |
14 |
15 | public function execute(args:Array) {}
16 |
17 | }
--------------------------------------------------------------------------------
/tools/cli/cli.hxml:
--------------------------------------------------------------------------------
1 | -main CLI
2 |
3 | -lib yaml
4 |
5 | -neko ../../run.n
6 | -dce full
7 |
--------------------------------------------------------------------------------
/tools/cli/commands/Clear.hx:
--------------------------------------------------------------------------------
1 | package commands;
2 |
3 | import Config;
4 | import sys.FileSystem;
5 | import sys.io.File;
6 | import haxe.io.Path;
7 |
8 | class Clear extends Command {
9 |
10 | var filesRemoved:Int = 0;
11 | var dirsRemoved:Int = 0;
12 |
13 | public function new() {
14 | super(
15 | 'clear',
16 | 'remove project build files: clear '
17 | );
18 | }
19 |
20 | override function execute(args:Array) {
21 | if(args.length == 0) {
22 | CLI.error('Target not defined');
23 | }
24 |
25 | var target = args[0];
26 | if(target != 'all' && CLI.targets.indexOf(target) == -1) {
27 | CLI.error('Unknown target, use: [all,${CLI.targets.join(",")}]');
28 | }
29 |
30 | var config:ConfigData = Config.get();
31 | var toRemove:Array = [];
32 | var projectTitle:String = config.project.title;
33 |
34 | if(target == 'all') {
35 | toRemove.push(Path.join([CLI.userDir, 'build']));
36 | } else {
37 | toRemove.push(Path.join([CLI.userDir, 'build/$target']));
38 | toRemove.push(Path.join([CLI.userDir, 'build/$target-resources']));
39 | toRemove.push(Path.join([CLI.userDir, 'build/$target-build']));
40 | toRemove.push(Path.join([CLI.userDir, 'build/$projectTitle-$target-intellij']));
41 | toRemove.push(Path.join([CLI.userDir, 'build/$projectTitle-$target.hxproj']));
42 | toRemove.push(Path.join([CLI.userDir, 'build/$projectTitle-$target.hxml']));
43 | toRemove.push(Path.join([CLI.userDir, 'build/project-$target.hxml']));
44 | toRemove.push(Path.join([CLI.userDir, 'build/temp']));
45 | toRemove.push(Path.join([CLI.userDir, 'build/khafile.js']));
46 | toRemove.push(Path.join([CLI.userDir, 'build/korefile.js']));
47 | toRemove.push(Path.join([CLI.userDir, 'build/icon.png']));
48 | }
49 |
50 | dirsRemoved = 0;
51 | filesRemoved = 0;
52 |
53 | for (s in toRemove) {
54 | if (FileSystem.exists(s)) {
55 | if(FileSystem.isDirectory(s)) {
56 | CLI.deleteDir(s);
57 | FileSystem.deleteDirectory(s);
58 | dirsRemoved++;
59 | } else {
60 | FileSystem.deleteFile(s);
61 | filesRemoved++;
62 | }
63 | }
64 | }
65 |
66 | // TODO: removed count is wrong
67 | CLI.print('Done: $dirsRemoved directories with $filesRemoved files was removed');
68 | }
69 |
70 | }
--------------------------------------------------------------------------------
/tools/cli/commands/Collect.hx:
--------------------------------------------------------------------------------
1 | package commands;
2 |
3 | import Config;
4 | import sys.FileSystem;
5 | import sys.io.File;
6 | import haxe.io.Path;
7 | using StringTools;
8 |
9 | class Collect extends Command {
10 |
11 | public function new() {
12 | super(
13 | 'collect',
14 | 'collect all libraries to project folder: collect [--clear, --verbose, --includeGit]'
15 | );
16 | }
17 |
18 | override function execute(args:Array) {
19 | var config:ConfigData = Config.get();
20 |
21 | var clear = false;
22 | var verbose = false;
23 | var includeGit = false;
24 |
25 | for (a in args) {
26 | switch (a) {
27 | case '--clear': clear = true;
28 | case '--verbose': verbose = true;
29 | case '--includeGit': includeGit = true;
30 | default:
31 | }
32 | }
33 |
34 | var libraries:Array = config.project.libraries;
35 |
36 | for (l in libraries) {
37 | var process = new sys.io.Process('haxelib', [ 'libpath', '$l']);
38 | if(process.exitCode() != 0) {
39 | var message = process.stderr.readAll().toString().trim();
40 | var pos = haxe.macro.Context.currentPos();
41 | CLI.error('Cannot execute clay2d collect ${message}');
42 | } else {
43 | var srcDir = process.stdout.readAll().toString().trim();
44 | srcDir = Path.normalize(srcDir);
45 | var pj = srcDir.split('/');
46 | var name = pj[pj.length-1];
47 | var dstDir = Path.join([CLI.userDir, name]);
48 | copyDir(srcDir, dstDir, verbose, clear, includeGit);
49 | }
50 | process.close();
51 | }
52 |
53 | var khaPath = Path.join([CLI.engineDir, CLI.backendPath, CLI.khaPath]);
54 | var dstDir = Path.join([CLI.userDir, CLI.khaPath]);
55 | copyDir(khaPath, dstDir, verbose, clear, includeGit);
56 | }
57 |
58 | function copyDir(srcDir:String, dstDir:String, verbose:Bool, clear:Bool, includeGit:Bool) {
59 | if(srcDir == dstDir) {
60 | CLI.error('can`t copy library $srcDir to same folder');
61 | return false;
62 | }
63 |
64 | if (FileSystem.exists(dstDir)) {
65 | if(clear) {
66 | CLI.print('delete $dstDir');
67 | CLI.deleteDir(dstDir, verbose);
68 | } else {
69 | CLI.error('directory $dstDir is not empty, skip copy library');
70 | return false;
71 | }
72 | }
73 | CLI.print('copy $srcDir to ${CLI.userDir}');
74 | CLI.copyDir(srcDir, dstDir, verbose, includeGit);
75 |
76 | return true;
77 | }
78 |
79 | }
80 |
81 |
--------------------------------------------------------------------------------
/tools/cli/commands/Help.hx:
--------------------------------------------------------------------------------
1 | package commands;
2 |
3 | class Help extends Command {
4 |
5 | public function new() {
6 | super(
7 | 'help',
8 | 'print command list'
9 | );
10 | }
11 |
12 | }
--------------------------------------------------------------------------------
/tools/cli/commands/Init.hx:
--------------------------------------------------------------------------------
1 | package commands;
2 |
3 | import sys.FileSystem;
4 | import haxe.io.Path;
5 |
6 | class Init extends Command {
7 |
8 | public function new() {
9 | super(
10 | 'init',
11 | 'initialize a new project: init [-t template]'
12 | );
13 | }
14 |
15 | override function execute(args:Array) {
16 |
17 | var templateName = 'empty';
18 | if(args.length > 0) {
19 | if(args[0] == '-t' && args[1] != null) {
20 | templateName = args[1];
21 | }
22 | }
23 | createProject(templateName);
24 |
25 | }
26 |
27 | function createProject(template:String) {
28 | var templatePath = Path.join([CLI.engineDir, CLI.templatesPath, template]);
29 | if (!FileSystem.exists(templatePath)) {
30 | CLI.error('Cant find ${template} template');
31 | }
32 |
33 | var dir = FileSystem.readDirectory(CLI.userDir);
34 | if(dir.length > 0) {
35 | CLI.error('${CLI.userDir} folder is not empty');
36 | }
37 |
38 | CLI.copyDir(templatePath, CLI.userDir);
39 | CLI.print('New ${CLI.engineName} project is created in ${CLI.userDir}');
40 | }
41 |
42 | }
--------------------------------------------------------------------------------
/tools/cli/commands/Launch.hx:
--------------------------------------------------------------------------------
1 | package commands;
2 |
3 | import Config;
4 | import sys.FileSystem;
5 | import sys.io.File;
6 | import haxe.io.Path;
7 |
8 | class Launch extends Command {
9 |
10 | public function new() {
11 | super(
12 | 'launch',
13 | 'launch project: launch '
14 | );
15 | }
16 |
17 | override function execute(args:Array) {
18 | if(args.length == 0) {
19 | CLI.error('Target not defined');
20 | }
21 |
22 | var target = args[0];
23 | if(CLI.targets.indexOf(target) == -1) {
24 | CLI.error('Unknown target, use: [${CLI.targets.join(",")}]');
25 | }
26 |
27 | var config:ConfigData = Config.get();
28 |
29 | config.debug = false;
30 | for (a in args) {
31 | switch (a) {
32 | case '--debug' : {
33 | config.debug = true;
34 | }
35 | }
36 | }
37 |
38 | launchProject(config, target);
39 | }
40 |
41 | function launchProject(config:ConfigData, target:String) {
42 | switch (target) {
43 | case 'html5' : {
44 | var port = 8080;
45 | if(config.html5 != null && config.html5.serverPort != null) {
46 | port = config.html5.serverPort;
47 | }
48 | var url = 'http://localhost:$port/';
49 | switch (Sys.systemName()) {
50 | case "Linux", "BSD": CLI.execute('xdg-open', ['$url']);
51 | case "Mac": CLI.execute('open', ['$url']);
52 | case "Windows": CLI.execute('start', ['$url']);
53 | default:
54 | }
55 | }
56 | case 'windows' : {
57 | var path = Path.join([CLI.userDir, 'build/windows/${config.project.title}.exe']);
58 | if(!FileSystem.exists(path)) {
59 | CLI.error('Can`t find app at: $path');
60 | }
61 | CLI.execute('start', ['cmd', "/c", '$path']); // todo: remove cmd ?
62 | }
63 | case 'windows-hl' : {
64 | var path = Path.join([CLI.userDir, 'build/windows-hl/${config.project.title}.exe']);
65 | if(!FileSystem.exists(path)) {
66 | CLI.error('Can`t find app at: $path');
67 | }
68 | CLI.execute('start', ['cmd', "/c", '$path']); // todo: remove cmd ?
69 | }
70 | case 'android-hl' : {
71 | var path:String;
72 |
73 | if(config.debug) {
74 | path = Path.join([CLI.userDir, 'build/android-hl-build/${config.project.title}/app/build/outputs/apk/debug/app-debug.apk']);
75 | } else {
76 | path = Path.join([CLI.userDir, 'build/android-hl-build/${config.project.title}/app/build/outputs/apk/release/app-release.apk']);
77 | }
78 |
79 | if(!FileSystem.exists(path)) {
80 | CLI.error('Can`t find app at: $path');
81 | }
82 | var pkg = Reflect.field(config.android, 'package');
83 |
84 | if(pkg == null) {
85 | CLI.print('Can`t find android package settings, use tech.kode.kha');
86 | pkg = 'tech.kode.kha';
87 | }
88 | // CLI.execute('start', ['cmd', "/c", 'adb', 'uninstall', '$pkg']);
89 | CLI.execute('start', ['cmd', "/c", 'adb', 'install', '-r', '$path']);
90 | if(config.debug) {
91 | CLI.execute('start', ['cmd', "/c", 'adb', 'logcat', '-s', 'Kinc', 'DEBUG', 'AndroidRuntime']);
92 | }
93 | CLI.execute('start', ['cmd', "/c", 'adb', 'shell', 'am', 'start', '-n', '$pkg/tech.kode.kore.KoreActivity']);
94 | }
95 | case 'linux': {
96 | var path = Path.join([CLI.userDir, 'build/linux/${config.project.title}']);
97 | if(!FileSystem.exists(path)) {
98 | CLI.error('Can`t find app at: $path');
99 | }
100 | CLI.execute('xdg-open', ['$path']);
101 | }
102 | case 'osx' : {
103 | var path = Path.join([CLI.userDir, 'build/osx/${config.project.title}']);
104 | if(!FileSystem.exists(path)) {
105 | CLI.error('Can`t find app at: $path');
106 | }
107 | CLI.execute('open', ['$path']);
108 | }
109 | }
110 |
111 | }
112 |
113 | }
--------------------------------------------------------------------------------
/tools/cli/commands/Run.hx:
--------------------------------------------------------------------------------
1 | package commands;
2 |
3 | import Config;
4 | import sys.FileSystem;
5 | import sys.io.File;
6 | import haxe.io.Path;
7 |
8 | class Run extends Command {
9 |
10 | public function new() {
11 | super(
12 | 'run',
13 | 'build and run current project: run [--debug]'
14 | );
15 | }
16 |
17 | override function execute(args:Array) {
18 | if(args.length == 0) {
19 | CLI.error('Target not defined');
20 | }
21 |
22 | var target = args[0];
23 | if(CLI.targets.indexOf(target) == -1) {
24 | CLI.error('Unknown target, use: [${CLI.targets.join(",")}]');
25 | }
26 |
27 | var buildCommand = CLI.commandMap.get('build');
28 | var launchCommand = CLI.commandMap.get('launch');
29 | buildCommand.execute(args);
30 | launchCommand.execute(args);
31 | }
32 |
33 | }
--------------------------------------------------------------------------------
/tools/cli/commands/Server.hx:
--------------------------------------------------------------------------------
1 | package commands;
2 |
3 | import Config;
4 | import haxe.io.Path;
5 |
6 | class Server extends Command {
7 |
8 | public function new() {
9 | super(
10 | 'server',
11 | 'launch server for html5 build'
12 | );
13 | }
14 |
15 | override function execute(args:Array) {
16 | var config:ConfigData = Config.get();
17 | var port = 8080;
18 |
19 | if(config.html5 != null && config.html5.serverPort != null) {
20 | port = config.html5.serverPort;
21 | }
22 |
23 | CLI.execute('start', ['cmd', "/c", '${CLI.khamakePath}', '--server', '--port', '$port']);
24 | }
25 |
26 | }
--------------------------------------------------------------------------------