├── sample-mac.hxml ├── .gitignore ├── src └── clay │ ├── native │ ├── NativeIO.hx │ ├── NativeAudio.hx │ ├── NativeAssets.hx │ ├── NativeAudioDataPCM.hx │ └── NativeAudioDataWAV.hx │ ├── audio │ ├── AudioHandle.hx │ ├── AudioDataOptions.hx │ ├── AudioEvent.hx │ ├── AudioState.hx │ ├── AudioFormat.hx │ ├── AudioInstance.hx │ ├── AudioErrorReason.hx │ ├── AudioSource.hx │ └── AudioData.hx │ ├── IntMap.hx │ ├── Assets.hx │ ├── Audio.hx │ ├── graphics │ ├── Vector2.hx │ ├── TextureAndSlot.hx │ ├── Vector3.hx │ ├── Color.hx │ ├── Vector4.hx │ ├── RenderTexture.hx │ ├── Shader.hx │ └── Texture.hx │ ├── Resource.hx │ ├── Macros.hx │ ├── buffers │ ├── TAError.hx │ ├── TypedArrayType.hx │ ├── ArrayBuffer.hx │ ├── Int8Array.hx │ ├── Int16Array.hx │ ├── Uint32Array.hx │ ├── Uint16Array.hx │ ├── Uint8Array.hx │ ├── Float64Array.hx │ ├── Int32Array.hx │ ├── Float32Array.hx │ ├── Uint8ClampedArray.hx │ ├── ArrayBufferIO.hx │ ├── DataView.hx │ └── DataViewBE.hx │ ├── sdl │ ├── SDLConfig.hx │ ├── Linc.hx │ ├── SDLIO.hx │ └── linc │ │ ├── linc_sdl.xml │ │ └── linc_sdl.h │ ├── soloud │ ├── SoloudBus.hx │ ├── SoloudAudioData.hx │ └── SoloudSound.hx │ ├── web │ ├── WebConfig.hx │ └── WebIO.hx │ ├── base │ ├── BaseRuntime.hx │ ├── BaseIO.hx │ ├── BaseAudio.hx │ └── BaseAssets.hx │ ├── Log.hx │ ├── Files.hx │ ├── Extensions.hx │ ├── PremultiplyAlpha.hx │ ├── Image.hx │ ├── ReusableArray.hx │ ├── Immediate.hx │ ├── Events.hx │ ├── Utils.hx │ ├── BackgroundQueue.hx │ ├── Runner.hx │ ├── ArrayPool.hx │ └── Input.hx ├── src-opengl └── clay │ ├── IO.hx │ ├── Runtime.hx │ ├── GraphicsBatcher.hx │ ├── GraphicsDriver.hx │ ├── opengl │ └── GL.hx │ └── Config.hx ├── sample-web.hxml ├── haxelib.json ├── sample-cpp.hxml ├── .vscode └── tasks.json ├── README.md ├── LICENSE ├── src-miniaudio └── clay │ └── soloud │ └── SoloudAudioBackend.hx ├── project └── web │ └── index.html └── sample └── src └── sample ├── SampleVertex.hx └── SampleIndices.hx /sample-mac.hxml: -------------------------------------------------------------------------------- 1 | sample-cpp.hxml 2 | -D mac -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /out 3 | project/web/app.js 4 | -------------------------------------------------------------------------------- /src/clay/native/NativeIO.hx: -------------------------------------------------------------------------------- 1 | package clay.native; 2 | 3 | import clay.base.BaseIO; 4 | 5 | class NativeIO extends BaseIO { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/clay/audio/AudioHandle.hx: -------------------------------------------------------------------------------- 1 | package clay.audio; 2 | 3 | /** An audio handle for tracking audio instances */ 4 | typedef AudioHandle = Null; 5 | -------------------------------------------------------------------------------- /src-opengl/clay/IO.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | #if clay_web 4 | typedef IO = clay.web.WebIO; 5 | #elseif clay_sdl 6 | typedef IO = clay.sdl.SDLIO; 7 | #end 8 | -------------------------------------------------------------------------------- /src/clay/IntMap.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | #if ceramic 4 | typedef IntMap = ceramic.IntMap; 5 | #else 6 | typedef IntMap = haxe.ds.IntMap; 7 | #end 8 | -------------------------------------------------------------------------------- /src-opengl/clay/Runtime.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | #if clay_web 4 | typedef Runtime = clay.web.WebRuntime; 5 | #elseif clay_sdl 6 | typedef Runtime = clay.sdl.SDLRuntime; 7 | #end 8 | -------------------------------------------------------------------------------- /src/clay/Assets.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | #if clay_web 4 | typedef Assets = clay.web.WebAssets; 5 | #elseif clay_native 6 | typedef Assets = clay.native.NativeAssets; 7 | #end 8 | -------------------------------------------------------------------------------- /src/clay/Audio.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | #if clay_web 4 | typedef Audio = clay.web.WebAudio; 5 | #elseif (clay_native && clay_soloud) 6 | typedef Audio = clay.soloud.SoloudAudio; 7 | #end 8 | -------------------------------------------------------------------------------- /src/clay/graphics/Vector2.hx: -------------------------------------------------------------------------------- 1 | package clay.graphics; 2 | 3 | @:structInit 4 | class Vector2 { 5 | 6 | public var x:Float = 0; 7 | 8 | public var y:Float = 0; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/clay/Resource.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | class Resource { 4 | 5 | /** 6 | * A string id to identify the resource 7 | */ 8 | public var id:String = null; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/clay/graphics/TextureAndSlot.hx: -------------------------------------------------------------------------------- 1 | package clay.graphics; 2 | 3 | @:structInit 4 | class TextureAndSlot { 5 | 6 | public var texture:Texture; 7 | 8 | public var slot:Int; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/clay/graphics/Vector3.hx: -------------------------------------------------------------------------------- 1 | package clay.graphics; 2 | 3 | @:structInit 4 | class Vector3 { 5 | 6 | public var x:Float = 0; 7 | 8 | public var y:Float = 0; 9 | 10 | public var z:Float = 0; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/clay/Macros.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | class Macros { 4 | 5 | macro static public function definedValue(define:String):haxe.macro.Expr { 6 | 7 | return macro $v{haxe.macro.Context.definedValue(define)}; 8 | 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /sample-web.hxml: -------------------------------------------------------------------------------- 1 | -cp src 2 | -cp sample/src 3 | --main sample.SampleVertex 4 | --js project/web/app.js 5 | -D clay 6 | -D clay_web 7 | -D web 8 | -D clay_app_id=claysample 9 | -lib linc_opengl 10 | -lib linc_ogg 11 | -lib linc_stb 12 | -lib linc_timestamp -------------------------------------------------------------------------------- /src/clay/graphics/Color.hx: -------------------------------------------------------------------------------- 1 | package clay.graphics; 2 | 3 | @:structInit 4 | @:publicFields 5 | class Color { 6 | 7 | var r:Float = 0; 8 | 9 | var g:Float = 0; 10 | 11 | var b:Float = 0; 12 | 13 | var a:Float = 1; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/clay/graphics/Vector4.hx: -------------------------------------------------------------------------------- 1 | package clay.graphics; 2 | 3 | @:structInit 4 | class Vector4 { 5 | 6 | public var x:Float = 0; 7 | 8 | public var y:Float = 0; 9 | 10 | public var z:Float = 0; 11 | 12 | public var w:Float = 0; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/clay/buffers/TAError.hx: -------------------------------------------------------------------------------- 1 | package clay.buffers; 2 | 3 | // Code imported from snowkit/snow (https://github.com/snowkit/snow/tree/fe93eb1ecfc82131a6143be1b3e3e0a274f4cf65) 4 | // Credits go to its original author (@ruby0x1 on Github) 5 | 6 | enum TAError { 7 | RangeError(reason:String); 8 | } -------------------------------------------------------------------------------- /haxelib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "clay", 3 | "url" : "https://github.com/ceramic-engine/clay/", 4 | "license": "MIT", 5 | "tags": [], 6 | "description": "", 7 | "version": "0.0.1", 8 | "classPath": "src/", 9 | "releasenote": "", 10 | "contributors": ["jeremyfa"], 11 | "dependencies": { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /sample-cpp.hxml: -------------------------------------------------------------------------------- 1 | -cp src 2 | -cp sample/src 3 | --main sample.SampleVertex 4 | --cpp out/cpp 5 | -D clay 6 | -D clay_native 7 | -D clay_sdl 8 | -D clay_use_glew 9 | -D clay_app_id=claysample 10 | -D clay_soloud 11 | -D hxcpp_static_std 12 | -lib hxcpp 13 | -lib linc_opengl 14 | -lib linc_ogg 15 | -lib linc_stb 16 | -lib linc_timestamp 17 | -lib linc_soloud -------------------------------------------------------------------------------- /src-opengl/clay/GraphicsBatcher.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | /** 4 | * Platform-specific graphics batcher for batched rendering. 5 | * 6 | * On OpenGL platforms, this resolves to GLGraphicsBatcher which provides 7 | * optimized vertex batching and draw call submission using GL APIs. 8 | */ 9 | typedef GraphicsBatcher = clay.opengl.GLGraphicsBatcher; 10 | -------------------------------------------------------------------------------- /src/clay/sdl/SDLConfig.hx: -------------------------------------------------------------------------------- 1 | package clay.sdl; 2 | 3 | @:structInit 4 | @:publicFields 5 | class SDLConfig { 6 | 7 | /** Custom uncaught error handler */ 8 | public var uncaughtErrorHandler:(error:Dynamic)->Void = null; 9 | 10 | /** 11 | * Toggle auto window swap 12 | */ 13 | public var autoSwap:Bool = true; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "haxe", 6 | "args": "active configuration", 7 | "problemMatcher": [ 8 | "$haxe-absolute", 9 | "$haxe", 10 | "$haxe-error", 11 | "$haxe-trace" 12 | ], 13 | "group": { 14 | "kind": "build", 15 | "isDefault": true 16 | }, 17 | "label": "haxe: active configuration" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /src-opengl/clay/GraphicsDriver.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | /** 4 | * Platform-specific graphics driver for GPU resource management. 5 | * 6 | * On OpenGL platforms, this resolves to GLGraphicsDriver which provides 7 | * texture, shader, and render target management using GL APIs. 8 | * 9 | * Access the driver instance via Clay.app.graphics. 10 | */ 11 | typedef GraphicsDriver = clay.opengl.GLGraphicsDriver; 12 | -------------------------------------------------------------------------------- /src/clay/soloud/SoloudBus.hx: -------------------------------------------------------------------------------- 1 | package clay.soloud; 2 | 3 | import soloud.Bus; 4 | 5 | @:allow(clay.soloud.SoloudAudio) 6 | class SoloudBus { 7 | 8 | @:unreflective public var bus(default, null):Bus; 9 | 10 | public function new() { 11 | this.bus = Bus.create(); 12 | } 13 | 14 | public function destroy() { 15 | bus.destroy(); 16 | bus = untyped __cpp__('NULL'); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/clay/audio/AudioDataOptions.hx: -------------------------------------------------------------------------------- 1 | package clay.audio; 2 | 3 | import clay.buffers.Uint8Array; 4 | 5 | /** Options for constructing an AudioData instance */ 6 | typedef AudioDataOptions = { 7 | 8 | @:optional var id:String; 9 | 10 | @:optional var rate:Int; 11 | @:optional var length:Int; 12 | @:optional var channels:Int; 13 | @:optional var bitsPerSample:Int; 14 | @:optional var format:AudioFormat; 15 | @:optional var samples:Uint8Array; 16 | @:optional var isStream:Bool; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/clay/audio/AudioEvent.hx: -------------------------------------------------------------------------------- 1 | package clay.audio; 2 | 3 | enum abstract AudioEvent(Int) from Int to Int { 4 | 5 | var END = 0; 6 | 7 | var DESTROYED = 1; 8 | 9 | var DESTROYED_SOURCE = 2; 10 | 11 | inline function toString() { 12 | return switch(this) { 13 | case END: 'END'; 14 | case DESTROYED: 'DESTROYED'; 15 | case DESTROYED_SOURCE: 'DESTROYED_SOURCE'; 16 | case _: '$this'; 17 | } 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/clay/audio/AudioState.hx: -------------------------------------------------------------------------------- 1 | package clay.audio; 2 | 3 | enum abstract AudioState(Int) from Int to Int { 4 | 5 | var INVALID = -1; 6 | 7 | var PAUSED = 0; 8 | 9 | var PLAYING = 1; 10 | 11 | var STOPPED = 2; 12 | 13 | inline function toString() { 14 | return switch(this) { 15 | case INVALID: 'INVALID'; 16 | case PAUSED: 'PAUSED'; 17 | case PLAYING: 'PLAYING'; 18 | case STOPPED: 'STOPPED'; 19 | case _: '$this'; 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/clay/buffers/TypedArrayType.hx: -------------------------------------------------------------------------------- 1 | package clay.buffers; 2 | 3 | // Code imported from snowkit/snow (https://github.com/snowkit/snow/tree/fe93eb1ecfc82131a6143be1b3e3e0a274f4cf65) 4 | // Credits go to its original author (@ruby0x1 on Github) 5 | 6 | enum abstract TypedArrayType(Int) from Int to Int { 7 | var None = 0; 8 | var Int8 = 1; 9 | var Int16 = 2; 10 | var Int32 = 3; 11 | var Uint8 = 4; 12 | var Uint8Clamped = 5; 13 | var Uint16 = 6; 14 | var Uint32 = 7; 15 | var Float32 = 8; 16 | var Float64 = 9; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/clay/web/WebConfig.hx: -------------------------------------------------------------------------------- 1 | package clay.web; 2 | 3 | @:structInit 4 | @:publicFields 5 | class WebConfig { 6 | 7 | var windowId:String = 'app'; 8 | 9 | var windowParent:js.html.Element = js.Browser.document.body; 10 | 11 | var preventDefaultContextMenu:Bool = #if clay_allow_default_context_menu false #else true #end; 12 | 13 | var preventDefaultMouseWheel:Bool = #if clay_allow_default_mouse_wheel false #else true #end; 14 | 15 | var preventDefaultTouches:Bool = #if clay_allow_default_touches false #else true #end; 16 | 17 | var preventDefaultKeys:Array = [ 18 | KeyCode.LEFT, KeyCode.RIGHT, KeyCode.UP, KeyCode.DOWN, 19 | KeyCode.BACKSPACE, KeyCode.TAB, KeyCode.DELETE, KeyCode.SPACE 20 | ]; 21 | 22 | var mouseUseBrowserWindowEvents:Bool = true; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/clay/base/BaseRuntime.hx: -------------------------------------------------------------------------------- 1 | package clay.base; 2 | 3 | class BaseRuntime { 4 | 5 | /** 6 | * Clay app 7 | */ 8 | public var app(default, null):Clay; 9 | 10 | public var name(default, null):String = null; 11 | 12 | function new(app:Clay) { 13 | 14 | this.app = app; 15 | 16 | } 17 | 18 | public function init():Void {} 19 | 20 | public function shutdown(immediate:Bool = false):Void {} 21 | 22 | public function ready():Void {} 23 | 24 | public function run():Bool { 25 | 26 | return true; 27 | 28 | } 29 | 30 | public function windowDevicePixelRatio():Float { 31 | 32 | return 1.0; 33 | 34 | } 35 | 36 | public function windowWidth():Int { 37 | 38 | return 0; 39 | 40 | } 41 | 42 | public function windowHeight():Int { 43 | 44 | return 0; 45 | 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/clay/buffers/ArrayBuffer.hx: -------------------------------------------------------------------------------- 1 | package clay.buffers; 2 | 3 | // Code imported from snowkit/snow (https://github.com/snowkit/snow/tree/fe93eb1ecfc82131a6143be1b3e3e0a274f4cf65) 4 | // Credits go to its original author (@ruby0x1 on Github) 5 | 6 | #if js 7 | 8 | typedef ArrayBuffer = js.lib.ArrayBuffer; 9 | 10 | #else 11 | 12 | import haxe.io.BytesData; 13 | 14 | @:forward 15 | abstract ArrayBufferImpl(BytesData) from BytesData to BytesData { 16 | 17 | public var byteLength (get, never) : Int; 18 | 19 | public inline function new( byteLength:Int ) { 20 | this = new BytesData(); 21 | if(byteLength>0) this[byteLength-1] = untyped 0; 22 | } 23 | 24 | inline function get_byteLength() { 25 | return this.length; 26 | } 27 | } 28 | 29 | typedef ArrayBuffer = ArrayBufferImpl; 30 | 31 | #end 32 | -------------------------------------------------------------------------------- /src/clay/Log.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | class Log { 4 | 5 | public static function debug(message:String, ?pos:haxe.PosInfos):Void { 6 | 7 | #if clay_debug 8 | haxe.Log.trace('[debug] ' + message, pos); 9 | #end 10 | 11 | } 12 | 13 | public static function info(message:String, ?pos:haxe.PosInfos):Void { 14 | 15 | haxe.Log.trace('[info] ' + message, pos); 16 | 17 | } 18 | 19 | public static function warning(message:String, ?pos:haxe.PosInfos):Void { 20 | 21 | haxe.Log.trace('[warning] ' + message, pos); 22 | 23 | } 24 | 25 | public static function error(message:String, ?pos:haxe.PosInfos):Void { 26 | 27 | haxe.Log.trace('[error] ' + message, pos); 28 | 29 | } 30 | 31 | public static function success(message:String, ?pos:haxe.PosInfos):Void { 32 | 33 | haxe.Log.trace('[success] ' + message, pos); 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Clay 3 | 4 | Clay is a lightweight toolkit to build apps, games and frameworks on native platforms and web browser, written with the [Haxe Programming Language](https://haxe.org). 5 | 6 | This is mostly a rewrite and a successor of the excellent snowkit's [snow](https://snowkit.github.io/snow/) library. 7 | 8 | In fact, **Clay** keeps a substantial portion of **snow**'s codebase, and borrows some snippets from [luxe alpha](https://luxeengine.com/alpha) as well. It has then been reworked to focus on its own goals: Haxe 4 support, only keeping features that are not overlapping with [ceramic](https://github.com/ceramic-engine/ceramic) and adapting the code style. 9 | 10 | Although it could be used as is. The intended usage of **Clay** is within [ceramic](https://github.com/ceramic-engine/ceramic) apps to provide native support of **Mac**, **Windows**, **Linux**, **iOS** and **Android** platforms as well as **Web** browsers using webgl. 11 | -------------------------------------------------------------------------------- /src/clay/base/BaseIO.hx: -------------------------------------------------------------------------------- 1 | package clay.base; 2 | 3 | import clay.buffers.Uint8Array; 4 | 5 | class BaseIO { 6 | 7 | /** 8 | * Clay app 9 | */ 10 | public var app(default, null):Clay; 11 | 12 | function new(app:Clay) { 13 | 14 | this.app = app; 15 | 16 | } 17 | 18 | public function init():Void {} 19 | 20 | public function shutdown():Void {} 21 | 22 | public function isSynchronous():Bool { 23 | 24 | return false; 25 | 26 | } 27 | 28 | public function appPath():String { 29 | 30 | return null; 31 | 32 | } 33 | 34 | public function appPathPrefs():String { 35 | 36 | return null; 37 | 38 | } 39 | 40 | public function loadData(path:String, binary:Bool = false, async:Bool = false, ?callback:(data:Uint8Array)->Void):Uint8Array { 41 | 42 | if (callback != null) { 43 | Immediate.push(() -> { 44 | callback(null); 45 | }); 46 | } 47 | return null; 48 | 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/clay/Files.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | import haxe.io.Path; 4 | import sys.FileSystem; 5 | 6 | class Files { 7 | 8 | public static function deleteRecursive(toDelete:String):Void { 9 | 10 | #if (cs || sys || node || nodejs || hxnodejs) 11 | 12 | if (!FileSystem.exists(toDelete)) return; 13 | 14 | if (FileSystem.isDirectory(toDelete)) { 15 | 16 | for (name in FileSystem.readDirectory(toDelete)) { 17 | 18 | var path = Path.join([toDelete, name]); 19 | if (FileSystem.isDirectory(path)) { 20 | deleteRecursive(path); 21 | } else { 22 | FileSystem.deleteFile(path); 23 | } 24 | } 25 | 26 | FileSystem.deleteDirectory(toDelete); 27 | 28 | } 29 | else { 30 | 31 | FileSystem.deleteFile(toDelete); 32 | 33 | } 34 | 35 | #else 36 | 37 | Log.warning('deleteRecursive() is not supported on this target'); 38 | 39 | #end 40 | 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /src/clay/soloud/SoloudAudioData.hx: -------------------------------------------------------------------------------- 1 | package clay.soloud; 2 | 3 | import clay.audio.AudioData; 4 | import soloud.Wav; 5 | import soloud.WavStream; 6 | 7 | class SoloudAudioData extends clay.audio.AudioData { 8 | 9 | @:unreflective public var wav:Wav; 10 | @:unreflective public var wavStream:WavStream; 11 | 12 | inline public function new( 13 | app:Clay, 14 | // ?wav:Wav, 15 | // ?wavStream:WavStream, 16 | options:AudioDataOptions 17 | ) { 18 | 19 | // this.wav = wav; 20 | // this.wavStream = wavStream; 21 | 22 | super(app, options); 23 | 24 | } 25 | 26 | override public function destroy() { 27 | 28 | if (destroyed) 29 | return; 30 | 31 | if (wav != null) { 32 | wav.destroy(); 33 | wav = untyped __cpp__('NULL'); 34 | } 35 | 36 | if (wavStream != null) { 37 | wavStream.destroy(); 38 | wavStream = untyped __cpp__('NULL'); 39 | } 40 | 41 | super.destroy(); 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2014-2015 Sven Bergström 4 | Copyright (c) 2020-2021 Jérémy Faivre 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /src-miniaudio/clay/soloud/SoloudAudioBackend.hx: -------------------------------------------------------------------------------- 1 | package clay.soloud; 2 | 3 | import soloud.Soloud.SoloudBackends; 4 | import soloud.Soloud; 5 | 6 | #if clay_sdl 7 | @:headerCode('#include ') 8 | #end 9 | 10 | /** 11 | * Audio backend initializer for miniaudio/SDL. 12 | * 13 | * This class handles the platform-specific audio backend initialization 14 | * for SoLoud. By default it uses miniaudio, but can use SDL2 if the 15 | * `soloud_use_sdl` define is set. 16 | * 17 | * This file is located in src-miniaudio and included via classpath for 18 | * standard desktop/mobile builds. Other platforms can provide 19 | * their own SoloudAudioBackend.hx to use different audio backends. 20 | */ 21 | class SoloudAudioBackend { 22 | 23 | /** 24 | * Initializes the SoLoud audio backend. 25 | * 26 | * @param soloud The SoLoud instance to initialize 27 | * @return 0 on success, error code on failure 28 | */ 29 | @:unreflective public static function init(soloud:Soloud):Int { 30 | #if (!soloud_use_miniaudio && soloud_use_sdl) 31 | return soloud.init(0, SDL2, 0, 64, 2); 32 | #else 33 | return soloud.init(0, MINIAUDIO, 0, 0, 2); 34 | #end 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /project/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Clay 7 | 8 | 9 | 10 | 11 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/clay/Extensions.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | /** A bunch of static extensions to make life easier. */ 4 | class Extensions { 5 | 6 | /// Array extensions 7 | 8 | #if !clay_debug_unsafe inline #end public static function unsafeGet(array:Array, index:Int):T { 9 | #if clay_debug_unsafe 10 | if (index < 0 || index >= array.length) throw 'Invalid unsafeGet: index=$index length=${array.length}'; 11 | #end 12 | #if cpp 13 | #if app_cpp_nativearray_unsafe 14 | return cpp.NativeArray.unsafeGet(array, index); 15 | #else 16 | return untyped array.__unsafe_get(index); 17 | #end 18 | #else 19 | return array[index]; 20 | #end 21 | } 22 | 23 | #if !clay_debug_unsafe inline #end public static function unsafeSet(array:Array, index:Int, value:T):Void { 24 | #if clay_debug_unsafe 25 | if (index < 0 || index >= array.length) throw 'Invalid unsafeSet: index=$index length=${array.length}'; 26 | #end 27 | #if cpp 28 | #if app_cpp_nativearray_unsafe 29 | cpp.NativeArray.unsafeSet(array, index, value); 30 | #else 31 | untyped array.__unsafe_set(index, value); 32 | #end 33 | #else 34 | array[index] = value; 35 | #end 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/clay/base/BaseAudio.hx: -------------------------------------------------------------------------------- 1 | package clay.base; 2 | 3 | #if ceramic 4 | import ceramic.Path; 5 | #else 6 | import haxe.io.Path; 7 | #end 8 | 9 | import clay.audio.AudioData; 10 | import clay.audio.AudioEvent; 11 | import clay.audio.AudioFormat; 12 | import clay.audio.AudioHandle; 13 | 14 | class BaseAudio { 15 | 16 | /** 17 | * Clay app 18 | */ 19 | public var app(default, null):Clay; 20 | 21 | function new(app:Clay) { 22 | 23 | this.app = app; 24 | 25 | } 26 | 27 | public function isSynchronous():Bool { 28 | 29 | return false; 30 | 31 | } 32 | 33 | public function init():Void {} 34 | 35 | public function ready():Void {} 36 | 37 | public function tick(delta:Float):Void {} 38 | 39 | public function shutdown():Void {} 40 | 41 | public function loadData(path:String, isStream:Bool, format:AudioFormat, async:Bool = false, ?callback:(data:AudioData)->Void):AudioData { 42 | 43 | if (callback != null) { 44 | Immediate.push(() -> { 45 | callback(null); 46 | }); 47 | } 48 | return null; 49 | 50 | } 51 | 52 | inline public function emitAudioEvent(event:AudioEvent, handle:AudioHandle):Void { 53 | 54 | app.events.audioEvent(event, handle); 55 | 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/clay/PremultiplyAlpha.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | import clay.buffers.Uint8Array; 4 | 5 | class PremultiplyAlpha { 6 | 7 | public static function premultiplyAlpha(pixels:Uint8Array) { 8 | 9 | var count = pixels.length; 10 | var index = 0; 11 | 12 | while (index < count) { 13 | 14 | var r = pixels[index+0]; 15 | var g = pixels[index+1]; 16 | var b = pixels[index+2]; 17 | var a = pixels[index+3] / 255.0; 18 | 19 | pixels[index+0] = Std.int(r*a); 20 | pixels[index+1] = Std.int(g*a); 21 | pixels[index+2] = Std.int(b*a); 22 | 23 | index += 4; 24 | 25 | } 26 | 27 | } 28 | 29 | public static function reversePremultiplyAlpha(pixels:Uint8Array) { 30 | 31 | var count = pixels.length; 32 | var index = 0; 33 | 34 | while (index < count) { 35 | 36 | var r = pixels[index+0]; 37 | var g = pixels[index+1]; 38 | var b = pixels[index+2]; 39 | var a = pixels[index+3] / 255.0; 40 | 41 | if (a > 0) { 42 | pixels[index+0] = Std.int(r/a); 43 | pixels[index+1] = Std.int(g/a); 44 | pixels[index+2] = Std.int(b/a); 45 | } 46 | 47 | index += 4; 48 | 49 | } 50 | 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /src/clay/audio/AudioFormat.hx: -------------------------------------------------------------------------------- 1 | package clay.audio; 2 | 3 | /** The type of format data for audio */ 4 | enum abstract AudioFormat(Null) from Null to Null { 5 | 6 | var UNKNOWN = 0; 7 | var CUSTOM = 1; 8 | var OGG = 2; 9 | var WAV = 3; 10 | var PCM = 4; 11 | var MP3 = 5; 12 | var FLAC = 6; 13 | 14 | inline function toString() { 15 | return switch(this) { 16 | case UNKNOWN: 'UNKNOWN'; 17 | case CUSTOM: 'CUSTOM'; 18 | case OGG: 'OGG'; 19 | case WAV: 'WAV'; 20 | case PCM: 'PCM'; 21 | case MP3: 'MP3'; 22 | case FLAC: 'FLAC'; 23 | case _: '$this'; 24 | } 25 | } 26 | 27 | /** Uses the extension of the given path to return the `AudioFormat` */ 28 | public inline static function fromPath(path:String):AudioFormat { 29 | 30 | #if ceramic 31 | var ext = ceramic.Path.extension(path); 32 | #else 33 | var ext = haxe.io.Path.extension(path); 34 | #end 35 | return switch ext.toLowerCase() { 36 | case 'wav': WAV; 37 | case 'ogg': OGG; 38 | case 'pcm': PCM; 39 | case 'mp3': MP3; 40 | case 'flac': FLAC; 41 | case _: UNKNOWN; 42 | } 43 | 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /src/clay/Image.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | import clay.buffers.Uint8Array; 4 | 5 | @:structInit 6 | class Image { 7 | 8 | /** Image width from source image */ 9 | public var width:Int = 0; 10 | /** Image height from source image */ 11 | public var height:Int = 0; 12 | /** The actual width, used when image is automatically padded to POT */ 13 | public var widthActual:Int = 0; 14 | /** The actual height, used when image is automatically padded to POT */ 15 | public var heightActual:Int = 0; 16 | /** used bits per pixel */ 17 | public var bitsPerPixel:Int = 4; 18 | /** source bits per pixel */ 19 | public var sourceBitsPerPixel:Int = 4; 20 | /** image pixel data */ 21 | public var pixels:Uint8Array = null; 22 | 23 | public function premultiplyAlpha():Void { 24 | 25 | if (bitsPerPixel == 4) { 26 | PremultiplyAlpha.premultiplyAlpha(pixels); 27 | } 28 | else { 29 | Log.warning('Can only premultiply alpha on images with 4 bits per pixels (RGBA)'); 30 | } 31 | 32 | } 33 | 34 | public function reversePremultiplyAlpha():Void { 35 | 36 | if (bitsPerPixel == 4) { 37 | PremultiplyAlpha.reversePremultiplyAlpha(pixels); 38 | } 39 | else { 40 | Log.warning('Can only reverse premultiply alpha on images with 4 bits per pixels (RGBA)'); 41 | } 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/clay/base/BaseAssets.hx: -------------------------------------------------------------------------------- 1 | package clay.base; 2 | 3 | #if ceramic 4 | import ceramic.Path; 5 | #else 6 | import haxe.io.Path; 7 | #end 8 | 9 | class BaseAssets { 10 | 11 | /** 12 | * Clay app 13 | */ 14 | public var app(default, null):Clay; 15 | 16 | function new(app:Clay) { 17 | 18 | this.app = app; 19 | 20 | } 21 | 22 | public function isSynchronous():Bool { 23 | 24 | return false; 25 | 26 | } 27 | 28 | public function fullPath(path:String):String { 29 | 30 | if (Path.isAbsolute(path)) { 31 | return path; 32 | } 33 | else { 34 | #if (ios || tvos) 35 | // This is because of how the files are put into the xcode project 36 | // for the iOS builds, it stores them inside of /assets to avoid 37 | // including the root in the project in the Resources/ folder 38 | return Path.join([app.io.appPath(), 'assets', path]); 39 | #else 40 | return Path.join([app.io.appPath(), path]); 41 | #end 42 | } 43 | 44 | } 45 | 46 | public function loadImage(path:String, components:Int = 4, async:Bool = false, ?callback:(image:Image)->Void):Image { 47 | 48 | if (callback != null) { 49 | Immediate.push(() -> { 50 | callback(null); 51 | }); 52 | } 53 | return null; 54 | 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /src-opengl/clay/opengl/GL.hx: -------------------------------------------------------------------------------- 1 | package clay.opengl; 2 | 3 | #if clay_web 4 | 5 | typedef GL = clay.opengl.web.GL; 6 | typedef GLActiveInfo = clay.opengl.web.GL.GLActiveInfo; 7 | typedef GLBuffer = clay.opengl.web.GL.GLBuffer; 8 | typedef GLContextAttributes = clay.opengl.web.GL.GLContextAttributes; 9 | typedef GLFramebuffer = clay.opengl.web.GL.GLFramebuffer; 10 | typedef GLProgram = clay.opengl.web.GL.GLProgram; 11 | typedef GLRenderbuffer = clay.opengl.web.GL.GLRenderbuffer; 12 | typedef GLShader = clay.opengl.web.GL.GLShader; 13 | typedef GLTexture = clay.opengl.web.GL.GLTexture; 14 | typedef GLUniformLocation = clay.opengl.web.GL.GLUniformLocation; 15 | 16 | #elseif (clay_native && linc_opengl) 17 | 18 | typedef GL = opengl.WebGL; 19 | typedef GLActiveInfo = opengl.WebGL.GLActiveInfo; 20 | typedef GLBuffer = opengl.WebGL.GLBuffer; 21 | typedef GLContextAttributes = opengl.WebGL.GLContextAttributes; 22 | typedef GLFramebuffer = opengl.WebGL.GLFramebuffer; 23 | typedef GLProgram = opengl.WebGL.GLProgram; 24 | typedef GLRenderbuffer = opengl.WebGL.GLRenderbuffer; 25 | typedef GLShader = opengl.WebGL.GLShader; 26 | typedef GLTexture = opengl.WebGL.GLTexture; 27 | typedef GLUniformLocation = opengl.WebGL.GLUniformLocation; 28 | 29 | #end 30 | -------------------------------------------------------------------------------- /src/clay/soloud/SoloudSound.hx: -------------------------------------------------------------------------------- 1 | package clay.soloud; 2 | 3 | import clay.audio.AudioHandle; 4 | import clay.audio.AudioInstance; 5 | import clay.audio.AudioSource; 6 | import clay.audio.AudioState; 7 | 8 | @:allow(clay.soloud.SoloudAudio) 9 | class SoloudSound { 10 | 11 | var source:AudioSource = null; 12 | 13 | var soloudHandle:Int = -1; 14 | 15 | var audioInstance:AudioInstance = null; 16 | 17 | var handle:AudioHandle = -1; 18 | 19 | var state:AudioState = INVALID; 20 | 21 | var loop:Bool = false; 22 | 23 | var pan:Float = 0.0; 24 | 25 | var pitch:Float = 1.0; 26 | 27 | var volume:Float = 0.5; 28 | 29 | var timeResume:Float = -1; 30 | 31 | var timeResumeAppTime:Float = -1; 32 | 33 | var timePause:Float = -1; 34 | 35 | var busIndex:Int = -1; 36 | 37 | private function new() { 38 | // 39 | } 40 | 41 | /// Pool 42 | 43 | static var _pool:Array = []; 44 | 45 | public static function get():SoloudSound { 46 | 47 | if (_pool.length > 0) 48 | return _pool.pop(); 49 | else { 50 | var instance = new SoloudSound(); 51 | return instance; 52 | } 53 | 54 | } 55 | 56 | public function recycle() { 57 | 58 | source = null; 59 | soloudHandle = -1; 60 | audioInstance = null; 61 | handle = -1; 62 | state = INVALID; 63 | loop = false; 64 | pan = 0.0; 65 | pitch = 1.0; 66 | timeResume = -1; 67 | timeResumeAppTime = -1; 68 | timePause = -1; 69 | busIndex = -1; 70 | _pool.push(this); 71 | 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/clay/audio/AudioInstance.hx: -------------------------------------------------------------------------------- 1 | package clay.audio; 2 | 3 | import clay.buffers.Uint8Array; 4 | 5 | class AudioInstance { 6 | 7 | public var source:AudioSource; 8 | 9 | public var handle:AudioHandle; 10 | 11 | public var destroyed:Bool = false; 12 | 13 | /** Create a new instance from the given audio source. 14 | Usually called via `source.instance()`, not directly. */ 15 | public function new(source:AudioSource, handle:AudioHandle) { 16 | 17 | this.source = source; 18 | this.handle = handle; 19 | 20 | } 21 | 22 | public function hasEnded():Bool { 23 | 24 | if (destroyed) 25 | throw 'Audio / Instance hasEnded queried after being destroyed'; 26 | 27 | return source.app.audio.stateOf(handle) == STOPPED; 28 | 29 | } 30 | 31 | public function destroy() { 32 | 33 | if (destroyed) 34 | throw 'Audio / Instance being destroyed more than once'; 35 | 36 | source.app.audio.handleInstanceDestroyed(handle); 37 | source.instanceKilled(this); 38 | destroyed = true; 39 | source = null; 40 | handle = -1; 41 | 42 | } 43 | 44 | public function dataGet(into:Uint8Array, start:Int, length:Int, intoResult:Array):Array { 45 | 46 | if (destroyed) 47 | throw 'Audio / Instance dataGet queried after being destroyed'; 48 | 49 | return source.data.portion(into, start, length, intoResult); 50 | 51 | } 52 | 53 | public function dataSeek(toSamples:Int):Bool { 54 | 55 | if (destroyed) 56 | throw 'Audio / Instance dataSeek queried after being destroyed'; 57 | 58 | return source.data.seek(toSamples); 59 | 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/clay/ReusableArray.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | #if ceramic 4 | 5 | typedef ReusableArray = ceramic.ReusableArray 6 | 7 | #else 8 | 9 | import haxe.ds.Vector; 10 | 11 | /** A reusable array to use in places that need a temporary array many times. 12 | Changing array size only increases the backing array size but never decreases it. */ 13 | class ReusableArray { 14 | 15 | @:noCompletion 16 | var _poolIndex:Int = -1; 17 | 18 | var vector:Vector; 19 | 20 | public var length(default,set):Int; 21 | 22 | inline public function new(length:Int) { 23 | 24 | this.length = length; 25 | 26 | } 27 | 28 | function set_length(length:Int):Int { 29 | if (vector == null) { 30 | vector = new Vector(length); 31 | this.length = length; 32 | return length; 33 | } 34 | if (length == this.length) return length; 35 | 36 | if (length > vector.length) { 37 | var newVector = new Vector(length); 38 | for (i in 0...this.length) { 39 | newVector.set(i, vector.get(i)); 40 | vector.set(i, null); 41 | } 42 | vector = newVector; 43 | for (i in this.length...length) { 44 | vector.set(i, null); 45 | } 46 | } 47 | else { 48 | for (i in length...this.length) { 49 | vector.set(i, null); 50 | } 51 | } 52 | 53 | this.length = length; 54 | 55 | return length; 56 | } 57 | 58 | inline public function get(index:Int):T { 59 | 60 | return vector.get(index); 61 | 62 | } 63 | 64 | inline public function set(index:Int, value:T):Void { 65 | 66 | vector.set(index, value); 67 | 68 | } 69 | 70 | } 71 | 72 | #end 73 | -------------------------------------------------------------------------------- /src/clay/graphics/RenderTexture.hx: -------------------------------------------------------------------------------- 1 | package clay.graphics; 2 | 3 | import clay.Types; 4 | 5 | class RenderTexture extends Texture { 6 | 7 | /** 8 | * Reference to the actual render target (data may vary depending on the rendering backend) 9 | */ 10 | public var renderTarget:RenderTarget; 11 | 12 | /** 13 | * Set to `true` to also allocate a depth buffer on this render target 14 | */ 15 | public var depth:Bool = false; 16 | 17 | /** 18 | * Set to `true` to also allocate a stencil buffer on this render target 19 | */ 20 | public var stencil:Bool = false; 21 | 22 | /** 23 | * Antialiasing value. Set it to `2`, `4` or `8` to enable antialiasing/multisampling. 24 | * Requires OpenGL ES 3 / WebGL 2 or above to work 25 | * @warning NOT IMPLEMENTED, so this doesn't have any effect for now! 26 | */ 27 | public var antialiasing:Int = 0; 28 | 29 | public function new() { 30 | 31 | super(); 32 | 33 | } 34 | 35 | override function init() { 36 | 37 | super.init(); 38 | 39 | var max = Clay.app.graphics.maxTextureSize(); 40 | 41 | if (widthActual > max) 42 | throw 'RenderTexture actual width bigger than maximum hardware size (width=$widthActual max=$max)'; 43 | if (heightActual > max) 44 | throw 'RenderTexture actual height bigger than maximum hardware size (height=$heightActual max=$max)'; 45 | 46 | // Create render target 47 | bind(); 48 | renderTarget = Clay.app.graphics.createRenderTarget( 49 | textureId, width, height, depth, stencil, antialiasing, 50 | 0, format, dataType 51 | ); 52 | 53 | } 54 | 55 | override function destroy() { 56 | 57 | super.destroy(); 58 | 59 | // Delete render target 60 | if (renderTarget != null) { 61 | Clay.app.graphics.deleteRenderTarget(renderTarget); 62 | renderTarget = null; 63 | } 64 | 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/clay/Immediate.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | import clay.ArrayPool; 4 | 5 | using clay.Extensions; 6 | 7 | class Immediate { 8 | 9 | static var immediateCallbacks:ArrayVoid> = []; 10 | 11 | static var immediateCallbacksCapacity:Int = 0; 12 | 13 | static var immediateCallbacksLen:Int = 0; 14 | 15 | /** 16 | * Schedule a callback that will be run when flush() is called 17 | */ 18 | public static function push(handleImmediate:Void->Void):Void { 19 | 20 | if (handleImmediate == null) { 21 | throw 'Immediate callback should not be null!'; 22 | } 23 | 24 | if (immediateCallbacksLen < immediateCallbacksCapacity) { 25 | immediateCallbacks.unsafeSet(immediateCallbacksLen, handleImmediate); 26 | immediateCallbacksLen++; 27 | } 28 | else { 29 | immediateCallbacks[immediateCallbacksLen++] = handleImmediate; 30 | immediateCallbacksCapacity++; 31 | } 32 | 33 | } 34 | 35 | /** Execute and flush every awaiting callback, including the ones that 36 | could have been added with `push()` after executing the existing callbacks. */ 37 | public static function flush():Bool { 38 | 39 | var didFlush = false; 40 | 41 | // Immediate callbacks 42 | while (immediateCallbacksLen > 0) { 43 | 44 | didFlush = true; 45 | 46 | var pool = ArrayPool.pool(immediateCallbacksLen); 47 | var callbacks = pool.get(); 48 | var len = immediateCallbacksLen; 49 | immediateCallbacksLen = 0; 50 | 51 | for (i in 0...len) { 52 | callbacks.set(i, immediateCallbacks.unsafeGet(i)); 53 | immediateCallbacks.unsafeSet(i, null); 54 | } 55 | 56 | for (i in 0...len) { 57 | var cb:Dynamic = callbacks.get(i); 58 | cb(); 59 | } 60 | 61 | pool.release(callbacks); 62 | 63 | } 64 | 65 | return didFlush; 66 | 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/clay/audio/AudioErrorReason.hx: -------------------------------------------------------------------------------- 1 | package clay.audio; 2 | 3 | enum abstract AudioErrorReason(Int) from Int to Int { 4 | 5 | var STOP_ALSOURCE; 6 | 7 | var DETACH_BUFFER; 8 | 9 | var DELETE_ALSOURCE; 10 | 11 | var GEN_BUFFERS; 12 | 13 | var PRE_FILL_BUFFER; 14 | 15 | var POST_FILL_BUFFER; 16 | 17 | var QUEUE_BUFFER; 18 | 19 | var INIT_QUEUE; 20 | 21 | var NEW_BUFFER; 22 | 23 | var ATTACH_BUFFER; 24 | 25 | var PRE_FLUSH_QUEUE; 26 | 27 | var FLUSH_QUEUE; 28 | 29 | var POST_FLUSH_QUEUE; 30 | 31 | var DELETE_BUFFER; 32 | 33 | var QUERY_PROCESSED_BUFFERS; 34 | 35 | var END; 36 | 37 | var PLAY; 38 | 39 | var PAUSE; 40 | 41 | var UNPAUSE; 42 | 43 | var STOP; 44 | 45 | var LOOP; 46 | 47 | var PRE_SOURCE_STOP; 48 | 49 | var SOURCE_UNQUEUE_BUFFER; 50 | 51 | inline function toString():String { 52 | 53 | return switch this { 54 | case STOP_ALSOURCE: 'STOP_ALSOURCE'; 55 | case DETACH_BUFFER: 'DETACH_BUFFER'; 56 | case DELETE_ALSOURCE: 'DELETE_ALSOURCE'; 57 | case GEN_BUFFERS: 'GEN_BUFFERS'; 58 | case PRE_FILL_BUFFER: 'PRE_FILL_BUFFER'; 59 | case POST_FILL_BUFFER: 'POST_FILL_BUFFER'; 60 | case QUEUE_BUFFER: 'QUEUE_BUFFER'; 61 | case INIT_QUEUE: 'INIT_QUEUE'; 62 | case NEW_BUFFER: 'NEW_BUFFER'; 63 | case ATTACH_BUFFER: 'ATTACH_BUFFER'; 64 | case PRE_FLUSH_QUEUE: 'PRE_FLUSH_QUEUE'; 65 | case FLUSH_QUEUE: 'FLUSH_QUEUE'; 66 | case POST_FLUSH_QUEUE: 'POST_FLUSH_QUEUE'; 67 | case DELETE_BUFFER: 'DELETE_BUFFER'; 68 | case QUERY_PROCESSED_BUFFERS: 'QUERY_PROCESSED_BUFFERS'; 69 | case END: 'END'; 70 | case PLAY: 'PLAY'; 71 | case PAUSE: 'PAUSE'; 72 | case UNPAUSE: 'UNPAUSE'; 73 | case STOP: 'STOP'; 74 | case LOOP: 'LOOP'; 75 | case PRE_SOURCE_STOP: 'PRE_SOURCE_STOP'; 76 | case SOURCE_UNQUEUE_BUFFER: 'SOURCE_UNQUEUE_BUFFER'; 77 | case _: '$this'; 78 | } 79 | 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/clay/Events.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | import clay.Types; 4 | import clay.audio.AudioEvent; 5 | import clay.audio.AudioHandle; 6 | 7 | #if clay_sdl 8 | import clay.sdl.SDL; 9 | #end 10 | 11 | #if clay_sdl 12 | @:headerCode('#include ') 13 | #end 14 | class Events { 15 | 16 | public function ready():Void {} 17 | 18 | public function tick(delta:Float):Void {} 19 | 20 | public function render():Void {} 21 | 22 | public function freeze():Void {} 23 | 24 | public function unfreeze():Void {} 25 | 26 | #if clay_sdl 27 | 28 | public function sdlEvent(event:SDLEvent):Void {} 29 | 30 | #end 31 | 32 | public function keyDown(keycode:Int, scancode:Int, repeat:Bool, mod:ModState, timestamp:Float, windowId:Int) {} 33 | 34 | public function keyUp(keycode:Int, scancode:Int, repeat:Bool, mod:ModState, timestamp:Float, windowId:Int) {} 35 | 36 | public function text(text:String, start:Int, length:Int, type:TextEventType, timestamp:Float, windowId:Int) {} 37 | 38 | public function mouseMove(x:Int, y:Int, xrel:Int, yrel:Int, timestamp:Float, windowId:Int) {} 39 | 40 | public function mouseDown(x:Int, y:Int, button:Int, timestamp:Float, windowId:Int) {} 41 | 42 | public function mouseUp(x:Int, y:Int, button:Int, timestamp:Float, windowId:Int) {} 43 | 44 | public function mouseWheel(x:Float, y:Float, timestamp:Float, windowId:Int) {} 45 | 46 | public function touchDown(x:Float, y:Float, dx:Float, dy:Float, touchId:Int, timestamp:Float) {} 47 | 48 | public function touchUp(x:Float, y:Float, dx:Float, dy:Float, touchId:Int, timestamp:Float) {} 49 | 50 | public function touchMove(x:Float, y:Float, dx:Float, dy:Float, touchId:Int, timestamp:Float) {} 51 | 52 | public function gamepadAxis(gamepad:Int, axis:Int, value:Float, timestamp:Float) {} 53 | 54 | public function gamepadDown(gamepad:Int, button:Int, value:Float, timestamp:Float) {} 55 | 56 | public function gamepadUp(gamepad:Int, button:Int, value:Float, timestamp:Float) {} 57 | 58 | public function gamepadGyro(gamepad:Int, dx:Float, dy:Float, dz:Float, timestamp:Float) {} 59 | 60 | public function gamepadDevice(gamepad:Int, name:String, type:GamepadDeviceEventType, timestamp:Float) {} 61 | 62 | public function windowEvent(type:WindowEventType, timestamp:Float, windowId:Int, x:Int, y:Int):Void {} 63 | 64 | public function appEvent(type:AppEventType):Void {} 65 | 66 | public function audioEvent(event:AudioEvent, handle:AudioHandle):Void {} 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/clay/Utils.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | class Utils { 4 | 5 | static var _nextUniqueIntCursor:Int = 0; 6 | static var _nextUniqueInt0:Int = Std.int(Math.random() * 0x7ffffffe); 7 | static var _nextUniqueInt1:Int = Std.int(Date.now().getTime() * 0.0001); 8 | static var _nextUniqueInt2:Int = Std.int(Math.random() * 0x7ffffffe); 9 | static var _nextUniqueInt3:Int = Std.int(Math.random() * 0x7ffffffe); 10 | 11 | #if (cpp || cs || sys) 12 | static var _uniqueIdMutex:sys.thread.Mutex = new sys.thread.Mutex(); 13 | #end 14 | 15 | /** Provides an identifier which is garanteed to be unique on this local device. 16 | It however doesn't garantee that this identifier is not predictable. */ 17 | public static function uniqueId():String { 18 | 19 | #if (cpp || cs || sys) 20 | _uniqueIdMutex.acquire(); 21 | #end 22 | 23 | switch (_nextUniqueIntCursor) { 24 | case 0: 25 | _nextUniqueInt0 = (_nextUniqueInt0 + 1) % 0x7fffffff; 26 | case 1: 27 | _nextUniqueInt1 = (_nextUniqueInt1 + 1) % 0x7fffffff; 28 | case 2: 29 | _nextUniqueInt2 = (_nextUniqueInt2 + 1) % 0x7fffffff; 30 | case 3: 31 | _nextUniqueInt3 = (_nextUniqueInt3 + 1) % 0x7fffffff; 32 | } 33 | _nextUniqueIntCursor = (_nextUniqueIntCursor + 1) % 4; 34 | 35 | var result = base62Id(_nextUniqueInt0) + '-' + base62Id() + '-' + base62Id(_nextUniqueInt1) + '-' + base62Id() + '-' + base62Id(_nextUniqueInt2) + '-' + base62Id() + '-' + base62Id(_nextUniqueInt3); 36 | 37 | #if (cpp || cs || sys) 38 | _uniqueIdMutex.release(); 39 | #end 40 | 41 | return result; 42 | 43 | } 44 | 45 | inline public static function base62Id(?val:Null):String { 46 | 47 | // http://www.anotherchris.net/csharp/friendly-unique-id-generation-part-2/#base62 48 | 49 | if (val == null) { 50 | val = Std.int(Math.random() * 0x7ffffffe); 51 | } 52 | 53 | inline function toChar(value:Int):String { 54 | if (value > 9) { 55 | var ascii = (65 + (value - 10)); 56 | if (ascii > 90) { ascii += 6; } 57 | return String.fromCharCode(ascii); 58 | } else return Std.string(value).charAt(0); 59 | } 60 | 61 | var r = Std.int(val % 62); 62 | var q = Std.int(val / 62); 63 | if (q > 0) return base62Id(q) + toChar(r); 64 | else return Std.string(toChar(r)); 65 | 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/clay/native/NativeAudio.hx: -------------------------------------------------------------------------------- 1 | package clay.native; 2 | 3 | import clay.Types; 4 | import clay.audio.AudioData; 5 | import clay.audio.AudioFormat; 6 | import clay.buffers.Uint8Array; 7 | import clay.native.NativeAudioDataOGG; 8 | import clay.native.NativeAudioDataPCM; 9 | import clay.native.NativeAudioDataWAV; 10 | 11 | class NativeAudio extends clay.base.BaseAudio { 12 | 13 | override function isSynchronous():Bool { 14 | 15 | return true; 16 | 17 | } 18 | 19 | override function loadData(path:String, isStream:Bool, format:AudioFormat, async:Bool = false, ?callback:(data:AudioData)->Void):AudioData { 20 | 21 | if (path == null) 22 | throw 'path is null!'; 23 | 24 | if (format == null) 25 | format = AudioFormat.fromPath(path); 26 | 27 | if (async) { 28 | Clay.app.backgroundQueue.schedule(function() { 29 | var data = loadData(path, isStream, format, false); 30 | Runner.runInMain(function() { 31 | if (callback != null) { 32 | Immediate.push(() -> { 33 | callback(data); 34 | }); 35 | } 36 | }); 37 | }); 38 | return null; 39 | } 40 | 41 | var data:AudioData = switch format { 42 | case WAV: NativeAudioDataWAV.WAV.fromFile(app, path, isStream); 43 | case OGG: NativeAudioDataOGG.OGG.fromFile(app, path, isStream); 44 | case PCM: NativeAudioDataPCM.PCM.fromFile(app, path, isStream); 45 | case _: null; 46 | } 47 | 48 | if (callback != null) { 49 | Immediate.push(() -> { 50 | callback(data); 51 | }); 52 | } 53 | return data; 54 | 55 | } 56 | 57 | /** Returns an AudioData instance from the given bytes */ 58 | public static function dataFromBytes(app:Clay, id:String, bytes:Uint8Array, ?format:AudioFormat):AudioData { 59 | 60 | if (id == null) 61 | throw 'id is null!'; 62 | if (bytes == null) 63 | throw 'bytes is null!'; 64 | 65 | if (format == null) format = AudioFormat.fromPath(id); 66 | 67 | var info = switch format { 68 | case WAV: NativeAudioDataWAV.WAV.fromBytes(app, id, bytes); 69 | case OGG: NativeAudioDataOGG.OGG.fromBytes(app, id, bytes); 70 | case PCM: NativeAudioDataPCM.PCM.fromBytes(app, id, bytes); 71 | case _: null; 72 | } 73 | 74 | return info; 75 | 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /src/clay/sdl/Linc.hx: -------------------------------------------------------------------------------- 1 | package clay.sdl; 2 | 3 | import haxe.io.Path; 4 | import haxe.macro.Context; 5 | import haxe.macro.Expr; 6 | 7 | using haxe.macro.PositionTools; 8 | 9 | 10 | class Linc { 11 | 12 | /** Adds a private internal inline static variable called __touch, 13 | which sets the value to the current time so that builds are always 14 | updated by the code, and native changes are dragged in automatically (except for header only changes) */ 15 | macro public static function touch() : Array { 16 | 17 | var _fields = Context.getBuildFields(); 18 | 19 | _fields.push({ 20 | name: '__touch', pos: Context.currentPos(), 21 | doc: null, meta: [], access: [APrivate, AStatic, AInline], 22 | kind: FVar(macro : String, macro $v{ Std.string(Date.now().getTime()) }), 23 | }); 24 | 25 | return _fields; 26 | 27 | } 28 | 29 | /** Adds a @:buildXml meta node with a linc and an tag. 30 | The set is named LINC_${_lib}_PATH, and points to the root folder of the library. 31 | That path is calculated from the calling file using the optional _relative_root, default ../ 32 | This means that somelib/ is the root. 33 | somelib/somelib/Somelib.hx is the calling file. 34 | LINC_SOMELIB_PATH is set to somelib/ 35 | ${LINC_SOMELIB_PATH}linc/linc_${_lib}.xml is added directly. */ 36 | macro public static function xml(_lib:String, _relative_root:String='../'):Array { 37 | 38 | var _pos = Context.currentPos(); 39 | var _pos_info = _pos.getInfos(); 40 | var _class = Context.getLocalClass(); 41 | 42 | var _source_path = Path.directory(_pos_info.file); 43 | if( !Path.isAbsolute(_source_path) ) { 44 | _source_path = Path.join([Sys.getCwd(), _source_path]); 45 | } 46 | 47 | _source_path = Path.normalize(_source_path); 48 | 49 | var _linc_lib_path = Path.normalize(Path.join([_source_path, _relative_root])); 50 | var _linc_include_path = Path.normalize(Path.join([ _linc_lib_path, './linc/linc_${_lib}.xml' ])); 51 | var _linc_lib_var = 'LINC_${_lib.toUpperCase()}_PATH'; 52 | 53 | var _define = ''; 54 | var _import_path = '$${$_linc_lib_var}linc/linc_${_lib}.xml'; 55 | var _import = ''; 56 | 57 | _class.get().meta.add(":buildXml", [{ expr:EConst( CString( '$_define\n$_import' ) ), pos:_pos }], _pos ); 58 | 59 | return Context.getBuildFields(); 60 | 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/clay/BackgroundQueue.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | #if (cpp || cs) 4 | import sys.thread.Mutex; 5 | #end 6 | 7 | /** 8 | * An utility to enqueue functions and execute them in background, in a serialized way, 9 | * meaning it is garanteed that no function in this queue will be run in parallel. An enqueued 10 | * function will always be started after every previous function has finished executing. 11 | */ 12 | #if (android && clay_sdl) 13 | @:headerCode('#include "linc_sdl.h"') 14 | #end 15 | class BackgroundQueue { 16 | 17 | /** 18 | * Time interval between each checks to see if there is something to run. 19 | */ 20 | public var checkInterval:Float; 21 | 22 | var runsInBackground:Bool = false; 23 | 24 | var stop:Bool = false; 25 | 26 | var pending:ArrayVoid> = []; 27 | 28 | #if (cpp || cs) 29 | var mutex:Mutex; 30 | #end 31 | 32 | public function new(checkInterval:Float = 0.05) { 33 | 34 | this.checkInterval = checkInterval; 35 | 36 | #if (cpp || cs) 37 | mutex = new Mutex(); 38 | runsInBackground = true; 39 | Runner.runInBackground(internalRunInBackground); 40 | #end 41 | 42 | } 43 | 44 | public function schedule(fn:Void->Void):Void { 45 | 46 | #if (cpp || cs) 47 | 48 | // Run in background with ceramic.Runner 49 | mutex.acquire(); 50 | pending.push(fn); 51 | mutex.release(); 52 | 53 | #elseif ceramic 54 | 55 | // Defer in main thread if background threading is not available 56 | ceramic.App.app.onceImmediate(fn); 57 | 58 | #else 59 | 60 | fn(); 61 | 62 | #end 63 | 64 | } 65 | 66 | #if (cpp || cs) 67 | 68 | private function internalRunInBackground():Void { 69 | 70 | #if (android && clay_sdl) 71 | // This lets us attach thread to JNI. 72 | // Required because some JNI calls could be done in background 73 | untyped __cpp__('SDL_GetAndroidJNIEnv()'); 74 | #end 75 | 76 | while (!stop) { 77 | var shouldSleep = true; 78 | 79 | mutex.acquire(); 80 | if (pending.length > 0) { 81 | var fn = pending.pop(); 82 | mutex.release(); 83 | 84 | shouldSleep = false; 85 | fn(); 86 | } 87 | else { 88 | mutex.release(); 89 | } 90 | 91 | if (shouldSleep) { 92 | Sys.sleep(checkInterval); 93 | } 94 | } 95 | 96 | } 97 | 98 | #end 99 | 100 | public function destroy():Void { 101 | 102 | #if (cpp || cs) 103 | 104 | mutex.acquire(); 105 | stop = true; 106 | mutex.release(); 107 | 108 | #else 109 | 110 | stop = true; 111 | 112 | #end 113 | 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/clay/native/NativeAssets.hx: -------------------------------------------------------------------------------- 1 | package clay.native; 2 | 3 | import clay.base.BaseAssets; 4 | import clay.buffers.Uint8Array; 5 | 6 | class NativeAssets extends BaseAssets { 7 | 8 | override function isSynchronous():Bool { 9 | 10 | return true; 11 | 12 | } 13 | 14 | override function loadImage(path:String, components:Int = 4, async:Bool = false, ?callback:(image:Image)->Void):Image { 15 | 16 | if (path == null) 17 | throw 'Image path is null!'; 18 | 19 | 20 | if (async) { 21 | Clay.app.backgroundQueue.schedule(function() { 22 | var image = loadImage(path, components, false); 23 | Runner.runInMain(function() { 24 | if (callback != null) { 25 | Immediate.push(() -> { 26 | callback(image); 27 | }); 28 | } 29 | }); 30 | }); 31 | return null; 32 | } 33 | else { 34 | // Get binary data 35 | var bytes:Uint8Array = null; 36 | bytes = app.io.loadData(path, true); 37 | if (bytes == null) { 38 | if (callback != null) { 39 | Immediate.push(() -> { 40 | callback(null); 41 | }); 42 | } 43 | return null; 44 | } 45 | 46 | // Decode binary data into image 47 | var image = imageFromBytes(bytes, components); 48 | if (callback != null) { 49 | Immediate.push(() -> { 50 | callback(image); 51 | }); 52 | } 53 | return image; 54 | } 55 | 56 | } 57 | 58 | public function imageFromBytes(bytes:Uint8Array, components:Int = 4, async:Bool = false, ?callback:(image:Image)->Void):Image { 59 | 60 | if (bytes == null) 61 | throw 'Image bytes are null!'; 62 | 63 | var info = stb.Image.load_from_memory(bytes.buffer, bytes.length, components); 64 | 65 | if (info == null) { 66 | if (callback != null) { 67 | Immediate.push(() -> { 68 | callback(null); 69 | }); 70 | } 71 | return null; 72 | } 73 | 74 | // var _pixel_bytes : haxe.io.Bytes = haxe.io.Bytes.ofData(_info.bytes); 75 | 76 | var image:Image = { 77 | width: info.w, 78 | height: info.h, 79 | widthActual: info.w, 80 | heightActual: info.h, 81 | sourceBitsPerPixel: info.comp, 82 | bitsPerPixel: info.req_comp, 83 | pixels: Uint8Array.fromBuffer(info.bytes, 0, info.bytes.length) 84 | }; 85 | if (callback != null) { 86 | Immediate.push(() -> { 87 | callback(image); 88 | }); 89 | } 90 | return image; 91 | 92 | } 93 | 94 | } -------------------------------------------------------------------------------- /src/clay/graphics/Shader.hx: -------------------------------------------------------------------------------- 1 | package clay.graphics; 2 | 3 | import clay.Types; 4 | 5 | class Shader extends Resource { 6 | 7 | #if clay_shader_from_source 8 | /** 9 | * Source code of vertex shader (glsl language on GL targets). 10 | * Only available when clay_shader_from_source is defined. 11 | */ 12 | public var vertSource:String = null; 13 | 14 | /** 15 | * Source code of fragment shader (glsl language on GL targets). 16 | * Only available when clay_shader_from_source is defined. 17 | */ 18 | public var fragSource:String = null; 19 | #else 20 | /** 21 | * Identifier for the precompiled shader (e.g., asset path or name). 22 | * Only available when clay_shader_from_source is NOT defined. 23 | */ 24 | public var shaderId:String = null; 25 | #end 26 | 27 | /** 28 | * A list of ordered attribute names that will 29 | * be assigned indexes in the order of the array. 30 | * (GL.bindAttribLocation() or similar) 31 | */ 32 | public var attributes:Array = null; 33 | 34 | /** 35 | * A list of ordered texture uniform names that will 36 | * be assigned indexes in the order of the array. 37 | * (GL.uniform1i() or similar) 38 | */ 39 | public var textures:Array = null; 40 | 41 | /** 42 | * Shader uniforms / named parameters. 43 | */ 44 | public var uniforms:Uniforms = null; 45 | 46 | /** 47 | * Reference to the actual GPU shader 48 | */ 49 | public var gpuShader:GpuShader = null; 50 | 51 | public function new() { 52 | 53 | } 54 | 55 | #if clay_shader_from_source 56 | /** 57 | * Call `init()` after you have set `vertSource` and `fragSource` properties. 58 | * (and optionally: `attributes` and `textures` properties) 59 | */ 60 | public function init():Void { 61 | 62 | gpuShader = Clay.app.graphics.createShader(vertSource, fragSource, attributes, textures); 63 | 64 | if (gpuShader == null) { 65 | throw 'Failed to create shader (id=$id)'; 66 | } 67 | 68 | uniforms = new Uniforms(gpuShader); 69 | 70 | } 71 | #else 72 | /** 73 | * Call `init()` after you have set `shaderId` property. 74 | * (and optionally: `attributes` and `textures` properties) 75 | */ 76 | public function init():Void { 77 | 78 | gpuShader = Clay.app.graphics.loadShader(shaderId, attributes, textures); 79 | 80 | if (gpuShader == null) { 81 | throw 'Failed to load shader (id=$id)'; 82 | } 83 | 84 | uniforms = new Uniforms(gpuShader); 85 | 86 | } 87 | #end 88 | 89 | public function activate():Void { 90 | 91 | Clay.app.graphics.useShader(gpuShader); 92 | 93 | Clay.app.graphics.synchronizeShaderMatrices(gpuShader); 94 | 95 | if (uniforms != null) { 96 | uniforms.apply(); 97 | } 98 | 99 | } 100 | 101 | public function destroy():Void { 102 | 103 | Clay.app.graphics.deleteShader(gpuShader); 104 | 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/clay/Runner.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | // Original source: https://gist.github.com/underscorediscovery/e66e72ec702bdcedf5af45f8f4712109 4 | 5 | #if (cpp || cs) 6 | import sys.thread.Deque; 7 | import sys.thread.Thread; 8 | #end 9 | 10 | /** 11 | A simple Haxe class for easily running threads and calling functions on the primary thread. 12 | from https://github.com/underscorediscovery/ 13 | 14 | Usage: 15 | - call Runner.init() from your primary thread 16 | - call Runner.tick() periodically to service callbacks (i.e inside your main loop) 17 | - use Runner.thread(function() { ... }) to make a thread 18 | - use Runner.runInMainThread(function() { ... }) to run code on the main thread 19 | - use runInMainThreadBlocking to run code on the main thread and wait for the return value 20 | 21 | */ 22 | class Runner { 23 | 24 | #if (cpp || cs) 25 | 26 | static var mainThread:Thread; 27 | 28 | static var queue:DequeVoid>; 29 | 30 | #end 31 | 32 | /** 33 | * Returns `true` if current running thread is main thread 34 | * @return Bool 35 | */ 36 | public inline static function currentIsMainThread():Bool { 37 | 38 | #if (cpp || cs) 39 | return mainThread == null || mainThread == Thread.current(); 40 | #else 41 | return true; 42 | #end 43 | 44 | } 45 | 46 | /** 47 | * Call this on your thread to make primary, 48 | * the calling thread will be used for callbacks. 49 | */ 50 | @:noCompletion public static function init() { 51 | #if (cpp || cs) 52 | queue = new DequeVoid>(); 53 | mainThread = Thread.current(); 54 | #end 55 | } 56 | 57 | /** 58 | * Call this on the primary manually, 59 | * Returns the number of callbacks called. 60 | */ 61 | @:noCompletion public static function tick():Void { 62 | 63 | #if (cpp || cs) 64 | var more = true; 65 | var count = 0; 66 | 67 | while (more) { 68 | var item = queue.pop(false); 69 | if (item != null) { 70 | count++; item(); item = null; 71 | } else { 72 | more = false; break; 73 | } 74 | } 75 | #end 76 | 77 | } 78 | 79 | /** 80 | * Returns `true` if _running in background_ is emulated on this platform by 81 | * running _background_ code in main thread instead of using background thread. 82 | */ 83 | inline public static function isEmulatingBackgroundWithMain():Bool { 84 | 85 | #if (cpp || cs) 86 | return false; 87 | #else 88 | return true; 89 | #end 90 | 91 | } 92 | 93 | /** 94 | * Call a function on the primary thread without waiting or blocking. 95 | * If you want return values see runInMainBlocking 96 | */ 97 | public static function runInMain(_fn:Void->Void) { 98 | 99 | #if (cpp || cs) 100 | queue.push(_fn); 101 | #else 102 | Immediate.push(_fn); 103 | #end 104 | 105 | } 106 | 107 | /** 108 | * Create a background thread using the given function, or just run (deferred) the function if threads are not supported 109 | */ 110 | public static function runInBackground(fn:Void->Void):Void { 111 | 112 | #if (cpp || cs) 113 | Thread.create(fn); 114 | #else 115 | Immediate.push(fn); 116 | #end 117 | 118 | } 119 | 120 | } -------------------------------------------------------------------------------- /src/clay/ArrayPool.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | #if ceramic 4 | 5 | typedef ArrayPool = ceramic.ArrayPool 6 | 7 | #else 8 | 9 | class ArrayPool { 10 | 11 | static var ALLOC_STEP = 10; 12 | 13 | /// Factory 14 | 15 | static var dynPool10:ArrayPool = new ArrayPool(10); 16 | 17 | static var dynPool100:ArrayPool = new ArrayPool(100); 18 | 19 | static var dynPool1000:ArrayPool = new ArrayPool(1000); 20 | 21 | static var dynPool10000:ArrayPool = new ArrayPool(10000); 22 | 23 | static var dynPool100000:ArrayPool = new ArrayPool(100000); 24 | 25 | static var didNotifyLargePool:Bool = false; 26 | 27 | public static function pool(size:Int):ArrayPool { 28 | 29 | if (size <= 10) { 30 | return cast dynPool10; 31 | } 32 | else if (size <= 100) { 33 | return cast dynPool100; 34 | } 35 | else if (size <= 1000) { 36 | return cast dynPool1000; 37 | } 38 | else if (size <= 10000) { 39 | return cast dynPool10000; 40 | } 41 | else if (size <= 100000) { 42 | return cast dynPool100000; 43 | } 44 | else { 45 | if (!didNotifyLargePool) { 46 | didNotifyLargePool = true; 47 | Log.warning('You should avoid asking a pool for arrays with more than 100000 elements (asked: $size) because it needs allocating a temporary one-time pool each time for that.'); 48 | } 49 | return new ArrayPool(size); 50 | } 51 | 52 | } 53 | 54 | /// Properties 55 | 56 | var arrays:ReusableArray = null; 57 | 58 | var nextFree:Int = 0; 59 | 60 | var arrayLengths:Int; 61 | 62 | /// Lifecycle 63 | 64 | public function new(arrayLengths:Int) { 65 | 66 | this.arrayLengths = arrayLengths; 67 | 68 | } 69 | 70 | /// Public API 71 | 72 | public function get(#if ceramic_debug_array_pool ?pos:haxe.PosInfos #end):ReusableArray { 73 | 74 | #if ceramic_debug_array_pool 75 | haxe.Log.trace('pool.get', pos); 76 | #end 77 | 78 | if (arrays == null) arrays = new ReusableArray(ALLOC_STEP); 79 | else if (nextFree >= arrays.length) arrays.length += ALLOC_STEP; 80 | 81 | var result:ReusableArray = arrays.get(nextFree); 82 | if (result == null) { 83 | result = new ReusableArray(arrayLengths); 84 | arrays.set(nextFree, result); 85 | } 86 | @:privateAccess result._poolIndex = nextFree; 87 | 88 | // Compute next free item 89 | while (true) { 90 | nextFree++; 91 | if (nextFree == arrays.length) break; 92 | var item:ReusableArray = arrays.get(nextFree); 93 | if (item == null) break; 94 | if (@:privateAccess item._poolIndex == -1) break; 95 | } 96 | 97 | return cast result; 98 | 99 | } 100 | 101 | public function release(array:ReusableArray):Void { 102 | 103 | #if ceramic_debug_array_pool 104 | haxe.Log.trace('pool.release', pos); 105 | #end 106 | 107 | var poolIndex = @:privateAccess array._poolIndex; 108 | @:privateAccess array._poolIndex = -1; 109 | if (nextFree > poolIndex) nextFree = poolIndex; 110 | for (i in 0...array.length) { 111 | array.set(i, null); 112 | } 113 | 114 | } 115 | 116 | } 117 | 118 | #end 119 | -------------------------------------------------------------------------------- /src/clay/audio/AudioSource.hx: -------------------------------------------------------------------------------- 1 | package clay.audio; 2 | 3 | class AudioSource { 4 | 5 | public var app:Clay; 6 | 7 | public var data:AudioData; 8 | 9 | /** Streams only: The size in bytes of a single stream buffer. 10 | This is ~1 sec in 16 bit mono. default:176400 11 | for most cases this can be left alone. */ 12 | public var streamBufferLength:Int = 176400; 13 | 14 | /** Streams only: The number of buffers to queue up. default:2 15 | For most cases this can be left alone. */ 16 | public var streamBufferCount:Int = 2; 17 | 18 | /** Whether this source has been destroyed */ 19 | public var destroyed:Bool = false; 20 | 21 | /** A unique key for this source */ 22 | public var sourceId:String; 23 | 24 | /** Local list of instances spawned from this source. 25 | used when destroying the source, to take instances with it. */ 26 | var instances:Array; 27 | 28 | public function new(app:Clay, data:AudioData) { 29 | 30 | this.app = app; 31 | this.data = data; 32 | this.sourceId = Utils.uniqueId(); 33 | 34 | Log.debug('AudioSource / `${this.sourceId}` / ${this.data.id}'); 35 | 36 | instances = []; 37 | 38 | if (data.duration <= 0) { 39 | data.duration = bytesToSeconds(data.length); 40 | } 41 | 42 | } 43 | 44 | /** Called by the audio system to obtain a new instance of this source. */ 45 | public function instance(handle:AudioHandle):AudioInstance { 46 | 47 | var instance = new AudioInstance(this, handle); 48 | 49 | if (instances.indexOf(instance) == -1) { 50 | instances.push(instance); 51 | } 52 | 53 | return instance; 54 | 55 | } 56 | 57 | /** A helper for converting bytes to seconds for a sound source */ 58 | public function bytesToSeconds(bytes:Int):Float { 59 | var word = data.bitsPerSample == 16 ? 2 : 1; 60 | var sampleFrames = (data.rate * data.channels * word); 61 | 62 | return bytes / sampleFrames; 63 | 64 | } 65 | 66 | /** A helper for converting seconds to bytes for this audio source */ 67 | public function secondsToBytes(seconds:Float):Int { 68 | 69 | var word = (data.bitsPerSample == 16) ? 2 : 1; 70 | var sampleFrames = (data.rate * data.channels * word); 71 | 72 | return Std.int(seconds * sampleFrames); 73 | 74 | } 75 | 76 | public function getDuration():Float { 77 | 78 | return data.duration; 79 | 80 | } 81 | 82 | public function destroy() { 83 | 84 | if (destroyed) { 85 | Log.debug('AudioSource / destroying already destroyed source!'); 86 | return; 87 | } 88 | 89 | destroyed = true; 90 | 91 | var c = instances.length; 92 | var i = 0; 93 | 94 | Log.debug('AudioSource / destroy / $sourceId / ${data.id}, stream=${data.isStream}, instances=$c'); 95 | 96 | while (i < c) { 97 | var instance = instances.pop(); 98 | instance.destroy(); 99 | instance = null; 100 | i++; 101 | } 102 | 103 | app.audio.handleSourceDestroyed(this); 104 | 105 | data.destroy(); 106 | data = null; 107 | instances = null; 108 | app = null; 109 | 110 | } 111 | 112 | @:allow(clay.audio.AudioInstance) 113 | function instanceKilled(instance:AudioInstance) { 114 | 115 | instances.remove(instance); 116 | 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /src/clay/audio/AudioData.hx: -------------------------------------------------------------------------------- 1 | package clay.audio; 2 | 3 | import clay.buffers.Uint8Array; 4 | 5 | @:structInit 6 | class AudioDataOptions { 7 | 8 | public var id:String = null; 9 | public var rate:Null = null; 10 | public var length:Null = null; 11 | public var duration:Null = null; 12 | public var channels:Null = null; 13 | public var bitsPerSample:Null = null; 14 | public var format:AudioFormat = null; 15 | public var isStream:Null = null; 16 | 17 | } 18 | 19 | /** An audio data object contains information about audio samples or streams, ready to be used. 20 | `AudioData` objects typically come from the `app.assets.audio` API or `app.audio.module.data_from_path`, 21 | since the implemenation details of decoding audio and streams are module level implementation details. 22 | This is stored by `AudioSource` and `AssetAudio` objects for example. */ 23 | @:allow(clay.audio.AudioInstance) 24 | @:structInit 25 | class AudioData { 26 | 27 | /** Access to the snow runtime */ 28 | public var app:Clay; 29 | 30 | /** The associated id for the data */ 31 | public var id:String = 'AudioData'; 32 | 33 | /** The sample rate in samples per second */ 34 | public var rate:Int = 44100; 35 | 36 | /** The PCM length in samples */ 37 | public var length:Int = 0; 38 | 39 | /** The duration in seconds of the PCM buffer */ 40 | public var duration:Float = 0; 41 | 42 | /** The number of channels for this data */ 43 | public var channels:Int = 1; 44 | 45 | /** The number of bits per sample for this data */ 46 | public var bitsPerSample:Int = 16; 47 | 48 | /** The audio format type of the sample data */ 49 | public var format:AudioFormat = UNKNOWN; 50 | 51 | /** Whether or not this data is a stream of samples */ 52 | public var isStream:Bool = false; 53 | 54 | /** Whether or not this data has been destroyed */ 55 | public var destroyed:Bool = false; 56 | 57 | inline public function new(app:Clay, options:AudioDataOptions) { 58 | 59 | this.app = app; 60 | 61 | if (options.id != null) 62 | this.id = options.id; 63 | 64 | if (options.rate != null) 65 | this.rate = options.rate; 66 | 67 | if (options.length != null) 68 | this.length = options.length; 69 | 70 | if (options.format != null) 71 | this.format = options.format; 72 | 73 | if (options.channels != null) 74 | this.channels = options.channels; 75 | 76 | if (options.bitsPerSample != null) 77 | this.bitsPerSample = options.bitsPerSample; 78 | 79 | if (options.isStream != null) 80 | this.isStream = options.isStream; 81 | 82 | if (options.duration != null) 83 | this.duration = options.duration; 84 | 85 | options = null; 86 | 87 | } 88 | 89 | /// Public API, typically populated by subclasses 90 | 91 | public function destroy() { 92 | 93 | if (destroyed) return; 94 | 95 | Log.debug('Audio / destroy AudioData `$id`'); 96 | destroyed = true; 97 | 98 | id = null; 99 | 100 | } 101 | 102 | /// Internal implementation details, populated by subclasses 103 | 104 | function seek(to:Int):Bool return false; 105 | 106 | function portion(into:Uint8Array, start:Int, len:Int, intoResult:Array):Array return intoResult; 107 | 108 | inline function toString() return '{ "AudioData":true, "id":$id, "rate":$rate, "length":$length, "channels":$channels, "format":"$format", "isStream":$isStream }'; 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/clay/Input.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | import clay.Types; 4 | 5 | class Input { 6 | 7 | /** 8 | * Clay app 9 | */ 10 | public var app(default, null):Clay; 11 | 12 | var modState:ModState; 13 | 14 | function new(app:Clay) { 15 | 16 | this.app = app; 17 | 18 | modState = new ModState(); 19 | modState.none = true; 20 | 21 | } 22 | 23 | public function init():Void {} 24 | 25 | public function shutdown():Void {} 26 | 27 | inline public function emitKeyDown(keyCode:KeyCode, scanCode:ScanCode, repeat:Bool, mod:ModState, timestamp:Float, windowId:Int) { 28 | 29 | app.events.keyDown(keyCode, scanCode, repeat, mod, timestamp, windowId); 30 | 31 | } 32 | 33 | inline public function emitKeyUp(keyCode:KeyCode, scanCode:ScanCode, repeat:Bool, mod:ModState, timestamp:Float, windowId:Int) { 34 | 35 | app.events.keyUp(keyCode, scanCode, repeat, mod, timestamp, windowId); 36 | 37 | } 38 | 39 | inline public function emitText(text:String, start:Int, length:Int, type:TextEventType, timestamp:Float, windowId:Int) { 40 | 41 | app.events.text(text, start, length, type, timestamp, windowId); 42 | 43 | } 44 | 45 | inline public function emitMouseMove(x:Int, y:Int, xrel:Int, yrel:Int, timestamp:Float, windowId:Int) { 46 | 47 | app.events.mouseMove(x, y, xrel, yrel, timestamp, windowId); 48 | 49 | } 50 | 51 | inline public function emitMouseDown(x:Int, y:Int, button:Int, timestamp:Float, windowId:Int) { 52 | 53 | app.events.mouseDown(x, y, button, timestamp, windowId); 54 | 55 | } 56 | 57 | inline public function emitMouseUp(x:Int, y:Int, button:Int, timestamp:Float, windowId:Int) { 58 | 59 | app.events.mouseUp(x, y, button, timestamp, windowId); 60 | 61 | } 62 | 63 | inline public function emitMouseWheel(x:Float, y:Float, timestamp:Float, windowId:Int) { 64 | 65 | app.events.mouseWheel(x, y, timestamp, windowId); 66 | 67 | } 68 | 69 | inline public function emitTouchDown(x:Float, y:Float, dx:Float, dy:Float, touchId:Int, timestamp:Float) { 70 | 71 | app.events.touchDown(x, y, dx, dy, touchId, timestamp); 72 | 73 | } 74 | 75 | inline public function emitTouchUp(x:Float, y:Float, dx:Float, dy:Float, touchId:Int, timestamp:Float) { 76 | 77 | app.events.touchUp(x, y, dx, dy, touchId, timestamp); 78 | 79 | } 80 | 81 | inline public function emitTouchMove(x:Float, y:Float, dx:Float, dy:Float, touchId:Int, timestamp:Float) { 82 | 83 | app.events.touchMove(x, y, dx, dy, touchId, timestamp); 84 | 85 | } 86 | 87 | inline public function emitGamepadAxis(gamepad:Int, axis:Int, value:Float, timestamp:Float) { 88 | 89 | app.events.gamepadAxis(gamepad, axis, value, timestamp); 90 | 91 | } 92 | 93 | inline public function emitGamepadDown(gamepad:Int, button:Int, value:Float, timestamp:Float) { 94 | 95 | app.events.gamepadDown(gamepad, button, value, timestamp); 96 | 97 | } 98 | 99 | inline public function emitGamepadUp(gamepad:Int, button:Int, value:Float, timestamp:Float) { 100 | 101 | app.events.gamepadUp(gamepad, button, value, timestamp); 102 | 103 | } 104 | 105 | inline public function emitGamepadGyro(gamepad:Int, dx:Float, dy:Float, dz:Float, timestamp:Float) { 106 | 107 | app.events.gamepadGyro(gamepad, dx, dy, dz, timestamp); 108 | 109 | } 110 | 111 | inline public function emitGamepadDevice(gamepad:Int, name:String, type:GamepadDeviceEventType, timestamp:Float) { 112 | 113 | app.events.gamepadDevice(gamepad, name, type, timestamp); 114 | 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /sample/src/sample/SampleVertex.hx: -------------------------------------------------------------------------------- 1 | package sample; 2 | 3 | import clay.opengl.GL; 4 | import clay.buffers.Float32Array; 5 | import clay.graphics.Shader; 6 | import clay.graphics.Graphics; 7 | import clay.Clay; 8 | 9 | using StringTools; 10 | 11 | class SampleVertexEvents extends clay.Events { 12 | 13 | public function new() {} 14 | 15 | override function tick(delta:Float) { 16 | 17 | SampleVertex.draw(); 18 | 19 | } 20 | 21 | override function ready():Void { 22 | 23 | SampleVertex.ready(); 24 | 25 | } 26 | 27 | } 28 | 29 | @:allow(sample.SampleVertexEvents) 30 | class SampleVertex { 31 | 32 | static var events:SampleVertexEvents; 33 | 34 | static var vertShaderData:String =' 35 | attribute vec3 vertexPosition; 36 | attribute vec4 vertexColor; 37 | 38 | varying vec4 color; 39 | 40 | uniform mat4 projectionMatrix; 41 | uniform mat4 modelViewMatrix; 42 | 43 | void main(void) { 44 | 45 | gl_Position = vec4(vertexPosition, 1.0); 46 | color = vertexColor; 47 | gl_PointSize = 1.0; 48 | 49 | } 50 | '.trim(); 51 | 52 | static var fragShaderData:String = ' 53 | #ifdef GL_ES 54 | precision mediump float; 55 | #else 56 | #define mediump 57 | #endif 58 | 59 | varying vec4 color; 60 | 61 | void main() { 62 | gl_FragColor = color; 63 | } 64 | '.trim(); 65 | 66 | static var shader:Shader; 67 | 68 | public static function main():Void { 69 | 70 | events = @:privateAccess new SampleVertexEvents(); 71 | @:privateAccess new Clay(configure, events); 72 | 73 | } 74 | 75 | static function configure(config:clay.Config) { 76 | 77 | config.window.resizable = true; 78 | 79 | config.render.stencil = 2; 80 | config.render.depth = 16; 81 | 82 | } 83 | 84 | static function ready():Void { 85 | 86 | trace('Create shader'); 87 | shader = new Shader(); 88 | shader.vertSource = vertShaderData; 89 | shader.fragSource = fragShaderData; 90 | shader.attributes = ['vertexPosition', 'vertexColor']; 91 | shader.init(); 92 | trace('Did init shader'); 93 | shader.activate(); 94 | 95 | } 96 | 97 | static function draw():Void { 98 | 99 | Graphics.clear(0.25, 0.25, 0.25, 1); 100 | Graphics.setViewport( 101 | 0, 0, 102 | Std.int(Clay.app.screenWidth * Clay.app.screenDensity), 103 | Std.int(Clay.app.screenHeight * Clay.app.screenDensity) 104 | ); 105 | 106 | var vertices = Float32Array.fromArray([ 107 | -0.8, -0.8, 0.0, 1.0, 108 | 0.0, 0.8, 0.0, 1.0, 109 | 0.8, -0.8, 0.0, 1.0 110 | ]); 111 | 112 | var colors = Float32Array.fromArray([ 113 | 1.0, 0.0, 0.0, 1.0, 114 | 0.0, 1.0, 0.0, 1.0, 115 | 0.0, 0.0, 1.0, 1.0 116 | ]); 117 | 118 | GL.enableVertexAttribArray(0); 119 | GL.enableVertexAttribArray(1); 120 | 121 | var verticesBuffer = GL.createBuffer(); 122 | GL.bindBuffer(GL.ARRAY_BUFFER, verticesBuffer); 123 | GL.bufferData(GL.ARRAY_BUFFER, vertices, GL.STREAM_DRAW); 124 | GL.vertexAttribPointer(0, 4, GL.FLOAT, false, 0, 0); 125 | 126 | var colorsBuffer = GL.createBuffer(); 127 | GL.bindBuffer(GL.ARRAY_BUFFER, colorsBuffer); 128 | GL.bufferData(GL.ARRAY_BUFFER, colors, GL.STREAM_DRAW); 129 | GL.vertexAttribPointer(1, 4, GL.FLOAT, false, 0, 0); 130 | 131 | GL.drawArrays(GL.TRIANGLES, 0, 3); 132 | 133 | GL.deleteBuffer(verticesBuffer); 134 | GL.deleteBuffer(colorsBuffer); 135 | 136 | GL.disableVertexAttribArray(0); 137 | GL.disableVertexAttribArray(1); 138 | 139 | Graphics.ensureNoError(); 140 | 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/clay/native/NativeAudioDataPCM.hx: -------------------------------------------------------------------------------- 1 | package clay.native; 2 | 3 | import clay.Types; 4 | import clay.audio.AudioData; 5 | import clay.buffers.Uint8Array; 6 | 7 | class NativeAudioDataPCM extends AudioData { 8 | 9 | public var handle:FileHandle; 10 | 11 | inline public function new(app:Clay, handle:FileHandle, options:AudioDataOptions) { 12 | 13 | this.handle = handle; 14 | 15 | super(app, options); 16 | 17 | } 18 | 19 | override public function destroy() { 20 | 21 | if (handle != null) { 22 | app.io.fileClose(handle); 23 | handle = null; 24 | } 25 | 26 | app = null; 27 | handle = null; 28 | 29 | super.destroy(); 30 | 31 | } 32 | 33 | override public function seek(to:Int):Bool { 34 | 35 | if (handle == null) return false; 36 | 37 | var result = app.io.fileSeek(handle, to, FileSeek.SET); 38 | 39 | return (result == 0); 40 | 41 | } 42 | 43 | override public function portion(into:Uint8Array, start:Int, len:Int, intoResult:Array):Array { 44 | 45 | inline function fail() { 46 | intoResult[0] = 0; 47 | intoResult[1] = 1; 48 | return intoResult; 49 | } 50 | 51 | if (handle == null) return fail(); 52 | 53 | if (start != -1) { 54 | seek(start); 55 | } 56 | 57 | var complete = false; 58 | var readLen = len; 59 | var curPos = app.io.fileTell(handle); 60 | var distanceToEnd = length - curPos; 61 | 62 | if (distanceToEnd <= readLen) { 63 | readLen = distanceToEnd; 64 | complete = true; 65 | } 66 | 67 | if (readLen <= 0) return fail(); 68 | 69 | Log.debug('Audio / PCM / reading $readLen bytes from $start'); 70 | 71 | // resize to fit the requested/remaining length 72 | var byteGap = (readLen & 0x03); 73 | var nElements = 1; 74 | var elementsRead = app.io.fileRead(handle, into, readLen, nElements); 75 | 76 | // If no elements were read, it was an error 77 | // or end of file so either way it's complete. 78 | if (elementsRead == 0) complete = true; 79 | 80 | Log.debug('Audio / PCM / total read $readLen bytes, complete? $complete'); 81 | 82 | intoResult[0] = readLen; 83 | intoResult[1] = (complete) ? 1 : 0; 84 | 85 | return intoResult; 86 | 87 | } 88 | 89 | } 90 | 91 | 92 | class PCM { 93 | 94 | public static function fromFile(app:Clay, path:String, isStream:Bool):AudioData { 95 | 96 | var handle = app.io.fileHandle(path, 'rb'); 97 | if (handle == null) return null; 98 | 99 | var length = app.io.fileSize(handle); 100 | var samples:Uint8Array = null; 101 | 102 | if (!isStream) { 103 | samples = new Uint8Array(length); 104 | var read = app.io.fileRead(handle, samples, length, 1); 105 | if (read != length) { 106 | samples = null; 107 | return null; 108 | } 109 | } 110 | 111 | // The sound format values are sane defaults - 112 | // change these values right before creating the sound itself. 113 | 114 | return new NativeAudioDataPCM(app, handle, { 115 | id: path, 116 | isStream: isStream, 117 | format: PCM, 118 | samples: samples, 119 | length: length, 120 | channels: 1, 121 | rate: 44100 122 | }); 123 | 124 | } 125 | 126 | public static function fromBytes(app:Clay, id:String, bytes:Uint8Array):AudioData { 127 | 128 | return new NativeAudioDataPCM(app, null, { 129 | id: id, 130 | isStream: false, 131 | format: PCM, 132 | samples: bytes, 133 | length: bytes.length, 134 | channels: 1, 135 | rate: 44100 136 | }); 137 | 138 | } 139 | 140 | 141 | } 142 | -------------------------------------------------------------------------------- /src/clay/buffers/Int8Array.hx: -------------------------------------------------------------------------------- 1 | package clay.buffers; 2 | 3 | // Code imported from snowkit/snow (https://github.com/snowkit/snow/tree/fe93eb1ecfc82131a6143be1b3e3e0a274f4cf65) 4 | // Credits go to its original author (@ruby0x1 on Github) 5 | 6 | #if js 7 | 8 | @:forward 9 | abstract Int8ArrayImplJS(js.lib.Int8Array) 10 | from js.lib.Int8Array 11 | to js.lib.Int8Array { 12 | 13 | public inline static var BYTES_PER_ELEMENT : Int = 1; 14 | 15 | inline public function new(_elements:Int) { 16 | this = new js.lib.Int8Array(_elements); 17 | } 18 | 19 | inline static public function fromArray(_array:Array) : Int8Array { 20 | return new js.lib.Int8Array(untyped _array); 21 | } 22 | 23 | inline static public function fromView(_view:ArrayBufferView) : Int8Array { 24 | return new js.lib.Int8Array(untyped _view); 25 | } 26 | 27 | inline static public function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int) : Int8Array { 28 | return new js.lib.Int8Array(_buffer, _byteOffset, _byteLength); 29 | } 30 | 31 | 32 | @:arrayAccess extern inline function __set(idx:Int, val:Int) : Void this[idx] = val; 33 | @:arrayAccess extern inline function __get(idx:Int) : Int return this[idx]; 34 | 35 | 36 | //non spec haxe conversions 37 | inline public static function fromBytes( bytes:haxe.io.Bytes, ?byteOffset:Int=0, ?len:Int ) : Int8Array { 38 | return new js.lib.Int8Array(cast bytes.getData(), byteOffset, len); 39 | } 40 | 41 | inline public function toBytes() : haxe.io.Bytes { 42 | #if (haxe_ver < 3.2) 43 | return @:privateAccess new haxe.io.Bytes( this.byteLength, cast new js.lib.Uint8Array(this.buffer) ); 44 | #else 45 | return @:privateAccess new haxe.io.Bytes( cast new js.lib.Uint8Array(this.buffer) ); 46 | #end 47 | } 48 | 49 | inline function toString() return 'Int8Array [byteLength:${this.byteLength}, length:${this.length}]'; 50 | 51 | } 52 | 53 | typedef Int8Array = Int8ArrayImplJS; 54 | 55 | #else 56 | 57 | import clay.buffers.ArrayBufferView; 58 | import clay.buffers.TypedArrayType; 59 | 60 | @:forward 61 | abstract Int8ArrayImpl(ArrayBufferView) from ArrayBufferView to ArrayBufferView { 62 | 63 | public inline static var BYTES_PER_ELEMENT : Int = 1; 64 | 65 | public var length (get, never):Int; 66 | 67 | inline public function new(_elements:Int) { 68 | this = ArrayBufferView.fromElements(Int8, _elements); 69 | } 70 | 71 | // @:generic 72 | static public inline function fromArray(_array:Array) : Int8Array { 73 | return ArrayBufferView.fromArray(Int8, cast _array); 74 | } 75 | 76 | static public inline function fromView(_view:ArrayBufferView) : Int8Array { 77 | return ArrayBufferView.fromView(Int8, _view); 78 | } 79 | 80 | static public inline function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int) : Int8Array { 81 | return ArrayBufferView.fromBuffer(Int8, _buffer, _byteOffset, _byteLength); 82 | } 83 | 84 | //Public API 85 | 86 | public inline function subarray( begin:Int, end:Null = null) : Int8Array return this.subarray(begin, end); 87 | 88 | 89 | inline public static function fromBytes(_bytes:haxe.io.Bytes, ?_byteOffset:Int=0, ?_byteLength:Int) : Int8Array { 90 | if(_byteLength == null) _byteLength = _bytes.length; 91 | return Int8Array.fromBuffer(_bytes.getData(), _byteOffset, _byteLength); 92 | } 93 | 94 | inline public function toBytes() : haxe.io.Bytes { 95 | return haxe.io.Bytes.ofData(this.buffer); 96 | } 97 | 98 | //Internal 99 | 100 | inline function get_length() return this.length; 101 | 102 | 103 | @:noCompletion 104 | @:arrayAccess extern 105 | public inline function __get(idx:Int) { 106 | return ArrayBufferIO.getInt8(this.buffer, this.byteOffset+idx); 107 | } 108 | 109 | @:noCompletion 110 | @:arrayAccess extern 111 | public inline function __set(idx:Int, val:Int) : Void { 112 | ArrayBufferIO.setInt8(this.buffer, this.byteOffset+idx, val); 113 | } 114 | 115 | inline function toString() return this == null ? null : 'Int8Array [byteLength:${this.byteLength}, length:${this.length}]'; 116 | 117 | } 118 | 119 | typedef Int8Array = Int8ArrayImpl; 120 | 121 | #end 122 | -------------------------------------------------------------------------------- /src/clay/buffers/Int16Array.hx: -------------------------------------------------------------------------------- 1 | package clay.buffers; 2 | 3 | // Code imported from snowkit/snow (https://github.com/snowkit/snow/tree/fe93eb1ecfc82131a6143be1b3e3e0a274f4cf65) 4 | // Credits go to its original author (@ruby0x1 on Github) 5 | 6 | #if js 7 | 8 | @:forward 9 | abstract Int16ArrayImplJS(js.lib.Int16Array) 10 | from js.lib.Int16Array 11 | to js.lib.Int16Array { 12 | 13 | public inline static var BYTES_PER_ELEMENT : Int = 2; 14 | 15 | inline public function new(_elements:Int) { 16 | this = new js.lib.Int16Array(_elements); 17 | } 18 | 19 | inline static public function fromArray(_array:Array) : Int16Array { 20 | return new js.lib.Int16Array(untyped _array); 21 | } 22 | 23 | inline static public function fromView(_view:ArrayBufferView) : Int16Array { 24 | return new js.lib.Int16Array(untyped _view); 25 | } 26 | 27 | inline static public function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int) : Int16Array { 28 | return new js.lib.Int16Array(_buffer, _byteOffset, Std.int(_byteLength/BYTES_PER_ELEMENT)); 29 | } 30 | 31 | @:arrayAccess extern inline function __set(idx:Int, val:Int) : Void this[idx] = val; 32 | @:arrayAccess extern inline function __get(idx:Int) : Int return this[idx]; 33 | 34 | 35 | //non spec haxe conversions 36 | inline public static function fromBytes( bytes:haxe.io.Bytes, ?byteOffset:Int=0, ?len:Int ) : Int16Array { 37 | if(byteOffset == null) return new js.lib.Int16Array(cast bytes.getData()); 38 | if(len == null) return new js.lib.Int16Array(cast bytes.getData(), byteOffset); 39 | return new js.lib.Int16Array(cast bytes.getData(), byteOffset, len); 40 | } 41 | 42 | inline public function toBytes() : haxe.io.Bytes { 43 | #if (haxe_ver < 3.2) 44 | return @:privateAccess new haxe.io.Bytes( this.byteLength, cast new js.lib.Uint8Array(this.buffer) ); 45 | #else 46 | return @:privateAccess new haxe.io.Bytes( cast new js.lib.Uint8Array(this.buffer) ); 47 | #end 48 | } 49 | 50 | inline function toString() return 'Int16Array [byteLength:${this.byteLength}, length:${this.length}]'; 51 | 52 | } 53 | 54 | typedef Int16Array = Int16ArrayImplJS; 55 | 56 | #else 57 | 58 | import clay.buffers.ArrayBufferView; 59 | import clay.buffers.TypedArrayType; 60 | 61 | @:forward 62 | abstract Int16ArrayImpl(ArrayBufferView) from ArrayBufferView to ArrayBufferView { 63 | 64 | public inline static var BYTES_PER_ELEMENT : Int = 2; 65 | 66 | public var length (get, never):Int; 67 | 68 | inline public function new(_elements:Int) { 69 | this = ArrayBufferView.fromElements(Int16, _elements); 70 | } 71 | 72 | // @:generic 73 | static public inline function fromArray(_array:Array) : Int16Array { 74 | return ArrayBufferView.fromArray(Int16, cast _array); 75 | } 76 | 77 | static public inline function fromView(_view:ArrayBufferView) : Int16Array { 78 | return ArrayBufferView.fromView(Int16, _view); 79 | } 80 | 81 | static public inline function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int) : Int16Array { 82 | return ArrayBufferView.fromBuffer(Int16, _buffer, _byteOffset, _byteLength); 83 | } 84 | 85 | //Public API 86 | 87 | public inline function subarray( begin:Int, end:Null = null) : Int16Array return this.subarray(begin, end); 88 | 89 | 90 | inline public static function fromBytes(_bytes:haxe.io.Bytes, ?_byteOffset:Int=0, ?_byteLength:Int) : Int16Array { 91 | if(_byteLength == null) _byteLength = _bytes.length; 92 | return Int16Array.fromBuffer(_bytes.getData(), _byteOffset, _byteLength); 93 | } 94 | 95 | inline public function toBytes() : haxe.io.Bytes { 96 | return haxe.io.Bytes.ofData(this.buffer); 97 | } 98 | 99 | //Internal 100 | 101 | inline function get_length() return this.length; 102 | 103 | 104 | @:noCompletion 105 | @:arrayAccess extern 106 | public inline function __get(idx:Int) { 107 | return ArrayBufferIO.getInt16(this.buffer, this.byteOffset+(idx*BYTES_PER_ELEMENT)); 108 | } 109 | 110 | @:noCompletion 111 | @:arrayAccess extern 112 | public inline function __set(idx:Int, val:Int) : Void { 113 | ArrayBufferIO.setInt16(this.buffer, this.byteOffset+(idx*BYTES_PER_ELEMENT), val); 114 | } 115 | 116 | inline function toString() return this == null ? null : 'Int16Array [byteLength:${this.byteLength}, length:${this.length}]'; 117 | 118 | } 119 | 120 | typedef Int16Array = Int16ArrayImpl; 121 | 122 | #end 123 | -------------------------------------------------------------------------------- /src/clay/buffers/Uint32Array.hx: -------------------------------------------------------------------------------- 1 | package clay.buffers; 2 | 3 | // Code imported from snowkit/snow (https://github.com/snowkit/snow/tree/fe93eb1ecfc82131a6143be1b3e3e0a274f4cf65) 4 | // Credits go to its original author (@ruby0x1 on Github) 5 | 6 | #if js 7 | 8 | @:forward 9 | abstract Uint32ArrayImplJS(js.lib.Uint32Array) 10 | from js.lib.Uint32Array 11 | to js.lib.Uint32Array { 12 | 13 | public inline static var BYTES_PER_ELEMENT : Int = 4; 14 | 15 | inline public function new(_elements:Int) { 16 | this = new js.lib.Uint32Array(_elements); 17 | } 18 | 19 | inline static public function fromArray(_array:Array) : Uint32Array { 20 | return new js.lib.Uint32Array(untyped _array); 21 | } 22 | 23 | inline static public function fromView(_view:ArrayBufferView) : Uint32Array { 24 | return new js.lib.Uint32Array(untyped _view); 25 | } 26 | 27 | inline static public function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int) : Uint32Array { 28 | return new js.lib.Uint32Array(_buffer, _byteOffset, Std.int(_byteLength/BYTES_PER_ELEMENT)); 29 | } 30 | 31 | @:arrayAccess extern inline function __set(idx:Int, val:UInt) : Void this[idx] = val; 32 | @:arrayAccess extern inline function __get(idx:Int) : UInt return this[idx]; 33 | 34 | 35 | //non spec haxe conversions 36 | inline public static function fromBytes( bytes:haxe.io.Bytes, ?byteOffset:Int=0, ?len:Int ) : Uint32Array { 37 | if(byteOffset == null) return new js.lib.Uint32Array(cast bytes.getData()); 38 | if(len == null) return new js.lib.Uint32Array(cast bytes.getData(), byteOffset); 39 | return new js.lib.Uint32Array(cast bytes.getData(), byteOffset, len); 40 | } 41 | 42 | inline public function toBytes() : haxe.io.Bytes { 43 | #if (haxe_ver < 3.2) 44 | return @:privateAccess new haxe.io.Bytes( this.byteLength, cast new js.lib.Uint8Array(this.buffer) ); 45 | #else 46 | return @:privateAccess new haxe.io.Bytes( cast new js.lib.Uint8Array(this.buffer) ); 47 | #end 48 | } 49 | 50 | inline function toString() return 'Uint32Array [byteLength:${this.byteLength}, length:${this.length}]'; 51 | 52 | } 53 | 54 | typedef Uint32Array = Uint32ArrayImplJS; 55 | 56 | #else 57 | 58 | import clay.buffers.ArrayBufferView; 59 | import clay.buffers.TypedArrayType; 60 | 61 | @:forward 62 | abstract Uint32ArrayImpl(ArrayBufferView) from ArrayBufferView to ArrayBufferView { 63 | 64 | public inline static var BYTES_PER_ELEMENT : Int = 4; 65 | 66 | public var length (get, never):Int; 67 | 68 | inline public function new(_elements:Int) { 69 | this = ArrayBufferView.fromElements(Uint32, _elements); 70 | } 71 | 72 | // @:generic 73 | static public inline function fromArray(_array:Array) : Uint32Array { 74 | return ArrayBufferView.fromArray(Uint32, cast _array); 75 | } 76 | 77 | static public inline function fromView(_view:ArrayBufferView) : Uint32Array { 78 | return ArrayBufferView.fromView(Uint32, _view); 79 | } 80 | 81 | static public inline function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int) : Uint32Array { 82 | return ArrayBufferView.fromBuffer(Uint32, _buffer, _byteOffset, _byteLength); 83 | } 84 | 85 | //Public API 86 | 87 | public inline function subarray( begin:Int, end:Null = null) : Uint32Array return this.subarray(begin, end); 88 | 89 | 90 | inline public static function fromBytes(_bytes:haxe.io.Bytes, ?_byteOffset:Int=0, ?_byteLength:Int) : Uint32Array { 91 | if(_byteLength == null) _byteLength = _bytes.length; 92 | return Uint32Array.fromBuffer(_bytes.getData(), _byteOffset, _byteLength); 93 | } 94 | 95 | inline public function toBytes() : haxe.io.Bytes { 96 | return haxe.io.Bytes.ofData(this.buffer); 97 | } 98 | 99 | //Internal 100 | 101 | inline function get_length() return this.length; 102 | 103 | 104 | @:noCompletion 105 | @:arrayAccess extern 106 | public inline function __get(idx:Int) { 107 | return ArrayBufferIO.getUint32(this.buffer, this.byteOffset+(idx*BYTES_PER_ELEMENT)); 108 | } 109 | 110 | @:noCompletion 111 | @:arrayAccess extern 112 | public inline function __set(idx:Int, val:UInt) : Void { 113 | ArrayBufferIO.setUint32(this.buffer, this.byteOffset+(idx*BYTES_PER_ELEMENT), val); 114 | } 115 | 116 | inline function toString() return this == null ? null : 'Uint32Array [byteLength:${this.byteLength}, length:${this.length}]'; 117 | 118 | } 119 | 120 | typedef Uint32Array = Uint32ArrayImpl; 121 | 122 | #end 123 | -------------------------------------------------------------------------------- /src/clay/buffers/Uint16Array.hx: -------------------------------------------------------------------------------- 1 | package clay.buffers; 2 | 3 | // Code imported from snowkit/snow (https://github.com/snowkit/snow/tree/fe93eb1ecfc82131a6143be1b3e3e0a274f4cf65) 4 | // Credits go to its original author (@ruby0x1 on Github) 5 | 6 | #if js 7 | 8 | @:forward 9 | abstract Uint16ArrayImplJS(js.lib.Uint16Array) 10 | from js.lib.Uint16Array 11 | to js.lib.Uint16Array { 12 | 13 | public inline static var BYTES_PER_ELEMENT : Int = 2; 14 | 15 | inline public function new(_elements:Int) { 16 | this = new js.lib.Uint16Array(_elements); 17 | } 18 | 19 | inline static public function fromArray(_array:Array) : Uint16Array { 20 | return new js.lib.Uint16Array(untyped _array); 21 | } 22 | 23 | inline static public function fromView(_view:ArrayBufferView) : Uint16Array { 24 | return new js.lib.Uint16Array(untyped _view); 25 | } 26 | 27 | inline static public function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int) : Uint16Array { 28 | return new js.lib.Uint16Array(_buffer, _byteOffset, Std.int(_byteLength/BYTES_PER_ELEMENT)); 29 | } 30 | 31 | 32 | @:arrayAccess extern inline function __set(idx:Int, val:UInt) : Void this[idx] = val; 33 | @:arrayAccess extern inline function __get(idx:Int) : UInt return this[idx]; 34 | 35 | 36 | //non spec haxe conversions 37 | inline public static function fromBytes( bytes:haxe.io.Bytes, ?byteOffset:Int=0, ?len:Int ) : Uint16Array { 38 | if(byteOffset == null) return new js.lib.Uint16Array(cast bytes.getData()); 39 | if(len == null) return new js.lib.Uint16Array(cast bytes.getData(), byteOffset); 40 | return new js.lib.Uint16Array(cast bytes.getData(), byteOffset, len); 41 | } 42 | 43 | inline public function toBytes() : haxe.io.Bytes { 44 | #if (haxe_ver < 3.2) 45 | return @:privateAccess new haxe.io.Bytes( this.byteLength, cast new js.lib.Uint8Array(this.buffer) ); 46 | #else 47 | return @:privateAccess new haxe.io.Bytes( cast new js.lib.Uint8Array(this.buffer) ); 48 | #end 49 | } 50 | 51 | inline function toString() return 'Uint16Array [byteLength:${this.byteLength}, length:${this.length}]'; 52 | 53 | } 54 | 55 | typedef Uint16Array = Uint16ArrayImplJS; 56 | 57 | #else 58 | 59 | import clay.buffers.ArrayBufferView; 60 | import clay.buffers.TypedArrayType; 61 | 62 | @:forward 63 | abstract Uint16ArrayImpl(ArrayBufferView) from ArrayBufferView to ArrayBufferView { 64 | 65 | public inline static var BYTES_PER_ELEMENT : Int = 2; 66 | 67 | public var length (get, never):Int; 68 | 69 | inline public function new(_elements:Int) { 70 | this = ArrayBufferView.fromElements(Uint16, _elements); 71 | } 72 | 73 | // @:generic 74 | static public inline function fromArray(_array:Array) : Uint16Array { 75 | return ArrayBufferView.fromArray(Uint16, cast _array); 76 | } 77 | 78 | static public inline function fromView(_view:ArrayBufferView) : Uint16Array { 79 | return ArrayBufferView.fromView(Uint16, _view); 80 | } 81 | 82 | static public inline function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int, ?_view:ArrayBufferView) : Uint16Array { 83 | return ArrayBufferView.fromBuffer(Uint16, _buffer, _byteOffset, _byteLength, _view); 84 | } 85 | 86 | //Public API 87 | 88 | public inline function subarray( begin:Int, end:Null = null) : Uint16Array return this.subarray(begin, end); 89 | 90 | 91 | inline public static function fromBytes(_bytes:haxe.io.Bytes, ?_byteOffset:Int=0, ?_byteLength:Int) : Uint16Array { 92 | if(_byteLength == null) _byteLength = _bytes.length; 93 | return Uint16Array.fromBuffer(_bytes.getData(), _byteOffset, _byteLength); 94 | } 95 | 96 | inline public function toBytes() : haxe.io.Bytes { 97 | return haxe.io.Bytes.ofData(this.buffer); 98 | } 99 | 100 | //Internal 101 | 102 | inline function get_length() return this.length; 103 | 104 | 105 | @:noCompletion 106 | @:arrayAccess extern 107 | public inline function __get(idx:Int) { 108 | return ArrayBufferIO.getUint16(this.buffer, this.byteOffset+(idx*BYTES_PER_ELEMENT)); 109 | } 110 | 111 | @:noCompletion 112 | @:arrayAccess extern 113 | public inline function __set(idx:Int, val:UInt) : Void { 114 | ArrayBufferIO.setUint16(this.buffer, this.byteOffset+(idx*BYTES_PER_ELEMENT), val); 115 | } 116 | 117 | inline function toString() return this == null ? null : 'Uint16Array [byteLength:${this.byteLength}, length:${this.length}]'; 118 | 119 | } 120 | 121 | typedef Uint16Array = Uint16ArrayImpl; 122 | 123 | #end 124 | -------------------------------------------------------------------------------- /src/clay/buffers/Uint8Array.hx: -------------------------------------------------------------------------------- 1 | package clay.buffers; 2 | 3 | // Code imported from snowkit/snow (https://github.com/snowkit/snow/tree/fe93eb1ecfc82131a6143be1b3e3e0a274f4cf65) 4 | // Credits go to its original author (@ruby0x1 on Github) 5 | 6 | #if js 7 | 8 | @:forward 9 | abstract Uint8ArrayImplJS(js.lib.Uint8Array) 10 | from js.lib.Uint8Array 11 | to js.lib.Uint8Array { 12 | 13 | public inline static var BYTES_PER_ELEMENT : Int = 1; 14 | 15 | inline public function new(_elements:Int) { 16 | this = new js.lib.Uint8Array(_elements); 17 | } 18 | 19 | inline static public function fromArray(_array:Array) : Uint8Array { 20 | return new js.lib.Uint8Array(untyped _array); 21 | } 22 | 23 | inline static public function fromView(_view:ArrayBufferView) : Uint8Array { 24 | return new js.lib.Uint8Array(untyped _view); 25 | } 26 | 27 | inline static public function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int) : Uint8Array { 28 | return new js.lib.Uint8Array(_buffer, _byteOffset, _byteLength); 29 | } 30 | 31 | @:arrayAccess extern inline function __set(idx:Int, val:UInt) : Void this[idx] = val; 32 | @:arrayAccess extern inline function __get(idx:Int) : Int return this[idx]; 33 | 34 | 35 | //non spec haxe conversions 36 | inline public static function fromBytes( bytes:haxe.io.Bytes, ?byteOffset:Int, ?len:Int ) : Uint8Array { 37 | if(byteOffset == null) return new js.lib.Uint8Array(cast bytes.getData()); 38 | if(len == null) return new js.lib.Uint8Array(cast bytes.getData(), byteOffset); 39 | return new js.lib.Uint8Array(cast bytes.getData(), byteOffset, len); 40 | } 41 | 42 | inline public function toBytes() : haxe.io.Bytes { 43 | #if (haxe_ver < 3.2) 44 | return @:privateAccess new haxe.io.Bytes( this.byteLength, cast new js.lib.Uint8Array(this.buffer) ); 45 | #else 46 | return @:privateAccess new haxe.io.Bytes( cast new js.lib.Uint8Array(this.buffer) ); 47 | #end 48 | } 49 | 50 | inline function toString() return 'Uint8Array [byteLength:${this.byteLength}, length:${this.length}]'; 51 | 52 | } 53 | 54 | typedef Uint8Array = Uint8ArrayImplJS; 55 | 56 | #else 57 | 58 | import clay.buffers.ArrayBufferView; 59 | import clay.buffers.TypedArrayType; 60 | 61 | @:forward 62 | abstract Uint8ArrayImpl(ArrayBufferView) from ArrayBufferView to ArrayBufferView { 63 | 64 | public inline static var BYTES_PER_ELEMENT : Int = 1; 65 | 66 | public var length (get, never):Int; 67 | 68 | inline public function new(_elements:Int) { 69 | this = ArrayBufferView.fromElements(Uint8, _elements); 70 | } 71 | 72 | // @:generic 73 | static public inline function fromArray(_array:Array) : Uint8Array { 74 | return ArrayBufferView.fromArray(Uint8, cast _array); 75 | } 76 | 77 | static public inline function fromView(_view:ArrayBufferView) : Uint8Array { 78 | return ArrayBufferView.fromView(Uint8, _view); 79 | } 80 | 81 | static public inline function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int, ?_view:ArrayBufferView) : Uint8Array { 82 | return ArrayBufferView.fromBuffer(Uint8, _buffer, _byteOffset, _byteLength, _view); 83 | } 84 | 85 | //Public API 86 | 87 | public inline function subarray( begin:Int, end:Null = null) : Uint8Array return this.subarray(begin, end); 88 | 89 | 90 | inline public static function fromBytes(_bytes:haxe.io.Bytes, ?_byteOffset:Int=0, ?_byteLength:Int) : Uint8Array { 91 | if(_byteLength == null) _byteLength = _bytes.length; 92 | return Uint8Array.fromBuffer(_bytes.getData(), _byteOffset, _byteLength); 93 | } 94 | 95 | inline public function toBytes() : haxe.io.Bytes { 96 | return haxe.io.Bytes.ofData(this.buffer); 97 | } 98 | 99 | //Internal 100 | 101 | inline function toString() return this == null ? null : 'Uint8Array [byteLength:${this.byteLength}, length:${this.length}]'; 102 | 103 | inline function get_length() return this.length; 104 | 105 | 106 | @:noCompletion 107 | @:arrayAccess extern 108 | public inline function __get(idx:Int) { 109 | return ArrayBufferIO.getUint8(this.buffer, this.byteOffset+idx); 110 | } 111 | 112 | @:noCompletion 113 | @:arrayAccess extern 114 | public inline function __set(idx:Int, val:UInt) : Void { 115 | ArrayBufferIO.setUint8(this.buffer, this.byteOffset+idx, val); 116 | } 117 | 118 | } 119 | 120 | typedef Uint8Array = Uint8ArrayImpl; 121 | 122 | #end 123 | -------------------------------------------------------------------------------- /src/clay/buffers/Float64Array.hx: -------------------------------------------------------------------------------- 1 | package clay.buffers; 2 | 3 | // Code imported from snowkit/snow (https://github.com/snowkit/snow/tree/fe93eb1ecfc82131a6143be1b3e3e0a274f4cf65) 4 | // Credits go to its original author (@ruby0x1 on Github) 5 | 6 | #if js 7 | 8 | @:forward 9 | abstract Float64ArrayImplJS(js.lib.Float64Array) 10 | from js.lib.Float64Array 11 | to js.lib.Float64Array { 12 | 13 | public inline static var BYTES_PER_ELEMENT : Int = 8; 14 | 15 | inline public function new(_elements:Int) { 16 | this = new js.lib.Float64Array(_elements); 17 | } 18 | 19 | inline static public function fromArray(_array:Array) : Float64Array { 20 | return new js.lib.Float64Array(untyped _array); 21 | } 22 | 23 | inline static public function fromView(_view:ArrayBufferView) : Float64Array { 24 | return new js.lib.Float64Array(untyped _view); 25 | } 26 | 27 | inline static public function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int) : Float64Array { 28 | return new js.lib.Float64Array(_buffer, _byteOffset, Std.int(_byteLength/BYTES_PER_ELEMENT)); 29 | } 30 | 31 | 32 | @:arrayAccess extern inline function __set(idx:Int, val:Float) : Void this[idx] = val; 33 | @:arrayAccess extern inline function __get(idx:Int) : Float return this[idx]; 34 | 35 | 36 | //non spec haxe conversions 37 | inline public static function fromBytes( bytes:haxe.io.Bytes, ?byteOffset:Int=0, ?len:Int ) : Float64Array { 38 | if(byteOffset == null) return new js.lib.Float64Array(cast bytes.getData()); 39 | if(len == null) return new js.lib.Float64Array(cast bytes.getData(), byteOffset); 40 | return new js.lib.Float64Array(cast bytes.getData(), byteOffset, len); 41 | } 42 | 43 | inline public function toBytes() : haxe.io.Bytes { 44 | #if (haxe_ver < 3.2) 45 | return @:privateAccess new haxe.io.Bytes( this.byteLength, cast new js.lib.Uint8Array(this.buffer) ); 46 | #else 47 | return @:privateAccess new haxe.io.Bytes( cast new js.lib.Uint8Array(this.buffer) ); 48 | #end 49 | } 50 | 51 | function toString() return 'Float64Array [byteLength:${this.byteLength}, length:${this.length}]'; 52 | 53 | } 54 | 55 | typedef Float64Array = Float64ArrayImplJS; 56 | 57 | #else 58 | 59 | import clay.buffers.ArrayBufferView; 60 | import clay.buffers.TypedArrayType; 61 | 62 | @:forward 63 | abstract Float64ArrayImpl(ArrayBufferView) from ArrayBufferView to ArrayBufferView { 64 | 65 | public inline static var BYTES_PER_ELEMENT : Int = 8; 66 | 67 | public var length (get, never):Int; 68 | 69 | inline public function new(_elements:Int) { 70 | this = ArrayBufferView.fromElements(Float64, _elements); 71 | } 72 | 73 | // @:generic 74 | static public inline function fromArray(_array:Array) : Float64Array { 75 | return ArrayBufferView.fromArray(Float64, cast _array); 76 | } 77 | 78 | static public inline function fromView(_view:ArrayBufferView) : Float64Array { 79 | return ArrayBufferView.fromView(Float64, _view); 80 | } 81 | 82 | static public inline function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int) : Float64Array { 83 | return ArrayBufferView.fromBuffer(Float64, _buffer, _byteOffset, _byteLength); 84 | } 85 | 86 | //Public API 87 | 88 | public inline function subarray( begin:Int, end:Null = null) : Float64Array return this.subarray(begin, end); 89 | 90 | 91 | inline public static function fromBytes(_bytes:haxe.io.Bytes, ?_byteOffset:Int=0, ?_byteLength:Int) : Float64Array { 92 | if(_byteLength == null) _byteLength = _bytes.length; 93 | return Float64Array.fromBuffer(_bytes.getData(), _byteOffset, _byteLength); 94 | } 95 | 96 | 97 | inline public function toBytes() : haxe.io.Bytes { 98 | return haxe.io.Bytes.ofData(this.buffer); 99 | } 100 | 101 | //Internal 102 | 103 | inline function get_length() return this.length; 104 | 105 | 106 | @:noCompletion 107 | @:arrayAccess extern 108 | public inline function __get(idx:Int) : Float { 109 | return ArrayBufferIO.getFloat64(this.buffer, this.byteOffset+(idx*BYTES_PER_ELEMENT)); 110 | } 111 | 112 | @:noCompletion 113 | @:arrayAccess extern 114 | public inline function __set(idx:Int, val:Float) : Void { 115 | ArrayBufferIO.setFloat64(this.buffer, this.byteOffset+(idx*BYTES_PER_ELEMENT), val); 116 | } 117 | 118 | inline function toString() return this == null ? null : 'Float64Array [byteLength:${this.byteLength}, length:${this.length}]'; 119 | 120 | } 121 | 122 | typedef Float64Array = Float64ArrayImpl; 123 | 124 | #end 125 | -------------------------------------------------------------------------------- /src/clay/buffers/Int32Array.hx: -------------------------------------------------------------------------------- 1 | package clay.buffers; 2 | 3 | // Code imported from snowkit/snow (https://github.com/snowkit/snow/tree/fe93eb1ecfc82131a6143be1b3e3e0a274f4cf65) 4 | // Credits go to its original author (@ruby0x1 on Github) 5 | 6 | #if js 7 | 8 | @:forward 9 | abstract Int32ArrayImplJS(js.lib.Int32Array) 10 | from js.lib.Int32Array 11 | to js.lib.Int32Array { 12 | 13 | public inline static var BYTES_PER_ELEMENT : Int = 4; 14 | 15 | inline public function new(_elements:Int) { 16 | this = new js.lib.Int32Array(_elements); 17 | } 18 | 19 | inline static public function fromArray(_array:Array) : Int32Array { 20 | return new js.lib.Int32Array(untyped _array); 21 | } 22 | 23 | inline static public function fromView(_view:ArrayBufferView) : Int32Array { 24 | return new js.lib.Int32Array(untyped _view); 25 | } 26 | 27 | inline static public function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int) : Int32Array { 28 | return new js.lib.Int32Array(_buffer, _byteOffset, Std.int(_byteLength/BYTES_PER_ELEMENT)); 29 | } 30 | 31 | @:arrayAccess extern inline function __set(idx:Int, val:Int) : Void this[idx] = val; 32 | @:arrayAccess extern inline function __get(idx:Int) : Int return this[idx]; 33 | 34 | 35 | //non spec haxe conversions 36 | inline public static function fromBytes( bytes:haxe.io.Bytes, ?byteOffset:Int=0, ?len:Int ) : Int32Array { 37 | if(byteOffset == null) return new js.lib.Int32Array(cast bytes.getData()); 38 | if(len == null) return new js.lib.Int32Array(cast bytes.getData(), byteOffset); 39 | return new js.lib.Int32Array(cast bytes.getData(), byteOffset, len); 40 | } 41 | 42 | inline public function toBytes() : haxe.io.Bytes { 43 | #if (haxe_ver < 3.2) 44 | return @:privateAccess new haxe.io.Bytes( this.byteLength, cast new js.lib.Uint8Array(this.buffer) ); 45 | #else 46 | return @:privateAccess new haxe.io.Bytes( cast new js.lib.Uint8Array(this.buffer) ); 47 | #end 48 | } 49 | 50 | inline function toString() return 'Int32Array [byteLength:${this.byteLength}, length:${this.length}]'; 51 | 52 | } 53 | 54 | typedef Int32Array = Int32ArrayImplJS; 55 | 56 | #else 57 | 58 | import clay.buffers.ArrayBufferView; 59 | import clay.buffers.TypedArrayType; 60 | 61 | @:forward 62 | abstract Int32ArrayImpl(ArrayBufferView) from ArrayBufferView to ArrayBufferView { 63 | 64 | public inline static var BYTES_PER_ELEMENT : Int = 4; 65 | 66 | public var length (get, never):Int; 67 | 68 | inline public function new(_elements:Int) { 69 | this = ArrayBufferView.fromElements(Int32, _elements); 70 | } 71 | 72 | // @:generic 73 | static public inline function fromArray(_array:Array) : Int32Array { 74 | return ArrayBufferView.fromArray(Int32, cast _array); 75 | } 76 | 77 | static public inline function fromView(_view:ArrayBufferView) : Int32Array { 78 | return ArrayBufferView.fromView(Int32, _view); 79 | } 80 | 81 | static public inline function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int) : Int32Array { 82 | return ArrayBufferView.fromBuffer(Int32, _buffer, _byteOffset, _byteLength); 83 | } 84 | 85 | //Public API 86 | 87 | public inline function subarray( begin:Int, end:Null = null) : Int32Array return this.subarray(begin, end); 88 | 89 | 90 | inline public static function fromBytes(_bytes:haxe.io.Bytes, ?_byteOffset:Int=0, ?_byteLength:Int) : Int32Array { 91 | if(_byteLength == null) _byteLength = _bytes.length; 92 | return Int32Array.fromBuffer(_bytes.getData(), _byteOffset, _byteLength); 93 | } 94 | 95 | inline public function toBytes() : haxe.io.Bytes { 96 | return haxe.io.Bytes.ofData(this.buffer); 97 | } 98 | 99 | //Internal 100 | 101 | inline function get_length() return this.length; 102 | 103 | 104 | @:noCompletion 105 | @:arrayAccess extern 106 | public inline function __get(idx:Int) { 107 | return ArrayBufferIO.getInt32(this.buffer, this.byteOffset+(idx*BYTES_PER_ELEMENT)); 108 | } 109 | 110 | @:noCompletion 111 | @:arrayAccess extern 112 | public inline function __set(idx:Int, val:Int) : Void { 113 | ArrayBufferIO.setInt32(this.buffer, this.byteOffset+(idx*BYTES_PER_ELEMENT), val); 114 | } 115 | 116 | inline function toString() return this == null ? null : 'Int32Array [byteLength:${this.byteLength}, length:${this.length}]'; 117 | 118 | } 119 | 120 | typedef Int32Array = Int32ArrayImpl; 121 | 122 | #end //!js 123 | -------------------------------------------------------------------------------- /src/clay/buffers/Float32Array.hx: -------------------------------------------------------------------------------- 1 | package clay.buffers; 2 | 3 | // Code imported from snowkit/snow (https://github.com/snowkit/snow/tree/fe93eb1ecfc82131a6143be1b3e3e0a274f4cf65) 4 | // Credits go to its original author (@ruby0x1 on Github) 5 | 6 | #if js 7 | 8 | @:forward 9 | @:arrayAccess 10 | abstract Float32ArrayImplJS(js.lib.Float32Array) 11 | from js.lib.Float32Array 12 | to js.lib.Float32Array { 13 | 14 | public inline static var BYTES_PER_ELEMENT : Int = 4; 15 | 16 | inline public function new(_elements:Int) { 17 | this = new js.lib.Float32Array(_elements); 18 | } 19 | 20 | inline static public function fromArray(_array:Array) : Float32Array { 21 | return new js.lib.Float32Array(untyped _array); 22 | } 23 | 24 | inline static public function fromView(_view:ArrayBufferView) : Float32Array { 25 | return new js.lib.Float32Array(untyped _view); 26 | } 27 | 28 | inline static public function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int) : Float32Array { 29 | return new js.lib.Float32Array(_buffer, _byteOffset, Std.int(_byteLength/BYTES_PER_ELEMENT)); 30 | } 31 | 32 | 33 | @:arrayAccess extern inline function __set(idx:Int, val:Float) : Void this[idx] = val; 34 | @:arrayAccess extern inline function __get(idx:Int) : Float return this[idx]; 35 | 36 | 37 | //non spec haxe conversions 38 | inline public static function fromBytes( bytes:haxe.io.Bytes, ?byteOffset:Int=0, ?len:Int ) : Float32Array { 39 | if(byteOffset == null) return new js.lib.Float32Array(cast bytes.getData()); 40 | if(len == null) return new js.lib.Float32Array(cast bytes.getData(), byteOffset); 41 | return new js.lib.Float32Array(cast bytes.getData(), byteOffset, len); 42 | } 43 | 44 | inline public function toBytes() : haxe.io.Bytes { 45 | #if (haxe_ver < 3.2) 46 | return @:privateAccess new haxe.io.Bytes( this.byteLength, cast new js.lib.Uint8Array(this.buffer) ); 47 | #else 48 | return @:privateAccess new haxe.io.Bytes( cast new js.lib.Uint8Array(this.buffer) ); 49 | #end 50 | } 51 | 52 | inline function toString() return 'Float32Array [byteLength:${this.byteLength}, length:${this.length}]'; 53 | 54 | } 55 | 56 | typedef Float32Array = Float32ArrayImplJS; 57 | 58 | #else 59 | 60 | import clay.buffers.ArrayBufferView; 61 | import clay.buffers.TypedArrayType; 62 | 63 | @:forward 64 | abstract Float32ArrayImpl(ArrayBufferView) from ArrayBufferView to ArrayBufferView { 65 | 66 | public inline static var BYTES_PER_ELEMENT : Int = 4; 67 | 68 | public var length (get, never):Int; 69 | 70 | inline public function new(_elements:Int) { 71 | this = ArrayBufferView.fromElements(Float32, _elements); 72 | } 73 | 74 | // @:generic //:todo: on use with generic: Type not found : clay.buffers._Float32Array.Float32Array_Impl_ 75 | inline static public function fromArray(_array:Array) : Float32Array { 76 | return ArrayBufferView.fromArray(Float32, cast _array); 77 | } 78 | 79 | inline static public function fromView(_view:ArrayBufferView) : Float32Array { 80 | return ArrayBufferView.fromView(Float32, _view); 81 | } 82 | 83 | inline static public function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int, ?_view:ArrayBufferView) : Float32Array { 84 | return ArrayBufferView.fromBuffer(Float32, _buffer, _byteOffset, _byteLength, _view); 85 | } 86 | 87 | //Public API 88 | 89 | public inline function subarray( begin:Int, end:Null = null) : Float32Array return this.subarray(begin, end); 90 | 91 | 92 | inline public static function fromBytes(_bytes:haxe.io.Bytes, ?_byteOffset:Int=0, ?_byteLength:Int) : Float32Array { 93 | if(_byteLength == null) _byteLength = _bytes.length; 94 | return Float32Array.fromBuffer(_bytes.getData(), _byteOffset, _byteLength); 95 | } 96 | 97 | inline public function toBytes() : haxe.io.Bytes { 98 | return haxe.io.Bytes.ofData(this.buffer); 99 | } 100 | 101 | //Internal 102 | 103 | inline function toString() return this == null ? null : 'Float32Array [byteLength:${this.byteLength}, length:${this.length}]'; 104 | 105 | extern inline function get_length() return this.length; 106 | 107 | 108 | @:noCompletion 109 | @:arrayAccess extern 110 | public inline function __get(idx:Int) : cpp.Float32 { 111 | return ArrayBufferIO.getFloat32(this.buffer, this.byteOffset+(idx*BYTES_PER_ELEMENT) ); 112 | } 113 | 114 | @:noCompletion 115 | @:arrayAccess extern 116 | public inline function __set(idx:Int, val:cpp.Float32) : Void { 117 | ArrayBufferIO.setFloat32(this.buffer, this.byteOffset+(idx*BYTES_PER_ELEMENT), val); 118 | } 119 | 120 | } 121 | 122 | typedef Float32Array = Float32ArrayImpl; 123 | 124 | #end //!js 125 | -------------------------------------------------------------------------------- /src/clay/buffers/Uint8ClampedArray.hx: -------------------------------------------------------------------------------- 1 | package clay.buffers; 2 | 3 | // Code imported from snowkit/snow (https://github.com/snowkit/snow/tree/fe93eb1ecfc82131a6143be1b3e3e0a274f4cf65) 4 | // Credits go to its original author (@ruby0x1 on Github) 5 | 6 | #if js 7 | 8 | @:forward 9 | abstract Uint8ClampedArrayImplJS(js.lib.Uint8ClampedArray) 10 | from js.lib.Uint8ClampedArray 11 | to js.lib.Uint8ClampedArray { 12 | 13 | public inline static var BYTES_PER_ELEMENT : Int = 1; 14 | 15 | inline public function new(_elements:Int) { 16 | this = new js.lib.Uint8ClampedArray(_elements); 17 | } 18 | 19 | inline static public function fromArray(_array:Array) : Uint8ClampedArray { 20 | return new js.lib.Uint8ClampedArray(untyped _array); 21 | } 22 | 23 | inline static public function fromView(_view:ArrayBufferView) : Uint8ClampedArray { 24 | return new js.lib.Uint8ClampedArray(untyped _view); 25 | } 26 | 27 | inline static public function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int) : Uint8ClampedArray { 28 | return new js.lib.Uint8ClampedArray(_buffer, _byteOffset, _byteLength); 29 | } 30 | 31 | @:arrayAccess extern inline function __set(idx:Int, val:UInt) : Void this[idx] = _clamp(val); 32 | @:arrayAccess extern inline function __get(idx:Int) : UInt return this[idx]; 33 | 34 | 35 | //non spec haxe conversions 36 | inline public static function fromBytes( bytes:haxe.io.Bytes, ?byteOffset:Int=0, ?len:Int ) : Uint8ClampedArray { 37 | if(byteOffset == null) return new js.lib.Uint8ClampedArray(cast bytes.getData()); 38 | if(len == null) return new js.lib.Uint8ClampedArray(cast bytes.getData(), byteOffset); 39 | return new js.lib.Uint8ClampedArray(cast bytes.getData(), byteOffset, len); 40 | } 41 | 42 | inline public function toBytes() : haxe.io.Bytes { 43 | #if (haxe_ver < 3.2) 44 | return @:privateAccess new haxe.io.Bytes( this.byteLength, cast new js.lib.Uint8Array(this.buffer) ); 45 | #else 46 | return @:privateAccess new haxe.io.Bytes( cast new js.lib.Uint8Array(this.buffer) ); 47 | #end 48 | } 49 | 50 | inline function toString() return 'Uint8ClampedArray [byteLength:${this.byteLength}, length:${this.length}]'; 51 | 52 | //internal 53 | //clamp a Int to a 0-255 Uint8 54 | static function _clamp(_in:Float) : Int { 55 | var _out = Std.int(_in); 56 | _out = _out > 255 ? 255 : _out; 57 | return _out < 0 ? 0 : _out; 58 | } 59 | 60 | } 61 | 62 | typedef Uint8ClampedArrayImplJS = Uint8ClampedArray; 63 | 64 | #else 65 | 66 | import clay.buffers.ArrayBufferView; 67 | import clay.buffers.TypedArrayType; 68 | 69 | @:forward 70 | @:arrayAccess 71 | abstract Uint8ClampedArrayImpl(ArrayBufferView) from ArrayBufferView to ArrayBufferView { 72 | 73 | public inline static var BYTES_PER_ELEMENT : Int = 1; 74 | 75 | public var length (get, never):Int; 76 | 77 | inline public function new(_elements:Int) { 78 | this = ArrayBufferView.fromElements(Uint8Clamped, _elements); 79 | } 80 | 81 | // @:generic 82 | static public inline function fromArray(_array:Array) : Uint8ClampedArray { 83 | return ArrayBufferView.fromArray(Uint8Clamped, cast _array); 84 | } 85 | 86 | static public inline function fromView(_view:ArrayBufferView) : Uint8ClampedArray { 87 | return ArrayBufferView.fromView(Uint8Clamped, _view); 88 | } 89 | 90 | static public inline function fromBuffer(_buffer:ArrayBuffer, _byteOffset:Int, _byteLength:Int) : Uint8ClampedArray { 91 | return ArrayBufferView.fromBuffer(Uint8Clamped, _buffer, _byteOffset, _byteLength); 92 | } 93 | 94 | //Public API 95 | 96 | public inline function subarray( begin:Int, end:Null = null) : Uint8ClampedArray return this.subarray(begin, end); 97 | 98 | 99 | inline public static function fromBytes(_bytes:haxe.io.Bytes, ?_byteOffset:Int=0, ?_byteLength:Int) : Uint8ClampedArray { 100 | if(_byteLength == null) _byteLength = _bytes.length; 101 | return Uint8ClampedArray.fromBuffer(_bytes.getData(), _byteOffset, _byteLength); 102 | } 103 | 104 | inline public function toBytes() : haxe.io.Bytes { 105 | return haxe.io.Bytes.ofData(this.buffer); 106 | } 107 | 108 | //Internal 109 | 110 | inline function get_length() return this.length; 111 | 112 | 113 | @:noCompletion 114 | @:arrayAccess extern 115 | public inline function __get(idx:Int) { 116 | return ArrayBufferIO.getUint8(this.buffer, this.byteOffset+idx); 117 | } 118 | 119 | @:noCompletion 120 | @:arrayAccess extern 121 | public inline function __set(idx:Int, val:UInt) : Void { 122 | ArrayBufferIO.setUint8Clamped(this.buffer, this.byteOffset+idx, val); 123 | } 124 | 125 | inline function toString() return this == null ? null : 'Uint8ClampedArray [byteLength:${this.byteLength}, length:${this.length}]'; 126 | 127 | } 128 | 129 | typedef Uint8ClampedArray = Uint8ClampedArrayImpl; 130 | 131 | #end 132 | -------------------------------------------------------------------------------- /src/clay/sdl/SDLIO.hx: -------------------------------------------------------------------------------- 1 | package clay.sdl; 2 | 3 | import clay.buffers.ArrayBufferView; 4 | import clay.buffers.Uint8Array; 5 | import clay.native.NativeIO; 6 | import clay.sdl.SDL; 7 | 8 | typedef FileHandle = SDLIOStreamPointer; 9 | 10 | enum abstract FileSeek(Int) from Int to Int { 11 | var SET = SDL.SDL_IO_SEEK_SET; 12 | var CUR = SDL.SDL_IO_SEEK_CUR; 13 | var END = SDL.SDL_IO_SEEK_END; 14 | } 15 | 16 | @:headerCode('#include ') 17 | class SDLIO extends NativeIO { 18 | 19 | override public function isSynchronous():Bool { 20 | 21 | return true; 22 | 23 | } 24 | 25 | override function appPath():String { 26 | 27 | var path = SDL.getBasePath(); 28 | if (path == null) path = ''; 29 | 30 | return path; 31 | 32 | } 33 | 34 | override function appPathPrefs():String { 35 | 36 | var parts = Clay.app.appId.split('.'); 37 | var appName = parts.pop(); 38 | var org = parts.join('.'); 39 | 40 | return SDL.getPrefPath(org, appName); 41 | 42 | } 43 | 44 | override function loadData(path:String, binary:Bool = false, async:Bool = false, ?callback:(data:Uint8Array)->Void):Uint8Array { 45 | 46 | if (path == null) 47 | throw 'Path is null!'; 48 | 49 | var dest:Uint8Array = null; 50 | if (async) { 51 | Clay.app.backgroundQueue.schedule(function() { 52 | dest = _doLoadData(path, binary); 53 | Runner.runInMain(function() { 54 | if (callback != null) { 55 | Immediate.push(() -> { 56 | callback(dest); 57 | }); 58 | } 59 | }); 60 | }); 61 | } 62 | else { 63 | dest = _doLoadData(path, binary); 64 | if (callback != null) { 65 | Immediate.push(() -> { 66 | callback(dest); 67 | }); 68 | } 69 | } 70 | return dest; 71 | 72 | } 73 | 74 | private function _doLoadData(path:String, binary:Bool):Uint8Array { 75 | 76 | var file = SDL.ioFromFile(path, binary ? 'rb' : 'r'); 77 | 78 | if (file == null) { 79 | return null; 80 | } 81 | var size = fileSize(file); 82 | var dest = new Uint8Array(size); 83 | 84 | if (size != 0) { 85 | // In SDL3, ioRead returns the number of bytes read directly 86 | var bytesRead = fileRead(file, dest, 1, size); 87 | if (bytesRead != size) { 88 | Log.warning('SDLIO / Warning: Expected to read $size bytes but got $bytesRead'); 89 | } 90 | } 91 | 92 | // close + release the file handle 93 | fileClose(file); 94 | 95 | return dest; 96 | 97 | } 98 | 99 | /// File IO 100 | 101 | public function fileHandle(path:String, mode:String = "rb"):FileHandle { 102 | 103 | return SDL.ioFromFile(path, mode); 104 | 105 | } 106 | 107 | public function fileHandleFromMem(mem:ArrayBufferView, size:Int):FileHandle { 108 | 109 | return SDL.ioFromMem(mem.buffer, size); 110 | 111 | } 112 | 113 | public function fileRead(file:FileHandle, dest:ArrayBufferView, size:Int, maxnum:Int):Int { 114 | 115 | if (file == null) 116 | throw 'Parameter `file` should not be null'; 117 | 118 | // SDL3's ioRead directly reads total bytes, so we calculate the total bytes to read 119 | var totalBytes = size * maxnum; 120 | var bytesRead = SDL.ioRead(file, dest.buffer, totalBytes); 121 | 122 | // Return the number of items read (compatible with SDL2 API) 123 | if (size > 0) { 124 | return Std.int(bytesRead / size); 125 | } 126 | return 0; 127 | 128 | } 129 | 130 | public function fileWrite(file:FileHandle, src:ArrayBufferView, size:Int, num:Int):Int { 131 | 132 | if (file == null) 133 | throw 'Parameter `file` should not be null'; 134 | 135 | // SDL3's ioWrite directly writes total bytes, so we calculate the total bytes to write 136 | var totalBytes = size * num; 137 | var bytesWritten = SDL.ioWrite(file, src.buffer, totalBytes); 138 | 139 | // Return the number of items written (compatible with SDL2 API) 140 | if (size > 0) { 141 | return Std.int(bytesWritten / size); 142 | } 143 | return 0; 144 | 145 | } 146 | 147 | public function fileSeek(file:FileHandle, offset:Int, whence:Int):Int { 148 | 149 | if (file == null) 150 | throw 'Parameter `file` should not be null'; 151 | 152 | return SDL.ioSeek(file, offset, whence); 153 | 154 | } 155 | 156 | public function fileTell(file:FileHandle):Int { 157 | 158 | if (file == null) 159 | throw 'Parameter `file` should not be null'; 160 | 161 | return SDL.ioTell(file); 162 | 163 | } 164 | 165 | public function fileClose(file:FileHandle):Int { 166 | 167 | if (file == null) 168 | throw 'Parameter `file` should not be null'; 169 | 170 | // SDL3's ioClose returns bool, but the original API expects an int 171 | // Return 0 for success (consistent with SDL2 behavior) 172 | return SDL.ioClose(file) ? 0 : -1; 173 | 174 | } 175 | 176 | public function fileSize(handle:FileHandle):UInt { 177 | 178 | var cur = fileTell(handle); 179 | fileSeek(handle, 0, FileSeek.END); 180 | var size = fileTell(handle); 181 | fileSeek(handle, cur, FileSeek.SET); 182 | return size; 183 | 184 | } 185 | 186 | } -------------------------------------------------------------------------------- /src/clay/web/WebIO.hx: -------------------------------------------------------------------------------- 1 | package clay.web; 2 | 3 | import clay.base.BaseIO; 4 | import clay.buffers.Uint8Array; 5 | 6 | using StringTools; 7 | 8 | class WebIO extends BaseIO { 9 | 10 | #if clay_web_use_electron_fs 11 | var testedElectronRemoteAvailability:Bool = false; 12 | var electronRemote:Dynamic = null; 13 | #end 14 | 15 | override function isSynchronous():Bool { 16 | 17 | #if clay_web_use_electron_fs 18 | 19 | bindElectronRemote(); 20 | 21 | return (electronRemote != null); 22 | 23 | #else 24 | 25 | return false; 26 | 27 | #end 28 | 29 | } 30 | 31 | override function loadData(path:String, binary:Bool = false, async:Bool = false, ?callback:(data:Uint8Array)->Void):Uint8Array { 32 | 33 | if (path == null) 34 | throw 'Path is null!'; 35 | 36 | #if clay_web_use_electron_fs 37 | 38 | bindElectronRemote(); 39 | 40 | if (electronRemote != null && !path.startsWith('http://') && !path.startsWith('https://')) { 41 | 42 | var fs = js.Syntax.code("{0}.require('fs')", electronRemote); 43 | var cwd = js.Syntax.code("{0}.process.cwd()", electronRemote); 44 | 45 | if (!async) { 46 | try { 47 | var result = fs.readFileSync(path); 48 | 49 | // Copy data and get rid of nodejs buffer 50 | var data = new Uint8Array(result.length); 51 | for (i in 0...result.length) { 52 | data[i] = js.Syntax.code("{0}[{1}]", result, i); 53 | } 54 | 55 | if (callback != null) { 56 | Immediate.push(() -> { 57 | callback(data); 58 | }); 59 | } 60 | return data; 61 | } 62 | catch (e:Dynamic) { 63 | Log.error('failed to read file at path $path: ' + e); 64 | if (callback != null) { 65 | Immediate.push(() -> { 66 | callback(null); 67 | }); 68 | } 69 | return null; 70 | } 71 | } 72 | else { 73 | fs.readFile(path, function(err, result) { 74 | try { 75 | if (err == null) { 76 | // Copy data and get rid of nodejs buffer 77 | var data = new Uint8Array(result.length); 78 | for (i in 0...result.length) { 79 | data[i] = js.Syntax.code("{0}[{1}]", result, i); 80 | } 81 | 82 | if (callback != null) { 83 | Immediate.push(() -> { 84 | callback(data); 85 | }); 86 | } 87 | } 88 | else { 89 | Log.error('failed to read file at path $path: ' + err); 90 | } 91 | } 92 | catch (e:Dynamic) { 93 | Log.error('failed to read file at path $path: ' + e); 94 | if (callback != null) { 95 | Immediate.push(() -> { 96 | callback(null); 97 | }); 98 | } 99 | } 100 | }); 101 | } 102 | 103 | return null; 104 | 105 | } 106 | else { 107 | 108 | #end 109 | 110 | var asyncHttp = true; 111 | 112 | var request = new js.html.XMLHttpRequest(); 113 | request.open("GET", path, asyncHttp); 114 | 115 | if (binary) { 116 | request.overrideMimeType('text/plain; charset=x-user-defined'); 117 | } else { 118 | request.overrideMimeType('text/plain; charset=UTF-8'); 119 | } 120 | 121 | // Only async can set this type 122 | if (asyncHttp) { 123 | request.responseType = js.html.XMLHttpRequestResponseType.ARRAYBUFFER; 124 | } 125 | 126 | request.onload = function(data) { 127 | 128 | if (request.status == 200) { 129 | var data = new Uint8Array(request.response); 130 | if (callback != null) { 131 | Immediate.push(() -> { 132 | callback(data); 133 | }); 134 | } 135 | } else { 136 | Log.error('Request status for path $path was ${request.status} / ${request.statusText}'); 137 | if (callback != null) { 138 | Immediate.push(() -> { 139 | callback(data); 140 | }); 141 | } 142 | } 143 | }; 144 | 145 | request.send(); 146 | 147 | return null; 148 | 149 | #if clay_web_use_electron_fs 150 | } 151 | #end 152 | 153 | } 154 | 155 | /// Internal 156 | 157 | #if clay_web_use_electron_fs 158 | 159 | inline function bindElectronRemote():Void { 160 | 161 | if (!testedElectronRemoteAvailability) { 162 | testedElectronRemoteAvailability = true; 163 | try { 164 | electronRemote = js.Syntax.code("require('@electron/remote')"); 165 | } 166 | catch (e:Dynamic) {} 167 | } 168 | 169 | } 170 | 171 | #end 172 | 173 | } 174 | -------------------------------------------------------------------------------- /sample/src/sample/SampleIndices.hx: -------------------------------------------------------------------------------- 1 | package sample; 2 | 3 | import clay.buffers.Uint8Array; 4 | import clay.opengl.GL; 5 | import clay.buffers.Float32Array; 6 | import clay.graphics.Shader; 7 | import clay.graphics.Graphics; 8 | import clay.Clay; 9 | 10 | using StringTools; 11 | 12 | class SampleIndicesEvents extends clay.Events { 13 | 14 | public function new() {} 15 | 16 | override function tick(delta:Float) { 17 | 18 | SampleIndices.draw(); 19 | 20 | } 21 | 22 | override function ready():Void { 23 | 24 | SampleIndices.ready(); 25 | 26 | } 27 | 28 | } 29 | 30 | @:allow(sample.SampleIndicesEvents) 31 | class SampleIndices { 32 | 33 | static var events:SampleIndicesEvents; 34 | 35 | static var vertShaderData:String =' 36 | attribute vec3 vertexPosition; 37 | attribute vec4 vertexColor; 38 | 39 | varying vec4 color; 40 | 41 | uniform mat4 projectionMatrix; 42 | uniform mat4 modelViewMatrix; 43 | 44 | void main(void) { 45 | 46 | gl_Position = vec4(vertexPosition, 1.0); 47 | color = vertexColor; 48 | gl_PointSize = 1.0; 49 | 50 | } 51 | '.trim(); 52 | 53 | static var fragShaderData:String = ' 54 | #ifdef GL_ES 55 | precision mediump float; 56 | #else 57 | #define mediump 58 | #endif 59 | 60 | varying vec4 color; 61 | 62 | void main() { 63 | gl_FragColor = color; 64 | } 65 | '.trim(); 66 | 67 | static var shader:Shader; 68 | 69 | public static function main():Void { 70 | 71 | events = @:privateAccess new SampleIndicesEvents(); 72 | @:privateAccess new Clay(configure, events); 73 | 74 | } 75 | 76 | static function configure(config:clay.Config) { 77 | 78 | config.window.resizable = true; 79 | 80 | config.render.stencil = 2; 81 | config.render.depth = 16; 82 | 83 | } 84 | 85 | static function ready():Void { 86 | 87 | trace('Create shader'); 88 | shader = new Shader(); 89 | shader.vertSource = vertShaderData; 90 | shader.fragSource = fragShaderData; 91 | shader.attributes = ['vertexPosition', 'vertexColor']; 92 | shader.init(); 93 | shader.activate(); 94 | 95 | } 96 | 97 | static function draw():Void { 98 | 99 | Graphics.clear(0.5, 0.25, 0.25, 1); 100 | Graphics.setViewport( 101 | 0, 0, 102 | Std.int(Clay.app.screenWidth * Clay.app.screenDensity), 103 | Std.int(Clay.app.screenHeight * Clay.app.screenDensity) 104 | ); 105 | 106 | var vertices = Float32Array.fromArray([ 107 | 0.0, 0.0, 0.0, 1.0, 108 | // Top 109 | -0.2, 0.8, 0.0, 1.0, 110 | 0.2, 0.8, 0.0, 1.0, 111 | 0.0, 0.8, 0.0, 1.0, 112 | 0.0, 1.0, 0.0, 1.0, 113 | // Bottom 114 | -0.2, -0.8, 0.0, 1.0, 115 | 0.2, -0.8, 0.0, 1.0, 116 | 0.0, -0.8, 0.0, 1.0, 117 | 0.0, -1.0, 0.0, 1.0, 118 | // Left 119 | -0.8, -0.2, 0.0, 1.0, 120 | -0.8, 0.2, 0.0, 1.0, 121 | -0.8, 0.0, 0.0, 1.0, 122 | -1.0, 0.0, 0.0, 1.0, 123 | // Right 124 | 0.8, -0.2, 0.0, 1.0, 125 | 0.8, 0.2, 0.0, 1.0, 126 | 0.8, 0.0, 0.0, 1.0, 127 | 1.0, 0.0, 0.0, 1.0 128 | ]); 129 | 130 | var colors = Float32Array.fromArray([ 131 | 1.0, 1.0, 1.0, 1.0, 132 | 133 | 0.0, 1.0, 0.0, 1.0, 134 | 0.0, 0.0, 1.0, 1.0, 135 | 0.0, 1.0, 1.0, 1.0, 136 | 1.0, 0.0, 0.0, 1.0, 137 | 138 | 0.0, 0.0, 1.0, 1.0, 139 | 0.0, 1.0, 0.0, 1.0, 140 | 0.0, 1.0, 1.0, 1.0, 141 | 1.0, 0.0, 0.0, 1.0, 142 | 143 | 0.0, 1.0, 0.0, 1.0, 144 | 0.0, 0.0, 1.0, 1.0, 145 | 0.0, 1.0, 1.0, 1.0, 146 | 1.0, 0.0, 0.0, 1.0, 147 | 148 | 0.0, 0.0, 1.0, 1.0, 149 | 0.0, 1.0, 0.0, 1.0, 150 | 0.0, 1.0, 1.0, 1.0, 151 | 1.0, 0.0, 0.0, 1.0 152 | ]); 153 | 154 | var indices = Uint8Array.fromArray([ 155 | // Top 156 | 0, 1, 3, 157 | 0, 3, 2, 158 | 3, 1, 4, 159 | 3, 4, 2, 160 | // Bottom 161 | 0, 5, 7, 162 | 0, 7, 6, 163 | 7, 5, 8, 164 | 7, 8, 6, 165 | // Left 166 | 0, 9, 11, 167 | 0, 11, 10, 168 | 11, 9, 12, 169 | 11, 12, 10, 170 | // Right 171 | 0, 13, 15, 172 | 0, 15, 14, 173 | 15, 13, 16, 174 | 15, 16, 14 175 | ]); 176 | 177 | GL.enableVertexAttribArray(0); 178 | GL.enableVertexAttribArray(1); 179 | 180 | var verticesBuffer = GL.createBuffer(); 181 | GL.bindBuffer(GL.ARRAY_BUFFER, verticesBuffer); 182 | GL.bufferData(GL.ARRAY_BUFFER, vertices, GL.STREAM_DRAW); 183 | GL.vertexAttribPointer(0, 4, GL.FLOAT, false, 0, 0); 184 | 185 | var colorsBuffer = GL.createBuffer(); 186 | GL.bindBuffer(GL.ARRAY_BUFFER, colorsBuffer); 187 | GL.bufferData(GL.ARRAY_BUFFER, colors, GL.STREAM_DRAW); 188 | GL.vertexAttribPointer(1, 4, GL.FLOAT, false, 0, 0); 189 | 190 | var indicesBuffer = GL.createBuffer(); 191 | GL.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, indicesBuffer); 192 | GL.bufferData(GL.ELEMENT_ARRAY_BUFFER, indices, GL.STREAM_DRAW); 193 | 194 | GL.drawElements(GL.TRIANGLES, indices.length, GL.UNSIGNED_BYTE, 0); 195 | 196 | GL.deleteBuffer(verticesBuffer); 197 | GL.deleteBuffer(colorsBuffer); 198 | 199 | GL.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, Graphics.NO_BUFFER); 200 | GL.deleteBuffer(indicesBuffer); 201 | 202 | GL.disableVertexAttribArray(0); 203 | GL.disableVertexAttribArray(1); 204 | 205 | } 206 | 207 | } 208 | -------------------------------------------------------------------------------- /src/clay/sdl/linc/linc_sdl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 |
29 | 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | 46 | 47 | 48 | 49 |
50 |
51 | 52 | 53 | 54 | 55 |
56 |
57 | 58 | 59 | 60 | 61 |
62 |
63 | 64 | 65 | 66 | 67 |
68 |
69 | 70 | 71 | 72 | 73 |
74 |
75 | 76 | 77 | 78 | 79 |
80 |
81 | 82 | 83 | 84 | 85 |
86 |
87 | 88 |
89 | 90 |
-------------------------------------------------------------------------------- /src/clay/sdl/linc/linc_sdl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef HXCPP_H 4 | #include 5 | #endif 6 | 7 | #include 8 | 9 | namespace linc { 10 | namespace sdl { 11 | 12 | struct SDLSize { 13 | int w; 14 | int h; 15 | }; 16 | 17 | struct SDLPoint { 18 | int x; 19 | int y; 20 | }; 21 | 22 | struct SDLRenderConfig { 23 | int depth; 24 | int stencil; 25 | int antialiasing; 26 | int redBits; 27 | int greenBits; 28 | int blueBits; 29 | int alphaBits; 30 | float defaultClearR; 31 | float defaultClearG; 32 | float defaultClearB; 33 | float defaultClearA; 34 | int openglMajor; 35 | int openglMinor; 36 | int openglProfile; // GLES = 2 37 | }; 38 | 39 | struct SDLNativePointers { 40 | SDL_Window* window; 41 | SDL_GLContext gl; 42 | SDL_Event* currentEvent; 43 | bool skipMouseEvents; 44 | bool skipKeyboardEvents; 45 | float minFrameTime; 46 | }; 47 | 48 | // Initialization 49 | bool init(); 50 | void bind(); 51 | void quit(); 52 | bool setHint(const char* name, const char* value); 53 | 54 | // Locale workaround 55 | extern void setLCNumericCLocale(); 56 | 57 | // Video 58 | bool initSubSystem(Uint32 flags); 59 | void quitSubSystem(Uint32 flags); 60 | bool setVideoDriver(::String driver); 61 | ::String getError(); 62 | 63 | // Window management 64 | SDL_Window* createWindow(::String title, int x, int y, int width, int height, Uint32 flags); 65 | SDL_WindowID getWindowID(SDL_Window* window); 66 | void setWindowTitle(SDL_Window* window, const char* title); 67 | void setWindowBordered(SDL_Window* window, bool bordered); 68 | bool setWindowFullscreenMode(SDL_Window* window, const SDL_DisplayMode* mode); 69 | bool setWindowFullscreen(SDL_Window* window, bool fullscreen); 70 | bool getWindowSize(SDL_Window* window, SDLSize* size); 71 | bool getWindowSizeInPixels(SDL_Window* window, SDLSize* size); 72 | bool getWindowPosition(SDL_Window* window, SDLPoint* position); 73 | const SDL_DisplayMode* getWindowFullscreenMode(SDL_Window* window); 74 | const SDL_DisplayMode* getDesktopDisplayMode(SDL_DisplayID displayID); 75 | SDL_DisplayID getPrimaryDisplay(); 76 | SDL_DisplayID getDisplayForWindow(SDL_Window* window); 77 | bool getWindowFlags(SDL_Window* window, SDL_WindowFlags* flags); 78 | 79 | // OpenGL 80 | bool GL_SetAttribute(int attr, int value); 81 | SDL_GLContext GL_CreateContext(SDL_Window* window); 82 | SDL_GLContext GL_GetCurrentContext(); 83 | int GL_GetAttribute(int attr); 84 | bool GL_MakeCurrent(SDL_Window* window, SDL_GLContext context); 85 | bool GL_SwapWindow(SDL_Window* window); 86 | bool GL_SetSwapInterval(int interval); 87 | void GL_DestroyContext(SDL_GLContext context); 88 | 89 | // Timer 90 | Uint64 getTicks(); 91 | void delay(Uint32 ms); 92 | 93 | // Events 94 | bool pollEvent(SDL_Event* event); 95 | void pumpEvents(); 96 | 97 | // Joystick/Gamepad 98 | int getNumJoysticks(); 99 | bool isGamepad(SDL_JoystickID instance_id); 100 | SDL_Joystick* openJoystick(SDL_JoystickID instance_id); 101 | void closeJoystick(SDL_Joystick* joystick); 102 | SDL_Gamepad* openGamepad(SDL_JoystickID instance_id); 103 | void closeGamepad(SDL_Gamepad* gamepad); 104 | const char* getGamepadNameForID(SDL_JoystickID instance_id); 105 | const char* getJoystickNameForID(SDL_JoystickID instance_id); 106 | bool gamepadHasRumble(SDL_Gamepad* gamepad); 107 | bool rumbleGamepad(SDL_Gamepad* gamepad, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms); 108 | bool setGamepadSensorEnabled(SDL_Gamepad* gamepad, int type, bool enabled); 109 | SDL_JoystickID getJoystickID(SDL_Joystick* joystick); 110 | 111 | // Display 112 | float getDisplayContentScale(SDL_DisplayID displayID); 113 | void getDisplayUsableBounds(SDL_DisplayID displayID, SDL_Rect* rect); 114 | 115 | // Event handlers 116 | #if (defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)) 117 | typedef ::cpp::Function < void() > InternaliOSAnimationCallback; 118 | bool setiOSAnimationCallback(SDL_Window* window, InternaliOSAnimationCallback callback); 119 | #endif 120 | 121 | typedef ::cpp::Function < int(SDL_Event*) > InternalEventWatcherCallback; 122 | bool setEventWatch(SDL_Window* window, InternalEventWatcherCallback eventWatcher); 123 | 124 | // Base path 125 | ::String getBasePath(); 126 | 127 | // Text input 128 | void startTextInput(SDL_Window* window); 129 | void stopTextInput(SDL_Window* window); 130 | void setTextInputArea(SDL_Window* window, const SDL_Rect* rect, int cursor); 131 | 132 | // IO operations 133 | SDL_IOStream* ioFromFile(const char* file, const char* mode); 134 | SDL_IOStream* ioFromMem(::Array mem, size_t size); 135 | size_t ioRead(SDL_IOStream* context, ::Array dest, size_t size); 136 | size_t ioWrite(SDL_IOStream* context, ::Array src, size_t size); 137 | Sint64 ioSeek(SDL_IOStream* context, Sint64 offset, int whence); 138 | Sint64 ioTell(SDL_IOStream* context); 139 | bool ioClose(SDL_IOStream* context); 140 | 141 | // Path operations 142 | const char* getPrefPath(const char* org, const char* app); 143 | 144 | // Clipboard 145 | bool hasClipboardText(); 146 | ::String getClipboardText(); 147 | bool setClipboardText(const char* text); 148 | 149 | bool byteOrderIsBigEndian(); 150 | SDL_Surface* createRGBSurfaceFrom(::Array pixels, int width, int height, int depth, int pitch, Uint32 rmask, Uint32 gmask, Uint32 bmask, Uint32 amask); 151 | void freeSurface(SDL_Surface* surface); 152 | } 153 | } -------------------------------------------------------------------------------- /src/clay/native/NativeAudioDataWAV.hx: -------------------------------------------------------------------------------- 1 | package clay.native; 2 | 3 | import clay.Types; 4 | import clay.audio.AudioData; 5 | 6 | import clay.buffers.Uint8Array; 7 | 8 | @:structInit 9 | class WavChunk { 10 | public var id:String; 11 | public var offset:Int; 12 | public var data:Uint8Array; 13 | public var dataLength:Int; 14 | } 15 | 16 | @:structInit 17 | class WavHandle { 18 | public var handle:FileHandle; 19 | public var offset:Int; 20 | } 21 | 22 | class NativeAudioDataWAV extends AudioData { 23 | 24 | public var dataOffset:Int; 25 | 26 | public var handle:FileHandle; 27 | 28 | inline public function new(app:Clay, handle:FileHandle, offset:Int, options:AudioDataOptions) { 29 | 30 | this.handle = handle; 31 | this.dataOffset = offset; 32 | 33 | super(app, options); 34 | 35 | } 36 | 37 | override public function destroy() { 38 | 39 | if (handle != null) { 40 | app.io.fileClose(handle); 41 | } 42 | 43 | handle = null; 44 | 45 | super.destroy(); 46 | 47 | } 48 | 49 | override public function seek(to:Int):Bool { 50 | 51 | var result = app.io.fileSeek(handle, dataOffset + to, FileSeek.SET); 52 | 53 | return result == 0; 54 | 55 | } 56 | 57 | override public function portion(into:Uint8Array, start:Int, len:Int, intoResult:Array):Array { 58 | 59 | if (start != -1) { 60 | seek(start + dataOffset); 61 | } 62 | 63 | var complete = false; 64 | var readLen = len; 65 | var currentPos = app.io.fileTell(handle) - dataOffset; 66 | var distanceToEnd = length - currentPos; 67 | 68 | if (distanceToEnd <= readLen) { 69 | readLen = distanceToEnd; 70 | complete = true; 71 | } 72 | 73 | if (readLen <= 0) { 74 | intoResult[0] = 0; 75 | intoResult[1] = 1; 76 | return intoResult; 77 | } 78 | 79 | Log.debug('Audio / WAV / reading $readLen bytes from $start'); 80 | 81 | // Resize to fit the requested/remaining length 82 | var byteGap = (readLen & 0x03); 83 | 84 | var nElements = 1; 85 | var elementsRead = app.io.fileRead(handle, into, readLen, nElements); 86 | 87 | // If no elements were read, it was an error 88 | // or end of file so either way it's complete. 89 | if (elementsRead == 0) complete = true; 90 | 91 | Log.debug('Audio / WAV / total read $readLen bytes, complete? $complete'); 92 | 93 | intoResult[0] = readLen; 94 | intoResult[1] = (complete) ? 1 : 0; 95 | 96 | return intoResult; 97 | 98 | } 99 | 100 | } 101 | 102 | class WAV { 103 | 104 | public static function fromFile(app:Clay, path:String, isStream:Bool):AudioData { 105 | 106 | var handle = app.io.fileHandle(path, 'rb'); 107 | 108 | return fromFileHandle(app, handle, path, isStream); 109 | 110 | } 111 | 112 | public static function fromBytes(app:Clay, path:String, bytes:Uint8Array):AudioData { 113 | 114 | var handle = app.io.fileHandleFromMem(bytes, bytes.length); 115 | 116 | return fromFileHandle(app, handle, path, false); 117 | 118 | } 119 | 120 | /// Helpers 121 | 122 | static var ID_DATA = 'data'; 123 | static var ID_FMT = 'fmt '; 124 | static var ID_WAVE = 'WAVE'; 125 | static var ID_RIFF = 'RIFF'; 126 | 127 | public static function fromFileHandle(app:Clay, handle:FileHandle, path:String, isStream:Bool):AudioData { 128 | 129 | if (handle == null) return null; 130 | 131 | var length = 0; 132 | var info = new NativeAudioDataWAV(app, handle, 0, { 133 | id : path, 134 | isStream : isStream, 135 | format : WAV, 136 | samples : null, 137 | length : length, 138 | channels : 1, 139 | rate : 44100 140 | }); 141 | 142 | var header = new Uint8Array(12); 143 | app.io.fileRead(handle, header, 12, 1); 144 | 145 | var bytes = header.toBytes(); 146 | var fileId = bytes.getString(0, 4); 147 | var fileFormat = bytes.getString(8, 4); 148 | 149 | header = null; 150 | bytes = null; 151 | 152 | if (fileId != ID_RIFF) { 153 | Log.debug('Audio / WAV / file `$path` has invalid header (id `$fileId`, expected RIFF)'); 154 | return null; 155 | } 156 | 157 | if(fileFormat != ID_WAVE) { 158 | Log.debug('Audio / WAV / file `$path` has invalid header (id `$fileFormat`, expected WAVE)'); 159 | return null; 160 | } 161 | 162 | var foundData = false; 163 | var foundFormat = false; 164 | var limit = 0; 165 | 166 | while (!foundFormat || !foundData) { 167 | 168 | var chunk = readChunk(app, handle, isStream); 169 | 170 | if (chunk.id == ID_FMT) { 171 | foundFormat = true; 172 | 173 | // 16 bytes size / at 174 | // short audioFormat; 2 / 0 175 | // short numChannels; 2 / 2 176 | // unsigned int sampleRate; 4 / 4 177 | // unsigned int byteRate; 4 / 8 178 | // short blockAlign; 2 / 12 179 | // short bitsPerSample; 2 / 14 180 | 181 | var format = chunk.data.toBytes(); 182 | var bitrate = format.getInt32(8); 183 | info.bitsPerSample = format.getUInt16(14); 184 | info.channels = format.getUInt16(2); 185 | info.rate = format.getInt32(4); 186 | format = null; 187 | } 188 | 189 | if (chunk.id == ID_DATA) { 190 | foundData = true; 191 | info.samples = chunk.data; 192 | info.length = chunk.dataLength; 193 | info.dataOffset = chunk.offset; 194 | } 195 | 196 | chunk.data = null; 197 | chunk = null; 198 | 199 | ++limit; 200 | 201 | if (limit >= 32) break; 202 | 203 | } 204 | 205 | return info; 206 | 207 | } 208 | 209 | public static function readChunk(app:Clay, handle:FileHandle, isStream:Bool):WavChunk { 210 | 211 | var headerSize = 8; 212 | var header = new Uint8Array(headerSize); 213 | 214 | app.io.fileRead(handle, header, headerSize, 1); 215 | 216 | var headerBytes = header.toBytes(); 217 | var chunkId = headerBytes.getString(0, 4); 218 | var chunkSize = headerBytes.getInt32(4); 219 | 220 | header = null; 221 | headerBytes = null; 222 | 223 | var data:Uint8Array = null; 224 | var pos = app.io.fileTell(handle); 225 | 226 | // We only read data/fmt chunks 227 | var isData = (chunkId == ID_DATA); 228 | var isFmt = (chunkId == ID_FMT); 229 | var shouldRead = isData || isFmt; 230 | 231 | // We don't need to read the sample data if streaming 232 | if (isData && isStream) { 233 | shouldRead = false; 234 | } 235 | 236 | if (shouldRead) { 237 | data = new Uint8Array(chunkSize); 238 | app.io.fileRead(handle, data, chunkSize, 1); 239 | } 240 | else { 241 | app.io.fileSeek(handle, pos + headerSize + chunkSize, SET); 242 | } 243 | 244 | return { 245 | id: chunkId, 246 | offset: pos, 247 | data: data, 248 | dataLength: chunkSize 249 | }; 250 | 251 | } 252 | 253 | } 254 | -------------------------------------------------------------------------------- /src/clay/buffers/ArrayBufferIO.hx: -------------------------------------------------------------------------------- 1 | package clay.buffers; 2 | 3 | // Code imported from snowkit/snow (https://github.com/snowkit/snow/tree/fe93eb1ecfc82131a6143be1b3e3e0a274f4cf65) 4 | // Credits go to its original author (@ruby0x1 on Github) 5 | 6 | #if cpp 7 | 8 | typedef UINT = Int; 9 | 10 | //:todo: ArrayBufferIO Big Endian 11 | 12 | class ArrayBufferIO { 13 | 14 | //8 15 | 16 | #if !clay_no_inline_buffers extern inline #end 17 | public static function getInt8( buffer:ArrayBuffer, byteOffset:Int ) : Int { 18 | 19 | return untyped __global__.__hxcpp_memory_get_byte(buffer, byteOffset); 20 | 21 | } 22 | 23 | #if !clay_no_inline_buffers extern inline #end 24 | public static function setInt8( buffer:ArrayBuffer, byteOffset:Int, value:Int ) : Void { 25 | 26 | untyped __global__.__hxcpp_memory_set_byte(buffer, byteOffset, value); 27 | 28 | } 29 | 30 | #if !clay_no_inline_buffers extern inline #end 31 | public static function getUint8( buffer:ArrayBuffer, byteOffset:Int ) : UINT { 32 | 33 | return untyped __global__.__hxcpp_memory_get_byte(buffer, byteOffset) & 0xff; 34 | 35 | } 36 | 37 | #if !clay_no_inline_buffers extern inline #end 38 | public static function setUint8Clamped( buffer:ArrayBuffer, byteOffset:Int, value:UINT ) : Void { 39 | 40 | setUint8( buffer, byteOffset, _clamp(value) ); 41 | 42 | } 43 | 44 | #if !clay_no_inline_buffers extern inline #end 45 | public static function setUint8( buffer:ArrayBuffer, byteOffset:Int, value:UINT ) : Void { 46 | 47 | untyped __global__.__hxcpp_memory_set_byte(buffer, byteOffset, value); 48 | 49 | } 50 | 51 | //16 52 | 53 | public static function getInt16( buffer:ArrayBuffer, byteOffset:Int ) : Int { 54 | 55 | untyped return __global__.__hxcpp_memory_get_i16(buffer, byteOffset); 56 | 57 | } 58 | 59 | public static function getInt16_BE( buffer:ArrayBuffer, byteOffset:Int ) : Int { 60 | 61 | untyped return __global__.__hxcpp_memory_get_i16(buffer, byteOffset); 62 | 63 | } 64 | 65 | public static function setInt16( buffer:ArrayBuffer, byteOffset:Int, value:Int ) : Void { 66 | 67 | untyped __global__.__hxcpp_memory_set_i16(buffer, byteOffset, value); 68 | 69 | } 70 | 71 | public static function setInt16_BE( buffer:ArrayBuffer, byteOffset:Int, value:Int ) : Void { 72 | 73 | untyped __global__.__hxcpp_memory_set_i16(buffer, byteOffset, value); 74 | 75 | } 76 | 77 | #if !clay_no_inline_buffers extern inline #end 78 | public static function getUint16( buffer:ArrayBuffer, byteOffset:Int ) : UINT { 79 | 80 | untyped return __global__.__hxcpp_memory_get_ui16(buffer, byteOffset) & 0xffff; 81 | 82 | } 83 | 84 | #if !clay_no_inline_buffers extern inline #end 85 | public static function getUint16_BE( buffer:ArrayBuffer, byteOffset:Int ) : UINT { 86 | 87 | untyped return __global__.__hxcpp_memory_get_ui16(buffer, byteOffset) & 0xffff; 88 | 89 | } 90 | 91 | #if !clay_no_inline_buffers extern inline #end 92 | public static function setUint16( buffer:ArrayBuffer, byteOffset:Int, value:UINT ) : Void { 93 | 94 | untyped __global__.__hxcpp_memory_set_ui16(buffer, byteOffset, value); 95 | 96 | } 97 | 98 | #if !clay_no_inline_buffers extern inline #end 99 | public static function setUint16_BE( buffer:ArrayBuffer, byteOffset:Int, value:UINT ) : Void { 100 | 101 | untyped __global__.__hxcpp_memory_set_ui16(buffer, byteOffset, value); 102 | 103 | } 104 | 105 | //32 106 | 107 | #if !clay_no_inline_buffers extern inline #end 108 | public static function getInt32( buffer:ArrayBuffer, byteOffset:Int ) : Int { 109 | 110 | untyped return __global__.__hxcpp_memory_get_i32(buffer, byteOffset); 111 | 112 | } 113 | 114 | #if !clay_no_inline_buffers extern inline #end 115 | public static function getInt32_BE( buffer:ArrayBuffer, byteOffset:Int ) : Int { 116 | 117 | untyped return __global__.__hxcpp_memory_get_i32(buffer, byteOffset); 118 | 119 | } 120 | 121 | #if !clay_no_inline_buffers extern inline #end 122 | public static function setInt32( buffer:ArrayBuffer, byteOffset:Int, value:Int ) : Void { 123 | 124 | untyped __global__.__hxcpp_memory_set_i32(buffer, byteOffset, value); 125 | 126 | } 127 | 128 | #if !clay_no_inline_buffers extern inline #end 129 | public static function setInt32_BE( buffer:ArrayBuffer, byteOffset:Int, value:Int ) : Void { 130 | 131 | untyped __global__.__hxcpp_memory_set_i32(buffer, byteOffset, value); 132 | 133 | } 134 | 135 | #if !clay_no_inline_buffers extern inline #end 136 | public static function getUint32( buffer:ArrayBuffer, byteOffset:Int ) : UINT { 137 | 138 | untyped return __global__.__hxcpp_memory_get_ui32(buffer, byteOffset); 139 | 140 | } 141 | 142 | #if !clay_no_inline_buffers extern inline #end 143 | public static function getUint32_BE( buffer:ArrayBuffer, byteOffset:Int ) : UINT { 144 | 145 | untyped return __global__.__hxcpp_memory_get_ui32(buffer, byteOffset); 146 | 147 | } 148 | 149 | #if !clay_no_inline_buffers extern inline #end 150 | public static function setUint32( buffer:ArrayBuffer, byteOffset:Int, value:UINT ) : Void { 151 | 152 | untyped __global__.__hxcpp_memory_set_ui32(buffer, byteOffset, value); 153 | 154 | } 155 | #if !clay_no_inline_buffers extern inline #end 156 | public static function setUint32_BE( buffer:ArrayBuffer, byteOffset:Int, value:UINT ) : Void { 157 | 158 | untyped __global__.__hxcpp_memory_set_ui32(buffer, byteOffset, value); 159 | 160 | } 161 | 162 | //Float 163 | 164 | #if !clay_no_inline_buffers extern inline #end 165 | public static function getFloat32( buffer:ArrayBuffer, byteOffset:Int ) : cpp.Float32 { 166 | 167 | untyped return __global__.__hxcpp_memory_get_float(buffer, byteOffset); 168 | 169 | } 170 | 171 | #if !clay_no_inline_buffers extern inline #end 172 | public static function getFloat32_BE( buffer:ArrayBuffer, byteOffset:Int ) : cpp.Float32 { 173 | 174 | untyped return __global__.__hxcpp_memory_get_float(buffer, byteOffset); 175 | 176 | } 177 | 178 | #if !clay_no_inline_buffers extern inline #end 179 | public static function setFloat32( buffer:ArrayBuffer, byteOffset:Int, value:cpp.Float32 ) : Void { 180 | 181 | untyped __global__.__hxcpp_memory_set_float(buffer, byteOffset, value); 182 | 183 | } 184 | 185 | #if !clay_no_inline_buffers extern inline #end 186 | public static function setFloat32_BE( buffer:ArrayBuffer, byteOffset:Int, value:cpp.Float32 ) : Void { 187 | 188 | untyped __global__.__hxcpp_memory_set_float(buffer, byteOffset, value); 189 | 190 | } 191 | 192 | #if !clay_no_inline_buffers extern inline #end 193 | public static function getFloat64( buffer:ArrayBuffer, byteOffset:Int ) : Float { 194 | 195 | untyped return __global__.__hxcpp_memory_get_double(buffer, byteOffset); 196 | 197 | } 198 | 199 | #if !clay_no_inline_buffers extern inline #end 200 | public static function getFloat64_BE( buffer:ArrayBuffer, byteOffset:Int ) : Float { 201 | 202 | untyped return __global__.__hxcpp_memory_get_double(buffer, byteOffset); 203 | 204 | } 205 | 206 | #if !clay_no_inline_buffers extern inline #end 207 | public static function setFloat64( buffer:ArrayBuffer, byteOffset:Int, value:Float ) : Void { 208 | 209 | untyped __global__.__hxcpp_memory_set_double(buffer, byteOffset, value); 210 | 211 | } 212 | 213 | #if !clay_no_inline_buffers extern inline #end 214 | public static function setFloat64_BE( buffer:ArrayBuffer, byteOffset:Int, value:Float ) : Void { 215 | 216 | untyped __global__.__hxcpp_memory_set_double(buffer, byteOffset, value); 217 | 218 | } 219 | 220 | //Internal 221 | 222 | #if !clay_no_inline_buffers extern inline #end 223 | //clamp a Int to a 0-255 Uint8 (for Uint8Clamped array) 224 | static function _clamp(_in:Float) : Int { 225 | 226 | var _out = Std.int(_in); 227 | _out = _out > 255 ? 255 : _out; 228 | return _out < 0 ? 0 : _out; 229 | 230 | } 231 | 232 | } 233 | 234 | #else 235 | 236 | #error "ArrayBufferIO is only implemented for the cpp target" 237 | 238 | #end //cpp -------------------------------------------------------------------------------- /src/clay/buffers/DataView.hx: -------------------------------------------------------------------------------- 1 | package clay.buffers; 2 | 3 | // Code imported from snowkit/snow (https://github.com/snowkit/snow/tree/fe93eb1ecfc82131a6143be1b3e3e0a274f4cf65) 4 | // Credits go to its original author (@ruby0x1 on Github) 5 | 6 | #if js 7 | 8 | @:forward 9 | abstract DataViewImplJS(js.lib.DataView) 10 | from js.lib.DataView 11 | to js.lib.DataView { 12 | 13 | public inline function new(buffer:ArrayBuffer, byteOffset:Null = null, byteLength:Null = null) { 14 | if(byteOffset != null && byteLength == null) this = new js.lib.DataView(buffer, byteOffset); 15 | else if(byteOffset != null && byteLength != null) this = new js.lib.DataView(buffer, byteOffset, byteLength); 16 | else this = new js.lib.DataView(buffer); 17 | } 18 | 19 | #if !clay_no_inline_buffers inline #end 20 | public function getInt8(byteOffset:Int) : Int { 21 | return this.getInt8(byteOffset); 22 | } 23 | 24 | #if !clay_no_inline_buffers inline #end 25 | public function getInt16(byteOffset:Int) : Int { 26 | return this.getInt16(byteOffset, true); 27 | } 28 | 29 | #if !clay_no_inline_buffers inline #end 30 | public function getInt32(byteOffset:Int) : Int { 31 | return this.getInt32(byteOffset, true); 32 | } 33 | 34 | #if !clay_no_inline_buffers inline #end 35 | public function getUint8(byteOffset:Int) : UInt { 36 | return this.getUint8(byteOffset); 37 | } 38 | 39 | #if !clay_no_inline_buffers inline #end 40 | public function getUint16(byteOffset:Int) : UInt { 41 | return this.getUint16(byteOffset, true); 42 | } 43 | 44 | #if !clay_no_inline_buffers inline #end 45 | public function getUint32(byteOffset:Int) : UInt { 46 | return this.getUint32(byteOffset, true); 47 | } 48 | 49 | #if !clay_no_inline_buffers inline #end 50 | public function getFloat32(byteOffset:Int) : Float { 51 | return this.getFloat32(byteOffset, true); 52 | } 53 | 54 | #if !clay_no_inline_buffers inline #end 55 | public function getFloat64(byteOffset:Int) : Float { 56 | return this.getFloat64(byteOffset, true); 57 | } 58 | 59 | 60 | 61 | 62 | #if !clay_no_inline_buffers inline #end 63 | public function setInt8(byteOffset:Int, value:Int) { 64 | this.setInt8(byteOffset, value); 65 | } 66 | 67 | #if !clay_no_inline_buffers inline #end 68 | public function setInt16(byteOffset:Int, value:Int) { 69 | this.setInt16(byteOffset, value, true); 70 | } 71 | 72 | #if !clay_no_inline_buffers inline #end 73 | public function setInt32(byteOffset:Int, value:Int) { 74 | this.setInt32(byteOffset, value, true); 75 | } 76 | 77 | #if !clay_no_inline_buffers inline #end 78 | public function setUint8(byteOffset:Int, value:UInt) { 79 | this.setUint8(byteOffset, value); 80 | } 81 | 82 | #if !clay_no_inline_buffers inline #end 83 | public function setUint16(byteOffset:Int, value:UInt) { 84 | this.setUint16(byteOffset, value, true); 85 | } 86 | 87 | #if !clay_no_inline_buffers inline #end 88 | public function setUint32(byteOffset:Int, value:UInt) { 89 | this.setUint32(byteOffset, value, true); 90 | } 91 | 92 | #if !clay_no_inline_buffers inline #end 93 | public function setFloat32(byteOffset:Int, value:Float) { 94 | this.setFloat32(byteOffset, value, true); 95 | } 96 | 97 | #if !clay_no_inline_buffers inline #end 98 | public function setFloat64(byteOffset:Int, value:Float) { 99 | this.setFloat64(byteOffset, value, true); 100 | } 101 | 102 | } 103 | 104 | typedef DataView = DataViewImplJS; 105 | 106 | #else 107 | 108 | import clay.buffers.ArrayBuffer; 109 | 110 | class DataViewImpl { 111 | 112 | public var buffer:ArrayBuffer; 113 | public var byteLength:Int; 114 | public var byteOffset:Int; 115 | 116 | #if !clay_no_inline_buffers inline #end 117 | public function new(buffer:ArrayBuffer, byteOffset:Int = 0, byteLength:Null = null) { 118 | 119 | if(byteOffset < 0) throw TAError.RangeError('DataView: byteOffset can\'t be negative'); 120 | 121 | var bufferByteLength = buffer.length; 122 | var viewByteLength = bufferByteLength - byteOffset; 123 | 124 | if(byteOffset > bufferByteLength) throw TAError.RangeError('DataView: byteOffset is past the buffer byte length'); 125 | 126 | if(byteLength != null) { 127 | 128 | if(byteLength < 0) throw TAError.RangeError('DataView: specified byteLength must be positive'); 129 | 130 | viewByteLength = byteLength; 131 | 132 | if(byteOffset + viewByteLength > bufferByteLength) throw TAError.RangeError('DataView: specified range would exceed the given buffer'); 133 | 134 | } 135 | 136 | this.buffer = buffer; 137 | this.byteLength = viewByteLength; 138 | this.byteOffset = byteOffset; 139 | 140 | } 141 | 142 | #if !clay_no_inline_buffers inline #end 143 | public function getInt8(byteOffset:Int) : Int { 144 | return ArrayBufferIO.getInt8(buffer, byteOffset); 145 | } 146 | 147 | #if !clay_no_inline_buffers inline #end 148 | public function getInt16(byteOffset:Int) : Int { 149 | return ArrayBufferIO.getInt16(buffer, byteOffset); 150 | } 151 | 152 | #if !clay_no_inline_buffers inline #end 153 | public function getInt32(byteOffset:Int) : Int { 154 | return ArrayBufferIO.getInt32(buffer, byteOffset); 155 | } 156 | 157 | #if !clay_no_inline_buffers inline #end 158 | public function getUint8(byteOffset:Int) : UInt { 159 | return ArrayBufferIO.getUint8(buffer, byteOffset); 160 | } 161 | 162 | #if !clay_no_inline_buffers inline #end 163 | public function getUint16(byteOffset:Int) : UInt { 164 | return ArrayBufferIO.getUint16(buffer, byteOffset); 165 | } 166 | 167 | #if !clay_no_inline_buffers inline #end 168 | public function getUint32(byteOffset:Int) : UInt { 169 | return ArrayBufferIO.getUint32(buffer, byteOffset); 170 | } 171 | 172 | #if !clay_no_inline_buffers inline #end 173 | public function getFloat32(byteOffset:Int) : Float { 174 | return ArrayBufferIO.getFloat32(buffer, byteOffset); 175 | } 176 | 177 | #if !clay_no_inline_buffers inline #end 178 | public function getFloat64(byteOffset:Int) : Float { 179 | return ArrayBufferIO.getFloat64(buffer, byteOffset); 180 | } 181 | 182 | 183 | 184 | #if !clay_no_inline_buffers inline #end 185 | public function setInt8(byteOffset:Int, value:Int) { 186 | ArrayBufferIO.setInt8(buffer, byteOffset, value); 187 | } 188 | 189 | #if !clay_no_inline_buffers inline #end 190 | public function setInt16(byteOffset:Int, value:Int) { 191 | ArrayBufferIO.setInt16(buffer, byteOffset, value); 192 | } 193 | 194 | #if !clay_no_inline_buffers inline #end 195 | public function setInt32(byteOffset:Int, value:Int) { 196 | ArrayBufferIO.setInt32(buffer, byteOffset, value); 197 | } 198 | 199 | #if !clay_no_inline_buffers inline #end 200 | public function setUint8(byteOffset:Int, value:UInt) { 201 | ArrayBufferIO.setUint8(buffer, byteOffset, value); 202 | } 203 | 204 | #if !clay_no_inline_buffers inline #end 205 | public function setUint16(byteOffset:Int, value:UInt) { 206 | ArrayBufferIO.setUint16(buffer, byteOffset, value); 207 | } 208 | 209 | #if !clay_no_inline_buffers inline #end 210 | public function setUint32(byteOffset:Int, value:UInt) { 211 | ArrayBufferIO.setUint32(buffer, byteOffset, value); 212 | } 213 | 214 | #if !clay_no_inline_buffers inline #end 215 | public function setFloat32(byteOffset:Int, value:Float) { 216 | ArrayBufferIO.setFloat32(buffer, byteOffset, value); 217 | } 218 | 219 | #if !clay_no_inline_buffers inline #end 220 | public function setFloat64(byteOffset:Int, value:Float) { 221 | ArrayBufferIO.setFloat64(buffer, byteOffset, value); 222 | } 223 | 224 | 225 | } 226 | 227 | typedef DataView = DataViewImpl; 228 | 229 | #end 230 | -------------------------------------------------------------------------------- /src/clay/graphics/Texture.hx: -------------------------------------------------------------------------------- 1 | package clay.graphics; 2 | 3 | import clay.buffers.Uint8Array; 4 | import clay.Types; 5 | 6 | /** 7 | * A high level texture object to make it easier to manage textures 8 | */ 9 | class Texture extends Resource { 10 | 11 | static var _nextIndex:Int = 1; 12 | 13 | public var index(default, null):Int; 14 | 15 | public var textureId(default, null):TextureId; 16 | 17 | /** 18 | * Is `true` if image has been processed to be stored as premultiplied alpha in GPU memory. 19 | */ 20 | public var premultiplyAlpha(default, null):Bool; 21 | 22 | /** 23 | * If `true`, the pixels buffer should store compressed image data that the GPU understands 24 | */ 25 | public var compressed:Bool = false; 26 | 27 | /** 28 | * The GPU texture format (RGBA, RGB...) 29 | */ 30 | public var format:TextureFormat = RGBA; 31 | 32 | /** 33 | * The GPU texture type (TEXTURE_2D) 34 | */ 35 | public var type:TextureType = TEXTURE_2D; 36 | 37 | /** 38 | * The GPU data type (UNSIGNED_BYTE) 39 | */ 40 | public var dataType:TextureDataType = UNSIGNED_BYTE; 41 | 42 | /** 43 | * When creating a texture manually, the width of this texture. 44 | */ 45 | public var width:Int = -1; 46 | 47 | /** 48 | * When creating a texture manually, the height of this texture. 49 | */ 50 | public var height:Int = -1; 51 | 52 | /** 53 | * The width of the actual texture, needed when the texture may be padded to POT sizes 54 | */ 55 | public var widthActual:Int = -1; 56 | 57 | /** 58 | * The height of the actual texture, needed when the texture may be padded to POT sizes 59 | */ 60 | public var heightActual:Int = -1; 61 | 62 | /** 63 | * When creating a texture manually, the pixels for this texture. 64 | * Properties `width` and `height` must be defined when providing pixels. 65 | */ 66 | public var pixels:Null = null; 67 | 68 | /** 69 | * Set the minification filter type (default LINEAR) 70 | */ 71 | public var filterMin(default, set):TextureFilter = LINEAR; 72 | function set_filterMin(filterMin:TextureFilter):TextureFilter { 73 | if (textureId != Clay.app.graphics.noTexture) { 74 | bind(); 75 | Clay.app.graphics.setTexture2dMinFilter(filterMin); 76 | } 77 | return this.filterMin = filterMin; 78 | } 79 | 80 | /** 81 | * Set the magnification filter type (default LINEAR) 82 | */ 83 | public var filterMag(default, set):TextureFilter = LINEAR; 84 | function set_filterMag(filterMag:TextureFilter):TextureFilter { 85 | if (textureId != Clay.app.graphics.noTexture) { 86 | bind(); 87 | Clay.app.graphics.setTexture2dMagFilter(filterMag); 88 | } 89 | return this.filterMag = filterMag; 90 | } 91 | 92 | /** 93 | * Set the s (horizontal) clamp type (default CLAMP_TO_EDGE) 94 | */ 95 | public var wrapS(default, set):TextureWrap = CLAMP_TO_EDGE; 96 | function set_wrapS(wrapS:TextureWrap):TextureWrap { 97 | if (textureId != Clay.app.graphics.noTexture) { 98 | bind(); 99 | Clay.app.graphics.setTexture2dWrapS(wrapS); 100 | } 101 | return this.wrapS = wrapS; 102 | } 103 | 104 | /** 105 | * Set the t (vertical) clamp type (default CLAMP_TO_EDGE) 106 | */ 107 | public var wrapT(default, set):TextureWrap = CLAMP_TO_EDGE; 108 | function set_wrapT(wrapT:TextureWrap):TextureWrap { 109 | if (textureId != Clay.app.graphics.noTexture) { 110 | bind(); 111 | Clay.app.graphics.setTexture2dWrapT(wrapT); 112 | } 113 | return this.wrapT = wrapT; 114 | } 115 | 116 | public function new() { 117 | 118 | this.index = _nextIndex++; 119 | this.textureId = Clay.app.graphics.noTexture; 120 | 121 | } 122 | 123 | public static function fromImage(image:Image, premultiplyAlpha:Bool = false):Texture { 124 | 125 | var texture = new Texture(); 126 | 127 | texture.premultiplyAlpha = premultiplyAlpha; 128 | 129 | // Preprocess pixels premultiplied alpha if needed (platform dependant) 130 | if (premultiplyAlpha && Clay.app.graphics.needsPreprocessedPremultipliedAlpha()) { 131 | image.premultiplyAlpha(); 132 | } 133 | 134 | // This could be improved, if needed 135 | if (image.bitsPerPixel != 4) 136 | throw 'Image must have 4 bits per pixels (RGBA format)'; 137 | 138 | texture.width = image.width; 139 | texture.height = image.height; 140 | texture.widthActual = image.widthActual; 141 | texture.heightActual = image.heightActual; 142 | texture.pixels = image.pixels; 143 | 144 | return texture; 145 | 146 | } 147 | 148 | /** 149 | * Initialize this texture. Must be called before using the actual texture. 150 | * When calling init(), properties should be defined accordingly. 151 | */ 152 | public function init() { 153 | 154 | textureId = Clay.app.graphics.createTextureId(); 155 | 156 | if (width > 0 && widthActual <= 0) { 157 | widthActual = width; 158 | } 159 | 160 | if (height > 0 && heightActual <= 0) { 161 | heightActual = height; 162 | } 163 | 164 | bind(); 165 | Clay.app.graphics.setTexture2dMinFilter(filterMin); 166 | Clay.app.graphics.setTexture2dMagFilter(filterMag); 167 | Clay.app.graphics.setTexture2dWrapT(wrapT); 168 | Clay.app.graphics.setTexture2dWrapS(wrapS); 169 | 170 | if (pixels != null) { 171 | if (width <= 0 || height <= 0) { 172 | throw 'Provided texture pixels with invalid size (width=$width height=$height)'; 173 | } 174 | 175 | submit(pixels); 176 | } 177 | 178 | } 179 | 180 | public function destroy() { 181 | 182 | if (textureId != Clay.app.graphics.noTexture) { 183 | Clay.app.graphics.deleteTexture(textureId); 184 | textureId = Clay.app.graphics.noTexture; 185 | } 186 | 187 | } 188 | 189 | /** Bind this texture to its active texture slot, 190 | and it's texture id to the texture type. Calling this 191 | repeatedly is fine, as the state is tracked by `Graphics`. */ 192 | public function bind(slot:Int = 0) { 193 | 194 | if (slot != -1) 195 | Clay.app.graphics.setActiveTexture(slot); 196 | 197 | switch type { 198 | case TEXTURE_2D: 199 | Clay.app.graphics.bindTexture2d(textureId); 200 | } 201 | 202 | } 203 | 204 | /** 205 | * Submit a pixels array to the texture id. Must match the type and format accordingly. 206 | * @param pixels 207 | */ 208 | public function submit(?pixels:Uint8Array) { 209 | 210 | var max = Clay.app.graphics.maxTextureSize(); 211 | 212 | if (pixels == null) { 213 | pixels = this.pixels; 214 | } 215 | 216 | if (pixels == null) 217 | throw 'Cannot submit texture pixels: pixels is null'; 218 | 219 | if (widthActual > max) 220 | throw 'Texture actual width bigger than maximum hardware size (width=$widthActual max=$max)'; 221 | if (heightActual > max) 222 | throw 'Texture actual height bigger than maximum hardware size (height=$heightActual max=$max)'; 223 | 224 | bind(); 225 | 226 | switch type { 227 | case TEXTURE_2D: 228 | if (compressed) { 229 | Clay.app.graphics.submitCompressedTexture2dPixels(0, format, widthActual, heightActual, pixels, premultiplyAlpha); 230 | } 231 | else { 232 | Clay.app.graphics.submitTexture2dPixels(0, format, widthActual, heightActual, dataType, pixels, premultiplyAlpha); 233 | } 234 | } 235 | 236 | } 237 | 238 | /** Fetch the pixels from the texture id, storing them in the provided array buffer view. 239 | Returns image pixels in RGBA format, as unsigned byte (0-255) values only. 240 | This means that the view must be `w * h * 4` in length, minimum. 241 | By default, x and y are 0, 0, and the texture `width` and `height` 242 | are used (not widthActual / heightActual) */ 243 | public function fetch(into:Uint8Array, x:Int = 0, y:Int = 0, w:Int = -1, h:Int = -1):Uint8Array { 244 | 245 | if (w <= 0) 246 | w = width; 247 | if (h <= 0) 248 | h = height; 249 | 250 | bind(); 251 | 252 | switch type { 253 | case TEXTURE_2D: 254 | Clay.app.graphics.fetchTexture2dPixels(into, x, y, w, h); 255 | } 256 | 257 | return into; 258 | 259 | } 260 | 261 | } 262 | -------------------------------------------------------------------------------- /src/clay/buffers/DataViewBE.hx: -------------------------------------------------------------------------------- 1 | package clay.buffers; 2 | 3 | // Code imported from snowkit/snow (https://github.com/snowkit/snow/tree/fe93eb1ecfc82131a6143be1b3e3e0a274f4cf65) 4 | // Credits go to its original author (@ruby0x1 on Github) 5 | 6 | import clay.buffers.DataView; 7 | 8 | #if js 9 | 10 | @:forward 11 | abstract DataViewBEImplJS(js.lib.DataView) 12 | from js.lib.DataView 13 | to js.lib.DataView { 14 | 15 | public inline function new( buffer:ArrayBuffer, byteOffset:Null = null, byteLength:Null = null ) { 16 | if(byteOffset != null && byteLength == null) this = new js.lib.DataView( buffer, byteOffset ); 17 | else if(byteOffset != null && byteLength != null) this = new js.lib.DataView( buffer, byteOffset, byteLength); 18 | else this = new js.lib.DataView( buffer ); 19 | } 20 | 21 | #if !clay_no_inline_buffers inline #end 22 | public function getInt8(byteOffset:Int) : Int { 23 | return this.getInt8( byteOffset); 24 | } 25 | 26 | #if !clay_no_inline_buffers inline #end 27 | public function getInt16(byteOffset:Int) : Int { 28 | return this.getInt16( byteOffset, false); 29 | } 30 | 31 | #if !clay_no_inline_buffers inline #end 32 | public function getInt32(byteOffset:Int) : Int { 33 | return this.getInt32( byteOffset, false); 34 | } 35 | 36 | #if !clay_no_inline_buffers inline #end 37 | public function getUint8(byteOffset:Int) : UInt { 38 | return this.getUint8( byteOffset); 39 | } 40 | 41 | #if !clay_no_inline_buffers inline #end 42 | public function getUint16(byteOffset:Int) : UInt { 43 | return this.getUint16( byteOffset, false); 44 | } 45 | 46 | #if !clay_no_inline_buffers inline #end 47 | public function getUint32(byteOffset:Int) : UInt { 48 | return this.getUint32( byteOffset, false); 49 | } 50 | 51 | #if !clay_no_inline_buffers inline #end 52 | public function getFloat32(byteOffset:Int) : Float { 53 | return this.getFloat32( byteOffset, false); 54 | } 55 | 56 | #if !clay_no_inline_buffers inline #end 57 | public function getFloat64(byteOffset:Int) : Float { 58 | return this.getFloat64( byteOffset, false); 59 | } 60 | 61 | 62 | 63 | 64 | #if !clay_no_inline_buffers inline #end 65 | public function setInt8( byteOffset:Int, value:Int ) { 66 | this.setInt8( byteOffset, value); 67 | } 68 | 69 | #if !clay_no_inline_buffers inline #end 70 | public function setInt16( byteOffset:Int, value:Int) { 71 | this.setInt16( byteOffset, value, false); 72 | } 73 | 74 | #if !clay_no_inline_buffers inline #end 75 | public function setInt32( byteOffset:Int, value:Int) { 76 | this.setInt32( byteOffset, value, false); 77 | } 78 | 79 | #if !clay_no_inline_buffers inline #end 80 | public function setUint8( byteOffset:Int, value:UInt ) { 81 | this.setUint8( byteOffset, value); 82 | } 83 | 84 | #if !clay_no_inline_buffers inline #end 85 | public function setUint16( byteOffset:Int, value:UInt) { 86 | this.setUint16( byteOffset, value, false); 87 | } 88 | 89 | #if !clay_no_inline_buffers inline #end 90 | public function setUint32( byteOffset:Int, value:UInt) { 91 | this.setUint32( byteOffset, value, false); 92 | } 93 | 94 | #if !clay_no_inline_buffers inline #end 95 | public function setFloat32( byteOffset:Int, value:Float) { 96 | this.setFloat32( byteOffset, value, false); 97 | } 98 | 99 | #if !clay_no_inline_buffers inline #end 100 | public function setFloat64( byteOffset:Int, value:Float) { 101 | this.setFloat64( byteOffset, value, false); 102 | } 103 | 104 | } 105 | 106 | typedef DataViewBE = DataViewBEImplJS; 107 | 108 | 109 | #else 110 | 111 | /** A big endian Data view, 112 | where all get/set calls will read/write in BE, 113 | overriding the behavior of the underlying dataview. 114 | Note that this class doesn't work correctly (yet) on CPP! */ 115 | class DataViewBEImpl { 116 | 117 | public var buffer:ArrayBuffer; 118 | public var byteLength:Int; 119 | public var byteOffset:Int; 120 | 121 | #if !clay_no_inline_buffers inline #end 122 | public function new(buffer:ArrayBuffer, byteOffset:Int = 0, byteLength:Null = null) { 123 | 124 | if(byteOffset < 0) throw TAError.RangeError('DataView: byteOffset can\'t be negative'); 125 | 126 | var bufferByteLength = buffer.length; 127 | var viewByteLength = bufferByteLength - byteOffset; 128 | 129 | if(byteOffset > bufferByteLength) throw TAError.RangeError('DataView: byteOffset is past the buffer byte length'); 130 | 131 | if(byteLength != null) { 132 | 133 | if(byteLength < 0) throw TAError.RangeError('DataView: specified byteLength must be positive'); 134 | 135 | viewByteLength = byteLength; 136 | 137 | if(byteOffset + viewByteLength > bufferByteLength) throw TAError.RangeError('DataView: specified range would exceed the given buffer'); 138 | 139 | } 140 | 141 | this.buffer = buffer; 142 | this.byteLength = viewByteLength; 143 | this.byteOffset = byteOffset; 144 | 145 | } 146 | 147 | #if !clay_no_inline_buffers inline #end 148 | public function getInt8(byteOffset:Int) : Int { 149 | return ArrayBufferIO.getInt8(buffer, byteOffset); 150 | } 151 | 152 | #if !clay_no_inline_buffers inline #end 153 | public function getInt16(byteOffset:Int) : Int { 154 | return ArrayBufferIO.getInt16_BE(buffer, byteOffset); 155 | } 156 | 157 | #if !clay_no_inline_buffers inline #end 158 | public function getInt32(byteOffset:Int) : Int { 159 | return ArrayBufferIO.getInt32_BE(buffer, byteOffset); 160 | } 161 | 162 | #if !clay_no_inline_buffers inline #end 163 | public function getUint8(byteOffset:Int) : UInt { 164 | return ArrayBufferIO.getUint8(buffer, byteOffset); 165 | } 166 | 167 | #if !clay_no_inline_buffers inline #end 168 | public function getUint16(byteOffset:Int) : UInt { 169 | return ArrayBufferIO.getUint16_BE(buffer, byteOffset); 170 | } 171 | 172 | #if !clay_no_inline_buffers inline #end 173 | public function getUint32(byteOffset:Int) : UInt { 174 | return ArrayBufferIO.getUint32_BE(buffer, byteOffset); 175 | } 176 | 177 | #if !clay_no_inline_buffers inline #end 178 | public function getFloat32(byteOffset:Int) : Float { 179 | return ArrayBufferIO.getFloat32_BE(buffer, byteOffset); 180 | } 181 | 182 | #if !clay_no_inline_buffers inline #end 183 | public function getFloat64(byteOffset:Int) : Float { 184 | return ArrayBufferIO.getFloat64_BE(buffer, byteOffset); 185 | } 186 | 187 | #if !clay_no_inline_buffers inline #end 188 | public function setInt8( byteOffset:Int, value:Int ) { 189 | ArrayBufferIO.setInt8(buffer, byteOffset, value); 190 | } 191 | 192 | #if !clay_no_inline_buffers inline #end 193 | public function setInt16( byteOffset:Int, value:Int) { 194 | ArrayBufferIO.setInt16_BE(buffer, byteOffset, value); 195 | } 196 | 197 | #if !clay_no_inline_buffers inline #end 198 | public function setInt32( byteOffset:Int, value:Int) { 199 | ArrayBufferIO.setInt32_BE(buffer, byteOffset, value); 200 | } 201 | 202 | #if !clay_no_inline_buffers inline #end 203 | public function setUint8( byteOffset:Int, value:UInt ) { 204 | ArrayBufferIO.setUint8(buffer, byteOffset, value); 205 | } 206 | 207 | #if !clay_no_inline_buffers inline #end 208 | public function setUint16( byteOffset:Int, value:UInt) { 209 | ArrayBufferIO.setUint16_BE(buffer, byteOffset, value); 210 | } 211 | 212 | #if !clay_no_inline_buffers inline #end 213 | public function setUint32( byteOffset:Int, value:UInt) { 214 | ArrayBufferIO.setUint32_BE(buffer, byteOffset, value); 215 | } 216 | 217 | #if !clay_no_inline_buffers inline #end 218 | public function setFloat32( byteOffset:Int, value:Float) { 219 | ArrayBufferIO.setFloat32_BE(buffer, byteOffset, value); 220 | } 221 | 222 | #if !clay_no_inline_buffers inline #end 223 | public function setFloat64( byteOffset:Int, value:Float) { 224 | ArrayBufferIO.setFloat64_BE(buffer, byteOffset, value); 225 | } 226 | 227 | } 228 | 229 | typedef DataViewBE = DataViewBEImpl; 230 | 231 | #end 232 | -------------------------------------------------------------------------------- /src-opengl/clay/Config.hx: -------------------------------------------------------------------------------- 1 | package clay; 2 | 3 | import clay.graphics.Color; 4 | 5 | #if clay_web 6 | typedef RuntimeConfig = clay.web.WebConfig; 7 | #elseif clay_sdl 8 | typedef RuntimeConfig = clay.sdl.SDLConfig; 9 | #end 10 | 11 | /** Config specific to the rendering context that would be used when creating windows */ 12 | @:structInit 13 | @:publicFields 14 | class RenderConfig { 15 | 16 | /** Request the number of depth bits for the rendering context. 17 | A value of 0 will not request a depth buffer. default: 0 */ 18 | var depth:Int = 0; 19 | 20 | /** Request the number of stencil bits for the rendering context. 21 | A value of 0 will not request a stencil buffer. default: 0 */ 22 | var stencil:Int = 0; 23 | 24 | /** A value of `0`, `2`, `4`, `8` or other valid system value. 25 | On WebGL contexts this value is true or false, bigger than 0 being true. 26 | On native contexts this value sets the MSAA typically. 27 | default webgl: 1 (enabled) 28 | default: 0 */ 29 | var antialiasing:Int = 0; 30 | 31 | /** Request a specific number of red bits for the rendering context. 32 | Unless you need to change this, don't. default: 8 */ 33 | var redBits:Int = 8; 34 | /** Request a specific number of green bits for the rendering context. 35 | Unless you need to change this, don't. default: 8 */ 36 | var greenBits:Int = 8; 37 | /** Request a specific number of blue bits for the rendering context. 38 | Unless you need to change this, don't. default: 8 */ 39 | var blueBits:Int = 8; 40 | /** Request a specific number of alpha bits for the rendering context. 41 | Unless you need to change this, don't. default: 8 */ 42 | var alphaBits:Int = 8; 43 | 44 | /** A color value that when creating the window, the window backbuffer will be cleared to. 45 | A framework above clay can also use this for default clear color if desired. 46 | The values are specified as 0..1. default: black, 0,0,0,1 */ 47 | var defaultClear:Color = { r: 0, g: 0, b: 0, a: 1 }; 48 | 49 | /** If applicable on the current platform, enables vsync */ 50 | var vsync:Bool = #if clay_no_vsync false #else true #end; 51 | 52 | #if clay_web 53 | 54 | /** WebGL render context specific settings */ 55 | var webgl:RenderConfigWebGL = null; 56 | 57 | #elseif clay_sdl 58 | 59 | /** OpenGL render context specific settings */ 60 | var opengl:RenderConfigOpenGL = null; 61 | 62 | #end 63 | 64 | } 65 | 66 | #if clay_web 67 | 68 | /** Config specific to a WebGL rendering context. 69 | See: https://www.khronos.org/registry/webgl/specs/latest/1.0/#WEBGLCONTEXTATTRIBUTES */ 70 | @:structInit 71 | @:publicFields 72 | class RenderConfigWebGL { 73 | 74 | /** The WebGL version to request. default: 1 */ 75 | var version:Int = 1; 76 | 77 | /** If the value is true, the drawing buffer has an alpha channel for the 78 | purposes of performing OpenGL destination alpha operations and 79 | compositing with the page. If the value is false, no alpha buffer is available. 80 | clay default: false 81 | webgl default: true */ 82 | var alpha:Bool = false; 83 | 84 | /** If the value is true, the drawing buffer has a depth buffer of at least 16 bits. 85 | If the value is false, no depth buffer is available. 86 | clay default: uses render config depth flag 87 | webgl default: true */ 88 | var depth:Bool = true; 89 | 90 | /** If the value is true, the drawing buffer has a stencil buffer of at least 8 bits. 91 | If the value is false, no stencil buffer is available. 92 | clay default: uses render config stencil flag 93 | webgl default: false */ 94 | var stencil:Bool = false; 95 | 96 | /** If the value is true and the implementation supports antialiasing the drawing buffer 97 | will perform antialiasing using its choice of technique (multisample/supersample) and quality. 98 | If the value is false or the implementation does not support 99 | antialiasing, no antialiasing is performed 100 | clay default: uses render config antialias flag 101 | webgl default: true */ 102 | var antialias:Bool = true; 103 | 104 | /** If the value is true the page compositor will assume the drawing buffer contains colors with premultiplied alpha. 105 | If the value is false the page compositor will assume that colors in the drawing buffer are not premultiplied. 106 | This flag is ignored if the alpha flag is false. 107 | clay default: false 108 | webgl default: true */ 109 | var premultipliedAlpha:Bool = false; 110 | 111 | /** If false, once the drawing buffer is presented as described in theDrawing Buffer section, 112 | the contents of the drawing buffer are cleared to their default values. All elements of the 113 | drawing buffer (color, depth and stencil) are cleared. If the value is true the buffers will 114 | not be cleared and will preserve their values until cleared or overwritten by the author. 115 | On some hardware setting the preserveDrawingBuffer flag to true can have significant performance implications. 116 | clay default: uses webgl default 117 | webgl default: false */ 118 | var preserveDrawingBuffer:Bool = false; 119 | 120 | /** Provides a hint to the implementation suggesting that, if possible, it creates a context 121 | that optimizes for power consumption over performance. For example, on hardware that has more 122 | than one GPU, it may be the case that one of them is less powerful but also uses less power. 123 | An implementation may choose to, and may have to, ignore this hint. 124 | clay default: uses webgl default 125 | webgl default: false */ 126 | var preferLowPowerToHighPerformance:Bool = false; 127 | 128 | /** If the value is true, context creation will fail if the implementation determines that the 129 | performance of the created WebGL context would be dramatically lower than that of a native 130 | application making equivalent OpenGL calls. 131 | clay default: uses webgl default 132 | webgl default: false */ 133 | var failIfMajorPerformanceCaveat:Bool = false; 134 | 135 | } 136 | 137 | #elseif clay_sdl 138 | 139 | /** A type of OpenGL context profile to request. see RenderConfigOpenGL for info */ 140 | enum abstract OpenGLProfile(Int) 141 | from Int to Int { 142 | 143 | var COMPATIBILITY = 0; 144 | 145 | var CORE = 1; 146 | 147 | var GLES = 2; 148 | 149 | inline function toString() { 150 | return switch(this) { 151 | case COMPATIBILITY: 'COMPATIBILITY'; 152 | case CORE: 'CORE'; 153 | case GLES: 'GLES'; 154 | case _: '$this'; 155 | } 156 | } 157 | 158 | } 159 | 160 | /** Config specific to an OpenGL rendering context. 161 | Note that these are hints to the system, 162 | you must always check the values after initializing 163 | for what you actually received. The OS/driver decides. */ 164 | @:structInit 165 | @:publicFields 166 | class RenderConfigOpenGL { 167 | 168 | /** The major OpenGL version to request */ 169 | var major:Int = 2; 170 | 171 | /** The minor OpenGL version to request */ 172 | var minor:Int = 0; 173 | 174 | /** The OpenGL context profile to request */ 175 | var profile:OpenGLProfile = OpenGLProfile.GLES; 176 | 177 | } 178 | 179 | #end 180 | 181 | /** Window configuration information for creating windows */ 182 | @:structInit 183 | @:publicFields 184 | class WindowConfig { 185 | 186 | /** create in fullscreen, default: false, `mobile` true */ 187 | var fullscreen:Bool = false; 188 | 189 | /** If false, the users native window/desktop resolution will be used instead of the specified window size. default: false 190 | On native, changing the users video mode is less than ideal, so trueFullscreen is commonly discouraged. */ 191 | var trueFullscreen:Bool = false; 192 | 193 | /** allow the window to be resized, default: true */ 194 | var resizable:Bool = true; 195 | 196 | /** create as a borderless window, default: false */ 197 | var borderless:Bool = false; 198 | 199 | /** window x at creation. Leave this alone to use the OS default. */ 200 | var x:Int = 0; 201 | 202 | /** window y at creation. Leave this alone to use the OS default. */ 203 | var y:Int = 0; 204 | 205 | /** window width at creation, default: 960 */ 206 | var width:Int = 960; 207 | 208 | /** window height at creation, default: 640 */ 209 | var height:Int = 640; 210 | 211 | /** window title, default: 'clay app' */ 212 | var title:String = null; 213 | 214 | /** disables input arriving at/from this window. default: false */ 215 | var noInput:Bool = false; 216 | 217 | /** Time in seconds to sleep when in the background. 218 | Setting this to zero disables the behavior. 219 | This has no effect on the web target, 220 | as there is no concept of sleep there (and browsers usually throttle background tabs). 221 | Higher sleep times (i.e 1/10 or 1/30) use less cpu. default: 1/15 */ 222 | var backgroundSleep:Float = 1.0 / 15; 223 | 224 | } 225 | 226 | @:structInit 227 | @:publicFields 228 | class Config { 229 | 230 | /** 231 | * The window config for the default window. default: see `WindowConfig` docs 232 | */ 233 | var window:WindowConfig = null; 234 | 235 | /** 236 | * The render config that specifies rendering and context backend specifics. 237 | */ 238 | var render:RenderConfig = null; 239 | 240 | /** 241 | * The runtime specific config 242 | */ 243 | var runtime:RuntimeConfig = null; 244 | 245 | /** 246 | * If this is non zero, updates will be forced to this rate 247 | */ 248 | var updateRate:Float = 0; 249 | 250 | } 251 | --------------------------------------------------------------------------------