├── .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 | } --------------------------------------------------------------------------------