├── .clang_complete ├── .editorconfig ├── .gitignore ├── .gitmodules ├── LICENSE.md ├── README.md ├── build.bat ├── run.bat ├── scripts └── genie.lua ├── test ├── assets │ ├── models │ │ └── chair.iqm │ ├── shaders │ │ ├── bgfx_shader.sh │ │ ├── bin │ │ │ ├── test.fs.bin │ │ │ └── test.vs.bin │ │ ├── test.fs.sc │ │ ├── test.vs.sc │ │ └── varying.def.sc │ └── textures │ │ └── grid.png ├── camera.lua ├── conf.lua ├── iqm.lua └── main.lua └── wrap_bgfx.cpp /.clang_complete: -------------------------------------------------------------------------------- 1 | -Iextern/bgfx/include 2 | -Iextern/bx/include 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{cpp,lua}] 4 | indent_style = tab 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.make 2 | *.vcxproj 3 | *.filters 4 | *.sln 5 | *.sdf 6 | *.opensdf 7 | /bin 8 | /obj 9 | /extern/LibOVR 10 | /extern/luajit 11 | /extern/sdl 12 | /scripts/include 13 | /scripts/Makefile 14 | /test/love 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "extern/bgfx"] 2 | path = extern/bgfx 3 | url = https://github.com/bkaradzic/bgfx.git 4 | [submodule "extern/bx"] 5 | path = extern/bx 6 | url = https://github.com/bkaradzic/bx.git 7 | [submodule "test/cpml"] 8 | path = test/cpml 9 | url = https://github.com/excessive/cpml.git 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | (c) 2016 Colby Klein 3 | 4 | (c) 2016 Landon Manning 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lua-bgfx 2 | BGFX bindings for Lua. 3 | 4 | [API Status](https://docs.google.com/spreadsheets/d/1GLlaigjpsc9rYyA8neXsRHI6jQZ_JfiN4Of8BJnct2s/edit?usp=sharing) 5 | 6 | # Compiling 7 | ## Windows 8 | 9 | ```bash 10 | # grab lua-bgfx and all the submodules... 11 | git clone https://github.com/excessive/lua-bgfx.git --recursive 12 | cd lua-bgfx 13 | 14 | # since SDL2 isn't likely to be available system-wide, so... 15 | git clone https://github.com/spurious/SDL-mirror.git extern/sdl 16 | 17 | # build 18 | cmd /Cbuild.bat 19 | ``` 20 | 21 | **Note**: *The build script assumes vs2013 - 12/15 might work, but you'll need to edit the `%GENIE% vs2013` line.* 22 | 23 | For the exceedingly lazy, you can run the test program with `run.bat` 24 | 25 | ## Linux 26 | First, install `SDL2-devel` and `luajit-devel` (on Fedora, you can just `dnf install SDL2-devel luajit-devel` 27 | 28 | ```bash 29 | # grab lua-bgfx and the submodules... 30 | git clone https://github.com/excessive/lua-bgfx.git --recursive && cd lua-bgfx 31 | 32 | # generate the project files... 33 | ./extern/bx/tools/bin/linux/genie gmake 34 | 35 | # build 36 | make -j9 37 | ``` 38 | 39 | 40 | # License 41 | **Note**: *The following is also in LICENSE.md.* 42 | 43 | ## The MIT License (MIT) 44 | (c) 2016 Colby Klein 45 | 46 | (c) 2016 Landon Manning 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 49 | 50 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 51 | 52 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 53 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | set VS=C:\Program Files (x86)\Microsoft Visual Studio 12.0 3 | set GENIE="%~dp0extern\bx\tools\bin\windows\genie.exe" 4 | call "%VS%\VC\vcvarsall.bat" x86 5 | set OLD=%CD% 6 | cd %~dp0scripts 7 | @echo on 8 | %GENIE% vs2013 9 | msbuild.exe lua-bgfx.sln /property:Configuration=Debug /m /t:lua-bgfx 10 | cd %OLD% 11 | -------------------------------------------------------------------------------- /run.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | set OLD=%CD% 3 | cd %~dp0test 4 | @echo on 5 | "%CD%\love\love.exe" . 6 | @echo off 7 | cd %OLD% 8 | @echo on 9 | -------------------------------------------------------------------------------- /scripts/genie.lua: -------------------------------------------------------------------------------- 1 | local BASE_DIR = path.getabsolute("..") 2 | local EXTERN_DIR = path.join(BASE_DIR, "extern") 3 | local OVR_DIR = path.join(EXTERN_DIR, "LibOVR") 4 | local use_ovr = false 5 | 6 | solution "lua-bgfx" do 7 | uuid "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942" 8 | configurations { "Debug", "Release" } 9 | platforms { "Native", "x32", "x64" } 10 | 11 | startproject "lua-bgfx" 12 | 13 | targetdir(path.join(BASE_DIR, "bin")) 14 | objdir(path.join(BASE_DIR, "obj")) 15 | 16 | configuration { "Debug" } 17 | flags { "Symbols" } 18 | 19 | configuration {} 20 | end 21 | 22 | if os.get() == "windows" then 23 | SDL_DIR = path.join(EXTERN_DIR, "sdl") 24 | project "SDL2" do 25 | uuid "3ABE8B7C-26F5-8C0D-CFE1-7210BBF7080F" 26 | targetname "SDL2" 27 | kind "SharedLib" 28 | language "C++" 29 | 30 | local headers = os.matchfiles(path.join(SDL_DIR, "include") .. "/*.h") 31 | local dir = path.join(BASE_DIR, "scripts/include") 32 | os.mkdir(dir) 33 | os.mkdir(path.join(dir, "SDL2")) 34 | for _, header in pairs(headers) do 35 | local file = path.getname(header) 36 | local folder = path.join(dir, "SDL2") 37 | local path = path.join(folder, file) 38 | os.copyfile(header, path) 39 | end 40 | 41 | local SDL_SRC = path.join(SDL_DIR, "src") 42 | 43 | includedirs { 44 | path.join(SDL_DIR, "include") 45 | } 46 | 47 | -- common files 48 | files { 49 | path.join(SDL_SRC, "*.c"), 50 | path.join(SDL_SRC, "atomic/*.c"), 51 | path.join(SDL_SRC, "audio/*.c"), 52 | path.join(SDL_SRC, "audio/dummy/*.c"), 53 | path.join(SDL_SRC, "audio/disk/*.c"), 54 | path.join(SDL_SRC, "core/*.c"), 55 | path.join(SDL_SRC, "cpuinfo/*.c"), 56 | path.join(SDL_SRC, "dynapi/*.c"), 57 | path.join(SDL_SRC, "events/*.c"), 58 | path.join(SDL_SRC, "file/*.c"), 59 | path.join(SDL_SRC, "filesystem/dummy/*.c"), 60 | path.join(SDL_SRC, "haptic/*.c"), 61 | path.join(SDL_SRC, "haptic/dummy/*.c"), 62 | path.join(SDL_SRC, "joystick/*.c"), 63 | path.join(SDL_SRC, "joystick/dummy/*.c"), 64 | path.join(SDL_SRC, "libm/*.c"), 65 | path.join(SDL_SRC, "loadso/*.c"), 66 | path.join(SDL_SRC, "main/*.c"), 67 | path.join(SDL_SRC, "power/*.c"), 68 | path.join(SDL_SRC, "render/*.c"), 69 | path.join(SDL_SRC, "stdlib/*.c"), 70 | path.join(SDL_SRC, "thread/*.c"), 71 | path.join(SDL_SRC, "timer/*.c"), 72 | path.join(SDL_SRC, "timer/dummy/*.c"), 73 | path.join(SDL_SRC, "video/*.c"), 74 | path.join(SDL_SRC, "video/dummy/*.c") 75 | } 76 | configuration { "windows", "vs*" } 77 | files { 78 | path.join(SDL_SRC, "audio/directsound/*.c"), 79 | -- this is, apparently, possible. 80 | path.join(SDL_SRC, "audio/pulseaudio/*.c"), 81 | path.join(SDL_SRC, "audio/xaudio2/*.c"), 82 | path.join(SDL_SRC, "audio/winmm/*.c"), 83 | path.join(SDL_SRC, "core/windows/*.c"), 84 | path.join(SDL_SRC, "filesystem/windows/*.c"), 85 | path.join(SDL_SRC, "haptic/windows/*.c"), 86 | path.join(SDL_SRC, "joystick/windows/*.c"), 87 | path.join(SDL_SRC, "loadso/windows/*.c"), 88 | path.join(SDL_SRC, "power/windows/*.c"), 89 | path.join(SDL_SRC, "render/direct3d/*.c"), 90 | path.join(SDL_SRC, "render/direct3d11/*.c"), 91 | path.join(SDL_SRC, "render/opengl/*.c"), 92 | path.join(SDL_SRC, "render/opengles/*.c"), 93 | path.join(SDL_SRC, "render/opengles2/*.c"), 94 | path.join(SDL_SRC, "render/software/*.c"), 95 | path.join(SDL_SRC, "thread/generic/SDL_syscond.c"), 96 | path.join(SDL_SRC, "thread/windows/*.c"), 97 | path.join(SDL_SRC, "timer/windows/*.c"), 98 | path.join(SDL_SRC, "video/windows/*.c") 99 | } 100 | links { 101 | "version", 102 | "imm32", 103 | "dxguid", 104 | "xinput", 105 | "winmm" 106 | } 107 | end 108 | end 109 | 110 | local function link_ovr() 111 | -- 32-bit 112 | configuration {"vs2013"} 113 | libdirs { path.join(OVR_DIR, "Lib/Windows/Win32/Release/VS2013") } 114 | configuration {"vs2015"} 115 | libdirs { path.join(OVR_DIR, "Lib/Windows/Win32/Release/VS2015") } 116 | 117 | -- 64-bit 118 | -- configuration {"vs2013", "x64"} 119 | -- libdirs { path.join(OVR_DIR, "Lib/Windows/x64/Release/VS2013") } 120 | -- configuration {"vs2015", "x64"} 121 | -- libdirs { path.join(OVR_DIR, "Lib/Windows/x64/Release/VS2015") } 122 | 123 | configuration {"vs*"} 124 | includedirs { path.join(OVR_DIR, "Include") } 125 | links { "LibOVR" } 126 | end 127 | 128 | project "BGFX" do 129 | uuid "EC77827C-D8AE-830D-819B-69106DB1FF0E" 130 | targetname "bgfx_s" 131 | kind "StaticLib" 132 | language "C++" 133 | local BX_DIR = path.join(EXTERN_DIR, "bx/include") 134 | local BGFX_DIR = path.join(EXTERN_DIR, "bgfx") 135 | local BGFX_SRC_DIR = path.join(BGFX_DIR, "src") 136 | includedirs { 137 | path.join(BGFX_DIR, "include"), 138 | path.join(BGFX_DIR, "3rdparty/khronos"), 139 | path.join(BGFX_DIR, "3rdparty"), 140 | BGFX_SRC_DIR, 141 | BX_DIR 142 | } 143 | files { 144 | path.join(BGFX_SRC_DIR, "amalgamated.cpp"), 145 | 146 | } 147 | configuration {"vs*"} 148 | if use_ovr and os.isdir(OVR_DIR) then 149 | defines { 150 | "BGFX_CONFIG_USE_OVR=1", 151 | "BGFX_CONFIG_MULTITHREADED=0" 152 | } 153 | link_ovr() 154 | end 155 | configuration {"vs*"} 156 | defines { "_CRT_SECURE_NO_WARNINGS" } 157 | links { 158 | "psapi" 159 | } 160 | includedirs { 161 | path.join(BGFX_DIR, "3rdparty/dxsdk/include"), 162 | path.join(BX_DIR, "compat/msvc") 163 | } 164 | configuration {"gmake"} 165 | buildoptions { 166 | "-std=c++11", 167 | "-fno-strict-aliasing", 168 | "-fpic", 169 | "-mstackrealign" 170 | } 171 | end 172 | 173 | project "lua-bgfx" do 174 | uuid "BB11813B-A7DE-DB46-D0F7-C9EEBC2311D5" 175 | targetprefix "" 176 | targetname "bgfx" 177 | kind "SharedLib" 178 | language "C++" 179 | 180 | files { 181 | path.join(BASE_DIR, "wrap_bgfx.cpp") 182 | } 183 | 184 | links { 185 | "BGFX" 186 | } 187 | 188 | includedirs { 189 | path.join(EXTERN_DIR, "bgfx/include"), 190 | path.join(EXTERN_DIR, "bx/include") 191 | } 192 | 193 | configuration {"vs*"} 194 | defines { "_CRT_SECURE_NO_WARNINGS" } 195 | includedirs { 196 | path.join(EXTERN_DIR, "luajit/src"), 197 | path.join(BASE_DIR, "scripts/include"), 198 | EXTERN_DIR 199 | } 200 | libdirs { 201 | path.join(EXTERN_DIR, "luajit/src"), 202 | } 203 | links { 204 | "version", 205 | "imm32", 206 | "dxguid", 207 | "xinput", 208 | "winmm", 209 | 210 | "SDL2", 211 | "lua51", 212 | "psapi" 213 | } 214 | if use_ovr and os.isdir(OVR_DIR) then 215 | link_ovr() 216 | end 217 | 218 | configuration {"linux"} 219 | includedirs { 220 | "/usr/include/luajit-2.0" 221 | } 222 | links { 223 | "luajit-5.1", 224 | "GL", 225 | "SDL2" 226 | } 227 | linkoptions { 228 | "-pthread" 229 | } 230 | 231 | configuration {"gmake"} 232 | buildoptions { 233 | "-std=c++11", 234 | "-fno-strict-aliasing", 235 | "-Wall", 236 | "-Wextra", 237 | "-mstackrealign" 238 | } 239 | end 240 | -------------------------------------------------------------------------------- /test/assets/models/chair.iqm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excessive/lua-bgfx/6ae61428d44e0b8e61e3472a349365a5bf478009/test/assets/models/chair.iqm -------------------------------------------------------------------------------- /test/assets/shaders/bgfx_shader.sh: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2016 Branimir Karadzic. All rights reserved. 3 | * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause 4 | */ 5 | 6 | #ifndef BGFX_SHADER_H_HEADER_GUARD 7 | #define BGFX_SHADER_H_HEADER_GUARD 8 | 9 | #if !defined(BGFX_CONFIG_MAX_BONES) 10 | # define BGFX_CONFIG_MAX_BONES 32 11 | #endif // !defined(BGFX_CONFIG_MAX_BONES) 12 | 13 | #ifndef __cplusplus 14 | 15 | #if BGFX_SHADER_LANGUAGE_HLSL > 3 16 | # define BRANCH [branch] 17 | # define LOOP [loop] 18 | # define UNROLL [unroll] 19 | #else 20 | # define BRANCH 21 | # define LOOP 22 | # define UNROLL 23 | #endif // BGFX_SHADER_LANGUAGE_HLSL > 3 24 | 25 | #if BGFX_SHADER_LANGUAGE_HLSL > 3 && BGFX_SHADER_TYPE_FRAGMENT 26 | # define EARLY_DEPTH_STENCIL [earlydepthstencil] 27 | #else 28 | # define EARLY_DEPTH_STENCIL 29 | #endif // BGFX_SHADER_LANGUAGE_HLSL > 3 && BGFX_SHADER_TYPE_FRAGMENT 30 | 31 | #if BGFX_SHADER_LANGUAGE_HLSL || BGFX_SHADER_LANGUAGE_PSSL 32 | # define CONST(_x) static const _x 33 | # define dFdx(_x) ddx(_x) 34 | # define dFdy(_y) ddy(-_y) 35 | # define inversesqrt(_x) rsqrt(_x) 36 | # define fract(_x) frac(_x) 37 | 38 | # define bvec2 bool2 39 | # define bvec3 bool3 40 | # define bvec4 bool4 41 | 42 | # if BGFX_SHADER_LANGUAGE_HLSL > 4 43 | # define REGISTER(_type, _reg) register(_type[_reg]) 44 | # else 45 | # define REGISTER(_type, _reg) register(_type ## _reg) 46 | # endif // BGFX_SHADER_LANGUAGE_HLSL 47 | 48 | # if BGFX_SHADER_LANGUAGE_HLSL > 3 || BGFX_SHADER_LANGUAGE_PSSL 49 | # if BGFX_SHADER_LANGUAGE_HLSL > 4 || BGFX_SHADER_LANGUAGE_PSSL 50 | # define dFdxCoarse(_x) ddx_coarse(_x) 51 | # define dFdxFine(_x) ddx_fine(_x) 52 | # define dFdyCoarse(_y) ddy_coarse(-_y) 53 | # define dFdyFine(_y) ddy_fine(-_y) 54 | # endif // BGFX_SHADER_LANGUAGE_HLSL > 4 55 | 56 | # if BGFX_SHADER_LANGUAGE_HLSL 57 | float intBitsToFloat(int _x) { return asfloat(_x); } 58 | vec2 intBitsToFloat(uint2 _x) { return asfloat(_x); } 59 | vec3 intBitsToFloat(uint3 _x) { return asfloat(_x); } 60 | vec4 intBitsToFloat(uint4 _x) { return asfloat(_x); } 61 | # endif // BGFX_SHADER_LANGUAGE_HLSL 62 | 63 | float uintBitsToFloat(uint _x) { return asfloat(_x); } 64 | vec2 uintBitsToFloat(uint2 _x) { return asfloat(_x); } 65 | vec3 uintBitsToFloat(uint3 _x) { return asfloat(_x); } 66 | vec4 uintBitsToFloat(uint4 _x) { return asfloat(_x); } 67 | 68 | uint floatBitsToUint(float _x) { return asuint(_x); } 69 | uvec2 floatBitsToUint(vec2 _x) { return asuint(_x); } 70 | uvec3 floatBitsToUint(vec3 _x) { return asuint(_x); } 71 | uvec4 floatBitsToUint(vec4 _x) { return asuint(_x); } 72 | 73 | int floatBitsToInt(float _x) { return asint(_x); } 74 | ivec2 floatBitsToInt(vec2 _x) { return asint(_x); } 75 | ivec3 floatBitsToInt(vec3 _x) { return asint(_x); } 76 | ivec4 floatBitsToInt(vec4 _x) { return asint(_x); } 77 | 78 | uint bitfieldReverse(uint _x) { return reversebits(_x); } 79 | uint2 bitfieldReverse(uint2 _x) { return reversebits(_x); } 80 | uint3 bitfieldReverse(uint3 _x) { return reversebits(_x); } 81 | uint4 bitfieldReverse(uint4 _x) { return reversebits(_x); } 82 | 83 | uint packHalf2x16(vec2 _x) 84 | { 85 | return (f32tof16(_x.x)<<16) | f32tof16(_x.y); 86 | } 87 | 88 | vec2 unpackHalf2x16(uint _x) 89 | { 90 | return vec2(f16tof32(_x >> 16), f16tof32(_x) ); 91 | } 92 | 93 | struct BgfxSampler2D 94 | { 95 | SamplerState m_sampler; 96 | Texture2D m_texture; 97 | }; 98 | 99 | struct BgfxISampler2D 100 | { 101 | Texture2D m_texture; 102 | }; 103 | 104 | struct BgfxUSampler2D 105 | { 106 | Texture2D m_texture; 107 | }; 108 | 109 | struct BgfxSampler2DArray 110 | { 111 | SamplerState m_sampler; 112 | Texture2DArray m_texture; 113 | }; 114 | 115 | struct BgfxSampler2DShadow 116 | { 117 | SamplerComparisonState m_sampler; 118 | Texture2D m_texture; 119 | }; 120 | 121 | struct BgfxSampler3D 122 | { 123 | SamplerState m_sampler; 124 | Texture3D m_texture; 125 | }; 126 | 127 | struct BgfxISampler3D 128 | { 129 | Texture3D m_texture; 130 | }; 131 | 132 | struct BgfxUSampler3D 133 | { 134 | Texture3D m_texture; 135 | }; 136 | 137 | struct BgfxSamplerCube 138 | { 139 | SamplerState m_sampler; 140 | TextureCube m_texture; 141 | }; 142 | 143 | struct BgfxSampler2DMS 144 | { 145 | Texture2DMS m_texture; 146 | }; 147 | 148 | vec4 bgfxTexture2D(BgfxSampler2D _sampler, vec2 _coord) 149 | { 150 | return _sampler.m_texture.Sample(_sampler.m_sampler, _coord); 151 | } 152 | 153 | vec4 bgfxTexture2DLod(BgfxSampler2D _sampler, vec2 _coord, float _level) 154 | { 155 | return _sampler.m_texture.SampleLevel(_sampler.m_sampler, _coord, _level); 156 | } 157 | 158 | vec4 bgfxTexture2DProj(BgfxSampler2D _sampler, vec3 _coord) 159 | { 160 | vec2 coord = _coord.xy * rcp(_coord.z); 161 | return _sampler.m_texture.Sample(_sampler.m_sampler, coord); 162 | } 163 | 164 | vec4 bgfxTexture2DProj(BgfxSampler2D _sampler, vec4 _coord) 165 | { 166 | vec2 coord = _coord.xy * rcp(_coord.w); 167 | return _sampler.m_texture.Sample(_sampler.m_sampler, coord); 168 | } 169 | 170 | vec4 bgfxTexture2DArray(BgfxSampler2DArray _sampler, vec3 _coord) 171 | { 172 | return _sampler.m_texture.Sample(_sampler.m_sampler, _coord); 173 | } 174 | 175 | vec4 bgfxTexture2DArrayLod(BgfxSampler2DArray _sampler, vec3 _coord, float _lod) 176 | { 177 | return _sampler.m_texture.SampleLevel(_sampler.m_sampler, _coord, _lod); 178 | } 179 | 180 | float bgfxShadow2D(BgfxSampler2DShadow _sampler, vec3 _coord) 181 | { 182 | return _sampler.m_texture.SampleCmpLevelZero(_sampler.m_sampler, _coord.xy, _coord.z); 183 | } 184 | 185 | float bgfxShadow2DProj(BgfxSampler2DShadow _sampler, vec4 _coord) 186 | { 187 | vec3 coord = _coord.xyz * rcp(_coord.w); 188 | return _sampler.m_texture.SampleCmpLevelZero(_sampler.m_sampler, coord.xy, coord.z); 189 | } 190 | 191 | vec4 bgfxTexture3D(BgfxSampler3D _sampler, vec3 _coord) 192 | { 193 | return _sampler.m_texture.Sample(_sampler.m_sampler, _coord); 194 | } 195 | 196 | vec4 bgfxTexture3DLod(BgfxSampler3D _sampler, vec3 _coord, float _level) 197 | { 198 | return _sampler.m_texture.SampleLevel(_sampler.m_sampler, _coord, _level); 199 | } 200 | 201 | ivec4 bgfxTexture3D(BgfxISampler3D _sampler, vec3 _coord) 202 | { 203 | uvec3 size; 204 | _sampler.m_texture.GetDimensions(size.x, size.y, size.z); 205 | return _sampler.m_texture.Load(ivec4(_coord * size, 0) ); 206 | } 207 | 208 | uvec4 bgfxTexture3D(BgfxUSampler3D _sampler, vec3 _coord) 209 | { 210 | uvec3 size; 211 | _sampler.m_texture.GetDimensions(size.x, size.y, size.z); 212 | return _sampler.m_texture.Load(ivec4(_coord * size, 0) ); 213 | } 214 | 215 | vec4 bgfxTextureCube(BgfxSamplerCube _sampler, vec3 _coord) 216 | { 217 | return _sampler.m_texture.Sample(_sampler.m_sampler, _coord); 218 | } 219 | 220 | vec4 bgfxTextureCubeLod(BgfxSamplerCube _sampler, vec3 _coord, float _level) 221 | { 222 | return _sampler.m_texture.SampleLevel(_sampler.m_sampler, _coord, _level); 223 | } 224 | 225 | vec4 bgfxTexelFetch(BgfxSampler2D _sampler, ivec2 _coord, int _lod) 226 | { 227 | return _sampler.m_texture.Load(ivec3(_coord, _lod) ); 228 | } 229 | 230 | ivec4 bgfxTexelFetch(BgfxISampler2D _sampler, ivec2 _coord, int _lod) 231 | { 232 | return _sampler.m_texture.Load(ivec3(_coord, _lod) ); 233 | } 234 | 235 | uvec4 bgfxTexelFetch(BgfxUSampler2D _sampler, ivec2 _coord, int _lod) 236 | { 237 | return _sampler.m_texture.Load(ivec3(_coord, _lod) ); 238 | } 239 | 240 | vec4 bgfxTexelFetch(BgfxSampler2DMS _sampler, ivec2 _coord, int _sampleIdx) 241 | { 242 | return _sampler.m_texture.Load(_coord, _sampleIdx); 243 | } 244 | 245 | vec4 bgfxTexelFetch(BgfxSampler3D _sampler, ivec3 _coord, int _lod) 246 | { 247 | return _sampler.m_texture.Load(ivec4(_coord, _lod) ); 248 | } 249 | 250 | # define SAMPLER2D(_name, _reg) \ 251 | uniform SamplerState _name ## Sampler : REGISTER(s, _reg); \ 252 | uniform Texture2D _name ## Texture : REGISTER(t, _reg); \ 253 | static BgfxSampler2D _name = { _name ## Sampler, _name ## Texture } 254 | # define ISAMPLER2D(_name, _reg) \ 255 | uniform Texture2D _name ## Texture : REGISTER(t, _reg); \ 256 | static BgfxISampler2D _name = { _name ## Texture } 257 | # define USAMPLER2D(_name, _reg) \ 258 | uniform Texture2D _name ## Texture : REGISTER(t, _reg); \ 259 | static BgfxUSampler2D _name = { _name ## Texture } 260 | # define sampler2D BgfxSampler2D 261 | # define texture2D(_sampler, _coord) bgfxTexture2D(_sampler, _coord) 262 | # define texture2DLod(_sampler, _coord, _level) bgfxTexture2DLod(_sampler, _coord, _level) 263 | # define texture2DProj(_sampler, _coord) bgfxTexture2DProj(_sampler, _coord) 264 | 265 | # define SAMPLER2DARRAY(_name, _reg) \ 266 | uniform SamplerState _name ## Sampler : REGISTER(s, _reg); \ 267 | uniform Texture2DArray _name ## Texture : REGISTER(t, _reg); \ 268 | static BgfxSampler2DArray _name = { _name ## Sampler, _name ## Texture } 269 | # define sampler2DArray BgfxSampler2DArray 270 | # define texture2DArray(_sampler, _coord) bgfxTexture2DArray(_sampler, _coord) 271 | # define texture2DArrayLod(_sampler, _coord, _lod) bgfxTexture2DArrayLod(_sampler, _coord, _lod) 272 | 273 | # define SAMPLER2DMS(_name, _reg) \ 274 | uniform Texture2DMS _name ## Texture : REGISTER(t, _reg); \ 275 | static BgfxSampler2DMS _name = { _name ## Texture } 276 | # define sampler2DMS BgfxSampler2DMS 277 | 278 | # define SAMPLER2DSHADOW(_name, _reg) \ 279 | uniform SamplerComparisonState _name ## Sampler : REGISTER(s, _reg); \ 280 | uniform Texture2D _name ## Texture : REGISTER(t, _reg); \ 281 | static BgfxSampler2DShadow _name = { _name ## Sampler, _name ## Texture } 282 | # define sampler2DShadow BgfxSampler2DShadow 283 | # define shadow2D(_sampler, _coord) bgfxShadow2D(_sampler, _coord) 284 | # define shadow2DProj(_sampler, _coord) bgfxShadow2DProj(_sampler, _coord) 285 | 286 | # define SAMPLER3D(_name, _reg) \ 287 | uniform SamplerState _name ## Sampler : REGISTER(s, _reg); \ 288 | uniform Texture3D _name ## Texture : REGISTER(t, _reg); \ 289 | static BgfxSampler3D _name = { _name ## Sampler, _name ## Texture } 290 | # define ISAMPLER3D(_name, _reg) \ 291 | uniform Texture3D _name ## Texture : REGISTER(t, _reg); \ 292 | static BgfxISampler3D _name = { _name ## Texture } 293 | # define USAMPLER3D(_name, _reg) \ 294 | uniform Texture3D _name ## Texture : REGISTER(t, _reg); \ 295 | static BgfxUSampler3D _name = { _name ## Texture } 296 | # define sampler3D BgfxSampler3D 297 | # define texture3D(_sampler, _coord) bgfxTexture3D(_sampler, _coord) 298 | # define texture3DLod(_sampler, _coord, _level) bgfxTexture3DLod(_sampler, _coord, _level) 299 | 300 | # define SAMPLERCUBE(_name, _reg) \ 301 | uniform SamplerState _name ## Sampler : REGISTER(s, _reg); \ 302 | uniform TextureCube _name ## Texture : REGISTER(t, _reg); \ 303 | static BgfxSamplerCube _name = { _name ## Sampler, _name ## Texture } 304 | # define samplerCube BgfxSamplerCube 305 | # define textureCube(_sampler, _coord) bgfxTextureCube(_sampler, _coord) 306 | # define textureCubeLod(_sampler, _coord, _level) bgfxTextureCubeLod(_sampler, _coord, _level) 307 | 308 | # define texelFetch(_sampler, _coord, _lod) bgfxTexelFetch(_sampler, _coord, _lod) 309 | # else 310 | 311 | # define sampler2DShadow sampler2D 312 | 313 | vec4 bgfxTexture2DProj(sampler2D _sampler, vec3 _coord) 314 | { 315 | return tex2Dproj(_sampler, vec4(_coord.xy, 0.0, _coord.z) ); 316 | } 317 | 318 | vec4 bgfxTexture2DProj(sampler2D _sampler, vec4 _coord) 319 | { 320 | return tex2Dproj(_sampler, _coord); 321 | } 322 | 323 | float bgfxShadow2D(sampler2DShadow _sampler, vec3 _coord) 324 | { 325 | #if 0 326 | float occluder = tex2D(_sampler, _coord.xy).x; 327 | return step(_coord.z, occluder); 328 | #else 329 | return tex2Dproj(_sampler, vec4(_coord.xy, _coord.z, 1.0) ).x; 330 | #endif // 0 331 | } 332 | 333 | float bgfxShadow2DProj(sampler2DShadow _sampler, vec4 _coord) 334 | { 335 | #if 0 336 | vec3 coord = _coord.xyz * rcp(_coord.w); 337 | float occluder = tex2D(_sampler, coord.xy).x; 338 | return step(coord.z, occluder); 339 | #else 340 | return tex2Dproj(_sampler, _coord).x; 341 | #endif // 0 342 | } 343 | 344 | # define SAMPLER2D(_name, _reg) uniform sampler2D _name : REGISTER(s, _reg) 345 | # define SAMPLER2DMS(_name, _reg) uniform sampler2DMS _name : REGISTER(s, _reg) 346 | # define texture2D(_sampler, _coord) tex2D(_sampler, _coord) 347 | # define texture2DProj(_sampler, _coord) bgfxTexture2DProj(_sampler, _coord) 348 | 349 | # define SAMPLER2DSHADOW(_name, _reg) uniform sampler2DShadow _name : REGISTER(s, _reg) 350 | # define shadow2D(_sampler, _coord) bgfxShadow2D(_sampler, _coord) 351 | # define shadow2DProj(_sampler, _coord) bgfxShadow2DProj(_sampler, _coord) 352 | 353 | # define SAMPLER3D(_name, _reg) uniform sampler3D _name : REGISTER(s, _reg) 354 | # define texture3D(_sampler, _coord) tex3D(_sampler, _coord) 355 | 356 | # define SAMPLERCUBE(_name, _reg) uniform samplerCUBE _name : REGISTER(s, _reg) 357 | # define textureCube(_sampler, _coord) texCUBE(_sampler, _coord) 358 | 359 | # if BGFX_SHADER_LANGUAGE_HLSL == 2 360 | # define texture2DLod(_sampler, _coord, _level) tex2D(_sampler, (_coord).xy) 361 | # define texture3DLod(_sampler, _coord, _level) tex3D(_sampler, (_coord).xyz) 362 | # define textureCubeLod(_sampler, _coord, _level) texCUBE(_sampler, (_coord).xyz) 363 | # else 364 | # define texture2DLod(_sampler, _coord, _level) tex2Dlod(_sampler, vec4( (_coord).xy, 0.0, _level) ) 365 | # define texture3DLod(_sampler, _coord, _level) tex3Dlod(_sampler, vec4( (_coord).xyz, _level) ) 366 | # define textureCubeLod(_sampler, _coord, _level) texCUBElod(_sampler, vec4( (_coord).xyz, _level) ) 367 | # endif // BGFX_SHADER_LANGUAGE_HLSL == 2 368 | 369 | # endif // BGFX_SHADER_LANGUAGE_HLSL > 3 370 | 371 | vec3 instMul(vec3 _vec, mat3 _mtx) { return mul(_mtx, _vec); } 372 | vec3 instMul(mat3 _mtx, vec3 _vec) { return mul(_vec, _mtx); } 373 | vec4 instMul(vec4 _vec, mat4 _mtx) { return mul(_mtx, _vec); } 374 | vec4 instMul(mat4 _mtx, vec4 _vec) { return mul(_vec, _mtx); } 375 | 376 | bvec2 lessThan(vec2 _a, vec2 _b) { return _a < _b; } 377 | bvec3 lessThan(vec3 _a, vec3 _b) { return _a < _b; } 378 | bvec4 lessThan(vec4 _a, vec4 _b) { return _a < _b; } 379 | 380 | bvec2 lessThanEqual(vec2 _a, vec2 _b) { return _a <= _b; } 381 | bvec3 lessThanEqual(vec3 _a, vec3 _b) { return _a <= _b; } 382 | bvec4 lessThanEqual(vec4 _a, vec4 _b) { return _a <= _b; } 383 | 384 | bvec2 greaterThan(vec2 _a, vec2 _b) { return _a > _b; } 385 | bvec3 greaterThan(vec3 _a, vec3 _b) { return _a > _b; } 386 | bvec4 greaterThan(vec4 _a, vec4 _b) { return _a > _b; } 387 | 388 | bvec2 greaterThanEqual(vec2 _a, vec2 _b) { return _a >= _b; } 389 | bvec3 greaterThanEqual(vec3 _a, vec3 _b) { return _a >= _b; } 390 | bvec4 greaterThanEqual(vec4 _a, vec4 _b) { return _a >= _b; } 391 | 392 | bvec2 notEqual(vec2 _a, vec2 _b) { return _a != _b; } 393 | bvec3 notEqual(vec3 _a, vec3 _b) { return _a != _b; } 394 | bvec4 notEqual(vec4 _a, vec4 _b) { return _a != _b; } 395 | 396 | bvec2 equal(vec2 _a, vec2 _b) { return _a == _b; } 397 | bvec3 equal(vec3 _a, vec3 _b) { return _a == _b; } 398 | bvec4 equal(vec4 _a, vec4 _b) { return _a == _b; } 399 | 400 | float mix(float _a, float _b, float _t) { return lerp(_a, _b, _t); } 401 | vec2 mix(vec2 _a, vec2 _b, vec2 _t) { return lerp(_a, _b, _t); } 402 | vec3 mix(vec3 _a, vec3 _b, vec3 _t) { return lerp(_a, _b, _t); } 403 | vec4 mix(vec4 _a, vec4 _b, vec4 _t) { return lerp(_a, _b, _t); } 404 | 405 | float mod(float _a, float _b) { return _a - _b * floor(_a / _b); } 406 | vec2 mod(vec2 _a, vec2 _b) { return _a - _b * floor(_a / _b); } 407 | vec3 mod(vec3 _a, vec3 _b) { return _a - _b * floor(_a / _b); } 408 | vec4 mod(vec4 _a, vec4 _b) { return _a - _b * floor(_a / _b); } 409 | 410 | #else 411 | # define CONST(_x) const _x 412 | # define atan2(_x, _y) atan(_x, _y) 413 | # define mul(_a, _b) ( (_a) * (_b) ) 414 | # define saturate(_x) clamp(_x, 0.0, 1.0) 415 | # define SAMPLER2D(_name, _reg) uniform sampler2D _name 416 | # define SAMPLER2DMS(_name, _reg) uniform sampler2DMS _name 417 | # define SAMPLER3D(_name, _reg) uniform sampler3D _name 418 | # define SAMPLERCUBE(_name, _reg) uniform samplerCube _name 419 | # define SAMPLER2DSHADOW(_name, _reg) uniform sampler2DShadow _name 420 | 421 | # define SAMPLER2DARRAY(_name, _reg) uniform sampler2DArray _name 422 | # define SAMPLER2DMSARRAY(_name, _reg) uniform sampler2DMSArray _name 423 | # define SAMPLERCUBEARRAY(_name, _reg) uniform samplerCubeArray _name 424 | # define SAMPLER2DARRAYSHADOW(_name, _reg) uniform sampler2DArrayShadow _name 425 | 426 | # if BGFX_SHADER_LANGUAGE_GLSL >= 130 427 | # define ISAMPLER2D(_name, _reg) uniform isampler2D _name 428 | # define USAMPLER2D(_name, _reg) uniform usampler2D _name 429 | # define ISAMPLER3D(_name, _reg) uniform isampler3D _name 430 | # define USAMPLER3D(_name, _reg) uniform usampler3D _name 431 | 432 | # define texture2D(_sampler, _coord) texture(_sampler, _coord) 433 | # define texture2DArray(_sampler, _coord) texture(_sampler, _coord) 434 | # define texture3D(_sampler, _coord) texture(_sampler, _coord) 435 | # endif // BGFX_SHADER_LANGUAGE_GLSL >= 130 436 | 437 | vec3 instMul(vec3 _vec, mat3 _mtx) { return mul(_vec, _mtx); } 438 | vec3 instMul(mat3 _mtx, vec3 _vec) { return mul(_mtx, _vec); } 439 | vec4 instMul(vec4 _vec, mat4 _mtx) { return mul(_vec, _mtx); } 440 | vec4 instMul(mat4 _mtx, vec4 _vec) { return mul(_mtx, _vec); } 441 | 442 | float rcp(float _a) { return 1.0/_a; } 443 | vec2 rcp(vec2 _a) { return vec2(1.0)/_a; } 444 | vec3 rcp(vec3 _a) { return vec3(1.0)/_a; } 445 | vec4 rcp(vec4 _a) { return vec4(1.0)/_a; } 446 | #endif // BGFX_SHADER_LANGUAGE_* 447 | 448 | vec2 vec2_splat(float _x) { return vec2(_x, _x); } 449 | vec3 vec3_splat(float _x) { return vec3(_x, _x, _x); } 450 | vec4 vec4_splat(float _x) { return vec4(_x, _x, _x, _x); } 451 | 452 | #if BGFX_SHADER_LANGUAGE_GLSL >= 130 || BGFX_SHADER_LANGUAGE_HLSL || BGFX_SHADER_LANGUAGE_PSSL 453 | uvec2 uvec2_splat(uint _x) { return uvec2(_x, _x); } 454 | uvec3 uvec3_splat(uint _x) { return uvec3(_x, _x, _x); } 455 | uvec4 uvec4_splat(uint _x) { return uvec4(_x, _x, _x, _x); } 456 | #endif // BGFX_SHADER_LANGUAGE_GLSL >= 130 || BGFX_SHADER_LANGUAGE_HLSL || BGFX_SHADER_LANGUAGE_PSSL 457 | 458 | uniform vec4 u_viewRect; 459 | uniform vec4 u_viewTexel; 460 | uniform mat4 u_view; 461 | uniform mat4 u_invView; 462 | uniform mat4 u_proj; 463 | uniform mat4 u_invProj; 464 | uniform mat4 u_viewProj; 465 | uniform mat4 u_invViewProj; 466 | uniform mat4 u_model[BGFX_CONFIG_MAX_BONES]; 467 | uniform mat4 u_modelView; 468 | uniform mat4 u_modelViewProj; 469 | uniform vec4 u_alphaRef4; 470 | #define u_alphaRef u_alphaRef4.x 471 | 472 | #endif // __cplusplus 473 | 474 | #endif // BGFX_SHADER_H_HEADER_GUARD 475 | -------------------------------------------------------------------------------- /test/assets/shaders/bin/test.fs.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excessive/lua-bgfx/6ae61428d44e0b8e61e3472a349365a5bf478009/test/assets/shaders/bin/test.fs.bin -------------------------------------------------------------------------------- /test/assets/shaders/bin/test.vs.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excessive/lua-bgfx/6ae61428d44e0b8e61e3472a349365a5bf478009/test/assets/shaders/bin/test.vs.bin -------------------------------------------------------------------------------- /test/assets/shaders/test.fs.sc: -------------------------------------------------------------------------------- 1 | $input v_color, v_uv, v_normal 2 | 3 | #include "bgfx_shader.sh" 4 | 5 | SAMPLER2D(s_tex, 0); 6 | 7 | const vec3 light = vec3(0.25, -0.25, 1.0); 8 | 9 | void main() 10 | { 11 | vec3 n = normalize(v_normal); 12 | vec3 l = normalize(light); 13 | float shade = max(dot(n, l), 0.0); 14 | 15 | vec4 out_color = texture2D(s_tex, v_uv) * v_color; 16 | out_color.rgb *= shade; 17 | 18 | gl_FragColor = out_color; 19 | } 20 | -------------------------------------------------------------------------------- /test/assets/shaders/test.vs.sc: -------------------------------------------------------------------------------- 1 | $input a_position, a_normal, a_color0, a_texcoord0 2 | $output v_color, v_normal, v_uv 3 | 4 | #include "bgfx_shader.sh" 5 | 6 | void main() 7 | { 8 | gl_Position = mul(u_modelViewProj, vec4(a_position, 1.0) ); 9 | v_color = a_color0; 10 | v_uv = a_texcoord0; 11 | v_normal = a_normal; 12 | } 13 | -------------------------------------------------------------------------------- /test/assets/shaders/varying.def.sc: -------------------------------------------------------------------------------- 1 | vec3 a_position : POSITION; 2 | vec3 a_normal : NORMAL; 3 | vec4 a_color0 : COLOR0; 4 | vec2 a_texcoord0 : TEXCOORD0; 5 | 6 | vec4 v_color : COLOR0 = vec4(1.0, 0.0, 1.0, 1.0); 7 | vec3 v_normal : NORMAL = vec3(0.0, 1.0, 0.0); 8 | vec2 v_uv : TEXCOORD0 = vec2(0.5, 0.5); 9 | -------------------------------------------------------------------------------- /test/assets/textures/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/excessive/lua-bgfx/6ae61428d44e0b8e61e3472a349365a5bf478009/test/assets/textures/grid.png -------------------------------------------------------------------------------- /test/camera.lua: -------------------------------------------------------------------------------- 1 | local cpml = require "cpml" 2 | local camera = {} 3 | 4 | local camera_mt = { 5 | __index = camera 6 | } 7 | 8 | local function new(options) 9 | local t = { 10 | fov = options.fov or 45, 11 | near = options.near or 0.1, -- 10cm 12 | far = options.far or 150.0, -- 150m 13 | exposure = options.exposure or 1.0, 14 | 15 | position = options.position or cpml.vec3(0, 0, 0), 16 | orientation = options.orientation or cpml.quat(0, 0, 0, 1), 17 | 18 | target = options.target or false, 19 | up = cpml.vec3.unit_z, 20 | 21 | orbit_offset = options.orbit_offset or cpml.vec3(0, 0, 0), 22 | offset = options.offset or cpml.vec3(0, 0, 0), 23 | 24 | view = cpml.mat4(), 25 | projection = cpml.mat4(), 26 | 27 | mouse_sensitivity = 0.1, 28 | pitch_limit_up = 0.3, 29 | pitch_limit_down = 0.9 30 | } 31 | t.direction = t.orientation * cpml.vec3.unit_y 32 | 33 | return setmetatable(t, camera_mt) 34 | end 35 | 36 | function camera:rotate_xy(mx, my) 37 | local sensitivity = self.mouse_sensitivity 38 | local mouse_direction = { 39 | x = math.rad(-mx * sensitivity), 40 | y = math.rad(-my * sensitivity) 41 | } 42 | 43 | -- get the axis to rotate around the x-axis. 44 | local axis = cpml.vec3():cross(self.direction, self.up) 45 | axis:normalize(axis) 46 | 47 | -- First, we apply a left/right rotation. 48 | self.orientation = cpml.quat.rotate(mouse_direction.x, self.up) * self.orientation 49 | 50 | -- Next, we apply up/down rotation. 51 | -- up/down rotation is applied after any other rotation (so that other rotations are not affected by it), 52 | -- hence we post-multiply it. 53 | local new_orientation = self.orientation * cpml.quat.rotate(mouse_direction.y, cpml.vec3.unit_x) 54 | local new_pitch = (new_orientation * cpml.vec3.unit_y):dot(self.up) 55 | 56 | -- Don't rotate up/down more than self.pitch_limit. 57 | -- We need to limit pitch, but the only reliable way we're going to get away with this is if we 58 | -- calculate the new orientation twice. If the new rotation is going to be over the threshold and 59 | -- Y will send you out any further, cancel it out. This prevents the camera locking up at +/-PITCH_LIMIT 60 | if new_pitch >= self.pitch_limit_up then 61 | mouse_direction.y = math.min(0, mouse_direction.y) 62 | elseif new_pitch <= -self.pitch_limit_down then 63 | mouse_direction.y = math.max(0, mouse_direction.y) 64 | end 65 | 66 | self.orientation = self.orientation * cpml.quat.rotate(mouse_direction.y, cpml.vec3.unit_x) 67 | 68 | -- Apply rotation to camera direction 69 | self.direction = self.orientation * cpml.vec3.unit_y 70 | end 71 | 72 | function camera:update(w, h) 73 | local aspect = math.max(w / h, h / w) 74 | local target = self.target and self.target or (self.position + self.direction) 75 | local look = cpml.mat4() 76 | look:look_at(look, self.position, target, cpml.vec3.unit_z) 77 | self.view:identity(self.view) 78 | :translate(self.view, self.orbit_offset) 79 | :mul(look, self.view) 80 | :translate(self.view, self.offset) 81 | 82 | self.projection = cpml.mat4.from_perspective(self.fov, aspect, self.near, self.far) 83 | end 84 | 85 | return setmetatable( 86 | { new = new }, 87 | { __call = function(_, ...) return new(...) end } 88 | ) 89 | -------------------------------------------------------------------------------- /test/conf.lua: -------------------------------------------------------------------------------- 1 | function love.conf(t) 2 | t.modules.graphics = false 3 | t.modules.window = true 4 | t.modules.audio = false 5 | t.modules.sound = false 6 | -- t.console = true 7 | io.stdout:setvbuf("no") -- Don't delay prints. 8 | end 9 | -------------------------------------------------------------------------------- /test/iqm.lua: -------------------------------------------------------------------------------- 1 | -- This is like the upstream version of the file, except +1's have been removed 2 | -- and output format changes have been made such that it works with lua-bgfx. 3 | local base = (...):gsub('%.init$', '') .. "." 4 | local ffi = require "ffi" 5 | 6 | local iqm = { 7 | _LICENSE = "Inter-Quake Model Loader is distributed under the terms of the MIT license. See LICENSE.md.", 8 | _FULL_LICENSE = [[ 9 | # Inter-Quake Model Loader is licensed under the MIT Open Source License. 10 | (http://www.opensource.org/licenses/mit-license.html) 11 | 12 | Copyright (c) 2015 excessive ❤ moé 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy 15 | of this software and associated documentation files (the "Software"), to deal 16 | in the Software without restriction, including without limitation the rights 17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | copies of the Software, and to permit persons to whom the Software is 19 | furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in 22 | all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 | THE SOFTWARE. 31 | ]], 32 | _URL = "https://github.com/excessive/iqm", 33 | _VERSION = "2.0.0", 34 | _DESCRIPTION = "Load an IQM 3D model into BGFX.", 35 | } 36 | 37 | ffi.cdef([[ 38 | typedef uint32_t uint; 39 | typedef uint8_t uchar; 40 | typedef uint8_t byte; 41 | 42 | struct iqmheader { 43 | char magic[16]; 44 | uint version; 45 | uint filesize; 46 | uint flags; 47 | uint num_text, ofs_text; 48 | uint num_meshes, ofs_meshes; 49 | uint num_vertexarrays, num_vertexes, ofs_vertexarrays; 50 | uint num_triangles, ofs_triangles, ofs_adjacency; 51 | uint num_joints, ofs_joints; 52 | uint num_poses, ofs_poses; 53 | uint num_anims, ofs_anims; 54 | uint num_frames, num_framechannels, ofs_frames, ofs_bounds; 55 | uint num_comment, ofs_comment; 56 | uint num_extensions, ofs_extensions; 57 | }; 58 | 59 | struct iqmmesh { 60 | uint name; 61 | uint material; 62 | uint first_vertex, num_vertexes; 63 | uint first_triangle, num_triangles; 64 | }; 65 | 66 | enum { 67 | IQM_POSITION = 0, 68 | IQM_TEXCOORD = 1, 69 | IQM_NORMAL = 2, 70 | IQM_TANGENT = 3, 71 | IQM_BLENDINDEXES = 4, 72 | IQM_BLENDWEIGHTS = 5, 73 | IQM_COLOR = 6, 74 | IQM_CUSTOM = 0x10 75 | }; 76 | 77 | enum { 78 | IQM_BYTE = 0, 79 | IQM_UBYTE = 1, 80 | IQM_SHORT = 2, 81 | IQM_USHORT = 3, 82 | IQM_INT = 4, 83 | IQM_UINT = 5, 84 | IQM_HALF = 6, 85 | IQM_FLOAT = 7, 86 | IQM_DOUBLE = 8, 87 | }; 88 | 89 | struct iqmvertexarray { 90 | uint type; 91 | uint flags; 92 | uint format; 93 | uint size; 94 | uint offset; 95 | }; 96 | 97 | struct iqmtriangle { 98 | uint vertex[3]; 99 | }; 100 | 101 | struct iqmadjacency { 102 | uint triangle[3]; 103 | }; 104 | 105 | struct iqmjoint { 106 | uint name; 107 | int parent; 108 | float translate[3], rotate[4], scale[3]; 109 | }; 110 | 111 | struct iqmpose { 112 | int parent; 113 | uint channelmask; 114 | float channeloffset[10], channelscale[10]; 115 | }; 116 | 117 | struct iqmanim { 118 | uint name; 119 | uint first_frame, num_frames; 120 | float framerate; 121 | uint flags; 122 | }; 123 | 124 | enum { 125 | IQM_LOOP = 1<<0 126 | }; 127 | 128 | struct iqmbounds { 129 | float bbmins[3], bbmaxs[3]; 130 | float xyradius, radius; 131 | }; 132 | 133 | ]]) 134 | 135 | local c = ffi.C 136 | 137 | iqm.lookup = {} 138 | 139 | local function check_magic(magic) 140 | return magic:sub(1,16) == "INTERQUAKEMODEL\0" 141 | end 142 | 143 | local function load_data(file) 144 | local is_buffer = check_magic(file) 145 | 146 | -- Make sure it's a valid IQM file 147 | if not is_buffer then 148 | assert(love.filesystem.isFile(file)) 149 | assert(check_magic(love.filesystem.read(file, 16))) 150 | end 151 | 152 | -- Decode the header, it's got all the offsets 153 | local iqm_header = ffi.typeof("struct iqmheader*") 154 | local size = ffi.sizeof("struct iqmheader") 155 | local header_data 156 | if is_buffer then 157 | header_data = file 158 | else 159 | header_data = love.filesystem.read(file, size) 160 | end 161 | local header = ffi.cast(iqm_header, header_data)[0] 162 | 163 | -- We only support IQM version 2 164 | assert(header.version == 2) 165 | 166 | -- Only read the amount of data declared by the header, just in case! 167 | local data = is_buffer and file or love.filesystem.read(file, header.filesize) 168 | 169 | return header, data 170 | end 171 | 172 | -- Read `num` element from `data` at `offset` and convert it to a table. 173 | local function read_offset(data, type, offset, num) 174 | local decoded = {} 175 | local type_ptr = ffi.typeof(type.."*") 176 | local size = ffi.sizeof(type) 177 | local ptr = ffi.cast(type_ptr, data:sub(offset+1)) 178 | for i = 1, num do 179 | table.insert(decoded, ptr[i-1]) 180 | end 181 | return decoded 182 | end 183 | 184 | -- a bit simpler than read_offset, as we don't bother converting to a table. 185 | local function read_ptr(data, type, offset) 186 | local type_ptr = ffi.typeof(type.."*") 187 | local size = ffi.sizeof(type) 188 | local ptr = ffi.cast(type_ptr, data:sub(offset+1)) 189 | return ptr 190 | end 191 | 192 | -- Collect all text data in the file. 193 | -- Not needed for meshes because everything is byte offsets - but very 194 | -- useful for debugging. 195 | local function dump_strings(text) 196 | local strings = {} 197 | local advance = 1 198 | 199 | -- header.num_text is the length of the text block in bytes. 200 | repeat 201 | local str = ffi.string(text + advance) 202 | table.insert(strings, str) 203 | advance = advance + str:len() + 1 204 | print(str) 205 | until advance >= header.num_text 206 | 207 | return strings 208 | end 209 | 210 | -- 'file' can be either a filename or IQM data (as long as the magic is intact) 211 | function iqm.load(file, save_data) 212 | -- HACK: Workaround for a bug in LuaJIT's GC - we need to turn it off for the 213 | -- rest of the function or we'll get a segfault shortly into these loops. 214 | -- 215 | -- I've got no idea why the GC thinks it can pull the rug out from under us, 216 | -- but I sure as hell don't appreciate it. Do NOT restart until the end. -ss 217 | collectgarbage("stop") 218 | 219 | local header, data = load_data(file) 220 | 221 | -- Decode the vertex arrays 222 | local vertex_arrays = read_offset( 223 | data, 224 | "struct iqmvertexarray", 225 | header.ofs_vertexarrays, 226 | header.num_vertexarrays 227 | ) 228 | 229 | local function translate_va(type) 230 | local types = { 231 | [c.IQM_POSITION] = "position", 232 | [c.IQM_TEXCOORD] = "texcoord", 233 | [c.IQM_NORMAL] = "normal", 234 | [c.IQM_TANGENT] = "tangent", 235 | [c.IQM_COLOR] = "color", 236 | [c.IQM_BLENDINDEXES] = "bone", 237 | [c.IQM_BLENDWEIGHTS] = "weight" 238 | } 239 | return types[type] or false 240 | end 241 | 242 | local function translate_format(type) 243 | local types = { 244 | [c.IQM_FLOAT] = "float", 245 | [c.IQM_UBYTE] = "byte", 246 | } 247 | return types[type] or false 248 | end 249 | 250 | local function translate_bgfx(type) 251 | local types = { 252 | position = "position", 253 | texcoord = "texcoord0", 254 | normal = "normal", 255 | tangent = "tangent", 256 | bone = "indices", 257 | weight = "weight", 258 | color = "color0", 259 | } 260 | return assert(types[type]) 261 | end 262 | 263 | -- Build iqm_vertex struct out of whatever is in this file 264 | local found = {} 265 | local found_names = {} 266 | local found_types = {} 267 | 268 | for _, va in ipairs(vertex_arrays) do 269 | while true do 270 | 271 | local type = translate_va(va.type) 272 | if not type then 273 | break 274 | end 275 | 276 | local format = assert(translate_format(va.format)) 277 | 278 | table.insert(found, string.format("%s %s[%d]", format, type, va.size)) 279 | table.insert(found_names, type) 280 | table.insert(found_types, { 281 | type = type, 282 | size = va.size, 283 | offset = va.offset, 284 | format = format, 285 | bgfx_type = translate_bgfx(type) 286 | }) 287 | 288 | break end 289 | end 290 | table.sort(found_names) 291 | local title = "iqm_vertex_" .. table.concat(found_names, "_") 292 | 293 | -- If we've already got a struct of this type, reuse it. 294 | local type = iqm.lookup[title] 295 | if not type then 296 | local def = string.format("struct %s {\n\t%s;\n};", title, table.concat(found, ";\n\t")) 297 | ffi.cdef(def) 298 | 299 | local ct = ffi.typeof("struct " .. title) 300 | iqm.lookup[title] = ct 301 | type = ct 302 | end 303 | 304 | local filedata = love.filesystem.newFileData(("\0"):rep(header.num_vertexes * ffi.sizeof(type)), "dummy") 305 | local vertices = ffi.cast("struct " .. title .. "*", filedata:getPointer()) 306 | 307 | -- TODO: Compute XY + spherical radiuses 308 | local computed_bbox = { min = {}, max = {} } 309 | 310 | -- Interleave vertex data 311 | for _, va in ipairs(found_types) do 312 | local ptr = read_ptr(data, va.format, va.offset) 313 | for i = 0, header.num_vertexes-1 do 314 | for j = 0, va.size-1 do 315 | vertices[i][va.type][j] = ptr[i*va.size+j] 316 | end 317 | if va.type == "position" then 318 | local v = vertices[i][va.type] 319 | for i = 1, 3 do 320 | computed_bbox.min[i] = math.min(computed_bbox.min[i] or v[i-1], v[i-1]) 321 | computed_bbox.max[i] = math.max(computed_bbox.max[i] or v[i-1], v[i-1]) 322 | end 323 | end 324 | end 325 | end 326 | 327 | -- Decode triangle data (index buffer) 328 | local triangles = read_offset( 329 | data, 330 | "struct iqmtriangle", 331 | header.ofs_triangles, 332 | header.num_triangles 333 | ) 334 | assert(#triangles == header.num_triangles) 335 | 336 | -- Translate indices for bgfx 337 | local indices = {} 338 | for _, triangle in ipairs(triangles) do 339 | table.insert(indices, triangle.vertex[0] + 1) 340 | -- IQM uses CW winding, but we want CCW. Reverse. 341 | table.insert(indices, triangle.vertex[2] + 1) 342 | table.insert(indices, triangle.vertex[1] + 1) 343 | end 344 | 345 | -- re-read the vertex data :( 346 | local save_buffer = {} 347 | if save_data then 348 | local buffer = {} 349 | for _, va in ipairs(found_types) do 350 | local ptr = read_ptr(data, va.format, va.offset) 351 | for i = 1, header.num_vertexes do 352 | buffer[i] = buffer[i] or {} 353 | buffer[i][va.type] = {} 354 | for j = 0, va.size-1 do 355 | buffer[i][va.type][j+1] = ptr[(i-1)*va.size+j] 356 | end 357 | end 358 | end 359 | for i, triangle in ipairs(triangles) do 360 | save_buffer[i] = { 361 | buffer[triangle.vertex[0] + 1], 362 | buffer[triangle.vertex[2] + 1], 363 | buffer[triangle.vertex[1] + 1] 364 | } 365 | end 366 | end 367 | 368 | local layout = {} 369 | for i, va in ipairs(found_types) do 370 | layout[i] = { 371 | attrib = va.bgfx_type, 372 | type = va.format, 373 | size = va.size, 374 | normalized = va.bgfx_type == "color0" 375 | } 376 | end 377 | 378 | local fmt = bgfx.new_vertex_format(layout) 379 | local vb = bgfx.new_vertex_buffer(filedata:getString(), fmt) 380 | local ib = bgfx.new_index_buffer(indices) 381 | 382 | -- Decode mesh/material names. 383 | local text = read_ptr( 384 | data, 385 | "char", 386 | header.ofs_text 387 | ) 388 | 389 | local objects = {} 390 | objects.bounds = {} 391 | objects.bounds.base = computed_bbox 392 | objects.triangles = save_buffer 393 | 394 | if header.ofs_bounds > 0 then 395 | local bounds = read_offset( 396 | data, 397 | "struct iqmbounds", 398 | header.ofs_bounds, 399 | header.num_frames 400 | ) 401 | for i, bb in ipairs(bounds) do 402 | table.insert(objects.bounds, { 403 | min = { bb.bbmins[0], bb.bbmins[1], bb.bbmins[2] }, 404 | max = { bb.bbmaxs[0], bb.bbmaxs[1], bb.bbmaxs[2] } 405 | }) 406 | end 407 | end 408 | 409 | -- Decode meshes 410 | local meshes = read_offset( 411 | data, 412 | "struct iqmmesh", 413 | header.ofs_meshes, 414 | header.num_meshes 415 | ) 416 | 417 | objects.has_joints = header.ofs_joints > 0 418 | objects.has_anims = header.ofs_anims > 0 419 | objects.mesh = vb 420 | objects.ibo = ib 421 | for i, mesh in ipairs(meshes) do 422 | local add = { 423 | first = mesh.first_triangle * 3, 424 | count = mesh.num_triangles * 3, 425 | material = ffi.string(text+mesh.material), 426 | name = ffi.string(text+mesh.name) 427 | } 428 | add.last = add.first + add.count 429 | table.insert(objects, add) 430 | end 431 | 432 | collectgarbage("restart") 433 | 434 | return objects 435 | end 436 | 437 | function iqm.load_anims(file) 438 | -- Require CPML here because loading the mesh does not depend on it. 439 | local cpml = require "cpml" 440 | 441 | -- See the comment in iqm.load. Do *NOT* remove. -ss 442 | collectgarbage("stop") 443 | local header, data = load_data(file) 444 | 445 | -- Decode mesh/material names. 446 | local text = read_ptr( 447 | data, 448 | "char", 449 | header.ofs_text 450 | ) 451 | 452 | local anims = {} 453 | 454 | if header.ofs_joints > 0 then 455 | local skeleton = {} 456 | local joints = read_offset(data, "struct iqmjoint", header.ofs_joints, header.num_joints) 457 | for i, joint in ipairs(joints) do 458 | joint.parent = joint.parent + 1 459 | local bone = { 460 | parent = joint.parent, 461 | name = ffi.string(text+joint.name), 462 | position = cpml.vec3(joint.translate[0], joint.translate[1], joint.translate[2]), 463 | rotation = cpml.quat(joint.rotate[0], joint.rotate[1], joint.rotate[2], joint.rotate[3]), 464 | scale = cpml.vec3(joint.scale[0], joint.scale[1], joint.scale[2]) 465 | } 466 | skeleton[i], skeleton[bone.name] = bone, bone 467 | end 468 | anims.skeleton = skeleton 469 | end 470 | 471 | if header.ofs_anims > 0 then 472 | local animdata = read_offset(data, "struct iqmanim", header.ofs_anims, header.num_anims) 473 | for i, anim in ipairs(animdata) do 474 | local a = { 475 | name = ffi.string(text+anim.name), 476 | first = anim.first_frame+1, 477 | last = anim.first_frame+anim.num_frames, 478 | framerate = anim.framerate, 479 | loop = bit.band(anim.flags, c.IQM_LOOP) == c.IQM_LOOP 480 | } 481 | 482 | anims[i], anims[a.name] = a, a 483 | end 484 | end 485 | 486 | if header.ofs_poses > 0 then 487 | local poses = read_offset(data, "struct iqmpose", header.ofs_poses, header.num_poses) 488 | local framedata = read_ptr(data, "unsigned short", header.ofs_frames) 489 | 490 | local function readv(p, i, mask) 491 | local v = p.channeloffset[i] 492 | if bit.band(p.channelmask, mask) > 0 then 493 | v = v + framedata[0] * p.channelscale[i] 494 | -- I can see your pointers from here~ o///o 495 | framedata = framedata + 1 496 | end 497 | return v 498 | end 499 | 500 | anims.frames = {} 501 | for i = 1, header.num_frames do 502 | local frame = {} 503 | for j, p in ipairs(poses) do 504 | -- This code is in touch with its sensitive side, please leave it be. 505 | local v = {} 506 | for o = 0, 9 do 507 | v[o+1] = readv(p, o, bit.lshift(1, o)) 508 | end 509 | table.insert(frame, { 510 | translate = cpml.vec3(v[1], v[2], v[3]), 511 | rotate = cpml.quat(v[4], v[5], v[6], v[7]), 512 | scale = cpml.vec3(v[8], v[9], v[10]) 513 | }) 514 | end 515 | table.insert(anims.frames, frame) 516 | end 517 | end 518 | 519 | collectgarbage("restart") 520 | return anims 521 | end 522 | 523 | return iqm 524 | -------------------------------------------------------------------------------- /test/main.lua: -------------------------------------------------------------------------------- 1 | local bgfx = require "bgfx" 2 | local cpml = require "cpml" 3 | local iqm = require "iqm" 4 | local model, program, tex, sampler 5 | 6 | local camera = require "camera" { 7 | orbit_offset = cpml.vec3(0, 0, -3), 8 | offset = cpml.vec3(0, 0, -1) 9 | } 10 | 11 | local w, h = love.window.getMode() 12 | 13 | function love.load() 14 | bgfx.init() 15 | bgfx.reset(w, h, { "vsync" }) 16 | bgfx.set_debug { "text" } 17 | 18 | local info = bgfx.get_renderer_info() 19 | print(info.name, info.type) 20 | 21 | -- local hmd = bgfx.get_hmd() or {} 22 | -- for k, v in pairs(hmd) do 23 | -- print(k, v) 24 | -- end 25 | 26 | local _tex = love.image.newImageData("assets/textures/grid.png") 27 | tex = bgfx.new_texture(_tex:getString(), _tex:getWidth(), _tex:getHeight(), false) 28 | sampler = bgfx.new_uniform("s_tex", "int", 1) 29 | 30 | model = iqm.load("assets/models/StagePolish0.11.iqm") 31 | -- model = iqm.load("assets/models/chair.iqm") 32 | 33 | bgfx.set_view_clear(0, { "color", "depth" }, 0x303030ff, 1.0, 0) 34 | bgfx.set_view_name(0, "igor") 35 | 36 | program = bgfx.new_program( 37 | love.filesystem.read("assets/shaders/bin/test.vs.bin"), 38 | love.filesystem.read("assets/shaders/bin/test.fs.bin") 39 | ) 40 | 41 | love.mouse.setRelativeMode(true) 42 | end 43 | 44 | function love.mousemoved(_, _, dx, dy) 45 | camera:rotate_xy(dx, dy) 46 | end 47 | 48 | function love.draw() 49 | bgfx.set_marker("miku") 50 | bgfx.debug_text_clear() 51 | bgfx.debug_text_print(0, 0, 0x6f, "ayy lmao") 52 | bgfx.set_view_rect(0, 0, 0, w, h) 53 | bgfx.touch(0) 54 | 55 | camera:update(w, h) 56 | bgfx.set_view_transform(0, camera.view, camera.projection) 57 | 58 | for _, mesh in ipairs(model) do 59 | local m = cpml.mat4() 60 | bgfx.set_vertex_buffer(model.mesh) 61 | bgfx.set_index_buffer(model.ibo, mesh.first, mesh.count) 62 | bgfx.set_state { 63 | "msaa", 64 | "alpha_write", 65 | "cull_ccw", 66 | "rgb_write", 67 | "depth_write", 68 | "depth_test_lequal" 69 | } 70 | bgfx.set_texture(0, sampler, tex) 71 | bgfx.set_transform(m) 72 | bgfx.submit(0, program) 73 | end 74 | 75 | bgfx.frame() 76 | end 77 | 78 | function love.quit() 79 | bgfx.shutdown() 80 | end 81 | 82 | function love.run() 83 | love.event.pump() 84 | 85 | if love.load then love.load(arg) end 86 | 87 | collectgarbage "collect" 88 | 89 | if love.timer then love.timer.step() end 90 | 91 | local dt = 0 92 | while true do 93 | if love.event then 94 | love.event.pump() 95 | for name, a,b,c,d,e,f in love.event.poll() do 96 | if name == "keypressed" and a == "escape" and 97 | (love.keyboard.isDown "lshift" or love.keyboard.isDown "rshift") 98 | then 99 | love.event.quit() 100 | elseif name == "quit" then 101 | if not love.quit or not love.quit() then 102 | return a 103 | end 104 | end 105 | love.handlers[name](a, b, c, d, e, f) 106 | end 107 | end 108 | 109 | -- Update dt, as we'll be passing it to update 110 | if love.timer then 111 | love.timer.step() 112 | dt = love.timer.getDelta() 113 | end 114 | 115 | if love.update then 116 | love.update(dt) 117 | end 118 | 119 | if love.draw then 120 | love.draw() 121 | end 122 | 123 | collectgarbage("step") 124 | love.timer.sleep(0.001) 125 | end 126 | end 127 | -------------------------------------------------------------------------------- /wrap_bgfx.cpp: -------------------------------------------------------------------------------- 1 | extern "C" { 2 | #include 3 | #include 4 | #include 5 | 6 | // "but holo, this is C++, why are you using the C API?", you might ask. 7 | // well, my dear reader, it is simple: C APIs are easier to comprehend. 8 | // 9 | // Lua doesn't really do C++-style magic anyhow. 10 | #include 11 | #include 12 | } 13 | 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | // buffer up to 512 mat4's worth of data, we can't even fit that in one send. 22 | #define UNIFORM_BUFFER_SIZE 512*16*sizeof(float) 23 | static bool shutdown = false; 24 | static void *uniform_buffer; 25 | 26 | #ifndef luaL_typerror 27 | LUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) { 28 | const char *msg = lua_pushfstring(L, 29 | "%s expected, got %s", tname, luaL_typename(L, narg) 30 | ); 31 | return luaL_argerror(L, narg, msg); 32 | } 33 | #endif 34 | 35 | #define UD_HANDLE_TYPE2(name) \ 36 | static bgfx_##name##_t *to_##name##_ud(lua_State *L, int index) { \ 37 | bgfx_##name##_t *ud = (bgfx_##name##_t*)lua_touserdata(L, index); \ 38 | if (ud == NULL) luaL_typerror(L, index, #name); \ 39 | return ud; \ 40 | } 41 | 42 | #define UD_HANDLE_TYPE(name) \ 43 | static bgfx_##name##_handle_t *to_##name##_ud(lua_State *L, int index) { \ 44 | bgfx_##name##_handle_t *ud = (bgfx_##name##_handle_t*)lua_touserdata(L, index); \ 45 | if (ud == NULL) luaL_typerror(L, index, #name); \ 46 | return ud; \ 47 | } 48 | 49 | UD_HANDLE_TYPE(program) 50 | UD_HANDLE_TYPE(texture) 51 | UD_HANDLE_TYPE(uniform) 52 | UD_HANDLE_TYPE(vertex_buffer) 53 | UD_HANDLE_TYPE(index_buffer) 54 | UD_HANDLE_TYPE(frame_buffer) 55 | 56 | UD_HANDLE_TYPE2(transient_index_buffer) 57 | UD_HANDLE_TYPE2(transient_vertex_buffer) 58 | UD_HANDLE_TYPE2(instance_data_buffer) 59 | UD_HANDLE_TYPE2(vertex_decl) 60 | 61 | #undef UD_HANDLE_TYPE 62 | #undef UD_HANDLE_TYPE2 63 | 64 | static const luaL_Reg program_fn[] = { 65 | { "__gc", [](lua_State *L) { 66 | if (shutdown) { return 0; } 67 | bgfx_program_handle_t *ud = to_program_ud(L, 1); 68 | bgfx_destroy_program(*ud); 69 | return 0; 70 | } }, 71 | { "__tostring", [](lua_State *L) { 72 | char buff[64]; 73 | sprintf(buff, "%p", to_program_ud(L, 1)); 74 | lua_pushfstring(L, "bgfx_program (%s)", buff); 75 | return 1; 76 | } }, 77 | { NULL, NULL } 78 | }; 79 | 80 | static const luaL_Reg texture_fn[] = { 81 | { "__gc", [](lua_State *L) { 82 | if (shutdown) { return 0; } 83 | return 0; 84 | bgfx_texture_handle_t *ud = to_texture_ud(L, 1); 85 | bgfx_destroy_texture(*ud); 86 | return 0; 87 | } }, 88 | { "__tostring", [](lua_State *L) { 89 | char buff[64]; 90 | sprintf(buff, "%p", to_texture_ud(L, 1)); 91 | lua_pushfstring(L, "bgfx_texture (%s)", buff); 92 | return 1; 93 | } }, 94 | { NULL, NULL } 95 | }; 96 | 97 | static const luaL_Reg uniform_fn[] = { 98 | { "__gc", [](lua_State *L) { 99 | if (shutdown) { return 0; } 100 | bgfx_uniform_handle_t *ud = to_uniform_ud(L, 1); 101 | bgfx_destroy_uniform(*ud); 102 | return 0; 103 | } }, 104 | { "__tostring", [](lua_State *L) { 105 | char buff[64]; 106 | sprintf(buff, "%p", to_uniform_ud(L, 1)); 107 | lua_pushfstring(L, "bgfx_uniform (%s)", buff); 108 | return 1; 109 | } }, 110 | { NULL, NULL } 111 | }; 112 | 113 | static const luaL_Reg vertex_format_fn[] = { 114 | { "__tostring", [](lua_State *L) { 115 | char buff[64]; 116 | sprintf(buff, "%p", to_vertex_decl_ud(L, 1)); 117 | lua_pushfstring(L, "bgfx_vertex_decl (%s)", buff); 118 | return 1; 119 | } }, 120 | { NULL, NULL } 121 | }; 122 | 123 | static const luaL_Reg vertex_buffer_fn[] = { 124 | { "__gc", [](lua_State *L) { 125 | if (shutdown) { return 0; } 126 | bgfx_vertex_buffer_handle_t *ud = to_vertex_buffer_ud(L, 1); 127 | bgfx_destroy_vertex_buffer(*ud); 128 | return 0; 129 | } }, 130 | { "__tostring", [](lua_State *L) { 131 | char buff[64]; 132 | sprintf(buff, "%p", to_vertex_buffer_ud(L, 1)); 133 | lua_pushfstring(L, "bgfx_vertex_buffer (%s)", buff); 134 | return 1; 135 | } }, 136 | { NULL, NULL } 137 | }; 138 | 139 | static const luaL_Reg index_buffer_fn[] = { 140 | { "__gc", [](lua_State *L) { 141 | if (shutdown) { return 0; } 142 | bgfx_index_buffer_handle_t *ud = to_index_buffer_ud(L, 1); 143 | bgfx_destroy_index_buffer(*ud); 144 | return 0; 145 | } }, 146 | { "__tostring", [](lua_State *L) { 147 | char buff[64]; 148 | sprintf(buff, "%p", to_index_buffer_ud(L, 1)); 149 | lua_pushfstring(L, "bgfx_index_buffer (%s)", buff); 150 | return 1; 151 | } }, 152 | { NULL, NULL } 153 | }; 154 | 155 | static const luaL_Reg frame_buffer_fn[] = { 156 | { "__gc", [](lua_State *L) { 157 | if (shutdown) { return 0; } 158 | bgfx_frame_buffer_handle_t *ud = to_frame_buffer_ud(L, 1); 159 | bgfx_destroy_frame_buffer(*ud); 160 | return 0; 161 | } }, 162 | { "__tostring", [](lua_State *L) { 163 | char buff[64]; 164 | sprintf(buff, "%p", to_frame_buffer_ud(L, 1)); 165 | lua_pushfstring(L, "bgfx_frame_buffer (%s)", buff); 166 | return 1; 167 | } }, 168 | { NULL, NULL } 169 | }; 170 | 171 | 172 | static const luaL_Reg transient_ib_fn[] = { 173 | { "__tostring", [](lua_State *L) { 174 | char buff[64]; 175 | sprintf(buff, "%p", to_frame_buffer_ud(L, 1)); 176 | lua_pushfstring(L, "bgfx_transient_index_buffer (%s)", buff); 177 | return 1; 178 | } }, 179 | { NULL, NULL } 180 | }; 181 | 182 | static const luaL_Reg transient_vb_fn[] = { 183 | { "__tostring", [](lua_State *L) { 184 | char buff[64]; 185 | sprintf(buff, "%p", to_frame_buffer_ud(L, 1)); 186 | lua_pushfstring(L, "bgfx_transient_vertex_buffer (%s)", buff); 187 | return 1; 188 | } }, 189 | { NULL, NULL } 190 | }; 191 | 192 | struct fuck_off_cpp { 193 | bool operator() (char const *a, char const *b) { 194 | return std::strcmp(a, b) < 0; 195 | } 196 | }; 197 | 198 | static std::map access_lookup = { 199 | { "read", BGFX_ACCESS_READ }, 200 | { "write", BGFX_ACCESS_WRITE }, 201 | { "read_write", BGFX_ACCESS_READWRITE } 202 | }; 203 | 204 | // incomplete: doesn't include the macro function stuff 205 | static std::map buffer_lookup = { 206 | { "none", BGFX_BUFFER_NONE }, 207 | 208 | { "compute_format_8x1", BGFX_BUFFER_COMPUTE_FORMAT_8x1 }, 209 | { "compute_format_8x2", BGFX_BUFFER_COMPUTE_FORMAT_8x2 }, 210 | { "compute_format_8x4", BGFX_BUFFER_COMPUTE_FORMAT_8x4 }, 211 | { "compute_format_16x1", BGFX_BUFFER_COMPUTE_FORMAT_16x1 }, 212 | { "compute_format_16x2", BGFX_BUFFER_COMPUTE_FORMAT_16x2 }, 213 | { "compute_format_16x4", BGFX_BUFFER_COMPUTE_FORMAT_16x4 }, 214 | { "compute_format_32x1", BGFX_BUFFER_COMPUTE_FORMAT_32x1 }, 215 | { "compute_format_32x2", BGFX_BUFFER_COMPUTE_FORMAT_32x2 }, 216 | { "compute_format_32x4", BGFX_BUFFER_COMPUTE_FORMAT_32x4 }, 217 | 218 | { "compute_type_uint", BGFX_BUFFER_COMPUTE_TYPE_UINT }, 219 | { "compute_type_int", BGFX_BUFFER_COMPUTE_TYPE_INT }, 220 | { "compute_type_float", BGFX_BUFFER_COMPUTE_TYPE_FLOAT }, 221 | 222 | { "compute_read", BGFX_BUFFER_COMPUTE_READ }, 223 | { "compute_write", BGFX_BUFFER_COMPUTE_WRITE }, 224 | 225 | { "draw_indirect", BGFX_BUFFER_DRAW_INDIRECT }, 226 | { "allow_resize", BGFX_BUFFER_ALLOW_RESIZE }, 227 | { "index32", BGFX_BUFFER_INDEX32 }//, 228 | 229 | //{ "compute_read_write", BGFX_BUFFER_COMPUTE_READ_WRITE } 230 | }; 231 | 232 | static std::map caps_lookup = { 233 | { "texture_compare_lequal", BGFX_CAPS_TEXTURE_COMPARE_LEQUAL }, 234 | { "texture_compare_all", BGFX_CAPS_TEXTURE_COMPARE_ALL }, 235 | 236 | { "vertex_attrib_half", BGFX_CAPS_VERTEX_ATTRIB_HALF }, 237 | { "vertex_attrib_uint10", BGFX_CAPS_VERTEX_ATTRIB_UINT10 }, 238 | 239 | { "texture_3d", BGFX_CAPS_TEXTURE_3D }, 240 | { "instancing", BGFX_CAPS_INSTANCING }, 241 | { "renderer_multithreading", BGFX_CAPS_RENDERER_MULTITHREADED }, 242 | { "fragment_depth", BGFX_CAPS_FRAGMENT_DEPTH }, 243 | { "blend_independent", BGFX_CAPS_BLEND_INDEPENDENT }, 244 | { "compute", BGFX_CAPS_COMPUTE }, 245 | { "fragment_ordering", BGFX_CAPS_FRAGMENT_ORDERING }, 246 | { "swap_chain", BGFX_CAPS_SWAP_CHAIN }, 247 | { "hmd", BGFX_CAPS_HMD }, 248 | { "index32", BGFX_CAPS_INDEX32 }, 249 | { "draw_indirect", BGFX_CAPS_DRAW_INDIRECT }, 250 | { "hidpi", BGFX_CAPS_HIDPI }, 251 | { "texture_blit", BGFX_CAPS_TEXTURE_BLIT }, 252 | { "texture_read_back", BGFX_CAPS_TEXTURE_READ_BACK }, 253 | { "occlusion_query", BGFX_CAPS_OCCLUSION_QUERY }, 254 | { "alpha_to_converge", BGFX_CAPS_ALPHA_TO_COVERAGE }, 255 | { "conservative_raster", BGFX_CAPS_CONSERVATIVE_RASTER }, 256 | 257 | { "format_texture_none", BGFX_CAPS_FORMAT_TEXTURE_NONE }, 258 | { "format_texture_2d", BGFX_CAPS_FORMAT_TEXTURE_2D }, 259 | { "format_texture_2d_srgb", BGFX_CAPS_FORMAT_TEXTURE_2D_SRGB }, 260 | { "format_texture_2d_emulated", BGFX_CAPS_FORMAT_TEXTURE_2D_EMULATED }, 261 | { "format_texture_3d", BGFX_CAPS_FORMAT_TEXTURE_3D }, 262 | { "format_texture_3d_srgb", BGFX_CAPS_FORMAT_TEXTURE_3D_SRGB }, 263 | { "format_texture_3d_emulated", BGFX_CAPS_FORMAT_TEXTURE_3D_EMULATED }, 264 | { "format_texture_cube", BGFX_CAPS_FORMAT_TEXTURE_CUBE }, 265 | { "format_texture_cube_srgb", BGFX_CAPS_FORMAT_TEXTURE_CUBE_SRGB }, 266 | { "format_texture_cube_emulated", BGFX_CAPS_FORMAT_TEXTURE_CUBE_EMULATED }, 267 | { "format_texture_vertex", BGFX_CAPS_FORMAT_TEXTURE_VERTEX }, 268 | { "format_texture_image", BGFX_CAPS_FORMAT_TEXTURE_IMAGE }, 269 | { "format_texture_framebuffer", BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER }, 270 | { "format_texture_framebuffer_msaa", BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA }, 271 | { "format_texture_msaa", BGFX_CAPS_FORMAT_TEXTURE_MSAA } 272 | }; 273 | 274 | static std::map clear_lookup = { 275 | { "none", BGFX_CLEAR_NONE }, 276 | { "color", BGFX_CLEAR_COLOR }, 277 | { "depth", BGFX_CLEAR_DEPTH }, 278 | { "stencil", BGFX_CLEAR_STENCIL }, 279 | 280 | { "discard_color_0", BGFX_CLEAR_DISCARD_COLOR_0 }, 281 | { "discard_color_1", BGFX_CLEAR_DISCARD_COLOR_1 }, 282 | { "discard_color_2", BGFX_CLEAR_DISCARD_COLOR_2 }, 283 | { "discard_color_3", BGFX_CLEAR_DISCARD_COLOR_3 }, 284 | { "discard_color_4", BGFX_CLEAR_DISCARD_COLOR_4 }, 285 | { "discard_color_5", BGFX_CLEAR_DISCARD_COLOR_5 }, 286 | { "discard_color_6", BGFX_CLEAR_DISCARD_COLOR_6 }, 287 | { "discard_color_7", BGFX_CLEAR_DISCARD_COLOR_7 }, 288 | { "discard_depth", BGFX_CLEAR_DISCARD_DEPTH }, 289 | { "discard_stencil", BGFX_CLEAR_DISCARD_STENCIL } 290 | }; 291 | 292 | static std::map cube_map_lookup = { 293 | { "positive_x", BGFX_CUBE_MAP_POSITIVE_X }, 294 | { "negative_x", BGFX_CUBE_MAP_NEGATIVE_X }, 295 | { "positive_y", BGFX_CUBE_MAP_POSITIVE_Y }, 296 | { "negative_y", BGFX_CUBE_MAP_NEGATIVE_Y }, 297 | { "positive_z", BGFX_CUBE_MAP_POSITIVE_Z }, 298 | { "negative_z", BGFX_CUBE_MAP_NEGATIVE_Z } 299 | }; 300 | 301 | static std::map debug_lookup = { 302 | { "none", BGFX_DEBUG_NONE }, 303 | { "wireframe", BGFX_DEBUG_WIREFRAME }, 304 | { "stats", BGFX_DEBUG_STATS }, 305 | { "ifh", BGFX_DEBUG_IFH }, 306 | { "text", BGFX_DEBUG_TEXT } 307 | }; 308 | 309 | static std::map hmd_lookup = { 310 | { "none", BGFX_HMD_NONE }, 311 | { "device_resolution", BGFX_HMD_DEVICE_RESOLUTION }, 312 | { "rendering", BGFX_HMD_RENDERING } 313 | }; 314 | 315 | static std::map reset_lookup = { 316 | { "none", BGFX_RESET_NONE }, 317 | { "fullscreen", BGFX_RESET_FULLSCREEN }, 318 | { "msaa_x2", BGFX_RESET_MSAA_X2 }, 319 | { "msaa_x4", BGFX_RESET_MSAA_X4 }, 320 | { "msaa_x8", BGFX_RESET_MSAA_X8 }, 321 | { "msaa_x16", BGFX_RESET_MSAA_X16 }, 322 | { "vsync", BGFX_RESET_VSYNC }, 323 | { "max_anisotropy", BGFX_RESET_MAXANISOTROPY }, 324 | { "capture", BGFX_RESET_CAPTURE }, 325 | { "hmd", BGFX_RESET_HMD }, 326 | { "hmd_debug", BGFX_RESET_HMD_DEBUG }, 327 | { "hmd_recenter", BGFX_RESET_HMD_RECENTER }, 328 | { "flush_after_render", BGFX_RESET_FLUSH_AFTER_RENDER }, 329 | { "flip_after_render", BGFX_RESET_FLIP_AFTER_RENDER }, 330 | { "srgb_backbuffer", BGFX_RESET_SRGB_BACKBUFFER }, 331 | { "hidpi", BGFX_RESET_HIDPI }, 332 | { "depth_clamp", BGFX_RESET_DEPTH_CLAMP }, 333 | { "suspend", BGFX_RESET_SUSPEND }, 334 | }; 335 | 336 | // incomplete: doesn't include the macro function stuff 337 | static std::map state_lookup = { 338 | { "none", BGFX_STATE_NONE }, 339 | { "default", BGFX_STATE_DEFAULT }, 340 | 341 | { "rgb_write", BGFX_STATE_RGB_WRITE }, 342 | { "alpha_write", BGFX_STATE_ALPHA_WRITE }, 343 | { "depth_write", BGFX_STATE_DEPTH_WRITE }, 344 | 345 | { "depth_test_less", BGFX_STATE_DEPTH_TEST_LESS }, 346 | { "depth_test_lequal", BGFX_STATE_DEPTH_TEST_LEQUAL }, 347 | { "depth_test_equal", BGFX_STATE_DEPTH_TEST_EQUAL }, 348 | { "depth_test_gequal", BGFX_STATE_DEPTH_TEST_GEQUAL }, 349 | { "depth_test_greater", BGFX_STATE_DEPTH_TEST_GREATER }, 350 | { "depth_test_notequal", BGFX_STATE_DEPTH_TEST_NOTEQUAL }, 351 | { "depth_test_never", BGFX_STATE_DEPTH_TEST_NEVER }, 352 | { "depth_test_always", BGFX_STATE_DEPTH_TEST_ALWAYS }, 353 | 354 | { "blend_zero", BGFX_STATE_BLEND_ZERO }, 355 | { "blend_one", BGFX_STATE_BLEND_ONE }, 356 | { "blend_src_color", BGFX_STATE_BLEND_SRC_COLOR }, 357 | { "blend_inv_src_color", BGFX_STATE_BLEND_INV_SRC_COLOR }, 358 | { "blend_src_alpha", BGFX_STATE_BLEND_SRC_ALPHA }, 359 | { "blend_inv_src_alpha", BGFX_STATE_BLEND_INV_SRC_ALPHA }, 360 | { "blend_dst_alpha", BGFX_STATE_BLEND_DST_ALPHA }, 361 | { "blend_inv_dst_alpha", BGFX_STATE_BLEND_INV_DST_ALPHA }, 362 | { "blend_dst_color", BGFX_STATE_BLEND_DST_COLOR }, 363 | { "blend_inv_dst_color", BGFX_STATE_BLEND_INV_DST_COLOR }, 364 | { "blend_src_alpha_sat", BGFX_STATE_BLEND_SRC_ALPHA_SAT }, 365 | // { "blend_factor", BGFX_STATE_BLEND_FACTOR }, 366 | // { "blend_inv_factor", BGFX_STATE_BLEND_INV_FACTOR }, 367 | 368 | { "alpha_to_coverage", BGFX_STATE_BLEND_ALPHA_TO_COVERAGE }, 369 | 370 | { "cull_cw", BGFX_STATE_CULL_CW }, 371 | { "cull_ccw", BGFX_STATE_CULL_CCW }, 372 | 373 | { "pt_tristrip", BGFX_STATE_PT_TRISTRIP }, 374 | { "pt_lines", BGFX_STATE_PT_LINES }, 375 | { "pt_linestrip", BGFX_STATE_PT_LINESTRIP }, 376 | { "pt_points", BGFX_STATE_PT_POINTS }, 377 | 378 | { "msaa", BGFX_STATE_MSAA }, 379 | { "line_aa", BGFX_STATE_LINEAA }, 380 | { "conservative_raster", BGFX_STATE_CONSERVATIVE_RASTER }, 381 | 382 | { "blend_add", BGFX_STATE_BLEND_ADD }, 383 | { "blend_alpha", BGFX_STATE_BLEND_ALPHA }, 384 | { "blend_darken", BGFX_STATE_BLEND_DARKEN }, 385 | { "blend_lighten", BGFX_STATE_BLEND_LIGHTEN }, 386 | { "blend_multiply", BGFX_STATE_BLEND_MULTIPLY }, 387 | { "blend_normal", BGFX_STATE_BLEND_NORMAL }, 388 | { "blend_screen", BGFX_STATE_BLEND_SCREEN }, 389 | { "blend_linear_burn", BGFX_STATE_BLEND_LINEAR_BURN } 390 | }; 391 | 392 | // incomplete: doesn't include the macro function stuff 393 | static std::map stencil_lookup = { 394 | { "test_less", BGFX_STENCIL_TEST_LESS }, 395 | { "test_lequal", BGFX_STENCIL_TEST_LEQUAL }, 396 | { "test_equal", BGFX_STENCIL_TEST_EQUAL }, 397 | { "test_gequal", BGFX_STENCIL_TEST_GEQUAL }, 398 | { "test_greater", BGFX_STENCIL_TEST_GREATER }, 399 | { "test_notequal", BGFX_STENCIL_TEST_NOTEQUAL }, 400 | { "test_never", BGFX_STENCIL_TEST_NEVER }, 401 | { "test_always", BGFX_STENCIL_TEST_ALWAYS }, 402 | 403 | { "op_fail_s_zero", BGFX_STENCIL_OP_FAIL_S_ZERO }, 404 | { "op_fail_s_keep", BGFX_STENCIL_OP_FAIL_S_KEEP }, 405 | { "op_fail_s_replace", BGFX_STENCIL_OP_FAIL_S_REPLACE }, 406 | { "op_fail_s_incr", BGFX_STENCIL_OP_FAIL_S_INCR }, 407 | { "op_fail_s_incrsat", BGFX_STENCIL_OP_FAIL_S_INCRSAT }, 408 | { "op_fail_s_decr", BGFX_STENCIL_OP_FAIL_S_DECR }, 409 | { "op_fail_s_decrsat", BGFX_STENCIL_OP_FAIL_S_DECRSAT }, 410 | { "op_fail_s_invert", BGFX_STENCIL_OP_FAIL_S_INVERT }, 411 | 412 | { "op_fail_z_zero", BGFX_STENCIL_OP_FAIL_Z_ZERO }, 413 | { "op_fail_z_keep", BGFX_STENCIL_OP_FAIL_Z_KEEP }, 414 | { "op_fail_z_replace", BGFX_STENCIL_OP_FAIL_Z_REPLACE }, 415 | { "op_fail_z_incr", BGFX_STENCIL_OP_FAIL_Z_INCR }, 416 | { "op_fail_z_incrsat", BGFX_STENCIL_OP_FAIL_Z_INCRSAT }, 417 | { "op_fail_z_decr", BGFX_STENCIL_OP_FAIL_Z_DECR }, 418 | { "op_fail_z_decrsat", BGFX_STENCIL_OP_FAIL_Z_DECRSAT }, 419 | { "op_fail_z_invert", BGFX_STENCIL_OP_FAIL_Z_INVERT }, 420 | 421 | { "op_pass_z_zero", BGFX_STENCIL_OP_PASS_Z_ZERO }, 422 | { "op_pass_z_keep", BGFX_STENCIL_OP_PASS_Z_KEEP }, 423 | { "op_pass_z_replace", BGFX_STENCIL_OP_PASS_Z_REPLACE }, 424 | { "op_pass_z_incr", BGFX_STENCIL_OP_PASS_Z_INCR }, 425 | { "op_pass_z_incrsat", BGFX_STENCIL_OP_PASS_Z_INCRSAT }, 426 | { "op_pass_z_decr", BGFX_STENCIL_OP_PASS_Z_DECR }, 427 | { "op_pass_z_decrsat", BGFX_STENCIL_OP_PASS_Z_DECRSAT }, 428 | { "op_pass_z_invert", BGFX_STENCIL_OP_PASS_Z_INVERT }, 429 | 430 | { "none", BGFX_STENCIL_NONE }, 431 | { "default", BGFX_STENCIL_DEFAULT }//, 432 | 433 | //{ "func_ref", BGFX_STENCIL_FUNC_REF }, 434 | //{ "func_rmask", BGFX_STENCIL_FUNC_RMASK } 435 | }; 436 | 437 | static std::map submit_eye_lookup = { 438 | { "left", BGFX_SUBMIT_EYE_LEFT }, 439 | { "right", BGFX_SUBMIT_EYE_RIGHT }, 440 | { "first", BGFX_SUBMIT_EYE_FIRST } 441 | }; 442 | 443 | // incomplete: doesn't include the macro function stuff 444 | static std::map texture_lookup = { 445 | { "none", BGFX_TEXTURE_NONE }, 446 | 447 | { "u_mirror", BGFX_TEXTURE_U_MIRROR }, 448 | { "u_clamp", BGFX_TEXTURE_U_CLAMP }, 449 | { "u_border", BGFX_TEXTURE_U_BORDER }, 450 | { "v_mirror", BGFX_TEXTURE_V_MIRROR }, 451 | { "v_clamp", BGFX_TEXTURE_V_CLAMP }, 452 | { "v_border", BGFX_TEXTURE_V_BORDER }, 453 | { "w_mirror", BGFX_TEXTURE_W_MIRROR }, 454 | { "w_clamp", BGFX_TEXTURE_W_CLAMP }, 455 | { "w_border", BGFX_TEXTURE_W_BORDER }, 456 | 457 | { "min_point", BGFX_TEXTURE_MIN_POINT }, 458 | { "min_anisotropic", BGFX_TEXTURE_MIN_ANISOTROPIC }, 459 | { "mag_point", BGFX_TEXTURE_MAG_POINT }, 460 | { "mag_anisotropic", BGFX_TEXTURE_MAG_ANISOTROPIC }, 461 | { "mip_point", BGFX_TEXTURE_MIP_POINT }, 462 | 463 | { "msaa_sample", BGFX_TEXTURE_MSAA_SAMPLE }, 464 | { "rt", BGFX_TEXTURE_RT }, 465 | { "rt_msaa_x2", BGFX_TEXTURE_RT_MSAA_X2 }, 466 | { "rt_msaa_x4", BGFX_TEXTURE_RT_MSAA_X4 }, 467 | { "rt_msaa_x8", BGFX_TEXTURE_RT_MSAA_X8 }, 468 | { "rt_msaa_x16", BGFX_TEXTURE_RT_MSAA_X16 }, 469 | { "rt_write_only", BGFX_TEXTURE_RT_WRITE_ONLY }, 470 | 471 | { "compare_less", BGFX_TEXTURE_COMPARE_LESS }, 472 | { "compare_lequal", BGFX_TEXTURE_COMPARE_LEQUAL }, 473 | { "compare_equal", BGFX_TEXTURE_COMPARE_EQUAL }, 474 | { "compare_gequal", BGFX_TEXTURE_COMPARE_GEQUAL }, 475 | { "compare_greater", BGFX_TEXTURE_COMPARE_GREATER }, 476 | { "compare_notequal", BGFX_TEXTURE_COMPARE_NOTEQUAL }, 477 | { "compare_never", BGFX_TEXTURE_COMPARE_NEVER }, 478 | { "compare_always", BGFX_TEXTURE_COMPARE_ALWAYS }, 479 | { "compute_write", BGFX_TEXTURE_COMPUTE_WRITE }, 480 | 481 | { "srgb", BGFX_TEXTURE_SRGB }, 482 | { "blit_dst", BGFX_TEXTURE_BLIT_DST }, 483 | { "read_back", BGFX_TEXTURE_READ_BACK }//, 484 | //{ "border_color", BGFX_TEXTURE_BORDER_COLOR } 485 | }; 486 | 487 | static std::map texture_format_lookup = { 488 | { "bc1", BGFX_TEXTURE_FORMAT_BC1 }, 489 | { "bc2", BGFX_TEXTURE_FORMAT_BC2 }, 490 | { "bc3", BGFX_TEXTURE_FORMAT_BC3 }, 491 | { "bc4", BGFX_TEXTURE_FORMAT_BC4 }, 492 | { "bc5", BGFX_TEXTURE_FORMAT_BC5 }, 493 | { "bc6h", BGFX_TEXTURE_FORMAT_BC6H }, 494 | { "bc7", BGFX_TEXTURE_FORMAT_BC7 }, 495 | { "etc1", BGFX_TEXTURE_FORMAT_ETC1 }, 496 | { "etc2", BGFX_TEXTURE_FORMAT_ETC2 }, 497 | { "etc2a", BGFX_TEXTURE_FORMAT_ETC2A }, 498 | { "etc2a1", BGFX_TEXTURE_FORMAT_ETC2A1 }, 499 | { "ptc12", BGFX_TEXTURE_FORMAT_PTC12 }, 500 | { "ptc14", BGFX_TEXTURE_FORMAT_PTC14 }, 501 | { "ptc12a", BGFX_TEXTURE_FORMAT_PTC12A }, 502 | { "ptc14a", BGFX_TEXTURE_FORMAT_PTC14A }, 503 | { "ptc22", BGFX_TEXTURE_FORMAT_PTC22 }, 504 | { "ptc24", BGFX_TEXTURE_FORMAT_PTC24 }, 505 | { "r1", BGFX_TEXTURE_FORMAT_R1 }, 506 | { "a8", BGFX_TEXTURE_FORMAT_A8 }, 507 | { "r8", BGFX_TEXTURE_FORMAT_R8 }, 508 | { "r8i", BGFX_TEXTURE_FORMAT_R8I }, 509 | { "r8u", BGFX_TEXTURE_FORMAT_R8U }, 510 | { "r8s", BGFX_TEXTURE_FORMAT_R8S }, 511 | { "r16", BGFX_TEXTURE_FORMAT_R16 }, 512 | { "r16i", BGFX_TEXTURE_FORMAT_R16I }, 513 | { "r16u", BGFX_TEXTURE_FORMAT_R16U }, 514 | { "r16f", BGFX_TEXTURE_FORMAT_R16F }, 515 | { "r16s", BGFX_TEXTURE_FORMAT_R16S }, 516 | { "r32i", BGFX_TEXTURE_FORMAT_R32I }, 517 | { "r32u", BGFX_TEXTURE_FORMAT_R32U }, 518 | { "r32f", BGFX_TEXTURE_FORMAT_R32F }, 519 | { "rg8", BGFX_TEXTURE_FORMAT_RG8 }, 520 | { "rg8i", BGFX_TEXTURE_FORMAT_RG8I }, 521 | { "rg8u", BGFX_TEXTURE_FORMAT_RG8U }, 522 | { "rg8s", BGFX_TEXTURE_FORMAT_RG8S }, 523 | { "rg16", BGFX_TEXTURE_FORMAT_RG16 }, 524 | { "rg16i", BGFX_TEXTURE_FORMAT_RG16I }, 525 | { "rg16u", BGFX_TEXTURE_FORMAT_RG16U }, 526 | { "rg16f", BGFX_TEXTURE_FORMAT_RG16F }, 527 | { "rg16s", BGFX_TEXTURE_FORMAT_RG16S }, 528 | { "rg32i", BGFX_TEXTURE_FORMAT_RG32I }, 529 | { "rg32u", BGFX_TEXTURE_FORMAT_RG32U }, 530 | { "rg32f", BGFX_TEXTURE_FORMAT_RG32F }, 531 | { "rgb8", BGFX_TEXTURE_FORMAT_RGB8 }, 532 | { "rgb8i", BGFX_TEXTURE_FORMAT_RGB8I }, 533 | { "rgb8u", BGFX_TEXTURE_FORMAT_RGB8U }, 534 | { "rgb8s", BGFX_TEXTURE_FORMAT_RGB8S }, 535 | { "rgb9e5f", BGFX_TEXTURE_FORMAT_RGB9E5F }, 536 | { "bgra8", BGFX_TEXTURE_FORMAT_BGRA8 }, 537 | { "rgba8", BGFX_TEXTURE_FORMAT_RGBA8 }, 538 | { "rgba8i", BGFX_TEXTURE_FORMAT_RGBA8I }, 539 | { "rgba8u", BGFX_TEXTURE_FORMAT_RGBA8U }, 540 | { "rgba8s", BGFX_TEXTURE_FORMAT_RGBA8S }, 541 | { "rgba16", BGFX_TEXTURE_FORMAT_RGBA16 }, 542 | { "rgba16i", BGFX_TEXTURE_FORMAT_RGBA16I }, 543 | { "rgba16u", BGFX_TEXTURE_FORMAT_RGBA16U }, 544 | { "rgba16f", BGFX_TEXTURE_FORMAT_RGBA16F }, 545 | { "rgba16s", BGFX_TEXTURE_FORMAT_RGBA16S }, 546 | { "rgba32i", BGFX_TEXTURE_FORMAT_RGBA32I }, 547 | { "rgba32u", BGFX_TEXTURE_FORMAT_RGBA32U }, 548 | { "rgba32f", BGFX_TEXTURE_FORMAT_RGBA32F }, 549 | { "r5g6b5", BGFX_TEXTURE_FORMAT_R5G6B5 }, 550 | { "rgba4", BGFX_TEXTURE_FORMAT_RGBA4 }, 551 | { "rgb5a1", BGFX_TEXTURE_FORMAT_RGB5A1 }, 552 | { "rgb10a2", BGFX_TEXTURE_FORMAT_RGB10A2 }, 553 | { "r11g11b10f", BGFX_TEXTURE_FORMAT_R11G11B10F }, 554 | { "d16", BGFX_TEXTURE_FORMAT_D16 }, 555 | { "d24", BGFX_TEXTURE_FORMAT_D24 }, 556 | { "d24s8", BGFX_TEXTURE_FORMAT_D24S8 }, 557 | { "d32", BGFX_TEXTURE_FORMAT_D32 }, 558 | { "d16f", BGFX_TEXTURE_FORMAT_D16F }, 559 | { "d24f", BGFX_TEXTURE_FORMAT_D24F }, 560 | { "d32f", BGFX_TEXTURE_FORMAT_D32F }, 561 | { "d0s8", BGFX_TEXTURE_FORMAT_D0S8 } 562 | }; 563 | 564 | static std::map view_lookup = { 565 | { "none", BGFX_VIEW_NONE }, 566 | { "stereo", BGFX_VIEW_STEREO } 567 | }; 568 | 569 | const char *renderer2str(bgfx_renderer_type_t t) { 570 | switch(t) { 571 | case BGFX_RENDERER_TYPE_DIRECT3D9: return "d3d9"; 572 | case BGFX_RENDERER_TYPE_DIRECT3D11: return "d3d11"; 573 | case BGFX_RENDERER_TYPE_DIRECT3D12: return "d3d12"; 574 | case BGFX_RENDERER_TYPE_METAL: return "metal"; 575 | case BGFX_RENDERER_TYPE_OPENGLES: return "opengles"; 576 | case BGFX_RENDERER_TYPE_OPENGL: return "opengl"; 577 | case BGFX_RENDERER_TYPE_VULKAN: return "vulkan"; 578 | default: break; 579 | } 580 | return "invalid"; 581 | } 582 | 583 | static void stack_dump(lua_State *L) { 584 | int i = lua_gettop(L); 585 | int m = i; 586 | printf("---------------- Stack Dump ----------------\n"); 587 | while(i) { 588 | int t = lua_type(L, i); 589 | int n = -(m - i + 1); 590 | switch (t) { 591 | case LUA_TSTRING: 592 | printf("%d (%d):`%s'\n", i, n, lua_tostring(L, i)); 593 | break; 594 | case LUA_TBOOLEAN: 595 | printf("%d (%d): %s\n", i, n, lua_toboolean(L, i) ? "true" : "false"); 596 | break; 597 | case LUA_TNUMBER: 598 | printf("%d (%d): %g\n", i, n, lua_tonumber(L, i)); 599 | break; 600 | default: 601 | printf("%d (%d): %s\n", i, n, lua_typename(L, t)); 602 | break; 603 | } 604 | i--; 605 | } 606 | printf("--------------- Stack Dump Finished ---------------\n"); 607 | } 608 | 609 | template 610 | static void table_scan(lua_State *L, int index, F fn) { 611 | lua_pushvalue(L, index); 612 | lua_pushnil(L); 613 | 614 | while (lua_next(L, -2)) { 615 | lua_pushvalue(L, -2); 616 | const char *key = lua_tostring(L, -1); 617 | const char *value = lua_tostring(L, -2); 618 | 619 | fn(key, value); 620 | 621 | lua_pop(L, 2); 622 | } 623 | 624 | lua_pop(L, 1); 625 | } 626 | 627 | #include 628 | #include 629 | 630 | static SDL_Window *_window = NULL; 631 | 632 | static const luaL_Reg m[] = { 633 | // bgfx.init() 634 | { "init", [](lua_State *L) { 635 | uniform_buffer = malloc(UNIFORM_BUFFER_SIZE); 636 | memset(uniform_buffer, 0, UNIFORM_BUFFER_SIZE); 637 | 638 | bool use_sdl_context = true; 639 | if (lua_isboolean(L, 1)) { 640 | use_sdl_context = lua_toboolean(L, 1) ? true : false; 641 | } 642 | if (use_sdl_context) { 643 | _window = SDL_GL_GetCurrentWindow(); 644 | 645 | bgfx_platform_data_t data; 646 | SDL_SysWMinfo wmi; 647 | SDL_VERSION(&wmi.version); 648 | if (!SDL_GetWindowWMInfo(_window, &wmi)) { 649 | return 0; 650 | } 651 | memset(&data, 0, sizeof(bgfx_platform_data_t)); 652 | #if BX_PLATFORM_LINUX || BX_PLATFORM_BSD 653 | data.ndt = wmi.info.x11.display; 654 | data.nwh = (void*)(uintptr_t)wmi.info.x11.window; 655 | #elif BX_PLATFORM_OSX 656 | data.nwh = wmi.info.cocoa.window; 657 | #elif BX_PLATFORM_WINDOWS 658 | data.nwh = wmi.info.win.window; 659 | #endif // BX_PLATFORM_ 660 | data.context = SDL_GL_GetCurrentContext(); 661 | SDL_GL_MakeCurrent(_window, NULL); 662 | 663 | bgfx_set_platform_data(&data); 664 | } 665 | if (!bgfx_init(BGFX_RENDERER_TYPE_OPENGL, BGFX_PCI_ID_NONE, 0, NULL, NULL)) { 666 | lua_pushstring(L, "Unable to initialize BGFX."); 667 | lua_error(L); 668 | return 0; 669 | } 670 | return 0; 671 | } }, 672 | 673 | // bgfx.shutdown() 674 | { "shutdown", [](lua_State *) { 675 | free(uniform_buffer); 676 | 677 | shutdown = true; 678 | bgfx_shutdown(); 679 | return 0; 680 | } }, 681 | 682 | // bgfx.debug_text_print(1, 1, 0x7f, string.format("hello %s!", "world")) 683 | { "debug_text_print", [](lua_State *L) { 684 | int n = lua_gettop(L); 685 | lua_assert(n == 4); 686 | (void)n; 687 | uint16_t x = (uint16_t)lua_tonumber(L, 1); 688 | uint16_t y = (uint16_t)lua_tonumber(L, 2); 689 | uint8_t attr = (uint8_t)lua_tonumber(L, 3); 690 | const char *str = lua_tostring(L, 4); 691 | bgfx_dbg_text_printf(x, y, attr, "%s", str); 692 | return 0; 693 | } }, 694 | 695 | // bgfx.debug_text_clear() 696 | { "debug_text_clear", [](lua_State *L) { 697 | int n = lua_gettop(L); 698 | lua_assert(n >= 0 || n <= 2); 699 | uint8_t attr = n >= 1 ? (uint8_t)lua_tonumber(L, 1) : 0; 700 | bool small = n >= 2 ? lua_toboolean(L, 2) > 0 : false; 701 | bgfx_dbg_text_clear(attr, small); 702 | return 0; 703 | } }, 704 | 705 | // bgfx.set_debug { 706 | // "wireframe", 707 | // "stats", 708 | // "ifh", 709 | // "text" 710 | // } 711 | { "set_debug", [](lua_State *L) { 712 | uint32_t debug = 0; 713 | 714 | table_scan(L, 1, [&](const char *, const char *v) { 715 | auto val = debug_lookup.find(v); 716 | if (val != debug_lookup.end()) { 717 | debug |= val->second; 718 | } 719 | }); 720 | 721 | bgfx_set_debug(debug); 722 | 723 | return 0; 724 | } }, 725 | 726 | // bgfx.reset (1280, 720, { 727 | // "vsync", 728 | // "depth_clamp", 729 | // "srgb_backbuffer", 730 | // "flip_after_render", 731 | // "flush_after_render" 732 | // }) 733 | { "reset", [](lua_State *L) { 734 | int n = lua_gettop(L); 735 | lua_assert(n == 3); 736 | (void)n; 737 | int w = (int)lua_tonumber(L, 1); 738 | int h = (int)lua_tonumber(L, 2); 739 | uint32_t reset = 0; 740 | 741 | table_scan(L, 3, [&](const char *, const char *v) { 742 | auto val = reset_lookup.find(v); 743 | if (val != reset_lookup.end()) { 744 | reset |= val->second; 745 | } 746 | }); 747 | 748 | bgfx_reset(w, h, reset); 749 | return 0; 750 | } }, 751 | 752 | // bgfx.reset_view(0) 753 | { "reset_view", [](lua_State *L){ 754 | bgfx_reset_view((uint8_t)luaL_checkinteger(L, 1)); 755 | return 0; 756 | } }, 757 | 758 | // bgfx.touch(0) 759 | { "touch", [](lua_State *L) { 760 | int n = lua_gettop(L); 761 | lua_assert(n == 1); 762 | (void)n; 763 | uint8_t id = (uint8_t)luaL_checkinteger(L, 1); 764 | unsigned int ret = bgfx_touch(id); 765 | lua_pushnumber(L, double(ret)); 766 | return 1; 767 | } }, 768 | 769 | // bgfx.frame() 770 | { "frame", [](lua_State *L) { 771 | int r = bgfx_frame(false); 772 | lua_pushnumber(L, (double)r); 773 | return 1; 774 | } }, 775 | 776 | // bgfx.set_state { 777 | // "msaa", 778 | // "alpha_write", 779 | // "rgb_write", 780 | // "cull_ccw", 781 | // "depth_write", 782 | // "depth_test_lequal" 783 | // } 784 | { "set_state", [](lua_State *L) { 785 | uint64_t flags = 0; 786 | uint32_t rgba = 0; 787 | 788 | table_scan(L, 1, [&](const char *, const char *v) { 789 | auto val = state_lookup.find(v); 790 | if (val != state_lookup.end()) { 791 | flags |= val->second; 792 | } 793 | }); 794 | 795 | bgfx_set_state((uint32_t)flags, rgba); 796 | 797 | return 0; 798 | } }, 799 | 800 | { "set_view_sequential", [](lua_State *L){ 801 | bool enabled = true; 802 | if (lua_isboolean(L, 2)) { 803 | enabled = lua_toboolean(L, 2) ? true : false; 804 | } 805 | bgfx_set_view_seq((uint8_t)luaL_checkinteger(L, 1), enabled); 806 | return 0; 807 | } }, 808 | 809 | // bgfx.set_view_rect(0, 0, 0, 1280, 720) 810 | // bgfx.set_view_rect(0) -- auto set to full extents 811 | { "set_view_rect", [](lua_State *L) { 812 | int n = lua_gettop(L); 813 | lua_assert(n >= 1); 814 | uint8_t id = (uint8_t)luaL_checkinteger(L, 1); 815 | uint16_t x = 0; 816 | uint16_t y = 0; 817 | if (n >= 3) { 818 | x = (uint16_t)lua_tonumber(L, 2); 819 | y = (uint16_t)lua_tonumber(L, 3); 820 | } 821 | if (n <= 3) { 822 | bgfx_set_view_rect_auto(id, x, y, BGFX_BACKBUFFER_RATIO_EQUAL); 823 | return 0; 824 | } 825 | if (n == 5) { 826 | uint16_t w = (uint16_t)lua_tonumber(L, 4); 827 | uint16_t h = (uint16_t)lua_tonumber(L, 5); 828 | bgfx_set_view_rect(id, x, y, w, h); 829 | } else { 830 | lua_assert(false); 831 | } 832 | return 0; 833 | } }, 834 | 835 | // bgfx.set_view_scissor(0, 400, 300, 800, 600) 836 | { "set_view_scissor", [](lua_State *L) { 837 | int n = lua_gettop(L); 838 | lua_assert(n == 5); 839 | (void)n; 840 | uint8_t id = (uint8_t)luaL_checkinteger(L, 1); 841 | uint16_t x = (uint16_t)lua_tonumber(L, 2); 842 | uint16_t y = (uint16_t)lua_tonumber(L, 3); 843 | uint16_t w = (uint16_t)lua_tonumber(L, 4); 844 | uint16_t h = (uint16_t)lua_tonumber(L, 5); 845 | bgfx_set_view_scissor(id, x, y, w, h); 846 | return 0; 847 | } }, 848 | 849 | { "set_view_clear", [](lua_State *L) { 850 | int n = lua_gettop(L); 851 | lua_assert(n == 5); 852 | (void)n; 853 | uint8_t id = (uint8_t)luaL_checkinteger(L, 1); 854 | 855 | uint16_t flags = 0; 856 | table_scan(L, 2, [&](const char *, const char *v) { 857 | auto val = clear_lookup.find(v); 858 | if (val != clear_lookup.end()) { 859 | flags |= val->second; 860 | } 861 | }); 862 | 863 | uint32_t rgba = (uint32_t)lua_tonumber(L, 3); 864 | float depth = (float)lua_tonumber(L, 4); 865 | uint8_t stencil = (uint8_t)lua_tonumber(L, 5); 866 | bgfx_set_view_clear(id, flags, rgba, depth, stencil); 867 | return 0; 868 | } }, 869 | 870 | // bgfx.set_view_name("steve") 871 | { "set_view_name", [](lua_State *L) { 872 | int n = lua_gettop(L); 873 | lua_assert(n == 2); 874 | (void)n; 875 | uint8_t id = (uint8_t)luaL_checkinteger(L, 1); 876 | const char *name = lua_tostring(L, 2); 877 | bgfx_set_view_name(id, name); 878 | return 0; 879 | } }, 880 | 881 | // bgfx.set_view_frame_buffer(0, frame_buffer) 882 | { "set_view_frame_buffer", [](lua_State *L) { 883 | uint8_t id = (uint8_t)luaL_checkinteger(L, 1); 884 | bgfx_frame_buffer_handle_t *fb = to_frame_buffer_ud(L, 2); 885 | bgfx_set_view_frame_buffer(id, *fb); 886 | return 0; 887 | } }, 888 | 889 | // bgfx.set_marker("miku") 890 | { "set_marker", [](lua_State *L) { 891 | bgfx_set_marker(luaL_checkstring(L, 1)); 892 | return 0; 893 | }}, 894 | 895 | // bgfx.submit(0, program) 896 | // bgfx.submit(0, program, 0, false) 897 | { "submit", [](lua_State *L) { 898 | int n = lua_gettop(L); 899 | lua_assert(n == 4); 900 | uint8_t id = (uint8_t)luaL_checkinteger(L, 1); 901 | 902 | bgfx_program_handle_t *program = to_program_ud(L, 2); 903 | int32_t depth = 0; 904 | bool preserve_state = false; 905 | if (n >= 3) { 906 | depth = (int32_t)lua_tonumber(L, 3); 907 | } 908 | if (n >= 4) { 909 | preserve_state = lua_toboolean(L, 4) ? true : false; 910 | } 911 | uint32_t r = bgfx_submit(id, *program, depth, preserve_state); 912 | lua_pushnumber(L, r); 913 | return 1; 914 | } }, 915 | 916 | { "dispatch", [](lua_State *L) { 917 | int n = lua_gettop(L); 918 | (void)n; 919 | lua_assert(n == 4); 920 | 921 | uint8_t id = (uint8_t)luaL_checkinteger(L, 1); 922 | bgfx_program_handle_t *program = to_program_ud(L, 2); 923 | 924 | uint16_t nx = luaL_checkinteger(L, 3); 925 | uint16_t ny = luaL_checkinteger(L, 4); 926 | uint16_t nz = luaL_checkinteger(L, 5); 927 | 928 | // TODO: stereo support 929 | uint32_t r = bgfx_dispatch(id, *program, nx, ny, nz, 0); 930 | 931 | lua_pushnumber(L, r); 932 | 933 | return 1; 934 | }}, 935 | 936 | // TODO: Stereo views 937 | { "set_view_transform", [](lua_State *L) { 938 | float view[16], proj[16]; 939 | memset(view, 0, 16*sizeof(float)); 940 | memset(proj, 0, 16*sizeof(float)); 941 | 942 | auto load_matrix = [](lua_State *L, int index, float *mtx) { 943 | lua_pushvalue(L, index); 944 | int num = lua_objlen(L, -1); 945 | 946 | // we only accept 4x4 matrices 947 | if (num % 16 != 0) { 948 | lua_pushfstring(L, "Invalid table length %d, must be divisible by 16.\n", num); 949 | lua_error(L); 950 | return; 951 | } 952 | 953 | for (int i=1; i<=16; i++) { 954 | lua_rawgeti(L, -1, i); 955 | if (lua_isnil(L,-1)) { 956 | lua_pop(L, 1); 957 | break; 958 | } 959 | mtx[i-1] = (float)luaL_checknumber(L, -1); 960 | lua_pop(L, 1); 961 | } 962 | 963 | // printf( 964 | // "%+2.2f %+2.2f %+2.2f %+2.2f\n" 965 | // "%+2.2f %+2.2f %+2.2f %+2.2f\n" 966 | // "%+2.2f %+2.2f %+2.2f %+2.2f\n" 967 | // "%+2.2f %+2.2f %+2.2f %+2.2f\n\n", 968 | // mtx[0], mtx[4], mtx[8], mtx[12], 969 | // mtx[1], mtx[5], mtx[9], mtx[13], 970 | // mtx[2], mtx[6], mtx[10], mtx[14], 971 | // mtx[3], mtx[7], mtx[11], mtx[15] 972 | // ); 973 | 974 | lua_pop(L, 1); 975 | }; 976 | 977 | uint8_t id = (uint8_t)luaL_checkinteger(L, 1); 978 | 979 | load_matrix(L, 2, view); 980 | load_matrix(L, 3, proj); 981 | 982 | bgfx_set_view_transform(id, view, proj); 983 | // bgfx_set_view_transform_stereo(id, view, proj_l, flags, proj_r); 984 | 985 | return 0; 986 | } }, 987 | 988 | // bgfx.set_transform(mtx) 989 | // bgfx.set_transform(cache_index, 1) 990 | { "set_transform", [](lua_State *L) { 991 | if (lua_isnumber(L, 1)) { 992 | uint32_t cache = (uint32_t)luaL_checkinteger(L, 1); 993 | uint16_t num = (uint16_t)luaL_checkinteger(L, 2); 994 | bgfx_set_transform_cached(cache, num); 995 | return 0; 996 | } 997 | 998 | // TODO: accept tables of matrices. 999 | int num = lua_objlen(L, 1); 1000 | 1001 | // we only accept 4x4 matrices 1002 | if (num % 16 != 0) { 1003 | lua_pushfstring(L, "Invalid table length %d, must be divisible by 16.\n", num); 1004 | lua_error(L); 1005 | return 0; 1006 | } 1007 | 1008 | float *mtx = new float[num]; 1009 | for (int i=1; ; i++) { 1010 | lua_rawgeti(L, -1, i); 1011 | if (lua_isnil(L,-1)) { 1012 | lua_pop(L, 1); 1013 | break; 1014 | } 1015 | mtx[i-1] = (uint16_t)luaL_checkinteger(L, -1); 1016 | lua_pop(L, 1); 1017 | } 1018 | 1019 | uint32_t cached = bgfx_set_transform(mtx, num / 16); 1020 | 1021 | delete[] mtx; 1022 | 1023 | lua_pushnumber(L, cached); 1024 | return 1; 1025 | } }, 1026 | 1027 | { "alloc_transform", [](lua_State *L) { 1028 | // TODO: accept tables of matrices. 1029 | int num = lua_objlen(L, 1); 1030 | 1031 | // we only accept 4x4 matrices 1032 | if (num % 16 != 0) { 1033 | lua_pushfstring(L, "Invalid table length %d, must be divisible by 16.\n", num); 1034 | lua_error(L); 1035 | return 0; 1036 | } 1037 | 1038 | float *mtx = new float[num]; 1039 | for (int i=1; ; i++) { 1040 | lua_rawgeti(L, -1, i); 1041 | if (lua_isnil(L,-1)) { 1042 | lua_pop(L, 1); 1043 | break; 1044 | } 1045 | mtx[i-1] = (uint16_t)luaL_checkinteger(L, -1); 1046 | lua_pop(L, 1); 1047 | } 1048 | 1049 | bgfx_transform_t transform; 1050 | uint32_t cached = bgfx_alloc_transform(&transform, num / 16); 1051 | 1052 | memcpy(transform.data, mtx, sizeof(float)*16); 1053 | 1054 | delete[] mtx; 1055 | 1056 | lua_pushnumber(L, cached); 1057 | return 1; 1058 | } }, 1059 | 1060 | // bgfx.new_frame_buffer(width, height, format, flags) 1061 | { "new_frame_buffer", [](lua_State *L) { 1062 | uint32_t flags = BGFX_TEXTURE_U_CLAMP | BGFX_TEXTURE_V_CLAMP; 1063 | 1064 | if (lua_istable(L, 1)) { 1065 | std::vector attachments; 1066 | 1067 | lua_pushvalue(L, 1); 1068 | for (int i=1; ; i++) { 1069 | lua_rawgeti(L, -1, i); 1070 | if (lua_isnil(L,-1)) { 1071 | lua_pop(L, 1); 1072 | break; 1073 | } 1074 | bgfx_attachment_t a; 1075 | a.mip = 0; 1076 | a.layer = 1; 1077 | 1078 | if (lua_isuserdata(L, -1)) { 1079 | a.handle = *to_texture_ud(L, -1); 1080 | } else { 1081 | table_scan(L, -1, [&](const char *k, const char *) { 1082 | std::string key(k); 1083 | if (key == "mip" || key == "layer") { 1084 | a.mip = (uint16_t)lua_tointeger(L, -2); 1085 | } 1086 | else if (key == "handle") { 1087 | a.handle = *to_texture_ud(L, -2); 1088 | } 1089 | }); 1090 | } 1091 | attachments.push_back(a); 1092 | lua_pop(L, 1); 1093 | } 1094 | lua_pop(L, 1); 1095 | 1096 | bgfx_frame_buffer_handle_t *ud = (bgfx_frame_buffer_handle_t*)lua_newuserdata(L, sizeof(bgfx_frame_buffer_handle_t)); 1097 | *ud = bgfx_create_frame_buffer_from_attachment(attachments.size(), attachments.data(), false); 1098 | 1099 | luaL_getmetatable(L, "bgfx_frame_buffer"); 1100 | lua_setmetatable(L, -2); 1101 | 1102 | return 1; 1103 | } 1104 | 1105 | uint16_t width = luaL_checkinteger(L, 1); 1106 | uint16_t height = luaL_checkinteger(L, 2); 1107 | 1108 | bgfx_texture_format_t format = BGFX_TEXTURE_FORMAT_RGBA8; 1109 | const char *_format = "rgba8"; 1110 | if (lua_isstring(L, 3)) { 1111 | _format = lua_tostring(L, 3); 1112 | } 1113 | 1114 | auto val = texture_format_lookup.find(_format); 1115 | if (val != texture_format_lookup.end()) { 1116 | format = val->second; 1117 | } 1118 | else { 1119 | lua_pushboolean(L, 0); 1120 | lua_pushstring(L, "Invalid texture format."); 1121 | return 2; 1122 | } 1123 | 1124 | if (lua_istable(L, 4)) { 1125 | flags = 0; 1126 | table_scan(L, 4, [&](const char *, const char *v) { 1127 | auto val = texture_lookup.find(v); 1128 | if (val != texture_lookup.end()) { 1129 | flags |= val->second; 1130 | } 1131 | }); 1132 | } 1133 | 1134 | bgfx_frame_buffer_handle_t *ud = (bgfx_frame_buffer_handle_t*)lua_newuserdata(L, sizeof(bgfx_frame_buffer_handle_t)); 1135 | *ud = bgfx_create_frame_buffer(width, height, format, flags); 1136 | 1137 | luaL_getmetatable(L, "bgfx_frame_buffer"); 1138 | lua_setmetatable(L, -2); 1139 | 1140 | return 1; 1141 | } }, 1142 | 1143 | // bgfx.get_texture(frame_buffer, attachment=0) 1144 | { "get_texture", [](lua_State *L) { 1145 | bgfx_frame_buffer_handle_t *fb = to_frame_buffer_ud(L, 1); 1146 | 1147 | uint8_t attachment = 0; 1148 | if (lua_isnumber(L, 2)) { 1149 | attachment = (uint8_t)lua_tointeger(L, 2); 1150 | } 1151 | 1152 | bgfx_texture_handle_t *tex = (bgfx_texture_handle_t*)lua_newuserdata(L, sizeof(bgfx_texture_handle_t)); 1153 | *tex = bgfx_get_texture(*fb, attachment); 1154 | 1155 | luaL_getmetatable(L, "bgfx_texture"); 1156 | lua_setmetatable(L, -2); 1157 | 1158 | return 1; 1159 | } }, 1160 | 1161 | // bgfx.is_texture_valid(format, flags, depth, cubemap, layers) 1162 | { "is_texture_valid", [](lua_State *L) { 1163 | bgfx_texture_format_t format = BGFX_TEXTURE_FORMAT_RGBA8; 1164 | 1165 | const char *_format = "rgba8"; 1166 | if (lua_isstring(L, 1)) { 1167 | _format = lua_tostring(L, 1); 1168 | } 1169 | 1170 | auto val = texture_format_lookup.find(_format); 1171 | if (val != texture_format_lookup.end()) { 1172 | format = val->second; 1173 | } 1174 | else { 1175 | lua_pushboolean(L, 0); 1176 | lua_pushstring(L, "Invalid texture format."); 1177 | return 2; 1178 | } 1179 | 1180 | uint32_t flags = BGFX_TEXTURE_U_CLAMP | BGFX_TEXTURE_V_CLAMP; 1181 | 1182 | if (lua_istable(L, 2)) { 1183 | flags = 0; 1184 | table_scan(L, 2, [&](const char *, const char *v) { 1185 | auto val = texture_lookup.find(v); 1186 | if (val != texture_lookup.end()) { 1187 | flags |= val->second; 1188 | } 1189 | }); 1190 | } 1191 | 1192 | uint16_t depth = (uint16_t)luaL_checkinteger(L, 3); 1193 | bool cubemap = false; 1194 | if (lua_isboolean(L, 4)) { 1195 | cubemap = lua_toboolean(L, 4) > 0; 1196 | } 1197 | 1198 | uint16_t layers = 1; 1199 | if (lua_isnumber(L, 5)) { 1200 | layers = (uint16_t)lua_tointeger(L, 5); 1201 | } 1202 | 1203 | bool valid = bgfx_is_texture_valid(depth, cubemap, layers, format, flags); 1204 | lua_pushboolean(L, valid ? 1 : 0); 1205 | return 1; 1206 | } }, 1207 | 1208 | // bgfx.new_texture(data, width, height, has_mips, format, flags) 1209 | { "new_texture", [](lua_State *L) { 1210 | const bgfx_memory_t *mem = NULL; 1211 | if (lua_isstring(L, 1)) { 1212 | size_t size = 0; 1213 | const char *data = lua_tolstring(L, 1, &size); 1214 | mem = bgfx_copy(data, size); 1215 | } 1216 | 1217 | bool has_mips = false; 1218 | const char *_format = "rgba8"; 1219 | 1220 | int n = lua_gettop(L); 1221 | if (n >= 4) { 1222 | has_mips = lua_toboolean(L, 4) > 0; 1223 | } 1224 | if (n >= 5) { 1225 | _format = luaL_checkstring(L, 5); 1226 | } 1227 | 1228 | bgfx_texture_format_t format = BGFX_TEXTURE_FORMAT_RGBA8; 1229 | 1230 | auto val = texture_format_lookup.find(_format); 1231 | if (val != texture_format_lookup.end()) { 1232 | format = val->second; 1233 | } 1234 | else { 1235 | lua_pushboolean(L, 0); 1236 | lua_pushstring(L, "Invalid texture format."); 1237 | return 2; 1238 | } 1239 | 1240 | uint16_t width = (uint16_t)luaL_checkinteger(L, 2); 1241 | uint16_t height = (uint16_t)luaL_checkinteger(L, 3); 1242 | 1243 | uint32_t flags = BGFX_TEXTURE_NONE; 1244 | if (lua_istable(L, 6)) { 1245 | flags = 0; 1246 | table_scan(L, 6, [&](const char *, const char *v) { 1247 | auto val = texture_lookup.find(v); 1248 | if (val != texture_lookup.end()) { 1249 | flags |= val->second; 1250 | } 1251 | }); 1252 | } 1253 | 1254 | bgfx_texture_handle_t *ud = (bgfx_texture_handle_t*)lua_newuserdata(L, sizeof(bgfx_texture_handle_t)); 1255 | *ud = bgfx_create_texture_2d(width, height, has_mips, 1, format, flags, mem); 1256 | 1257 | luaL_getmetatable(L, "bgfx_texture"); 1258 | lua_setmetatable(L, -2); 1259 | 1260 | return 1; 1261 | } }, 1262 | 1263 | { "new_uniform", [](lua_State *L) { 1264 | const char *name = luaL_checkstring(L, 1); 1265 | const char *type_raw = luaL_checkstring(L, 2); 1266 | uint16_t count = 1; 1267 | if (lua_isnumber(L, 3)) { 1268 | count = lua_tointeger(L, 3); 1269 | } 1270 | 1271 | bgfx_uniform_type_t type = BGFX_UNIFORM_TYPE_COUNT; 1272 | if (strcmp(type_raw, "int") == 0) { 1273 | type = BGFX_UNIFORM_TYPE_INT1; 1274 | } 1275 | else if (strcmp(type_raw, "vec4") == 0) { 1276 | type = BGFX_UNIFORM_TYPE_VEC4; 1277 | } 1278 | else if (strcmp(type_raw, "mat3") == 0) { 1279 | type = BGFX_UNIFORM_TYPE_MAT3; 1280 | } 1281 | else if (strcmp(type_raw, "mat4") == 0) { 1282 | type = BGFX_UNIFORM_TYPE_MAT4; 1283 | } 1284 | else { 1285 | lua_pushboolean(L, 0); 1286 | lua_pushstring(L, "Invalid uniform type. Must be `int`, `vec4`, `mat3` or `mat4`."); 1287 | return 2; 1288 | } 1289 | 1290 | bgfx_uniform_handle_t *ud = (bgfx_uniform_handle_t*)lua_newuserdata(L, sizeof(bgfx_uniform_handle_t)); 1291 | *ud = bgfx_create_uniform(name, type, count); 1292 | 1293 | luaL_getmetatable(L, "bgfx_uniform"); 1294 | lua_setmetatable(L, -2); 1295 | 1296 | return 1; 1297 | } }, 1298 | 1299 | { "set_texture", [](lua_State *L) { 1300 | uint8_t stage = luaL_checkinteger(L, 1); 1301 | bgfx_uniform_handle_t *uniform = to_uniform_ud(L, 2); 1302 | bgfx_texture_handle_t *texture = to_texture_ud(L, 3); 1303 | 1304 | bgfx_set_texture(stage, *uniform, *texture, UINT32_MAX); 1305 | 1306 | return 0; 1307 | } }, 1308 | 1309 | { "set_image", [](lua_State *L) { 1310 | uint8_t stage = luaL_checkinteger(L, 1); 1311 | bgfx_uniform_handle_t *uniform = to_uniform_ud(L, 2); 1312 | bgfx_texture_handle_t *texture = to_texture_ud(L, 3); 1313 | 1314 | uint8_t mip = (uint8_t)luaL_checkinteger(L, 4); 1315 | 1316 | const char *_access = luaL_checkstring(L, 5); 1317 | bgfx_access_t access = BGFX_ACCESS_READ; 1318 | 1319 | auto aval = access_lookup.find(_access); 1320 | if (aval != access_lookup.end()) { 1321 | access = aval->second; 1322 | } 1323 | else { 1324 | lua_pushfstring(L, "Invalid access specifier: '%s'", _access); 1325 | lua_error(L); 1326 | } 1327 | 1328 | bgfx_texture_format_t format = BGFX_TEXTURE_FORMAT_RGBA8; 1329 | 1330 | const char *_format = "rgba8"; 1331 | if (lua_isstring(L, 6)) { 1332 | _format = lua_tostring(L, 6); 1333 | } 1334 | 1335 | auto val = texture_format_lookup.find(_format); 1336 | if (val != texture_format_lookup.end()) { 1337 | format = val->second; 1338 | } 1339 | else { 1340 | lua_pushboolean(L, 0); 1341 | lua_pushstring(L, "Invalid texture format."); 1342 | return 2; 1343 | } 1344 | 1345 | bgfx_set_image(stage, *uniform, *texture, mip, access, format); 1346 | 1347 | return 0; 1348 | } }, 1349 | 1350 | { "set_compute_index_buffer", [](lua_State *L) { 1351 | uint8_t stage = luaL_checkinteger(L, 1); 1352 | bgfx_index_buffer_handle_t *ibh = to_index_buffer_ud(L, 2); 1353 | 1354 | const char *_access = luaL_checkstring(L, 3); 1355 | bgfx_access_t access = BGFX_ACCESS_READ; 1356 | 1357 | auto val = access_lookup.find(_access); 1358 | if (val != access_lookup.end()) { 1359 | access = val->second; 1360 | } 1361 | else { 1362 | lua_pushfstring(L, "Invalid access specifier: '%s'", _access); 1363 | lua_error(L); 1364 | return 0; 1365 | } 1366 | 1367 | bgfx_set_compute_index_buffer(stage, *ibh, access); 1368 | return 0; 1369 | } }, 1370 | 1371 | { "set_compute_vertex_buffer", [](lua_State *L) { 1372 | uint8_t stage = luaL_checkinteger(L, 1); 1373 | bgfx_vertex_buffer_handle_t *vbh = to_vertex_buffer_ud(L, 2); 1374 | 1375 | const char *_access = luaL_checkstring(L, 3); 1376 | bgfx_access_t access = BGFX_ACCESS_READ; 1377 | 1378 | auto val = access_lookup.find(_access); 1379 | if (val != access_lookup.end()) { 1380 | access = val->second; 1381 | } 1382 | else { 1383 | lua_pushfstring(L, "Invalid access specifier: '%s'", _access); 1384 | lua_error(L); 1385 | return 0; 1386 | } 1387 | 1388 | bgfx_set_compute_vertex_buffer(stage, *vbh, access); 1389 | return 0; 1390 | } }, 1391 | 1392 | { "set_uniform", [](lua_State *L) { 1393 | auto scan_inner = [&](size_t size, int idx, int j) { 1394 | if (lua_istable(L, idx)) { 1395 | lua_pushvalue(L, idx); 1396 | lua_pushnil(L); 1397 | 1398 | int i = 0; 1399 | while (lua_next(L, -2)) { 1400 | lua_pushvalue(L, -2); 1401 | 1402 | if (size == 1) { 1403 | int n = lua_tointeger(L, -1); 1404 | int *idata = (int*)uniform_buffer; 1405 | idata[j*size+i] = n; 1406 | } 1407 | else { 1408 | float n = (float)lua_tonumber(L, -2); 1409 | float *fdata = (float*)uniform_buffer; 1410 | fdata[j*size+i] = n; 1411 | } 1412 | 1413 | lua_pop(L, 2); 1414 | i = i + 1; 1415 | } 1416 | lua_pop(L, 1); 1417 | } 1418 | }; 1419 | 1420 | auto scan_outer = [&](int idx) { 1421 | if (lua_istable(L, idx)) { 1422 | lua_pushvalue(L, idx); 1423 | lua_pushnil(L); 1424 | 1425 | int i = 0; 1426 | while (lua_next(L, -2)) { 1427 | lua_pushvalue(L, -2); 1428 | 1429 | // sanity check: we only support float 4, int, mat3 and mat4. 1430 | size_t size = lua_objlen(L, -2); 1431 | if (size != 16 && size != 9 && size != 4 && size != 1) { 1432 | lua_pop(L, 2); 1433 | break; 1434 | } 1435 | scan_inner(size, -2, i); 1436 | 1437 | lua_pop(L, 2); 1438 | i = i + 1; 1439 | } 1440 | lua_pop(L, 1); 1441 | } 1442 | }; 1443 | 1444 | scan_outer(2); 1445 | 1446 | uint16_t count = 1; 1447 | if (lua_isnumber(L, 3)) { 1448 | count = lua_tointeger(L, 3); 1449 | } 1450 | 1451 | bgfx_uniform_handle_t *uniform = to_uniform_ud(L, 1); 1452 | bgfx_set_uniform(*uniform, uniform_buffer, count); 1453 | 1454 | return 0; 1455 | } }, 1456 | 1457 | { "new_program", [](lua_State *L) { 1458 | bool compute = false; 1459 | 1460 | if (!lua_isstring(L, 1)) { 1461 | lua_pushboolean(L, 0); 1462 | return 1; 1463 | } 1464 | 1465 | if (lua_isboolean(L, 2)) { 1466 | compute = true; 1467 | } 1468 | 1469 | if (!compute && !lua_isstring(L, 2)) { 1470 | lua_pushboolean(L, 0); 1471 | return 1; 1472 | } 1473 | 1474 | size_t size = 0; 1475 | const char *data = lua_tolstring(L, 1, &size); 1476 | bgfx_shader_handle_t vsh = bgfx_create_shader(bgfx_copy(data, size)); 1477 | bgfx_shader_handle_t fsh; 1478 | 1479 | if (!compute) { 1480 | data = lua_tolstring(L, 2, &size); 1481 | fsh = bgfx_create_shader(bgfx_copy(data, size)); 1482 | } 1483 | 1484 | bgfx_program_handle_t *ud = (bgfx_program_handle_t*)lua_newuserdata(L, sizeof(bgfx_program_handle_t)); 1485 | 1486 | if (compute) { 1487 | *ud = bgfx_create_compute_program(vsh, true); 1488 | } 1489 | else { 1490 | *ud = bgfx_create_program(vsh, fsh, true); 1491 | } 1492 | 1493 | luaL_getmetatable(L, "bgfx_program"); 1494 | lua_setmetatable(L, -2); 1495 | 1496 | return 1; 1497 | } }, 1498 | 1499 | { "new_vertex_format", [](lua_State *L) { 1500 | bgfx_vertex_decl_t *decl = (bgfx_vertex_decl_t*)lua_newuserdata(L, sizeof(bgfx_vertex_decl_t)); 1501 | bgfx_renderer_type_t renderer = bgfx_get_renderer_type(); 1502 | bgfx_vertex_decl_begin(decl, renderer); 1503 | 1504 | luaL_getmetatable(L, "bgfx_vertex_decl"); 1505 | lua_setmetatable(L, -2); 1506 | 1507 | static std::map format_lookup = { 1508 | { "position", BGFX_ATTRIB_POSITION }, 1509 | { "normal", BGFX_ATTRIB_NORMAL }, 1510 | { "tangent", BGFX_ATTRIB_TANGENT }, 1511 | { "bitangent", BGFX_ATTRIB_BITANGENT }, 1512 | { "color0", BGFX_ATTRIB_COLOR0 }, 1513 | { "color1", BGFX_ATTRIB_COLOR1 }, 1514 | { "indices", BGFX_ATTRIB_INDICES }, 1515 | { "weight", BGFX_ATTRIB_WEIGHT }, 1516 | { "texcoord0", BGFX_ATTRIB_TEXCOORD0 }, 1517 | { "texcoord1", BGFX_ATTRIB_TEXCOORD1 }, 1518 | { "texcoord2", BGFX_ATTRIB_TEXCOORD2 }, 1519 | { "texcoord3", BGFX_ATTRIB_TEXCOORD3 }, 1520 | { "texcoord4", BGFX_ATTRIB_TEXCOORD4 }, 1521 | { "texcoord5", BGFX_ATTRIB_TEXCOORD5 }, 1522 | { "texcoord6", BGFX_ATTRIB_TEXCOORD6 }, 1523 | { "texcoord7", BGFX_ATTRIB_TEXCOORD7 } 1524 | }; 1525 | 1526 | static std::map type_lookup = { 1527 | { "byte", BGFX_ATTRIB_TYPE_UINT8 }, 1528 | { "short", BGFX_ATTRIB_TYPE_INT16 }, 1529 | { "float", BGFX_ATTRIB_TYPE_FLOAT } 1530 | // { "half", BGFX_ATTRIB_TYPE_HALF } 1531 | // { "uint10", BGFX_ATTRIB_TYPE_UINT10 } 1532 | }; 1533 | 1534 | table_scan(L, -2, [&](const char *, const char *) { 1535 | bgfx_attrib_t attrib = BGFX_ATTRIB_POSITION; 1536 | bgfx_attrib_type_t type = BGFX_ATTRIB_TYPE_FLOAT; 1537 | uint8_t size = 1; 1538 | bool normalized = false; 1539 | bool as_int = false; 1540 | 1541 | table_scan(L, -2, [&](const char *k, const char *v) { 1542 | std::string key = std::string(k); 1543 | 1544 | if (key == "type") { 1545 | auto val = type_lookup.find(v); 1546 | if (val != type_lookup.end()) { 1547 | type = val->second; 1548 | } 1549 | return; 1550 | } else if (key == "attrib") { 1551 | auto val = format_lookup.find(v); 1552 | if (val != format_lookup.end()) { 1553 | attrib = val->second; 1554 | } 1555 | return; 1556 | } else if (key == "size") { 1557 | size = (uint8_t)atoi(v); 1558 | return; 1559 | } else if (key == "normalized") { 1560 | normalized = lua_toboolean(L, -2) > 0; 1561 | return; 1562 | } 1563 | }); 1564 | 1565 | bgfx_vertex_decl_add(decl, attrib, size, type, normalized, as_int); 1566 | }); 1567 | 1568 | bgfx_vertex_decl_end(decl); 1569 | 1570 | return 1; 1571 | }}, 1572 | 1573 | { "new_vertex_buffer", [](lua_State *L) { 1574 | bgfx_vertex_buffer_handle_t *ud = (bgfx_vertex_buffer_handle_t*)lua_newuserdata(L, sizeof(bgfx_vertex_buffer_handle_t)); 1575 | 1576 | if (!lua_isstring(L, 1)) { 1577 | lua_pushboolean(L, 0); 1578 | return 1; 1579 | } 1580 | 1581 | size_t size = 0; 1582 | const char *data = lua_tolstring(L, 1, &size); 1583 | const bgfx_memory_t *mem = bgfx_copy(data, size); 1584 | 1585 | // this is absolutely going to segfault when gc happens 1586 | // const bgfx_memory_t *mem = bgfx_make_ref(data, size); 1587 | bgfx_vertex_decl_t *decl = to_vertex_decl_ud(L, 2); 1588 | 1589 | *ud = bgfx_create_vertex_buffer(mem, decl, 0); 1590 | 1591 | luaL_getmetatable(L, "bgfx_vertex_buffer"); 1592 | lua_setmetatable(L, -2); 1593 | 1594 | return 1; 1595 | } }, 1596 | 1597 | { "new_index_buffer", [](lua_State *L) { 1598 | size_t vertices = lua_objlen(L, -1); 1599 | size_t bytes = sizeof(uint16_t); 1600 | bool index32 = false; 1601 | if (vertices > USHRT_MAX) { 1602 | bytes = sizeof(uint32_t); 1603 | index32 = true; 1604 | } 1605 | const bgfx_memory_t *mem = bgfx_alloc(bytes * vertices); 1606 | 1607 | uint16_t* data16 = (uint16_t*)mem->data; 1608 | uint32_t* data32 = (uint32_t*)mem->data; 1609 | 1610 | for (int i=1; ; i++) { 1611 | lua_rawgeti(L, -1, i); 1612 | if (lua_isnil(L,-1)) { 1613 | lua_pop(L, 1); 1614 | break; 1615 | } 1616 | if (index32) { 1617 | data32[i-1] = (uint32_t)luaL_checkinteger(L, -1) - 1; 1618 | } 1619 | else { 1620 | data16[i-1] = (uint16_t)luaL_checkinteger(L, -1) - 1; 1621 | } 1622 | lua_pop(L,1); 1623 | } 1624 | 1625 | bgfx_index_buffer_handle_t *ud = (bgfx_index_buffer_handle_t*)lua_newuserdata(L, sizeof(bgfx_index_buffer_handle_t)); 1626 | *ud = bgfx_create_index_buffer(mem, index32 ? BGFX_BUFFER_INDEX32 : 0); 1627 | 1628 | luaL_getmetatable(L, "bgfx_index_buffer"); 1629 | lua_setmetatable(L, -2); 1630 | return 1; 1631 | } }, 1632 | 1633 | // bgfx.check_avail_instance_data_buffer(num, stride) 1634 | { "check_avail_instance_data_buffer", [](lua_State *L) { 1635 | uint32_t num = (uint32_t)luaL_checkinteger(L, 1); 1636 | uint16_t stride = luaL_checkinteger(L, 2); 1637 | 1638 | bool avail = bgfx_check_avail_instance_data_buffer(num, stride); 1639 | 1640 | lua_pushboolean(L, avail ? 1 : 0); 1641 | return 1; 1642 | } }, 1643 | 1644 | // bgfx.check_avail_transient_buffer("index", num) 1645 | // bgfx.check_avail_transient_buffer("vertex", num, decl) 1646 | // bgfx.check_avail_transient_buffer("both", vertices, decl, indices) 1647 | { "check_avail_transient_buffer", [](lua_State *L) { 1648 | std::string type(luaL_checkstring(L, 1)); 1649 | uint32_t num = (uint32_t)luaL_checkinteger(L, 2); 1650 | 1651 | bool avail = false; 1652 | if (type == "index") { 1653 | avail = bgfx_check_avail_transient_index_buffer(num); 1654 | } 1655 | else if (type == "vertex") { 1656 | bgfx_vertex_decl_t *decl = to_vertex_decl_ud(L, 3); 1657 | avail =bgfx_check_avail_transient_vertex_buffer(num, decl); 1658 | } 1659 | else if (type == "both") { 1660 | bgfx_vertex_decl_t *decl = to_vertex_decl_ud(L, 3); 1661 | uint32_t indices = luaL_checkinteger(L, 4); 1662 | avail = bgfx_check_avail_transient_buffers(num, decl, indices); 1663 | } 1664 | else { 1665 | lua_pushstring(L, "Invalid transient buffer type."); 1666 | lua_error(L); 1667 | return 0; 1668 | } 1669 | 1670 | lua_pushboolean(L, avail ? 1 : 0); 1671 | return 1; 1672 | } }, 1673 | 1674 | // bgfx.new_transient_buffer("index", indices) 1675 | // bgfx.new_transient_buffer("vertex", num, decl, data) 1676 | // bgfx.new_transient_buffer("both", num, decl, data, indices) 1677 | { "new_transient_buffer", [](lua_State *L) { 1678 | std::string type(luaL_checkstring(L, 1)); 1679 | 1680 | auto load_tib = [&](bgfx_transient_index_buffer_t *tib, int idx) { 1681 | size_t vertices = lua_objlen(L, idx); 1682 | uint16_t* data16 = (uint16_t*)tib->data; 1683 | 1684 | for (int i=1; ; i++) { 1685 | lua_rawgeti(L, -1, i); 1686 | if (lua_isnil(L,-1)) { 1687 | lua_pop(L, 1); 1688 | break; 1689 | } 1690 | else { 1691 | data16[i-1] = (uint16_t)luaL_checkinteger(L, -1) - 1; 1692 | } 1693 | lua_pop(L, 1); 1694 | } 1695 | }; 1696 | 1697 | if (type == "index" ) { 1698 | bgfx_transient_index_buffer_t *tib = (bgfx_transient_index_buffer_t*)lua_newuserdata(L, sizeof(bgfx_transient_index_buffer_t)); 1699 | 1700 | if (!lua_istable(L, 2)) { 1701 | lua_pushstring(L, "Indices must be a table"); 1702 | lua_error(L); 1703 | return 0; 1704 | } 1705 | 1706 | uint32_t num = lua_objlen(L, 2); 1707 | bgfx_alloc_transient_index_buffer(tib, num); 1708 | load_tib(tib, 2); 1709 | 1710 | luaL_getmetatable(L, "bgfx_transient_index_buffer"); 1711 | lua_setmetatable(L, -2); 1712 | } 1713 | else if (type == "vertex") { 1714 | bgfx_transient_vertex_buffer_t *tvb = (bgfx_transient_vertex_buffer_t*)lua_newuserdata(L, sizeof(bgfx_transient_vertex_buffer_t)); 1715 | 1716 | uint32_t num = (uint32_t)luaL_checkinteger(L, 2); 1717 | if (!lua_isstring(L, 4)) { 1718 | lua_pushboolean(L, 0); 1719 | return 1; 1720 | } 1721 | 1722 | bgfx_vertex_decl_t *decl = to_vertex_decl_ud(L, 3); 1723 | bgfx_alloc_transient_vertex_buffer(tvb, num, decl); 1724 | 1725 | size_t size = 0; 1726 | const char *data = lua_tolstring(L, 4, &size); 1727 | memcpy(tvb->data, data, size); 1728 | 1729 | luaL_getmetatable(L, "bgfx_transient_vertex_buffer"); 1730 | lua_setmetatable(L, -2); 1731 | } 1732 | else if (type == "both") { 1733 | // bgfx_vertex_decl_t *decl = to_vertex_decl_ud(L, 3); 1734 | 1735 | // if (!lua_istable(L, 4)) { 1736 | // lua_pushstring(L, "Indices must be a table."); 1737 | // lua_error(L); 1738 | // return 0; 1739 | // } 1740 | 1741 | // uint32_t indices = (uint32_t)lua_objlen(L, 4); 1742 | 1743 | // bgfx_alloc_transient_buffers(&tvb, decl, num, &tib, indices); 1744 | 1745 | // load tvb and tib 1746 | 1747 | // push transient_index_buffer ud 1748 | // push transient_vertex_buffer ud 1749 | 1750 | return 0; 1751 | } 1752 | else { 1753 | lua_pushstring(L, "Invalid transient buffer type."); 1754 | lua_error(L); 1755 | return 0; 1756 | } 1757 | 1758 | return 1; 1759 | } }, 1760 | 1761 | // const bgfx_instance_data_buffer_t* bgfx_alloc_instance_data_buffer(uint32_t _num, uint16_t _stride); 1762 | 1763 | // bgfx.set_vertex_buffer(vb, 0, 32) 1764 | { "set_vertex_buffer", [](lua_State *L) { 1765 | int n = lua_gettop(L); 1766 | lua_assert(n == 3); 1767 | (void)n; 1768 | 1769 | bgfx_vertex_buffer_handle_t* handle = to_vertex_buffer_ud(L, 1); 1770 | uint32_t start = 0; 1771 | uint32_t num = UINT32_MAX; 1772 | if (lua_isnumber(L, 2)) { 1773 | start = (uint32_t)lua_tonumber(L, 2); 1774 | } 1775 | if (lua_isnumber(L, 3)) { 1776 | num = (uint32_t)lua_tonumber(L, 3); 1777 | } 1778 | 1779 | bgfx_set_vertex_buffer(*handle, start, num); 1780 | return 0; 1781 | } }, 1782 | 1783 | // bgfx.set_transient_vertex_buffer(vb, 0, 32) 1784 | { "set_transient_vertex_buffer", [](lua_State *L) { 1785 | int n = lua_gettop(L); 1786 | lua_assert(n == 3); 1787 | (void)n; 1788 | 1789 | bgfx_transient_vertex_buffer_t* handle = to_transient_vertex_buffer_ud(L, 1); 1790 | uint32_t start = 0; 1791 | uint32_t num = UINT32_MAX; 1792 | if (lua_isnumber(L, 2)) { 1793 | start = (uint32_t)lua_tonumber(L, 2); 1794 | } 1795 | if (lua_isnumber(L, 3)) { 1796 | num = (uint32_t)lua_tonumber(L, 3); 1797 | } 1798 | 1799 | bgfx_set_transient_vertex_buffer(handle, start, num); 1800 | return 0; 1801 | } }, 1802 | 1803 | // bgfx.set_index_buffer(ib, 0, 60) 1804 | { "set_index_buffer", [](lua_State *L) { 1805 | int n = lua_gettop(L); 1806 | lua_assert(n == 3); 1807 | (void)n; 1808 | 1809 | bgfx_index_buffer_handle_t* handle = to_index_buffer_ud(L, 1); 1810 | int first = (int)lua_tonumber(L, 2); 1811 | int num = (int)lua_tonumber(L, 3); 1812 | bgfx_set_index_buffer(*handle, first, num); 1813 | return 0; 1814 | } }, 1815 | 1816 | // bgfx.set_transient_index_buffer(tib, 0, 60) 1817 | { "set_transient_index_buffer", [](lua_State *L) { 1818 | int n = lua_gettop(L); 1819 | lua_assert(n == 3); 1820 | (void)n; 1821 | 1822 | bgfx_transient_index_buffer_t* handle = to_transient_index_buffer_ud(L, 1); 1823 | int first = (int)lua_tonumber(L, 2); 1824 | int num = (int)lua_tonumber(L, 3); 1825 | bgfx_set_transient_index_buffer(handle, first, num); 1826 | return 0; 1827 | } }, 1828 | 1829 | { "set_stencil", [](lua_State *L) { 1830 | uint32_t front = (uint32_t)luaL_checkinteger(L, 1); 1831 | uint32_t back = BGFX_STENCIL_NONE; 1832 | if (lua_isnumber(L, 2)) { 1833 | back = (uint32_t)lua_tointeger(L, 2); 1834 | } 1835 | bgfx_set_stencil(front, back); 1836 | return 0; 1837 | } }, 1838 | 1839 | { "set_scissor", [](lua_State *L) { 1840 | if (lua_gettop(L) == 1) { 1841 | uint16_t cache = (uint16_t)luaL_checkinteger(L, 1); 1842 | bgfx_set_scissor_cached(cache); 1843 | return 0; 1844 | } 1845 | 1846 | uint16_t x = (uint16_t)luaL_checkinteger(L, 1); 1847 | uint16_t y = (uint16_t)luaL_checkinteger(L, 2); 1848 | uint16_t w = (uint16_t)luaL_checkinteger(L, 3); 1849 | uint16_t h = (uint16_t)luaL_checkinteger(L, 4); 1850 | bgfx_set_scissor(x, y, w, h); 1851 | return 0; 1852 | } }, 1853 | 1854 | { "discard", [](lua_State *) { 1855 | bgfx_discard(); 1856 | return 0; 1857 | } }, 1858 | 1859 | // local hmd = bgfx.get_hmd() 1860 | { "get_hmd", [](lua_State *L) { 1861 | const bgfx_hmd_t *hmd = bgfx_get_hmd(); 1862 | 1863 | if (hmd) { 1864 | lua_createtable(L, 0, 4); 1865 | 1866 | lua_pushstring(L, "width"); 1867 | lua_pushnumber(L, hmd->width); 1868 | lua_settable(L, -3); 1869 | 1870 | lua_pushstring(L, "height"); 1871 | lua_pushnumber(L, hmd->height); 1872 | lua_settable(L, -3); 1873 | 1874 | // TODO: populate eye fields 1875 | lua_pushstring(L, "eye"); 1876 | lua_pushnil(L); 1877 | lua_settable(L, -3); 1878 | return 1; 1879 | } 1880 | 1881 | lua_pushboolean(L, 0); 1882 | 1883 | return 1; 1884 | } }, 1885 | 1886 | { "get_renderer_info", [](lua_State *L) { 1887 | bgfx_renderer_type_t t = bgfx_get_renderer_type(); 1888 | 1889 | lua_newtable(L); 1890 | 1891 | lua_pushstring(L, "name"); 1892 | lua_pushstring(L, bgfx_get_renderer_name(t)); 1893 | lua_settable(L, -3); 1894 | 1895 | lua_pushstring(L, "type"); 1896 | lua_pushstring(L, renderer2str(t)); 1897 | lua_settable(L, -3); 1898 | 1899 | return 1; 1900 | } }, 1901 | 1902 | { NULL, NULL } 1903 | }; 1904 | 1905 | #ifdef _MSC_VER 1906 | # define LUA_EXPORT __declspec(dllexport) 1907 | #else 1908 | # define LUA_EXPORT 1909 | #endif 1910 | 1911 | extern "C" LUA_EXPORT int luaopen_bgfx(lua_State*); 1912 | 1913 | LUA_EXPORT 1914 | int luaopen_bgfx(lua_State *L) { 1915 | 1916 | #define REGISTER_MT(name, functions) \ 1917 | luaL_newmetatable(L, name); \ 1918 | luaL_register(L, NULL, functions); \ 1919 | lua_pushvalue(L, -1); \ 1920 | lua_setfield(L, -1, "__index") \ 1921 | 1922 | REGISTER_MT("bgfx_texture", texture_fn); 1923 | REGISTER_MT("bgfx_uniform", uniform_fn); 1924 | REGISTER_MT("bgfx_program", program_fn); 1925 | REGISTER_MT("bgfx_vertex_decl", vertex_format_fn); 1926 | REGISTER_MT("bgfx_vertex_buffer", vertex_buffer_fn); 1927 | REGISTER_MT("bgfx_index_buffer", index_buffer_fn); 1928 | REGISTER_MT("bgfx_frame_buffer", frame_buffer_fn); 1929 | REGISTER_MT("bgfx_transient_index_buffer", transient_ib_fn); 1930 | REGISTER_MT("bgfx_transient_vertex_buffer", transient_vb_fn); 1931 | 1932 | lua_pop(L, lua_gettop(L)); 1933 | 1934 | luaL_register(L, "bgfx", m); 1935 | 1936 | // shut up gcc when we aren't doing any stack dumps 1937 | #ifdef __GCC__ 1938 | (void)stack_dump; 1939 | #endif 1940 | 1941 | return 1; 1942 | } 1943 | --------------------------------------------------------------------------------