├── .github ├── FUNDING.yml └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── CHANGES.md ├── CMakeLists.txt ├── LICENSE ├── README.md ├── Tupfile.lua ├── etc ├── Activity.java ├── AndroidManifest.xml ├── AppRun ├── Info.plist ├── VarelaRound.ttf ├── boot.lua ├── logo.frag ├── logo.svg ├── lovr.desktop ├── lovr.html ├── lovr.icns ├── lovr.ico ├── lovr.rc ├── lovrc.bat ├── monkey.glb ├── monkey.h ├── monkeycrush.lua ├── nogame │ ├── arg.lua │ ├── conf.lua │ ├── logo.spv │ └── main.lua ├── shaders.h ├── shaders │ ├── animator.comp │ ├── blender.comp │ ├── cubemap.frag │ ├── cubemap.vert │ ├── equirect.frag │ ├── fill.vert │ ├── fill_array.frag │ ├── font.frag │ ├── lovr.glsl │ ├── mask.vert │ ├── normal.frag │ ├── tallymerge.comp │ ├── unlit.frag │ └── unlit.vert └── webxr.js ├── plugins └── README.md ├── src ├── api │ ├── api.c │ ├── api.h │ ├── l_audio.c │ ├── l_audio_source.c │ ├── l_data.c │ ├── l_data_blob.c │ ├── l_data_image.c │ ├── l_data_modelData.c │ ├── l_data_rasterizer.c │ ├── l_data_sound.c │ ├── l_event.c │ ├── l_filesystem.c │ ├── l_filesystem_file.c │ ├── l_graphics.c │ ├── l_graphics_buffer.c │ ├── l_graphics_font.c │ ├── l_graphics_material.c │ ├── l_graphics_mesh.c │ ├── l_graphics_model.c │ ├── l_graphics_pass.c │ ├── l_graphics_readback.c │ ├── l_graphics_sampler.c │ ├── l_graphics_shader.c │ ├── l_graphics_texture.c │ ├── l_headset.c │ ├── l_headset_layer.c │ ├── l_lovr.c │ ├── l_math.c │ ├── l_math_curve.c │ ├── l_math_randomGenerator.c │ ├── l_math_vectors.c │ ├── l_physics.c │ ├── l_physics_collider.c │ ├── l_physics_contact.c │ ├── l_physics_joints.c │ ├── l_physics_shapes.c │ ├── l_physics_world.c │ ├── l_system.c │ ├── l_thread.c │ ├── l_thread_channel.c │ ├── l_thread_thread.c │ └── l_timer.c ├── core │ ├── fs.c │ ├── fs.h │ ├── gpu.h │ ├── gpu_vk.c │ ├── gpu_web.c │ ├── job.c │ ├── job.h │ ├── maf.h │ ├── os.h │ ├── os_android.c │ ├── os_glfw.h │ ├── os_linux.c │ ├── os_macos.c │ ├── os_wasm.c │ ├── os_win32.c │ ├── spv.c │ └── spv.h ├── lib │ ├── dmon │ │ ├── dmon.c │ │ └── dmon.h │ ├── jsmn │ │ ├── LICENSE │ │ ├── jsmn.c │ │ └── jsmn.h │ ├── luax │ │ ├── lutf8lib.c │ │ └── lutf8lib.h │ ├── miniaudio │ │ ├── miniaudio.c │ │ └── miniaudio.h │ ├── minimp3 │ │ ├── minimp3.c │ │ ├── minimp3.h │ │ └── minimp3_ex.h │ ├── miniz │ │ ├── miniz_tinfl.c │ │ └── miniz_tinfl.h │ ├── noise │ │ ├── simplexnoise1234.c │ │ └── simplexnoise1234.h │ ├── stb │ │ ├── stb_image.c │ │ ├── stb_image.h │ │ ├── stb_truetype.c │ │ ├── stb_truetype.h │ │ ├── stb_vorbis.c │ │ └── stb_vorbis.h │ └── std │ │ ├── stdatomic.h │ │ └── threads.h ├── main.c ├── modules │ ├── audio │ │ ├── audio.c │ │ ├── audio.h │ │ ├── spatializer.h │ │ ├── spatializer_oculus.c │ │ ├── spatializer_phonon.c │ │ └── spatializer_simple.c │ ├── data │ │ ├── blob.c │ │ ├── blob.h │ │ ├── image.c │ │ ├── image.h │ │ ├── modelData.c │ │ ├── modelData.h │ │ ├── modelData_gltf.c │ │ ├── modelData_obj.c │ │ ├── modelData_stl.c │ │ ├── rasterizer.c │ │ ├── rasterizer.h │ │ ├── sound.c │ │ └── sound.h │ ├── event │ │ ├── event.c │ │ └── event.h │ ├── filesystem │ │ ├── filesystem.c │ │ └── filesystem.h │ ├── graphics │ │ ├── graphics.c │ │ └── graphics.h │ ├── headset │ │ ├── headset.c │ │ ├── headset.h │ │ ├── headset_openxr.c │ │ ├── headset_simulator.c │ │ └── headset_webxr.c │ ├── math │ │ ├── math.c │ │ └── math.h │ ├── physics │ │ ├── physics.c │ │ └── physics.h │ ├── system │ │ ├── system.c │ │ └── system.h │ ├── thread │ │ ├── thread.c │ │ └── thread.h │ └── timer │ │ ├── timer.c │ │ └── timer.h ├── util.c └── util.h └── test ├── conf.lua ├── lovr ├── data.lua ├── filesystem.lua ├── graphics.lua ├── headset.lua ├── init.lua ├── math.lua ├── physics.lua ├── thread.lua └── timer.lua ├── lust.lua └── main.lua /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: bjornbytes 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CMAKE_BUILD_TYPE: ${{ github.event_name == 'pull_request' && 'Debug' || 'Release' }} 7 | 8 | jobs: 9 | windows: 10 | name: Windows 11 | runs-on: windows-latest 12 | defaults: 13 | run: 14 | shell: cmd 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | with: 19 | submodules: 'recursive' 20 | - name: Configure 21 | run: cmake -B build -D LOVR_VERSION_HASH=%GITHUB_SHA:~0,6% 22 | - name: Build 23 | run: cmake --build build --config %CMAKE_BUILD_TYPE% 24 | - name: Test 25 | run: build\%CMAKE_BUILD_TYPE%\lovr test 26 | - name: Upload 27 | uses: actions/upload-artifact@v4 28 | with: 29 | name: lovr.exe 30 | path: | 31 | build/Release/lovr.exe 32 | build/Release/lovrc.bat 33 | build/Release/*.dll 34 | 35 | linux: 36 | name: Linux 37 | runs-on: ubuntu-22.04 38 | steps: 39 | - name: Update Packages 40 | run: sudo apt update 41 | - name: Install Packages 42 | run: sudo apt install -y xorg-dev libx11-xcb-dev libxcb-glx0-dev libxkbcommon-dev libxkbcommon-x11-dev libxcb-xkb-dev libfuse2 libcurl4-openssl-dev libssl-dev 43 | - name: Checkout 44 | uses: actions/checkout@v4 45 | with: 46 | submodules: 'recursive' 47 | - name: Init 48 | run: cmake -B build -D LOVR_BUILD_BUNDLE=ON -D LOVR_VERSION_HASH=${GITHUB_SHA::6} 49 | - name: Build 50 | run: cmake --build build -j 51 | - name: Test 52 | run: ./build/bin/lovr test 53 | - name: AppImage 54 | run: > 55 | curl -sOL https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage && 56 | chmod +x ./appimagetool-x86_64.AppImage && 57 | ./appimagetool-x86_64.AppImage build/bin && 58 | mv LÖVR-x86_64.AppImage lovr-x86_64.AppImage 59 | - name: Upload 60 | uses: actions/upload-artifact@v4 61 | with: 62 | name: lovr.appimage 63 | path: lovr-x86_64.AppImage 64 | 65 | android: 66 | name: Android 67 | strategy: 68 | matrix: 69 | abi: [arm64-v8a, x86_64] 70 | runs-on: ubuntu-22.04 71 | steps: 72 | - name: Update Packages 73 | run: sudo apt update 74 | - name: Install Packages 75 | run: sudo apt install -y glslang-tools 76 | - name: Install Android 29 77 | run: ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --install "platforms;android-29" 78 | - name: Checkout 79 | uses: actions/checkout@v4 80 | with: 81 | submodules: 'recursive' 82 | - name: Init 83 | run: > 84 | mkdir build && 85 | cd build && 86 | keytool 87 | -genkey 88 | -dname 'cn=Unknown, ou=Unknown, o=Unknown, l=Unknown, st=Unknown, c=Unknown' 89 | -keystore key.keystore 90 | -keypass hunter2 91 | -storepass hunter2 92 | -alias key 93 | -keyalg RSA 94 | -keysize 2048 95 | -validity 10000 && 96 | cmake .. 97 | -D CMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake 98 | -D ANDROID_SDK=$ANDROID_HOME 99 | -D ANDROID_ABI=${{ matrix.abi }} 100 | -D ANDROID_STL=c++_shared 101 | -D ANDROID_NATIVE_API_LEVEL=29 102 | -D ANDROID_BUILD_TOOLS_VERSION=34.0.0 103 | -D ANDROID_KEYSTORE=key.keystore 104 | -D ANDROID_KEYSTORE_PASS=pass:hunter2 105 | -D LOVR_VERSION_HASH=${GITHUB_SHA::6} 106 | - name: Build 107 | run: cmake --build build 108 | - name: Upload 109 | uses: actions/upload-artifact@v4 110 | with: 111 | name: ${{ matrix.abi == 'arm64-v8a' && 'lovr.apk' || 'lovr-x64.apk' }} 112 | path: build/lovr.apk 113 | 114 | macos: 115 | name: macOS 116 | runs-on: macos-latest 117 | steps: 118 | - name: Install Wulkan 119 | env: 120 | VKV: 1.3.275.0 121 | run: | 122 | curl -sOL https://sdk.lunarg.com/sdk/download/$VKV/mac/vulkansdk-macos-$VKV.dmg?Human=true 123 | hdiutil attach vulkansdk-macos-$VKV.dmg 124 | cp -R /Volumes/vulkansdk-macos-$VKV . 125 | hdiutil detach /Volumes/vulkansdk-macos-$VKV 126 | export VULKAN_SDK=${{ runner.temp }}/vulkan && echo "VULKAN_SDK=$VULKAN_SDK/macOS" >> $GITHUB_ENV 127 | sudo vulkansdk-macos-$VKV/InstallVulkan.app/Contents/MacOS/InstallVulkan --root $VULKAN_SDK --accept-licenses --default-answer --confirm-command install 128 | - name: Checkout 129 | uses: actions/checkout@v4 130 | with: 131 | submodules: 'recursive' 132 | - name: Init 133 | run: cmake -B build -D LOVR_BUILD_BUNDLE=ON -D LOVR_VERSION_HASH=${GITHUB_SHA::6} 134 | - name: Build 135 | run: cmake --build build -j 136 | - name: Test 137 | run: ./build/lovr.app/Contents/MacOS/lovr test 138 | - name: Package 139 | run: > 140 | cp $VULKAN_SDK/lib/libvulkan.1.3.275.dylib build/lovr.app/Contents/MacOS/libvulkan.1.dylib && 141 | cp $VULKAN_SDK/lib/libMoltenVK.dylib build/lovr.app/Contents/MacOS && 142 | mkdir -p build/lovr.app/Contents/Resources/vulkan/icd.d && 143 | cp $VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json build/lovr.app/Contents/Resources/vulkan/icd.d && 144 | sed -i '' 's|../../../lib/libMoltenVK.dylib|../../../MacOS/libMoltenVK.dylib|' build/lovr.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json && 145 | ditto -c -k --keepParent build/lovr.app lovr.zip 146 | - name: Upload 147 | uses: actions/upload-artifact@v4 148 | with: 149 | name: lovr.app 150 | path: lovr.zip 151 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .tup 2 | .obj 3 | bin 4 | build* 5 | *.*.h 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/glfw"] 2 | path = deps/glfw 3 | url = https://github.com/glfw/glfw 4 | [submodule "deps/lua"] 5 | path = deps/lua 6 | url = https://github.com/lua/lua 7 | [submodule "deps/msdfgen"] 8 | path = deps/msdfgen 9 | url = https://github.com/bjornbytes/msdfgen 10 | [submodule "deps/luajit"] 11 | path = deps/luajit 12 | url = https://github.com/WohlSoft/LuaJIT 13 | [submodule "deps/openxr"] 14 | path = deps/openxr 15 | url = https://github.com/khronosgroup/openxr-sdk 16 | [submodule "deps/glslang"] 17 | path = deps/glslang 18 | url = https://github.com/KhronosGroup/glslang 19 | [submodule "deps/vulkan-headers"] 20 | path = deps/vulkan-headers 21 | url = https://github.com/KhronosGroup/Vulkan-Headers 22 | [submodule "plugins/lua-enet"] 23 | path = plugins/lua-enet 24 | url = https://github.com/bjornbytes/lua-enet 25 | [submodule "plugins/lovr-http"] 26 | path = plugins/lovr-http 27 | url = https://github.com/bjornbytes/lovr-http 28 | [submodule "deps/tracy"] 29 | path = deps/tracy 30 | url = https://github.com/wolfpld/tracy 31 | [submodule "deps/joltc"] 32 | path = deps/joltc 33 | url = https://github.com/amerkoleci/joltc 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2025 Bjorn Swenson and other LÖVR contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LÖVR 2 | 3 | 4 | 5 | > **A simple Lua framework for rapidly building VR experiences.** 6 | 7 | You can use LÖVR to easily create VR experiences without much setup or programming experience. The framework is tiny, fast, open source, and supports lots of different platforms and devices. 8 | 9 | [![Build](https://github.com/bjornbytes/lovr/actions/workflows/build.yml/badge.svg?event=push)](https://github.com/bjornbytes/lovr/actions/workflows/build.yml) 10 | [![Version](https://img.shields.io/github/release/bjornbytes/lovr.svg?label=version)](https://github.com/bjornbytes/lovr/releases) 11 | [![Matrix](https://img.shields.io/badge/chat-matrix-0ba378.svg)](https://lovr.org/matrix) 12 | [![Discord](https://img.shields.io/badge/chat-discord-404eed.svg)](https://lovr.org/discord) 13 | 14 | [**Homepage**](https://lovr.org) | [**Documentation**](https://lovr.org/docs) | [**FAQ**](https://lovr.org/docs/FAQ) 15 | 16 |

17 | 18 | 19 | 20 |

21 | 22 | Features 23 | --- 24 | 25 | - **Cross-Platform** - Runs on Windows, macOS, Linux, and Android. 26 | - **Cross-Device** - Supports Vive/Index, Oculus Rift/Quest, Windows MR, and has a VR simulator. 27 | - **Beginner-friendly** - Simple VR scenes can be created in just a few lines of Lua. 28 | - **Fast** - Written in C11 and scripted with LuaJIT, includes optimized single-pass stereo rendering. 29 | - **Asset Import** - Supports 3D models (glTF, OBJ), skeletal animation, HDR textures, cubemaps, fonts, etc. 30 | - **Spatialized Audio** - Audio is automatically spatialized using HRTFs. 31 | - **Vector Library** - Efficient first-class support for 3D vectors, quaternions, and matrices. 32 | - **3D Rigid Body Physics** - Including 7 collider shapes, triangle mesh colliders, and 6 joint types. 33 | - **Compute Shaders** - For high performance GPU tasks, like particles. 34 | 35 | Getting Started 36 | --- 37 | 38 | It's really easy to get started making things with LÖVR. Grab a copy of the executable from , 39 | then write a `main.lua` script and drag it onto the executable. Here are some example projects to try: 40 | 41 | #### Hello World 42 | 43 | ```lua 44 | function lovr.draw(pass) 45 | pass:text('Hello World!', 0, 1.7, -3, .5) 46 | end 47 | ``` 48 | 49 | #### Spinning Cube 50 | 51 | ```lua 52 | function lovr.draw(pass) 53 | pass:cube(0, 1.7, -1, .5, lovr.timer.getTime()) 54 | end 55 | ``` 56 | 57 | #### Hand Tracking 58 | 59 | ```lua 60 | function lovr.draw(pass) 61 | for _, hand in ipairs(lovr.headset.getHands()) do 62 | pass:sphere(vec3(lovr.headset.getPosition(hand)), .1) 63 | end 64 | end 65 | ``` 66 | 67 | #### 3D Models 68 | 69 | ```lua 70 | function lovr.load() 71 | model = lovr.graphics.newModel('model.gltf') 72 | end 73 | 74 | function lovr.draw(pass) 75 | pass:draw(model, x, y, z) 76 | end 77 | ``` 78 | 79 | More examples are on the [docs page](https://lovr.org/docs/Intro/Hello_World). 80 | 81 | Building 82 | --- 83 | 84 | You can build LÖVR from source using CMake. Here are the steps using the command line: 85 | 86 | ```console 87 | mkdir build 88 | cd build 89 | cmake .. 90 | cmake --build . 91 | ``` 92 | 93 | See the [Compiling Guide](https://lovr.org/docs/Compiling) for more info. 94 | 95 | Resources 96 | --- 97 | 98 | - [**Documentation**](https://lovr.org/docs): Guides, tutorials, examples, and API documentation. 99 | - [**FAQ**](https://lovr.org/docs/FAQ): Frequently Asked Questions. 100 | - [**Matrix**](https://lovr.org/matrix): The LÖVR community for discussion and support. 101 | - [**Nightly Builds**](https://lovr.org/download/nightly): Nightly builds for Windows. 102 | - [**Compiling Guide**](https://lovr.org/docs/Compiling): Information on compiling LÖVR from source. 103 | - [**Contributing**](https://lovr.org/docs/Contributing): Guide for helping out with development 💜 104 | - [**LÖVE**](https://love2d.org): LÖVR is heavily inspired by LÖVE, a 2D game framework. 105 | 106 | Contributors 107 | --- 108 | 109 | - [@bjornbytes](https://github.com/bjornbytes) 110 | - [@shakesoda](https://github.com/shakesoda) 111 | - [@bcampbell](https://github.com/bcampbell) 112 | - [@mcclure](https://github.com/mcclure) 113 | - [@nevyn](https://github.com/nevyn) 114 | - [@porglezomp](https://github.com/porglezomp) 115 | - [@jmiskovic](https://github.com/jmiskovic) 116 | - [@wallbraker](https://github.com/wallbraker) 117 | 118 | License 119 | --- 120 | 121 | MIT, see [`LICENSE`](LICENSE) for details. 122 | -------------------------------------------------------------------------------- /etc/Activity.java: -------------------------------------------------------------------------------- 1 | package @ANDROID_PACKAGE@; 2 | 3 | import android.Manifest; 4 | import android.app.NativeActivity; 5 | import android.content.pm.PackageManager; 6 | import android.os.Build; 7 | 8 | public class Activity extends NativeActivity { 9 | static { 10 | System.loadLibrary("openxr_loader"); 11 | System.loadLibrary("lovr"); 12 | } 13 | 14 | protected native void lovrPermissionEvent(int permission, boolean granted); 15 | 16 | @Override 17 | public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { 18 | if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { 19 | lovrPermissionEvent(0, true); 20 | } else { 21 | lovrPermissionEvent(0, false); 22 | } 23 | } 24 | 25 | private void requestAudioCapturePermission() { 26 | if (checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { 27 | requestPermissions(new String[] { Manifest.permission.RECORD_AUDIO }, 1); 28 | } else { 29 | lovrPermissionEvent(0, true); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /etc/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /etc/AppRun: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | IMAGE_DIR="$(dirname "$(readlink -f "$0")")" 4 | 5 | exec "$IMAGE_DIR/lovr" "$@" 6 | -------------------------------------------------------------------------------- /etc/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleExecutable 6 | lovr 7 | CFBundleIconFile 8 | lovr 9 | CFBundleIdentifier 10 | org.lovr 11 | CFBundleName 12 | LÖVR 13 | CFBundlePackageType 14 | APPL 15 | CFBundleVersion 16 | v0.18.0 17 | NSHighResolutionCapable 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /etc/VarelaRound.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjornbytes/lovr/b14f88847dc9669425f631948245b437628323c3/etc/VarelaRound.ttf -------------------------------------------------------------------------------- /etc/logo.frag: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_multiview : require 3 | #extension GL_GOOGLE_include_directive : require 4 | 5 | #include "shaders/lovr.glsl" 6 | 7 | vec4 lovrmain() { 8 | float y = UV.y; 9 | vec2 uv = vec2(UV.x, 1. - UV.y); 10 | uv = uv * 4. - 2.; 11 | const float k = sqrt(3.); 12 | uv.x = abs(uv.x) - 1.; 13 | uv.y = uv.y + 1. / k + .25; 14 | if (uv.x + k * uv.y > 0.) { 15 | uv = vec2(uv.x - k * uv.y, -k * uv.x - uv.y) / 2.; 16 | } 17 | uv.x -= clamp(uv.x, -2., 0.); 18 | float sdf = -length(uv) * sign(uv.y) - .5; 19 | float w = fwidth(sdf) * .5; 20 | float alpha = smoothstep(.22 + w, .22 - w, sdf); 21 | vec3 color = mix(vec3(.094, .662, .890), vec3(.913, .275, .6), clamp(y * 1.5 - .25, 0., 1.)); 22 | color = mix(color, vec3(.2, .2, .24), smoothstep(-.12 + w, -.12 - w, sdf)); 23 | return vec4(pow(color, vec3(2.2)), alpha); 24 | } 25 | -------------------------------------------------------------------------------- /etc/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /etc/lovr.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Exec=AppRun 3 | Name=LÖVR 4 | Type=Application 5 | Icon=logo 6 | Categories=Development; 7 | -------------------------------------------------------------------------------- /etc/lovr.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 33 | 34 | 35 | LÖVR 36 | 37 | 38 | 39 |
40 | 41 |
42 | 43 | 105 | 106 | {{{ SCRIPT }}} 107 | 108 | 109 | -------------------------------------------------------------------------------- /etc/lovr.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjornbytes/lovr/b14f88847dc9669425f631948245b437628323c3/etc/lovr.icns -------------------------------------------------------------------------------- /etc/lovr.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjornbytes/lovr/b14f88847dc9669425f631948245b437628323c3/etc/lovr.ico -------------------------------------------------------------------------------- /etc/lovr.rc: -------------------------------------------------------------------------------- 1 | GLFW_ICON ICON lovr.ico 2 | -------------------------------------------------------------------------------- /etc/lovrc.bat: -------------------------------------------------------------------------------- 1 | lovr --console %* 2 | -------------------------------------------------------------------------------- /etc/monkey.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjornbytes/lovr/b14f88847dc9669425f631948245b437628323c3/etc/monkey.glb -------------------------------------------------------------------------------- /etc/monkeycrush.lua: -------------------------------------------------------------------------------- 1 | -- Usage: lovr etc/monkeycrush.lua > etc/monkey.h 2 | 3 | local etc = lovr.filesystem.getSource() 4 | local success, model = assert(pcall(lovr.data.newModelData, 'monkey.glb')) 5 | 6 | local min, max = lovr.math.newVec3(math.huge), lovr.math.newVec3(-math.huge) 7 | 8 | for i = 1, model:getMeshVertexCount(1) do 9 | local x, y, z, nx, ny, nz = model:getMeshVertex(1, i) 10 | min.x, min.y, min.z = math.min(x, min.x), math.min(y, min.y), math.min(z, min.z) 11 | max.x, max.y, max.z = math.max(x, max.x), math.max(y, max.y), math.max(z, max.z) 12 | end 13 | 14 | local scale = .5 15 | min:mul(scale) 16 | max:mul(scale) 17 | 18 | local center = Vec3(max + min):mul(.5) 19 | local extent = Vec3(max - min) 20 | local halfExtent = extent / 2 21 | local bounds = { center[1], center[2], center[3], halfExtent[1], halfExtent[2], halfExtent[3] } 22 | 23 | io.write(('float monkey_bounds[6] = { %ff, %ff, %ff, %ff, %ff, %ff };\n'):format(unpack(bounds))) 24 | io.write(('float monkey_offset[3] = { %ff, %ff, %ff };\n'):format(min:unpack())) 25 | io.write('\n') 26 | 27 | io.write('uint8_t monkey_vertices[] = {\n') 28 | for i = 1, model:getMeshVertexCount(1) do 29 | local x, y, z, nx, ny, nz = model:getMeshVertex(1, i) 30 | local position = vec3(x, y, z):mul(scale) 31 | local normal = vec3(nx, ny, nz) 32 | 33 | local qx, qy, qz = ((position - min) / extent * 255 + .5):unpack() 34 | local qnx, qny, qnz = ((normal / 2 + .5) * 255 + .5):unpack() 35 | 36 | qx, qy, qz = math.floor(qx), math.floor(qy), math.floor(qz) 37 | qnx, qny, qnz = math.floor(qnx), math.floor(qny), math.floor(qnz) 38 | 39 | io.write((' %d, %d, %d, %d, %d, %d,\n'):format(qx, qy, qz, qnx, qny, qnz)) 40 | 41 | lovr.math.drain() 42 | end 43 | io.write('};\n\n') 44 | 45 | io.write('uint16_t monkey_indices[] = {\n ') 46 | for i = 1, model:getMeshIndexCount(1) do 47 | local index = model:getMeshIndex(1, i) - 1 48 | io.write((' %d,'):format(index)) 49 | if i % 10 == 0 then 50 | io.write('\n ') 51 | end 52 | end 53 | io.write('\n};\n') 54 | 55 | lovr.event.quit() 56 | -------------------------------------------------------------------------------- /etc/nogame/arg.lua: -------------------------------------------------------------------------------- 1 | function lovr.arg(arg) 2 | local options = { 3 | _help = { short = '-h', long = '--help', help = 'Show help and exit' }, 4 | _version = { short = '-v', long = '--version', help = 'Show version and exit' }, 5 | console = { long = '--console', help = 'Attach Windows console' }, 6 | debug = { long = '--debug', help = 'Enable debugging checks and logging' }, 7 | simulator = { long = '--simulator', help = 'Force headset simulator' }, 8 | watch = { short = '-w', long = '--watch', help = 'Watch files and restart on change' } 9 | } 10 | 11 | local shift 12 | 13 | for i, argument in ipairs(arg) do 14 | if argument:match('^%-') then 15 | for name, option in pairs(options) do 16 | if argument == option.short or argument == option.long then 17 | arg[name] = true 18 | break 19 | end 20 | end 21 | else 22 | shift = i 23 | break 24 | end 25 | end 26 | 27 | shift = shift or (#arg + 1) 28 | 29 | for i = 0, #arg do 30 | arg[i - shift], arg[i] = arg[i], nil 31 | end 32 | 33 | if arg.console or arg._help or arg._version then 34 | local ok, system = pcall(require, 'lovr.system') 35 | if ok and system then system.openConsole() end 36 | end 37 | 38 | if arg._help then 39 | local message = {} 40 | 41 | local list = {} 42 | for name, option in pairs(options) do 43 | option.name = name 44 | table.insert(list, option) 45 | end 46 | 47 | table.sort(list, function(a, b) return a.name < b.name end) 48 | 49 | for i, option in ipairs(list) do 50 | if option.short and option.long then 51 | table.insert(message, (' %s, %s\t\t%s'):format(option.short, option.long, option.help)) 52 | else 53 | table.insert(message, (' %s\t\t%s'):format(option.long or option.short, option.help)) 54 | end 55 | end 56 | 57 | table.insert(message, 1, 'usage: lovr [options] []\n') 58 | table.insert(message, 2, 'options:') 59 | table.insert(message, '\n can be a Lua file, a folder, or a zip archive') 60 | print(table.concat(message, '\n')) 61 | os.exit(0) 62 | end 63 | 64 | if arg._version then 65 | if select('#', lovr.getVersion()) >= 5 then 66 | print(('LOVR %d.%d.%d (%s) %s'):format(lovr.getVersion())) 67 | else 68 | print(('LOVR %d.%d.%d (%s)'):format(lovr.getVersion())) 69 | end 70 | os.exit(0) 71 | end 72 | 73 | return function(conf) 74 | if arg.debug then 75 | conf.graphics.debug = true 76 | conf.headset.debug = true 77 | end 78 | 79 | if arg.simulator then 80 | conf.headset.drivers = { 'simulator' } 81 | end 82 | 83 | if arg.watch then 84 | lovr.filesystem.watch() 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /etc/nogame/conf.lua: -------------------------------------------------------------------------------- 1 | function lovr.conf(t) 2 | t.audio.start = false 3 | t.headset.supersample = true 4 | end 5 | -------------------------------------------------------------------------------- /etc/nogame/logo.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjornbytes/lovr/b14f88847dc9669425f631948245b437628323c3/etc/nogame/logo.spv -------------------------------------------------------------------------------- /etc/nogame/main.lua: -------------------------------------------------------------------------------- 1 | function lovr.load() 2 | if not lovr.graphics then 3 | print(string.format('LÖVR %d.%d.%d\nNo game', lovr.getVersion())) 4 | lovr.event.quit() 5 | return 6 | end 7 | 8 | if not lovr.headset or lovr.headset.getPassthrough() == 'opaque' then 9 | lovr.graphics.setBackgroundColor(0x20232c) 10 | end 11 | 12 | logo = lovr.graphics.newShader('unlit', 'logo.spv') 13 | end 14 | 15 | function lovr.draw(pass) 16 | local padding = .1 17 | local font = lovr.graphics.getDefaultFont() 18 | local fade = .315 + .685 * math.abs(math.sin(lovr.headset.getTime() * 2)) 19 | local titlePosition = 1.5 - padding 20 | local subtitlePosition = titlePosition - font:getHeight() * .25 - padding 21 | 22 | pass:setFaceCull(true) 23 | pass:setShader(logo) 24 | pass:plane(0, 2, -3) 25 | pass:setShader() 26 | 27 | pass:text('LÖVR', -.012, titlePosition, -3, .25, quat(0, 0, 1, 0), nil, 'center', 'top') 28 | 29 | pass:setColor(.9, .9, .9, fade) 30 | pass:text('No game :(', -.005, subtitlePosition, -3, .15, 0, 0, 1, 0, nil, 'center', 'top') 31 | end 32 | 33 | function lovr.keypressed(key) 34 | if key == 'escape' then 35 | lovr.event.quit() 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /etc/shaders.h: -------------------------------------------------------------------------------- 1 | #include "shaders/unlit.vert.h" 2 | #include "shaders/unlit.frag.h" 3 | #include "shaders/normal.frag.h" 4 | #include "shaders/font.frag.h" 5 | #include "shaders/cubemap.vert.h" 6 | #include "shaders/cubemap.frag.h" 7 | #include "shaders/equirect.frag.h" 8 | #include "shaders/fill.vert.h" 9 | #include "shaders/fill_array.frag.h" 10 | #include "shaders/mask.vert.h" 11 | #include "shaders/animator.comp.h" 12 | #include "shaders/blender.comp.h" 13 | #include "shaders/tallymerge.comp.h" 14 | 15 | #include "shaders/lovr.glsl.h" 16 | 17 | #define LOCATION_POSITION 10 18 | #define LOCATION_NORMAL 11 19 | #define LOCATION_UV 12 20 | #define LOCATION_COLOR 13 21 | #define LOCATION_TANGENT 14 22 | 23 | #define LAST_BUILTIN_BINDING 3 24 | -------------------------------------------------------------------------------- /etc/shaders/animator.comp: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_GOOGLE_include_directive : require 3 | 4 | #include "lovr.glsl" 5 | 6 | layout(local_size_x = 32, local_size_x_id = 0) in; 7 | 8 | layout(push_constant) uniform PushConstants { 9 | uint baseVertex; 10 | uint vertexCount; 11 | bool inplace; 12 | }; 13 | 14 | struct ModelVertex { 15 | float x, y, z; 16 | uint normal; 17 | float u, v; 18 | uint color; 19 | uint tangent; 20 | }; 21 | 22 | struct SkinVertex { 23 | uint indices; 24 | uint weights; 25 | }; 26 | 27 | layout(set = 0, binding = 0) buffer restrict readonly VertexIn { ModelVertex vertexIn[]; }; 28 | layout(set = 0, binding = 1) buffer restrict VertexOut { ModelVertex vertexOut[]; }; 29 | layout(set = 0, binding = 2) buffer restrict readonly VertexWeights { SkinVertex skin[]; }; 30 | layout(set = 0, binding = 3) uniform JointTransforms { mat4 joints[256]; }; 31 | 32 | void lovrmain() { 33 | if (GlobalThreadID.x >= vertexCount) return; 34 | uint vertexIndex = baseVertex + GlobalThreadID.x; 35 | 36 | uint indices = skin[vertexIndex].indices; 37 | uint i0 = (indices >> 0) & 0xff; 38 | uint i1 = (indices >> 8) & 0xff; 39 | uint i2 = (indices >> 16) & 0xff; 40 | uint i3 = (indices >> 24) & 0xff; 41 | vec4 weights = unpackUnorm4x8(skin[vertexIndex].weights); 42 | 43 | // Model loader does not currently renormalize weights post-quantization 44 | weights /= weights[0] + weights[1] + weights[2] + weights[3]; 45 | 46 | mat4 matrix = mat4(0); 47 | matrix += joints[i0] * weights[0]; 48 | matrix += joints[i1] * weights[1]; 49 | matrix += joints[i2] * weights[2]; 50 | matrix += joints[i3] * weights[3]; 51 | 52 | ModelVertex vertex = inplace ? vertexOut[vertexIndex] : vertexIn[vertexIndex]; 53 | vec4 position = vec4(vertex.x, vertex.y, vertex.z, 1.); 54 | vec3 normal = normalize(unpackSnorm10x3(vertex.normal).xyz); 55 | 56 | vec3 skinnedPosition = (matrix * position).xyz; 57 | vec3 skinnedNormal = mat3(matrix) * normal; 58 | 59 | vertexOut[vertexIndex].x = skinnedPosition.x; 60 | vertexOut[vertexIndex].y = skinnedPosition.y; 61 | vertexOut[vertexIndex].z = skinnedPosition.z; 62 | vertexOut[vertexIndex].normal = packSnorm10x3(vec4(skinnedNormal, 0.)); 63 | } 64 | -------------------------------------------------------------------------------- /etc/shaders/blender.comp: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_GOOGLE_include_directive : require 3 | 4 | #include "lovr.glsl" 5 | 6 | layout(local_size_x = 32, local_size_x_id = 0) in; 7 | 8 | layout(push_constant) uniform PushConstants { 9 | uint baseVertex; 10 | uint vertexCount; 11 | uint blendShapeCount; 12 | uint baseBlendVertex; 13 | bool inplace; 14 | }; 15 | 16 | struct ModelVertex { 17 | float px, py, pz; 18 | uint normal; 19 | float u, v; 20 | uint color; 21 | uint tangent; 22 | }; 23 | 24 | struct BlendVertex { 25 | float px, py, pz; 26 | float nx, ny, nz; 27 | float tx, ty, tz; 28 | }; 29 | 30 | layout(set = 0, binding = 0) buffer restrict readonly RawVertices { ModelVertex rawVertices[]; }; 31 | layout(set = 0, binding = 1) buffer restrict Vertices { ModelVertex vertices[]; }; 32 | layout(set = 0, binding = 2) buffer restrict readonly BlendVertices { BlendVertex blendVertex[]; }; 33 | layout(set = 0, binding = 3) uniform Weights { vec4 weights[16]; }; 34 | 35 | void lovrmain() { 36 | if (GlobalThreadID.x >= vertexCount) return; 37 | uint vertexIndex = baseVertex + GlobalThreadID.x; 38 | uint blendVertexIndex = baseBlendVertex + GlobalThreadID.x; 39 | 40 | ModelVertex vertex = inplace ? vertices[vertexIndex] : rawVertices[vertexIndex]; 41 | 42 | vec4 normal = unpackSnorm10x3(vertex.normal); 43 | vec4 tangent = unpackSnorm10x3(vertex.tangent); 44 | 45 | for (uint i = 0; i < blendShapeCount; i++, blendVertexIndex += vertexCount) { 46 | float weight = weights[i / 4][i % 4]; 47 | 48 | if (weight == 0.) { 49 | continue; 50 | } 51 | 52 | BlendVertex blendShape = blendVertex[blendVertexIndex]; 53 | 54 | vertex.px += blendShape.px * weight; 55 | vertex.py += blendShape.py * weight; 56 | vertex.pz += blendShape.pz * weight; 57 | 58 | normal.x += blendShape.nx * weight; 59 | normal.y += blendShape.ny * weight; 60 | normal.z += blendShape.nz * weight; 61 | 62 | tangent.x += blendShape.tx * weight; 63 | tangent.y += blendShape.ty * weight; 64 | tangent.z += blendShape.tz * weight; 65 | } 66 | 67 | vertex.normal = packSnorm10x3(normal); 68 | vertex.tangent = packSnorm10x3(tangent); 69 | 70 | vertices[vertexIndex] = vertex; 71 | } 72 | -------------------------------------------------------------------------------- /etc/shaders/cubemap.frag: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_multiview : require 3 | #extension GL_GOOGLE_include_directive : require 4 | 5 | #include "lovr.glsl" 6 | 7 | layout(set = 1, binding = 1) uniform textureCube SkyboxTexture; 8 | 9 | vec4 lovrmain() { 10 | return Color * getPixel(SkyboxTexture, Normal * vec3(1, 1, -1)); 11 | } 12 | -------------------------------------------------------------------------------- /etc/shaders/cubemap.vert: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_multiview : require 3 | #extension GL_GOOGLE_include_directive : require 4 | 5 | #include "lovr.glsl" 6 | 7 | vec4 lovrmain() { 8 | const vec2 uvs[6] = vec2[6]( 9 | vec2(-1, -1), 10 | vec2(-1, +1), 11 | vec2(+1, -1), 12 | vec2(+1, -1), 13 | vec2(-1, +1), 14 | vec2(+1, +1) 15 | ); 16 | 17 | vec2 uv = uvs[VertexIndex % 6]; 18 | vec3 ray = vec3(uv, -1.); 19 | mat3 inverseViewOrientation = transpose(mat3(ViewFromLocal)); 20 | Normal = inverseViewOrientation * (InverseProjection * vec4(ray, 1.)).xyz; 21 | return vec4(uv, 0, 1); 22 | } 23 | -------------------------------------------------------------------------------- /etc/shaders/equirect.frag: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_multiview : require 3 | #extension GL_GOOGLE_include_directive : require 4 | 5 | #include "lovr.glsl" 6 | 7 | vec4 lovrmain() { 8 | vec3 dir = normalize(Normal); 9 | 10 | float theta1 = -atan(dir.x, dir.z) / (2. * PI); 11 | float theta2 = fract(theta1); 12 | 13 | float dx1 = dFdx(theta1); 14 | float dy1 = dFdy(theta1); 15 | float dx2 = dFdx(theta2); 16 | float dy2 = dFdy(theta2); 17 | 18 | float phi = acos(dir.y) / PI; 19 | 20 | vec2 dx = vec2(abs(dx1) - 1e-5 < abs(dx2) ? dx1 : dx2, dFdx(phi)); 21 | vec2 dy = vec2(abs(dy1) - 1e-5 < abs(dy2) ? dy1 : dy2, dFdy(phi)); 22 | 23 | return Color * textureGrad(sampler2D(ColorTexture, Sampler), vec2(theta1, phi), dx, dy); 24 | } 25 | -------------------------------------------------------------------------------- /etc/shaders/fill.vert: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_multiview : require 3 | #extension GL_GOOGLE_include_directive : require 4 | 5 | #include "lovr.glsl" 6 | 7 | vec4 lovrmain() { 8 | float x = -1 + float((VertexIndex & 1) << 2); 9 | float y = -1 + float((VertexIndex & 2) << 1); 10 | UV = vec2(x, y) * .5 + .5; 11 | return vec4(x, y, 1., 1.); 12 | } 13 | -------------------------------------------------------------------------------- /etc/shaders/fill_array.frag: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_multiview : require 3 | #extension GL_EXT_samplerless_texture_functions : require 4 | #extension GL_GOOGLE_include_directive : require 5 | 6 | #include "lovr.glsl" 7 | 8 | layout(set = 1, binding = 1) uniform texture2DArray ArrayTexture; 9 | 10 | vec4 lovrmain() { 11 | return Color * getPixel(ArrayTexture, UV, min(ViewIndex, textureSize(ArrayTexture, 0).z - 1)); 12 | } 13 | -------------------------------------------------------------------------------- /etc/shaders/font.frag: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_multiview : require 3 | #extension GL_EXT_samplerless_texture_functions : require 4 | #extension GL_GOOGLE_include_directive : require 5 | 6 | #include "lovr.glsl" 7 | 8 | float screenPxRange() { 9 | vec2 screenTexSize = vec2(1.) / fwidth(UV); 10 | return max(.5 * dot(Material.sdfRange, screenTexSize), 1.); 11 | } 12 | 13 | float median(float r, float g, float b) { 14 | return max(min(r, g), min(max(r, g), b)); 15 | } 16 | 17 | vec4 lovrmain() { 18 | vec3 msdf = getPixel(ColorTexture, UV).rgb; 19 | float sdf = median(msdf.r, msdf.g, msdf.b); 20 | float screenPxDistance = screenPxRange() * (sdf - .5); 21 | float alpha = clamp(screenPxDistance + .5, 0., 1.); 22 | if (alpha <= 0.) discard; 23 | return vec4(Color.rgb, Color.a * alpha); 24 | } 25 | -------------------------------------------------------------------------------- /etc/shaders/mask.vert: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_multiview : require 3 | #extension GL_GOOGLE_include_directive : require 4 | 5 | #include "lovr.glsl" 6 | 7 | vec4 lovrmain() { 8 | if (VertexPosition.z != ViewIndex) { 9 | return vec4(0.); 10 | } 11 | 12 | vec4 clip = Projection * vec4(VertexPosition.xy, -1., 1.); 13 | clip.z = clip.w; // Sets NDC z to 1 (near plane) 14 | return clip; 15 | } 16 | -------------------------------------------------------------------------------- /etc/shaders/normal.frag: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_multiview : require 3 | #extension GL_GOOGLE_include_directive : require 4 | 5 | #include "lovr.glsl" 6 | 7 | vec4 lovrmain() { 8 | return vec4(normalize(Normal) * .5 + .5, 1); 9 | } 10 | -------------------------------------------------------------------------------- /etc/shaders/tallymerge.comp: -------------------------------------------------------------------------------- 1 | #version 460 2 | 3 | layout(local_size_x = 32, local_size_x_id = 0) in; 4 | 5 | layout(push_constant) uniform PushConstants { 6 | uint count; 7 | uint views; 8 | }; 9 | 10 | layout(set = 0, binding = 0) buffer readonly restrict Src { uint src[]; }; 11 | layout(set = 0, binding = 1) buffer writeonly restrict Dst { uint dst[]; }; 12 | 13 | void main() { 14 | uint index = gl_GlobalInvocationID.x; 15 | if (index >= count) return; 16 | uint base = index * views; 17 | 18 | uint total = 0; 19 | 20 | for (uint i = 0; i < views; i++) { 21 | total += src[base + i]; 22 | } 23 | 24 | dst[index] = total; 25 | } 26 | -------------------------------------------------------------------------------- /etc/shaders/unlit.frag: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_multiview : require 3 | #extension GL_GOOGLE_include_directive : require 4 | 5 | #include "lovr.glsl" 6 | 7 | vec4 lovrmain() { 8 | return DefaultColor; 9 | } 10 | -------------------------------------------------------------------------------- /etc/shaders/unlit.vert: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_multiview : require 3 | #extension GL_GOOGLE_include_directive : require 4 | 5 | #include "lovr.glsl" 6 | 7 | vec4 lovrmain() { 8 | return DefaultPosition; 9 | } 10 | -------------------------------------------------------------------------------- /plugins/README.md: -------------------------------------------------------------------------------- 1 | Plugins 2 | === 3 | 4 | Plugins are optional native libraries that are built with LÖVR. CMake will scan `plugins` for any 5 | folder with a `CMakeLists.txt` and include it in the build. Any CMake targets the plugin declares 6 | will automatically include and link with LÖVR's copy of Lua, and any shared libraries they produce 7 | will be copied next to the `lovr` executable, allowing them to be used from Lua via `require`. 8 | 9 | Prebuilt plugin libraries can also be copied next to the lovr executable. However, this won't work 10 | easily for Android builds, since the APK requires re-signing if it is modified. 11 | 12 | See the [Plugins documentation](https://lovr.org/docs/Plugins) for more info and a list of known 13 | plugins. 14 | 15 | LÖVR includes some "core plugins" here which are included in builds by default. They are entirely 16 | optional -- their folders here can be removed or their libraries can be deleted after building 17 | without disturbing the LÖVR build. 18 | -------------------------------------------------------------------------------- /src/api/l_data_blob.c: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include "data/blob.h" 3 | #include "util.h" 4 | 5 | static int l_lovrBlobGetName(lua_State* L) { 6 | Blob* blob = luax_checktype(L, 1, Blob); 7 | lua_pushstring(L, blob->name); 8 | return 1; 9 | } 10 | 11 | static int l_lovrBlobGetPointer(lua_State* L) { 12 | Blob* blob = luax_checktype(L, 1, Blob); 13 | lua_pushlightuserdata(L, blob->data); 14 | return 1; 15 | } 16 | 17 | static int l_lovrBlobGetSize(lua_State* L) { 18 | Blob* blob = luax_checktype(L, 1, Blob); 19 | lua_pushinteger(L, blob->size); 20 | return 1; 21 | } 22 | 23 | static int l_lovrBlobGetString(lua_State* L) { 24 | Blob* blob = luax_checktype(L, 1, Blob); 25 | 26 | lua_Integer offset = luaL_optinteger(L, 2, 0); 27 | luax_check(L, offset >= 0, "Blob byte offset can not be negative"); 28 | luax_check(L, (size_t) offset < blob->size, "Blob byte offset must be less than the size of the Blob"); 29 | 30 | lua_Integer length = luaL_optinteger(L, 3, blob->size - offset); 31 | luax_check(L, length >= 0, "Length can not be negative"); 32 | luax_check(L, (size_t) length <= blob->size - offset, "Blob:getString range overflows the size of the Blob"); 33 | 34 | lua_pushlstring(L, (char*) blob->data + offset, (size_t) length); 35 | return 1; 36 | } 37 | 38 | #define l_lovrBlobGet(L, T)\ 39 | Blob* blob = luax_checktype(L, 1, Blob);\ 40 | lua_Integer offset = luaL_optinteger(L, 2, 0);\ 41 | luax_check(L, offset >= 0, "Blob byte offset can not be negative");\ 42 | luax_check(L, (size_t) offset < blob->size, "Blob byte offset must be less than the size of the Blob");\ 43 | lua_Integer count = luaL_optinteger(L, 3, 1);\ 44 | luax_check(L, count > 0, "Count must be greater than zero");\ 45 | luax_check(L, (size_t) count * sizeof(T) <= blob->size - (size_t) offset, "Byte range overflows the size of the Blob");\ 46 | const T* data = (const T*) ((char*) blob->data + offset);\ 47 | for (lua_Integer i = 0; i < count; i++) lua_pushnumber(L, (lua_Number) data[i]);\ 48 | return count; 49 | 50 | static int l_lovrBlobGetI8(lua_State* L) { l_lovrBlobGet(L, int8_t); } 51 | static int l_lovrBlobGetU8(lua_State* L) { l_lovrBlobGet(L, uint8_t); } 52 | static int l_lovrBlobGetI16(lua_State* L) { l_lovrBlobGet(L, int16_t); } 53 | static int l_lovrBlobGetU16(lua_State* L) { l_lovrBlobGet(L, uint16_t); } 54 | static int l_lovrBlobGetI32(lua_State* L) { l_lovrBlobGet(L, int32_t); } 55 | static int l_lovrBlobGetU32(lua_State* L) { l_lovrBlobGet(L, uint32_t); } 56 | static int l_lovrBlobGetF32(lua_State* L) { l_lovrBlobGet(L, float); } 57 | static int l_lovrBlobGetF64(lua_State* L) { l_lovrBlobGet(L, double); } 58 | 59 | #define l_lovrBlobSet(L, T)\ 60 | Blob* blob = luax_checktype(L, 1, Blob);\ 61 | lua_Integer offset = luaL_checkinteger(L, 2);\ 62 | luax_check(L, offset >= 0, "Blob byte offset can not be negative");\ 63 | luax_check(L, (size_t) offset < blob->size, "Blob byte offset must be less than the size of the Blob");\ 64 | bool table = lua_istable(L, 3);\ 65 | int count = table ? luax_len(L, 3) : lua_gettop(L) - 2;\ 66 | luax_check(L, (size_t) count * sizeof(T) <= blob->size - (size_t) offset, "Byte range overflows the size of the Blob");\ 67 | T* data = (T*) ((char*) blob->data + offset);\ 68 | if (table) {\ 69 | for (int i = 0; i < count; i++) {\ 70 | lua_rawgeti(L, 3, i + 1);\ 71 | data[i] = (T) lua_tonumber(L, -1);\ 72 | lua_pop(L, 1);\ 73 | }\ 74 | } else {\ 75 | for (int i = 0; i < count; i++) {\ 76 | data[i] = luaL_checknumber(L, i + 3);\ 77 | }\ 78 | }\ 79 | return count; 80 | 81 | static int l_lovrBlobSetI8(lua_State* L) { l_lovrBlobSet(L, int8_t); } 82 | static int l_lovrBlobSetU8(lua_State* L) { l_lovrBlobSet(L, uint8_t); } 83 | static int l_lovrBlobSetI16(lua_State* L) { l_lovrBlobSet(L, int16_t); } 84 | static int l_lovrBlobSetU16(lua_State* L) { l_lovrBlobSet(L, uint16_t); } 85 | static int l_lovrBlobSetI32(lua_State* L) { l_lovrBlobSet(L, int32_t); } 86 | static int l_lovrBlobSetU32(lua_State* L) { l_lovrBlobSet(L, uint32_t); } 87 | static int l_lovrBlobSetF32(lua_State* L) { l_lovrBlobSet(L, float); } 88 | static int l_lovrBlobSetF64(lua_State* L) { l_lovrBlobSet(L, double); } 89 | 90 | const luaL_Reg lovrBlob[] = { 91 | { "getName", l_lovrBlobGetName }, 92 | { "getPointer", l_lovrBlobGetPointer }, 93 | { "getSize", l_lovrBlobGetSize }, 94 | { "getString", l_lovrBlobGetString }, 95 | { "getI8", l_lovrBlobGetI8 }, 96 | { "getU8", l_lovrBlobGetU8 }, 97 | { "getI16", l_lovrBlobGetI16 }, 98 | { "getU16", l_lovrBlobGetU16 }, 99 | { "getI32", l_lovrBlobGetI32 }, 100 | { "getU32", l_lovrBlobGetU32 }, 101 | { "getF32", l_lovrBlobGetF32 }, 102 | { "getF64", l_lovrBlobGetF64 }, 103 | { "setI8", l_lovrBlobSetI8 }, 104 | { "setU8", l_lovrBlobSetU8 }, 105 | { "setI16", l_lovrBlobSetI16 }, 106 | { "setU16", l_lovrBlobSetU16 }, 107 | { "setI32", l_lovrBlobSetI32 }, 108 | { "setU32", l_lovrBlobSetU32 }, 109 | { "setF32", l_lovrBlobSetF32 }, 110 | { "setF64", l_lovrBlobSetF64 }, 111 | { NULL, NULL } 112 | }; 113 | -------------------------------------------------------------------------------- /src/api/l_data_image.c: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include "data/image.h" 3 | #include "data/blob.h" 4 | #include "util.h" 5 | 6 | StringEntry lovrTextureFormat[] = { 7 | [FORMAT_R8] = ENTRY("r8"), 8 | [FORMAT_RG8] = ENTRY("rg8"), 9 | [FORMAT_RGBA8] = ENTRY("rgba8"), 10 | [FORMAT_BGRA8] = ENTRY("bgra8"), 11 | [FORMAT_R16] = ENTRY("r16"), 12 | [FORMAT_RG16] = ENTRY("rg16"), 13 | [FORMAT_RGBA16] = ENTRY("rgba16"), 14 | [FORMAT_R16F] = ENTRY("r16f"), 15 | [FORMAT_RG16F] = ENTRY("rg16f"), 16 | [FORMAT_RGBA16F] = ENTRY("rgba16f"), 17 | [FORMAT_R32F] = ENTRY("r32f"), 18 | [FORMAT_RG32F] = ENTRY("rg32f"), 19 | [FORMAT_RGBA32F] = ENTRY("rgba32f"), 20 | [FORMAT_RGB565] = ENTRY("rgb565"), 21 | [FORMAT_RGB5A1] = ENTRY("rgb5a1"), 22 | [FORMAT_RGB10A2] = ENTRY("rgb10a2"), 23 | [FORMAT_RG11B10F] = ENTRY("rg11b10f"), 24 | [FORMAT_D16] = ENTRY("d16"), 25 | [FORMAT_D24] = ENTRY("d24"), 26 | [FORMAT_D32F] = ENTRY("d32f"), 27 | [FORMAT_D24S8] = ENTRY("d24s8"), 28 | [FORMAT_D32FS8] = ENTRY("d32fs8"), 29 | [FORMAT_BC1] = ENTRY("bc1"), 30 | [FORMAT_BC2] = ENTRY("bc2"), 31 | [FORMAT_BC3] = ENTRY("bc3"), 32 | [FORMAT_BC4U] = ENTRY("bc4u"), 33 | [FORMAT_BC4S] = ENTRY("bc4s"), 34 | [FORMAT_BC5U] = ENTRY("bc5u"), 35 | [FORMAT_BC5S] = ENTRY("bc5s"), 36 | [FORMAT_BC6UF] = ENTRY("bc6uf"), 37 | [FORMAT_BC6SF] = ENTRY("bc6sf"), 38 | [FORMAT_BC7] = ENTRY("bc7"), 39 | [FORMAT_ASTC_4x4] = ENTRY("astc4x4"), 40 | [FORMAT_ASTC_5x4] = ENTRY("astc5x4"), 41 | [FORMAT_ASTC_5x5] = ENTRY("astc5x5"), 42 | [FORMAT_ASTC_6x5] = ENTRY("astc6x5"), 43 | [FORMAT_ASTC_6x6] = ENTRY("astc6x6"), 44 | [FORMAT_ASTC_8x5] = ENTRY("astc8x5"), 45 | [FORMAT_ASTC_8x6] = ENTRY("astc8x6"), 46 | [FORMAT_ASTC_8x8] = ENTRY("astc8x8"), 47 | [FORMAT_ASTC_10x5] = ENTRY("astc10x5"), 48 | [FORMAT_ASTC_10x6] = ENTRY("astc10x6"), 49 | [FORMAT_ASTC_10x8] = ENTRY("astc10x8"), 50 | [FORMAT_ASTC_10x10] = ENTRY("astc10x10"), 51 | [FORMAT_ASTC_12x10] = ENTRY("astc12x10"), 52 | [FORMAT_ASTC_12x12] = ENTRY("astc12x12"), 53 | { 0 } 54 | }; 55 | 56 | static int l_lovrImageGetBlob(lua_State* L) { 57 | Image* image = luax_checktype(L, 1, Image); 58 | Blob* blob = lovrImageGetBlob(image); 59 | luax_pushtype(L, Blob, blob); 60 | return 1; 61 | } 62 | 63 | static int l_lovrImageGetPointer(lua_State* L) { 64 | Image* image = luax_checktype(L, 1, Image); 65 | uint32_t level = luax_optu32(L, 2, 1) - 1; 66 | uint32_t layer = luax_optu32(L, 3, 1) - 1; 67 | void* pointer = lovrImageGetLayerData(image, level, layer); 68 | lua_pushlightuserdata(L, pointer); 69 | return 1; 70 | } 71 | 72 | static int l_lovrImageGetWidth(lua_State* L) { 73 | Image* image = luax_checktype(L, 1, Image); 74 | uint32_t width = lovrImageGetWidth(image, 0); 75 | lua_pushinteger(L, width); 76 | return 1; 77 | } 78 | 79 | static int l_lovrImageGetHeight(lua_State* L) { 80 | Image* image = luax_checktype(L, 1, Image); 81 | uint32_t height = lovrImageGetHeight(image, 0); 82 | lua_pushinteger(L, height); 83 | return 1; 84 | } 85 | 86 | static int l_lovrImageGetDimensions(lua_State* L) { 87 | Image* image = luax_checktype(L, 1, Image); 88 | uint32_t width = lovrImageGetWidth(image, 0); 89 | uint32_t height = lovrImageGetHeight(image, 0); 90 | lua_pushinteger(L, width); 91 | lua_pushinteger(L, height); 92 | return 2; 93 | } 94 | 95 | static int l_lovrImageGetFormat(lua_State* L) { 96 | Image* image = luax_checktype(L, 1, Image); 97 | TextureFormat format = lovrImageGetFormat(image); 98 | luax_pushenum(L, TextureFormat, format); 99 | return 1; 100 | } 101 | 102 | static int l_lovrImageGetPixel(lua_State* L) { 103 | Image* image = luax_checktype(L, 1, Image); 104 | uint32_t x = luax_checku32(L, 2); 105 | uint32_t y = luax_checku32(L, 3); 106 | float pixel[4] = { 0.f, 0.f, 0.f, 1.f }; 107 | luax_assert(L, lovrImageGetPixel(image, x, y, pixel)); 108 | lua_pushnumber(L, pixel[0]); 109 | lua_pushnumber(L, pixel[1]); 110 | lua_pushnumber(L, pixel[2]); 111 | lua_pushnumber(L, pixel[3]); 112 | return 4; 113 | } 114 | 115 | static int l_lovrImageSetPixel(lua_State* L) { 116 | Image* image = luax_checktype(L, 1, Image); 117 | uint32_t x = luax_checku32(L, 2); 118 | uint32_t y = luax_checku32(L, 3); 119 | float pixel[4] = { 120 | luax_optfloat(L, 4, 1.f), 121 | luax_optfloat(L, 5, 1.f), 122 | luax_optfloat(L, 6, 1.f), 123 | luax_optfloat(L, 7, 1.f) 124 | }; 125 | luax_assert(L, lovrImageSetPixel(image, x, y, pixel)); 126 | return 0; 127 | } 128 | 129 | void mapPixel(void* userdata, uint32_t x, uint32_t y, float pixel[4]) { 130 | lua_State* L = userdata; 131 | lua_pushvalue(L, 2); 132 | lua_pushinteger(L, x); 133 | lua_pushinteger(L, y); 134 | lua_pushnumber(L, pixel[0]); 135 | lua_pushnumber(L, pixel[1]); 136 | lua_pushnumber(L, pixel[2]); 137 | lua_pushnumber(L, pixel[3]); 138 | lua_call(L, 6, 4); 139 | if (!lua_isnil(L, -4)) pixel[0] = luax_tofloat(L, -4); 140 | if (!lua_isnil(L, -3)) pixel[1] = luax_tofloat(L, -3); 141 | if (!lua_isnil(L, -2)) pixel[2] = luax_tofloat(L, -2); 142 | if (!lua_isnil(L, -1)) pixel[3] = luax_tofloat(L, -1); 143 | lua_pop(L, 4); 144 | } 145 | 146 | static int l_lovrImageMapPixel(lua_State* L) { 147 | Image* image = luax_checktype(L, 1, Image); 148 | luaL_checktype(L, 2, LUA_TFUNCTION); 149 | uint32_t x = luax_optu32(L, 3, 0); 150 | uint32_t y = luax_optu32(L, 4, 0); 151 | uint32_t w = luax_optu32(L, 5, lovrImageGetWidth(image, 0)); 152 | uint32_t h = luax_optu32(L, 6, lovrImageGetHeight(image, 0)); 153 | lua_settop(L, 2); 154 | luax_assert(L, lovrImageMapPixel(image, x, y, w, h, mapPixel, L)); 155 | return 0; 156 | } 157 | 158 | static int l_lovrImagePaste(lua_State* L) { 159 | Image* dst = luax_checktype(L, 1, Image); 160 | Image* src = luax_checktype(L, 2, Image); 161 | uint32_t srcOffset[2], dstOffset[2], extent[2]; 162 | dstOffset[0] = luax_optu32(L, 3, 0); 163 | dstOffset[1] = luax_optu32(L, 4, 0); 164 | srcOffset[0] = luax_optu32(L, 5, 0); 165 | srcOffset[1] = luax_optu32(L, 6, 0); 166 | extent[0] = luax_optu32(L, 7, lovrImageGetWidth(src, 0)); 167 | extent[1] = luax_optu32(L, 8, lovrImageGetHeight(src, 0)); 168 | luax_assert(L, lovrImageCopy(src, dst, srcOffset, dstOffset, extent)); 169 | return 0; 170 | } 171 | 172 | static int l_lovrImageEncode(lua_State* L) { 173 | Image* image = luax_checktype(L, 1, Image); 174 | Blob* blob = lovrImageEncode(image); 175 | luax_assert(L, blob); 176 | luax_pushtype(L, Blob, blob); 177 | lovrRelease(blob, lovrBlobDestroy); 178 | return 1; 179 | } 180 | 181 | const luaL_Reg lovrImage[] = { 182 | { "getBlob", l_lovrImageGetBlob }, 183 | { "getPointer", l_lovrImageGetPointer }, 184 | { "getWidth", l_lovrImageGetWidth }, 185 | { "getHeight", l_lovrImageGetHeight }, 186 | { "getDimensions", l_lovrImageGetDimensions }, 187 | { "getFormat", l_lovrImageGetFormat }, 188 | { "getPixel", l_lovrImageGetPixel }, 189 | { "setPixel", l_lovrImageSetPixel }, 190 | { "mapPixel", l_lovrImageMapPixel }, 191 | { "paste", l_lovrImagePaste }, 192 | { "encode", l_lovrImageEncode }, 193 | { NULL, NULL } 194 | }; 195 | -------------------------------------------------------------------------------- /src/api/l_filesystem_file.c: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include "data/blob.h" 3 | #include "filesystem/filesystem.h" 4 | #include "util.h" 5 | #include 6 | 7 | static int l_lovrFileGetMode(lua_State* L) { 8 | File* file = luax_checktype(L, 1, File); 9 | OpenMode mode = lovrFileGetMode(file); 10 | luax_pushenum(L, OpenMode, mode); 11 | return 1; 12 | } 13 | 14 | static int l_lovrFileGetPath(lua_State* L) { 15 | File* file = luax_checktype(L, 1, File); 16 | const char* path = lovrFileGetPath(file); 17 | lua_pushstring(L, path); 18 | return 1; 19 | } 20 | 21 | static int l_lovrFileGetSize(lua_State* L) { 22 | File* file = luax_checktype(L, 1, File); 23 | uint64_t size; 24 | if (lovrFileGetSize(file, &size)) { 25 | if (size >= 1ull << 53) { 26 | lua_pushnil(L); 27 | lua_pushstring(L, "Too big"); 28 | return 2; 29 | } else { 30 | lua_pushinteger(L, size); 31 | return 1; 32 | } 33 | } else { 34 | lua_pushnil(L); 35 | lua_pushstring(L, lovrGetError()); 36 | return 2; 37 | } 38 | } 39 | 40 | static int l_lovrFileRead(lua_State* L) { 41 | File* file = luax_checktype(L, 1, File); 42 | size_t size; 43 | if (lua_type(L, 2) == LUA_TNUMBER) { 44 | lua_Number n = lua_tonumber(L, 2); 45 | luax_check(L, n >= 0, "Number of bytes to read can not be negative"); 46 | luax_check(L, n < 9007199254740992.0, "Number of bytes to read must be less than 2^53"); 47 | size = MIN((size_t) n, SIZE_MAX); 48 | } else { 49 | uint64_t fileSize; 50 | luax_assert(L, lovrFileGetSize(file, &fileSize)); 51 | fileSize -= lovrFileTell(file); 52 | size = MIN(fileSize, SIZE_MAX); 53 | } 54 | size_t count; 55 | void* data = lovrMalloc(size); 56 | bool success = lovrFileRead(file, data, size, &count); 57 | if (success) { 58 | lua_pushlstring(L, data, count); 59 | lua_pushnumber(L, count); 60 | lovrFree(data); 61 | return 2; 62 | } else { 63 | lovrFree(data); 64 | return luax_pushnilerror(L); 65 | } 66 | } 67 | 68 | static int l_lovrFileWrite(lua_State* L) { 69 | File* file = luax_checktype(L, 1, File); 70 | const void* data; 71 | size_t size; 72 | Blob* blob = luax_totype(L, 2, Blob); 73 | if (blob) { 74 | data = blob->data; 75 | size = blob->size; 76 | } else if (lua_type(L, 2) == LUA_TSTRING) { 77 | data = lua_tolstring(L, 2, &size); 78 | } else { 79 | return luax_typeerror(L, 2, "string or Blob"); 80 | } 81 | if (lua_type(L, 3) == LUA_TNUMBER) { 82 | lua_Number n = lua_tonumber(L, 2); 83 | luax_check(L, n >= 0, "Number of bytes to write can not be negative"); 84 | luax_check(L, n < 9007199254740992.0, "Number of bytes to write must be less than 2^53"); 85 | luax_check(L, n <= size, "Number of bytes to write is bigger than the size of the source"); 86 | size = (size_t) n; 87 | } 88 | size_t count; 89 | return luax_pushsuccess(L, lovrFileWrite(file, data, size, &count)); 90 | } 91 | 92 | static int l_lovrFileSeek(lua_State* L) { 93 | File* file = luax_checktype(L, 1, File); 94 | lua_Number offset = luaL_checknumber(L, 2); 95 | luax_check(L, offset >= 0 && offset < 9007199254740992.0, "Invalid seek position"); 96 | return luax_pushsuccess(L, lovrFileSeek(file, offset)); 97 | } 98 | 99 | static int l_lovrFileTell(lua_State* L) { 100 | File* file = luax_checktype(L, 1, File); 101 | uint64_t offset = lovrFileTell(file); 102 | if (offset >= 1ull << 53) { 103 | lua_pushnil(L); 104 | } else { 105 | lua_pushinteger(L, offset); 106 | } 107 | return 1; 108 | } 109 | 110 | static int l_lovrFileIsEOF(lua_State* L) { 111 | File* file = luax_checktype(L, 1, File); 112 | OpenMode mode = lovrFileGetMode(file); 113 | if (mode == OPEN_READ) { 114 | uint64_t size; 115 | if (!lovrFileGetSize(file, &size)) { 116 | lua_pushboolean(L, true); 117 | } else { 118 | uint64_t offset = lovrFileTell(file); 119 | lua_pushboolean(L, offset >= size); 120 | } 121 | } else { 122 | lua_pushboolean(L, false); 123 | } 124 | return 1; 125 | } 126 | 127 | const luaL_Reg lovrFile[] = { 128 | { "getMode", l_lovrFileGetMode }, 129 | { "getPath", l_lovrFileGetPath }, 130 | { "getSize", l_lovrFileGetSize }, 131 | { "read", l_lovrFileRead }, 132 | { "write", l_lovrFileWrite }, 133 | { "seek", l_lovrFileSeek }, 134 | { "tell", l_lovrFileTell }, 135 | { "isEOF", l_lovrFileIsEOF }, 136 | { NULL, NULL } 137 | }; 138 | -------------------------------------------------------------------------------- /src/api/l_graphics_font.c: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include "graphics/graphics.h" 3 | #include "data/rasterizer.h" 4 | #include "util.h" 5 | #include 6 | 7 | ColoredString* luax_checkcoloredstrings(lua_State* L, int index, uint32_t* count, ColoredString* stack) { 8 | if (lua_istable(L, index)) { 9 | *count = luax_len(L, index) / 2; 10 | ColoredString* strings = lovrMalloc(*count * sizeof(*strings)); 11 | for (uint32_t i = 0; i < *count; i++) { 12 | lua_rawgeti(L, index, i * 2 + 1); 13 | lua_rawgeti(L, index, i * 2 + 2); 14 | luax_optcolor(L, -2, strings[i].color); 15 | luax_check(L, lua_isstring(L, -1), "Expected a string to print"); 16 | strings[i].string = luaL_checklstring(L, -1, &strings[i].length); 17 | lua_pop(L, 2); 18 | } 19 | return strings; 20 | } else { 21 | stack->string = luaL_checklstring(L, index, &stack->length); 22 | stack->color[0] = stack->color[1] = stack->color[2] = stack->color[3] = 1.f; 23 | return *count = 1, stack; 24 | } 25 | } 26 | 27 | static int l_lovrFontGetRasterizer(lua_State* L) { 28 | Font* font = luax_checktype(L, 1, Font); 29 | const FontInfo* info = lovrFontGetInfo(font); 30 | luax_pushtype(L, Rasterizer, info->rasterizer); 31 | return 1; 32 | } 33 | 34 | static int l_lovrFontGetPixelDensity(lua_State* L) { 35 | Font* font = luax_checktype(L, 1, Font); 36 | float density = lovrFontGetPixelDensity(font); 37 | lua_pushnumber(L, density); 38 | return 1; 39 | } 40 | 41 | static int l_lovrFontSetPixelDensity(lua_State* L) { 42 | Font* font = luax_checktype(L, 1, Font); 43 | Rasterizer* rasterizer = lovrFontGetInfo(font)->rasterizer; 44 | float pixelDensity = luax_optfloat(L, 2, lovrRasterizerGetLeading(rasterizer)); 45 | lovrFontSetPixelDensity(font, pixelDensity); 46 | return 0; 47 | } 48 | 49 | static int l_lovrFontGetLineSpacing(lua_State* L) { 50 | Font* font = luax_checktype(L, 1, Font); 51 | float spacing = lovrFontGetLineSpacing(font); 52 | lua_pushnumber(L, spacing); 53 | return 1; 54 | } 55 | 56 | static int l_lovrFontSetLineSpacing(lua_State* L) { 57 | Font* font = luax_checktype(L, 1, Font); 58 | float spacing = luax_optfloat(L, 2, 1.f); 59 | lovrFontSetLineSpacing(font, spacing); 60 | return 0; 61 | } 62 | 63 | static int l_lovrFontGetAscent(lua_State* L) { 64 | Font* font = luax_checktype(L, 1, Font); 65 | const FontInfo* info = lovrFontGetInfo(font); 66 | float density = lovrFontGetPixelDensity(font); 67 | float ascent = lovrRasterizerGetAscent(info->rasterizer); 68 | lua_pushnumber(L, ascent / density); 69 | return 1; 70 | } 71 | 72 | static int l_lovrFontGetDescent(lua_State* L) { 73 | Font* font = luax_checktype(L, 1, Font); 74 | const FontInfo* info = lovrFontGetInfo(font); 75 | float density = lovrFontGetPixelDensity(font); 76 | float descent = lovrRasterizerGetDescent(info->rasterizer); 77 | lua_pushnumber(L, descent / density); 78 | return 1; 79 | } 80 | 81 | static int l_lovrFontGetHeight(lua_State* L) { 82 | Font* font = luax_checktype(L, 1, Font); 83 | const FontInfo* info = lovrFontGetInfo(font); 84 | float density = lovrFontGetPixelDensity(font); 85 | float height = lovrRasterizerGetLeading(info->rasterizer); 86 | lua_pushnumber(L, height / density); 87 | return 1; 88 | } 89 | 90 | static int l_lovrFontGetKerning(lua_State* L) { 91 | Font* font = luax_checktype(L, 1, Font); 92 | Rasterizer* rasterizer = lovrFontGetInfo(font)->rasterizer; 93 | uint32_t first = luax_checkcodepoint(L, 2); 94 | uint32_t second = luax_checkcodepoint(L, 3); 95 | float kerning = lovrRasterizerGetKerning(rasterizer, first, second); 96 | float density = lovrFontGetPixelDensity(font); 97 | lua_pushnumber(L, kerning / density); 98 | return 1; 99 | } 100 | 101 | static int l_lovrFontGetWidth(lua_State* L) { 102 | Font* font = luax_checktype(L, 1, Font); 103 | uint32_t count; 104 | ColoredString stack; 105 | ColoredString* strings = luax_checkcoloredstrings(L, 2, &count, &stack); 106 | float width = lovrFontGetWidth(font, strings, count); 107 | lua_pushnumber(L, width); 108 | return 1; 109 | } 110 | 111 | static void online(void* context, const char* string, size_t length) { 112 | lua_State* L = context; 113 | int index = luax_len(L, -1) + 1; 114 | lua_pushlstring(L, string, length); 115 | lua_rawseti(L, -2, index); 116 | } 117 | 118 | static int l_lovrFontGetLines(lua_State* L) { 119 | Font* font = luax_checktype(L, 1, Font); 120 | uint32_t count; 121 | ColoredString stack; 122 | ColoredString* strings = luax_checkcoloredstrings(L, 2, &count, &stack); 123 | float wrap = luax_checkfloat(L, 3); 124 | lua_newtable(L); 125 | lovrFontGetLines(font, strings, count, wrap, online, L); 126 | if (strings != &stack) lovrFree(strings); 127 | return 1; 128 | } 129 | 130 | static int l_lovrFontGetVertices(lua_State* L) { 131 | Font* font = luax_checktype(L, 1, Font); 132 | uint32_t count; 133 | ColoredString stack; 134 | ColoredString* strings = luax_checkcoloredstrings(L, 2, &count, &stack); 135 | float wrap = luax_optfloat(L, 3, 0.f); 136 | HorizontalAlign halign = luax_checkenum(L, 4, HorizontalAlign, "center"); 137 | VerticalAlign valign = luax_checkenum(L, 5, VerticalAlign, "middle"); 138 | size_t totalLength = 0; 139 | for (uint32_t i = 0; i < count; i++) { 140 | totalLength += strings[i].length; 141 | } 142 | 143 | GlyphVertex* vertices = lovrMalloc(totalLength * 4 * sizeof(GlyphVertex)); 144 | uint32_t glyphCount, lineCount; 145 | Material* material; 146 | 147 | bool success = lovrFontGetVertices(font, strings, count, wrap, halign, valign, vertices, &glyphCount, &lineCount, &material, false); 148 | if (strings != &stack) lovrFree(strings); 149 | 150 | if (!success) { 151 | lovrFree(vertices); 152 | luax_assert(L, false); 153 | } 154 | 155 | int vertexCount = glyphCount * 4; 156 | lua_createtable(L, vertexCount, 0); 157 | for (int i = 0; i < vertexCount; i++) { 158 | lua_createtable(L, 4, 0); 159 | lua_pushnumber(L, vertices[i].position.x); 160 | lua_rawseti(L, -2, 1); 161 | lua_pushnumber(L, vertices[i].position.y); 162 | lua_rawseti(L, -2, 2); 163 | lua_pushnumber(L, vertices[i].uv.u / 65535.f); 164 | lua_rawseti(L, -2, 3); 165 | lua_pushnumber(L, vertices[i].uv.v / 65535.f); 166 | lua_rawseti(L, -2, 4); 167 | lua_rawseti(L, -2, i + 1); 168 | } 169 | luax_pushtype(L, Material, material); 170 | lovrFree(vertices); 171 | return 2; 172 | } 173 | 174 | const luaL_Reg lovrFont[] = { 175 | { "getRasterizer", l_lovrFontGetRasterizer }, 176 | { "getPixelDensity", l_lovrFontGetPixelDensity }, 177 | { "setPixelDensity", l_lovrFontSetPixelDensity }, 178 | { "getLineSpacing", l_lovrFontGetLineSpacing }, 179 | { "setLineSpacing", l_lovrFontSetLineSpacing }, 180 | { "getAscent", l_lovrFontGetAscent }, 181 | { "getDescent", l_lovrFontGetDescent }, 182 | { "getHeight", l_lovrFontGetHeight }, 183 | { "getKerning", l_lovrFontGetKerning }, 184 | { "getWidth", l_lovrFontGetWidth }, 185 | { "getLines", l_lovrFontGetLines }, 186 | { "getVertices", l_lovrFontGetVertices }, 187 | { NULL, NULL } 188 | }; 189 | -------------------------------------------------------------------------------- /src/api/l_graphics_material.c: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include "graphics/graphics.h" 3 | #include "util.h" 4 | 5 | Material* luax_optmaterial(lua_State* L, int index) { 6 | if (lua_isnoneornil(L, index)) { 7 | return NULL; 8 | } else { 9 | Texture* texture = luax_totype(L, index, Texture); 10 | if (texture) { 11 | Material* material = lovrTextureToMaterial(texture); 12 | luax_assert(L, material); 13 | return material; 14 | } else { 15 | return luax_checktype(L, index, Material); 16 | } 17 | } 18 | } 19 | 20 | static int l_lovrMaterialGetProperties(lua_State* L) { 21 | Material* material = luax_checktype(L, 1, Material); 22 | const MaterialInfo* info = lovrMaterialGetInfo(material); 23 | lua_newtable(L); 24 | 25 | lua_createtable(L, 4, 0); 26 | lua_pushnumber(L, info->data.color[0]); 27 | lua_rawseti(L, -2, 1); 28 | lua_pushnumber(L, info->data.color[1]); 29 | lua_rawseti(L, -2, 2); 30 | lua_pushnumber(L, info->data.color[2]); 31 | lua_rawseti(L, -2, 3); 32 | lua_pushnumber(L, info->data.color[3]); 33 | lua_rawseti(L, -2, 4); 34 | lua_setfield(L, -2, "color"); 35 | 36 | lua_createtable(L, 4, 0); 37 | lua_pushnumber(L, info->data.glow[0]); 38 | lua_rawseti(L, -2, 1); 39 | lua_pushnumber(L, info->data.glow[1]); 40 | lua_rawseti(L, -2, 2); 41 | lua_pushnumber(L, info->data.glow[2]); 42 | lua_rawseti(L, -2, 3); 43 | lua_pushnumber(L, info->data.glow[3]); 44 | lua_rawseti(L, -2, 4); 45 | lua_setfield(L, -2, "glow"); 46 | 47 | lua_createtable(L, 2, 0); 48 | lua_pushnumber(L, info->data.uvShift[0]); 49 | lua_rawseti(L, -2, 1); 50 | lua_pushnumber(L, info->data.uvShift[1]); 51 | lua_rawseti(L, -2, 2); 52 | lua_setfield(L, -2, "uvShift"); 53 | 54 | lua_createtable(L, 2, 0); 55 | lua_pushnumber(L, info->data.uvScale[0]); 56 | lua_rawseti(L, -2, 1); 57 | lua_pushnumber(L, info->data.uvScale[1]); 58 | lua_rawseti(L, -2, 2); 59 | lua_setfield(L, -2, "uvScale"); 60 | 61 | lua_pushnumber(L, info->data.metalness), lua_setfield(L, -2, "metalness"); 62 | lua_pushnumber(L, info->data.roughness), lua_setfield(L, -2, "roughness"); 63 | lua_pushnumber(L, info->data.clearcoat), lua_setfield(L, -2, "clearcoat"); 64 | lua_pushnumber(L, info->data.clearcoatRoughness), lua_setfield(L, -2, "clearcoatRoughness"); 65 | lua_pushnumber(L, info->data.occlusionStrength), lua_setfield(L, -2, "occlusionStrength"); 66 | lua_pushnumber(L, info->data.normalScale), lua_setfield(L, -2, "normalScale"); 67 | lua_pushnumber(L, info->data.alphaCutoff), lua_setfield(L, -2, "alphaCutoff"); 68 | luax_pushtype(L, Texture, info->texture), lua_setfield(L, -2, "texture"); 69 | luax_pushtype(L, Texture, info->glowTexture), lua_setfield(L, -2, "glowTexture"); 70 | luax_pushtype(L, Texture, info->metalnessTexture), lua_setfield(L, -2, "metalnessTexture"); 71 | luax_pushtype(L, Texture, info->roughnessTexture), lua_setfield(L, -2, "roughnessTexture"); 72 | luax_pushtype(L, Texture, info->clearcoatTexture), lua_setfield(L, -2, "clearcoatTexture"); 73 | luax_pushtype(L, Texture, info->occlusionTexture), lua_setfield(L, -2, "occlusionTexture"); 74 | luax_pushtype(L, Texture, info->normalTexture), lua_setfield(L, -2, "normalTexture"); 75 | 76 | return 1; 77 | } 78 | 79 | const luaL_Reg lovrMaterial[] = { 80 | { "getProperties", l_lovrMaterialGetProperties }, 81 | { NULL, NULL } 82 | }; 83 | -------------------------------------------------------------------------------- /src/api/l_graphics_readback.c: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include "graphics/graphics.h" 3 | #include "data/blob.h" 4 | #include "data/image.h" 5 | #include "util.h" 6 | 7 | static int l_lovrReadbackIsComplete(lua_State* L) { 8 | Readback* readback = luax_checktype(L, 1, Readback); 9 | bool complete = lovrReadbackIsComplete(readback); 10 | lua_pushboolean(L, complete); 11 | return 1; 12 | } 13 | 14 | static int l_lovrReadbackWait(lua_State* L) { 15 | Readback* readback = luax_checktype(L, 1, Readback); 16 | bool waited; 17 | luax_assert(L, lovrReadbackWait(readback, &waited)); 18 | lua_pushboolean(L, waited); 19 | return 1; 20 | } 21 | 22 | static int l_lovrReadbackGetData(lua_State* L) { 23 | Readback* readback = luax_checktype(L, 1, Readback); 24 | DataField* format; 25 | uint32_t count; 26 | void* data = lovrReadbackGetData(readback, &format, &count); 27 | if (data && format) { 28 | return luax_pushbufferdata(L, format, count, data); 29 | } else { 30 | lua_pushnil(L); 31 | return 1; 32 | } 33 | } 34 | 35 | static int l_lovrReadbackGetBlob(lua_State* L) { 36 | Readback* readback = luax_checktype(L, 1, Readback); 37 | Blob* blob = lovrReadbackGetBlob(readback); 38 | luax_pushtype(L, Blob, blob); 39 | return 1; 40 | } 41 | 42 | static int l_lovrReadbackGetImage(lua_State* L) { 43 | Readback* readback = luax_checktype(L, 1, Readback); 44 | Image* image = lovrReadbackGetImage(readback); 45 | luax_pushtype(L, Image, image); 46 | return 1; 47 | } 48 | 49 | const luaL_Reg lovrReadback[] = { 50 | { "isComplete", l_lovrReadbackIsComplete }, 51 | { "wait", l_lovrReadbackWait }, 52 | { "getData", l_lovrReadbackGetData }, 53 | { "getBlob", l_lovrReadbackGetBlob }, 54 | { "getImage", l_lovrReadbackGetImage }, 55 | { NULL, NULL } 56 | }; 57 | -------------------------------------------------------------------------------- /src/api/l_graphics_sampler.c: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include "graphics/graphics.h" 3 | #include "util.h" 4 | 5 | static int l_lovrSamplerGetFilter(lua_State* L) { 6 | Sampler* sampler = luax_checktype(L, 1, Sampler); 7 | const SamplerInfo* info = lovrSamplerGetInfo(sampler); 8 | luax_pushenum(L, FilterMode, info->min); 9 | luax_pushenum(L, FilterMode, info->mag); 10 | luax_pushenum(L, FilterMode, info->mip); 11 | return 3; 12 | } 13 | 14 | static int l_lovrSamplerGetWrap(lua_State* L) { 15 | Sampler* sampler = luax_checktype(L, 1, Sampler); 16 | const SamplerInfo* info = lovrSamplerGetInfo(sampler); 17 | luax_pushenum(L, WrapMode, info->wrap[0]); 18 | luax_pushenum(L, WrapMode, info->wrap[1]); 19 | luax_pushenum(L, WrapMode, info->wrap[2]); 20 | return 3; 21 | } 22 | 23 | static int l_lovrSamplerGetCompareMode(lua_State* L) { 24 | Sampler* sampler = luax_checktype(L, 1, Sampler); 25 | const SamplerInfo* info = lovrSamplerGetInfo(sampler); 26 | luax_pushenum(L, CompareMode, info->compare); 27 | return 1; 28 | } 29 | 30 | static int l_lovrSamplerGetAnisotropy(lua_State* L) { 31 | Sampler* sampler = luax_checktype(L, 1, Sampler); 32 | const SamplerInfo* info = lovrSamplerGetInfo(sampler); 33 | lua_pushnumber(L, info->anisotropy); 34 | return 1; 35 | } 36 | 37 | static int l_lovrSamplerGetMipmapRange(lua_State* L) { 38 | Sampler* sampler = luax_checktype(L, 1, Sampler); 39 | const SamplerInfo* info = lovrSamplerGetInfo(sampler); 40 | lua_pushnumber(L, info->range[0]); 41 | lua_pushnumber(L, info->range[1]); 42 | return 2; 43 | } 44 | 45 | const luaL_Reg lovrSampler[] = { 46 | { "getFilter", l_lovrSamplerGetFilter }, 47 | { "getWrap", l_lovrSamplerGetWrap }, 48 | { "getCompareMode", l_lovrSamplerGetCompareMode }, 49 | { "getAnisotropy", l_lovrSamplerGetAnisotropy }, 50 | { "getMipmapRange", l_lovrSamplerGetMipmapRange }, 51 | { NULL, NULL } 52 | }; 53 | -------------------------------------------------------------------------------- /src/api/l_graphics_shader.c: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include "graphics/graphics.h" 3 | #include "util.h" 4 | #include 5 | 6 | static int l_lovrShaderClone(lua_State* L) { 7 | Shader* shader = luax_checktype(L, 1, Shader); 8 | luaL_checktype(L, 2, LUA_TTABLE); 9 | lua_pushnil(L); 10 | 11 | arr_t(ShaderFlag) flags; 12 | arr_init(&flags); 13 | 14 | while (lua_next(L, 2) != 0) { 15 | ShaderFlag flag = { 0 }; 16 | flag.value = lua_isboolean(L, -1) ? (double) lua_toboolean(L, -1) : lua_tonumber(L, -1); 17 | switch (lua_type(L, -2)) { 18 | case LUA_TSTRING: flag.name = lua_tostring(L, -2); break; 19 | case LUA_TNUMBER: flag.id = lua_tointeger(L, -2); break; 20 | default: 21 | arr_free(&flags); 22 | luaL_error(L, "Unexpected ShaderFlag key type (%s)", lua_typename(L, lua_type(L, -2))); 23 | } 24 | arr_push(&flags, flag); 25 | lua_pop(L, 1); 26 | } 27 | 28 | if (flags.length >= 1000) { 29 | arr_free(&flags); 30 | luaL_error(L, "Too many Shader flags"); 31 | } 32 | 33 | Shader* clone = lovrShaderClone(shader, flags.data, (uint32_t) flags.length); 34 | arr_free(&flags); 35 | luax_assert(L, clone); 36 | luax_pushtype(L, Shader, clone); 37 | lovrRelease(clone, lovrShaderDestroy); 38 | return 1; 39 | } 40 | 41 | static int l_lovrShaderGetLabel(lua_State* L) { 42 | Shader* shader = luax_checktype(L, 1, Shader); 43 | const ShaderInfo* info = lovrShaderGetInfo(shader); 44 | lua_pushstring(L, info->label); 45 | return 1; 46 | } 47 | 48 | static int l_lovrShaderGetType(lua_State* L) { 49 | Shader* shader = luax_checktype(L, 1, Shader); 50 | const ShaderInfo* info = lovrShaderGetInfo(shader); 51 | luax_pushenum(L, ShaderType, info->type); 52 | return 1; 53 | } 54 | 55 | static int l_lovrShaderHasStage(lua_State* L) { 56 | Shader* shader = luax_checktype(L, 1, Shader); 57 | ShaderStage stage = luax_checkenum(L, 2, ShaderStage, NULL); 58 | bool present = lovrShaderHasStage(shader, stage); 59 | lua_pushboolean(L, present); 60 | return 1; 61 | } 62 | 63 | static int l_lovrShaderHasAttribute(lua_State* L) { 64 | Shader* shader = luax_checktype(L, 1, Shader); 65 | const char* name; 66 | uint32_t location; 67 | if (lua_type(L, 2) == LUA_TNUMBER) { 68 | location = luax_checku32(L, 2); 69 | name = NULL; 70 | } else { 71 | name = lua_tostring(L, 2); 72 | location = 0; 73 | } 74 | bool present = lovrShaderHasAttribute(shader, name, location); 75 | lua_pushboolean(L, present); 76 | return 1; 77 | } 78 | 79 | static int l_lovrShaderHasVariable(lua_State* L) { 80 | Shader* shader = luax_checktype(L, 1, Shader); 81 | const char* variable = luaL_checkstring(L, 2); 82 | bool present = lovrShaderHasVariable(shader, variable); 83 | lua_pushboolean(L, present); 84 | return 1; 85 | } 86 | 87 | static int l_lovrShaderGetWorkgroupSize(lua_State* L) { 88 | Shader* shader = luax_checktype(L, 1, Shader); 89 | 90 | if (!lovrShaderHasStage(shader, STAGE_COMPUTE)) { 91 | lua_pushnil(L); 92 | return 1; 93 | } 94 | 95 | uint32_t size[3]; 96 | lovrShaderGetWorkgroupSize(shader, size); 97 | lua_pushinteger(L, size[0]); 98 | lua_pushinteger(L, size[1]); 99 | lua_pushinteger(L, size[2]); 100 | return 3; 101 | } 102 | 103 | static int l_lovrShaderGetBufferFormat(lua_State* L) { 104 | Shader* shader = luax_checktype(L, 1, Shader); 105 | const char* name = luaL_checkstring(L, 2); 106 | 107 | uint32_t fieldCount; 108 | const DataField* format = lovrShaderGetBufferFormat(shader, name, &fieldCount); 109 | 110 | if (!format) { 111 | lua_pushnil(L); 112 | return 1; 113 | } 114 | 115 | // If the buffer just has a single array in it, unwrap it 116 | if (format->fieldCount == 1 && format->fields->length > 0) { 117 | const DataField* array = format->fields; 118 | 119 | if (array->fields) { // Array of structs 120 | luax_pushbufferformat(L, array->fields, array->fieldCount); 121 | } else { 122 | lua_createtable(L, 1, 1); 123 | luax_pushenum(L, DataType, array->type); 124 | lua_rawseti(L, -2, 1); 125 | } 126 | 127 | lua_pushinteger(L, array->stride); 128 | lua_setfield(L, -2, "stride"); 129 | 130 | if (array->length == ~0u) { 131 | lua_pushnil(L); 132 | } else { 133 | lua_pushinteger(L, array->length); 134 | } 135 | } else { 136 | luax_pushbufferformat(L, format->fields, format->fieldCount); 137 | lua_pushnil(L); 138 | } 139 | 140 | return 2; 141 | } 142 | 143 | const luaL_Reg lovrShader[] = { 144 | { "clone", l_lovrShaderClone }, 145 | { "getLabel", l_lovrShaderGetLabel }, 146 | { "getType", l_lovrShaderGetType }, 147 | { "hasStage", l_lovrShaderHasStage }, 148 | { "hasAttribute", l_lovrShaderHasAttribute }, 149 | { "hasVariable", l_lovrShaderHasVariable }, 150 | { "getWorkgroupSize", l_lovrShaderGetWorkgroupSize }, 151 | { "getBufferFormat", l_lovrShaderGetBufferFormat }, 152 | { NULL, NULL } 153 | }; 154 | -------------------------------------------------------------------------------- /src/api/l_headset_layer.c: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include "headset/headset.h" 3 | #include "core/maf.h" 4 | #include "util.h" 5 | 6 | static int l_lovrLayerGetPosition(lua_State* L) { 7 | Layer* layer = luax_checktype(L, 1, Layer); 8 | float position[3], orientation[4]; 9 | lovrHeadsetInterface->getLayerPose(layer, position, orientation); 10 | lua_pushnumber(L, position[0]); 11 | lua_pushnumber(L, position[1]); 12 | lua_pushnumber(L, position[2]); 13 | return 3; 14 | } 15 | 16 | static int l_lovrLayerSetPosition(lua_State* L) { 17 | Layer* layer = luax_checktype(L, 1, Layer); 18 | float position[3], orientation[4]; 19 | lovrHeadsetInterface->getLayerPose(layer, position, orientation); 20 | luax_readvec3(L, 2, position, NULL); 21 | lovrHeadsetInterface->setLayerPose(layer, position, orientation); 22 | return 0; 23 | } 24 | 25 | static int l_lovrLayerGetOrientation(lua_State* L) { 26 | Layer* layer = luax_checktype(L, 1, Layer); 27 | float position[3], orientation[4], angle, ax, ay, az; 28 | lovrHeadsetInterface->getLayerPose(layer, position, orientation); 29 | quat_getAngleAxis(orientation, &angle, &ax, &ay, &az); 30 | lua_pushnumber(L, angle); 31 | lua_pushnumber(L, ax); 32 | lua_pushnumber(L, ay); 33 | lua_pushnumber(L, az); 34 | return 4; 35 | } 36 | 37 | static int l_lovrLayerSetOrientation(lua_State* L) { 38 | Layer* layer = luax_checktype(L, 1, Layer); 39 | float position[3], orientation[4]; 40 | lovrHeadsetInterface->getLayerPose(layer, position, orientation); 41 | luax_readquat(L, 2, orientation, NULL); 42 | lovrHeadsetInterface->setLayerPose(layer, position, orientation); 43 | return 0; 44 | } 45 | 46 | static int l_lovrLayerGetPose(lua_State* L) { 47 | Layer* layer = luax_checktype(L, 1, Layer); 48 | float position[3], orientation[4], angle, ax, ay, az; 49 | lovrHeadsetInterface->getLayerPose(layer, position, orientation); 50 | lua_pushnumber(L, position[0]); 51 | lua_pushnumber(L, position[1]); 52 | lua_pushnumber(L, position[2]); 53 | quat_getAngleAxis(orientation, &angle, &ax, &ay, &az); 54 | lua_pushnumber(L, angle); 55 | lua_pushnumber(L, ax); 56 | lua_pushnumber(L, ay); 57 | lua_pushnumber(L, az); 58 | return 7; 59 | } 60 | 61 | static int l_lovrLayerSetPose(lua_State* L) { 62 | Layer* layer = luax_checktype(L, 1, Layer); 63 | float position[3], orientation[4]; 64 | int index = luax_readvec3(L, 2, position, NULL); 65 | luax_readquat(L, index, orientation, NULL); 66 | lovrHeadsetInterface->setLayerPose(layer, position, orientation); 67 | return 0; 68 | } 69 | 70 | static int l_lovrLayerGetDimensions(lua_State* L) { 71 | Layer* layer = luax_checktype(L, 1, Layer); 72 | float width, height; 73 | lovrHeadsetInterface->getLayerDimensions(layer, &width, &height); 74 | lua_pushnumber(L, width); 75 | lua_pushnumber(L, height); 76 | return 2; 77 | } 78 | 79 | static int l_lovrLayerSetDimensions(lua_State* L) { 80 | Layer* layer = luax_checktype(L, 1, Layer); 81 | float width = luax_checkfloat(L, 2); 82 | float height = luax_checkfloat(L, 3); 83 | lovrHeadsetInterface->setLayerDimensions(layer, width, height); 84 | return 0; 85 | } 86 | 87 | static int l_lovrLayerGetCurve(lua_State* L) { 88 | Layer* layer = luax_checktype(L, 1, Layer); 89 | float curve = lovrHeadsetInterface->getLayerCurve(layer); 90 | lua_pushnumber(L, curve); 91 | return 1; 92 | } 93 | 94 | static int l_lovrLayerSetCurve(lua_State* L) { 95 | Layer* layer = luax_checktype(L, 1, Layer); 96 | float curve = luax_optfloat(L, 2, 0.f); 97 | luax_assert(L, lovrHeadsetInterface->setLayerCurve(layer, curve)); 98 | return 0; 99 | } 100 | 101 | static int l_lovrLayerGetColor(lua_State* L) { 102 | Layer* layer = luax_checktype(L, 1, Layer); 103 | float color[4]; 104 | lovrHeadsetInterface->getLayerColor(layer, color); 105 | lua_pushnumber(L, color[0]); 106 | lua_pushnumber(L, color[1]); 107 | lua_pushnumber(L, color[2]); 108 | lua_pushnumber(L, color[3]); 109 | return 4; 110 | } 111 | 112 | static int l_lovrLayerSetColor(lua_State* L) { 113 | Layer* layer = luax_checktype(L, 1, Layer); 114 | float color[4]; 115 | luax_readcolor(L, 1, color); 116 | lovrHeadsetInterface->setLayerColor(layer, color); 117 | return 0; 118 | } 119 | 120 | static int l_lovrLayerGetViewport(lua_State* L) { 121 | Layer* layer = luax_checktype(L, 1, Layer); 122 | int32_t viewport[4]; 123 | lovrHeadsetInterface->getLayerViewport(layer, viewport); 124 | lua_pushinteger(L, viewport[0]); 125 | lua_pushinteger(L, viewport[1]); 126 | lua_pushinteger(L, viewport[2]); 127 | lua_pushinteger(L, viewport[3]); 128 | return 4; 129 | } 130 | 131 | static int l_lovrLayerSetViewport(lua_State* L) { 132 | Layer* layer = luax_checktype(L, 1, Layer); 133 | int32_t viewport[4]; 134 | viewport[0] = luax_optu32(L, 2, 0); 135 | viewport[1] = luax_optu32(L, 3, 0); 136 | viewport[2] = luax_optu32(L, 4, 0); 137 | viewport[3] = luax_optu32(L, 5, 0); 138 | lovrHeadsetInterface->setLayerViewport(layer, viewport); 139 | return 0; 140 | } 141 | 142 | static int l_lovrLayerGetTexture(lua_State* L) { 143 | Layer* layer = luax_checktype(L, 1, Layer); 144 | struct Texture* texture = lovrHeadsetInterface->getLayerTexture(layer); 145 | luax_assert(L, texture); 146 | luax_pushtype(L, Texture, texture); 147 | return 1; 148 | } 149 | 150 | static int l_lovrLayerGetPass(lua_State* L) { 151 | Layer* layer = luax_checktype(L, 1, Layer); 152 | struct Pass* pass = lovrHeadsetInterface->getLayerPass(layer); 153 | luax_assert(L, pass); 154 | luax_pushtype(L, Pass, pass); 155 | return 1; 156 | } 157 | 158 | const luaL_Reg lovrLayer[] = { 159 | { "getPosition", l_lovrLayerGetPosition }, 160 | { "setPosition", l_lovrLayerSetPosition }, 161 | { "getOrientation", l_lovrLayerGetOrientation }, 162 | { "setOrientation", l_lovrLayerSetOrientation }, 163 | { "getPose", l_lovrLayerGetPose }, 164 | { "setPose", l_lovrLayerSetPose }, 165 | { "getDimensions", l_lovrLayerGetDimensions }, 166 | { "setDimensions", l_lovrLayerSetDimensions }, 167 | { "getCurve", l_lovrLayerGetCurve }, 168 | { "setCurve", l_lovrLayerSetCurve }, 169 | { "getColor", l_lovrLayerGetColor }, 170 | { "setColor", l_lovrLayerSetColor }, 171 | { "getViewport", l_lovrLayerGetViewport }, 172 | { "setViewport", l_lovrLayerSetViewport }, 173 | { "getTexture", l_lovrLayerGetTexture }, 174 | { "getPass", l_lovrLayerGetPass }, 175 | { NULL, NULL } 176 | }; 177 | -------------------------------------------------------------------------------- /src/api/l_lovr.c: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include "util.h" 3 | 4 | #define _STRINGIFY(x) #x 5 | #define STRINGIFY(x) _STRINGIFY(x) 6 | 7 | static int l_lovrGetVersion(lua_State* L) { 8 | lua_pushinteger(L, LOVR_VERSION_MAJOR); 9 | lua_pushinteger(L, LOVR_VERSION_MINOR); 10 | lua_pushinteger(L, LOVR_VERSION_PATCH); 11 | lua_pushliteral(L, LOVR_VERSION_ALIAS); 12 | #ifdef LOVR_VERSION_HASH 13 | lua_pushstring(L, STRINGIFY(LOVR_VERSION_HASH)); 14 | return 5; 15 | #else 16 | return 4; 17 | #endif 18 | } 19 | 20 | static const luaL_Reg lovr[] = { 21 | { "_setConf", luax_setconf }, 22 | { "getVersion", l_lovrGetVersion }, 23 | { NULL, NULL } 24 | }; 25 | 26 | int luaopen_lovr(lua_State* L) { 27 | lua_newtable(L); 28 | luax_register(L, lovr); 29 | return 1; 30 | } 31 | -------------------------------------------------------------------------------- /src/api/l_math_curve.c: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include "util.h" 3 | 4 | static int l_lovrCurveEvaluate(lua_State* L) { 5 | Curve* curve = luax_checktype(L, 1, Curve); 6 | float t = luax_checkfloat(L, 2); 7 | float point[4]; 8 | luax_assert(L, lovrCurveEvaluate(curve, t, point)); 9 | lua_pushnumber(L, point[0]); 10 | lua_pushnumber(L, point[1]); 11 | lua_pushnumber(L, point[2]); 12 | return 3; 13 | } 14 | 15 | static int l_lovrCurveGetTangent(lua_State* L) { 16 | Curve* curve = luax_checktype(L, 1, Curve); 17 | float t = luax_checkfloat(L, 2); 18 | float point[4]; 19 | lovrCurveGetTangent(curve, t, point); 20 | lua_pushnumber(L, point[0]); 21 | lua_pushnumber(L, point[1]); 22 | lua_pushnumber(L, point[2]); 23 | return 3; 24 | } 25 | 26 | static int l_lovrCurveRender(lua_State* L) { 27 | Curve* curve = luax_checktype(L, 1, Curve); 28 | int n = luaL_optinteger(L, 2, 32); 29 | float t1 = luax_optfloat(L, 3, 0.); 30 | float t2 = luax_optfloat(L, 4, 1.); 31 | if (lovrCurveGetPointCount(curve) == 2) { 32 | n = 2; 33 | } 34 | lua_createtable(L, n * 3, 0); 35 | float step = 1.f / (n - 1); 36 | for (int i = 0; i < n; i++) { 37 | float point[4]; 38 | lovrCurveEvaluate(curve, t1 + (t2 - t1) * i * step, point); 39 | lua_pushnumber(L, point[0]); 40 | lua_rawseti(L, -2, 3 * i + 1); 41 | lua_pushnumber(L, point[1]); 42 | lua_rawseti(L, -2, 3 * i + 2); 43 | lua_pushnumber(L, point[2]); 44 | lua_rawseti(L, -2, 3 * i + 3); 45 | } 46 | return 1; 47 | } 48 | 49 | static int l_lovrCurveSlice(lua_State* L) { 50 | Curve* curve = luax_checktype(L, 1, Curve); 51 | float t1 = luax_checkfloat(L, 2); 52 | float t2 = luax_checkfloat(L, 3); 53 | Curve* subcurve = lovrCurveSlice(curve, t1, t2); 54 | luax_assert(L, subcurve); 55 | luax_pushtype(L, Curve, subcurve); 56 | lovrRelease(subcurve, lovrCurveDestroy); 57 | return 1; 58 | } 59 | 60 | static int l_lovrCurveGetPointCount(lua_State* L) { 61 | Curve* curve = luax_checktype(L, 1, Curve); 62 | lua_pushinteger(L, lovrCurveGetPointCount(curve)); 63 | return 1; 64 | } 65 | 66 | static int l_lovrCurveGetPoint(lua_State* L) { 67 | Curve* curve = luax_checktype(L, 1, Curve); 68 | size_t index = luaL_checkinteger(L, 2) - 1; 69 | luax_check(L, index < lovrCurveGetPointCount(curve), "Invalid Curve point index: %d", index + 1); 70 | float point[4]; 71 | lovrCurveGetPoint(curve, index, point); 72 | lua_pushnumber(L, point[0]); 73 | lua_pushnumber(L, point[1]); 74 | lua_pushnumber(L, point[2]); 75 | return 3; 76 | } 77 | 78 | static int l_lovrCurveSetPoint(lua_State* L) { 79 | Curve* curve = luax_checktype(L, 1, Curve); 80 | size_t index = luaL_checkinteger(L, 2) - 1; 81 | luax_check(L, index < lovrCurveGetPointCount(curve), "Invalid Curve point index: %d", index + 1); 82 | float point[4]; 83 | luax_readvec3(L, 3, point, NULL); 84 | lovrCurveSetPoint(curve, index, point); 85 | return 0; 86 | } 87 | 88 | static int l_lovrCurveAddPoint(lua_State* L) { 89 | Curve* curve = luax_checktype(L, 1, Curve); 90 | float point[4]; 91 | int i = luax_readvec3(L, 2, point, NULL); 92 | size_t index = lua_isnoneornil(L, i) ? lovrCurveGetPointCount(curve) : (size_t) luaL_checkinteger(L, i) - 1; 93 | luax_check(L, index <= lovrCurveGetPointCount(curve), "Invalid Curve point index: %d", index + 1); 94 | lovrCurveAddPoint(curve, point, index); 95 | return 0; 96 | } 97 | 98 | static int l_lovrCurveRemovePoint(lua_State* L) { 99 | Curve* curve = luax_checktype(L, 1, Curve); 100 | size_t index = luaL_checkinteger(L, 2) - 1; 101 | luax_check(L, index < lovrCurveGetPointCount(curve), "Invalid Curve point index: %d", index + 1); 102 | lovrCurveRemovePoint(curve, index); 103 | return 0; 104 | } 105 | 106 | const luaL_Reg lovrCurve[] = { 107 | { "evaluate", l_lovrCurveEvaluate }, 108 | { "getTangent", l_lovrCurveGetTangent }, 109 | { "render", l_lovrCurveRender }, 110 | { "slice", l_lovrCurveSlice }, 111 | { "getPointCount", l_lovrCurveGetPointCount }, 112 | { "getPoint", l_lovrCurveGetPoint }, 113 | { "setPoint", l_lovrCurveSetPoint }, 114 | { "addPoint", l_lovrCurveAddPoint }, 115 | { "removePoint", l_lovrCurveRemovePoint }, 116 | { NULL, NULL } 117 | }; 118 | -------------------------------------------------------------------------------- /src/api/l_math_randomGenerator.c: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include "util.h" 3 | #include 4 | 5 | static double luax_checkrandomseedpart(lua_State* L, int index) { 6 | double x = luaL_checknumber(L, index); 7 | 8 | if (!isfinite(x)) { 9 | luaL_argerror(L, index, "invalid random seed"); 10 | } 11 | 12 | return x; 13 | } 14 | 15 | uint64_t luax_checkrandomseed(lua_State* L, int index) { 16 | Seed seed; 17 | 18 | if (lua_isnoneornil(L, index + 1)) { 19 | seed.b64 = luax_checkrandomseedpart(L, index); 20 | } else { 21 | seed.b32.lo = luax_checkrandomseedpart(L, index); 22 | seed.b32.hi = luax_checkrandomseedpart(L, index + 1); 23 | } 24 | 25 | return seed.b64; 26 | } 27 | 28 | int l_lovrRandomGeneratorGetSeed(lua_State* L) { 29 | RandomGenerator* generator = luax_checktype(L, 1, RandomGenerator); 30 | Seed seed = lovrRandomGeneratorGetSeed(generator); 31 | lua_pushnumber(L, seed.b32.lo); 32 | lua_pushnumber(L, seed.b32.hi); 33 | return 2; 34 | } 35 | 36 | int l_lovrRandomGeneratorSetSeed(lua_State* L) { 37 | RandomGenerator* generator = luax_checktype(L, 1, RandomGenerator); 38 | Seed seed = { .b64 = luax_checkrandomseed(L, 2) }; 39 | lovrRandomGeneratorSetSeed(generator, seed); 40 | return 0; 41 | } 42 | 43 | static int l_lovrRandomGeneratorGetState(lua_State* L) { 44 | RandomGenerator* generator = luax_checktype(L, 1, RandomGenerator); 45 | size_t length = 32; 46 | char state[32]; 47 | lovrRandomGeneratorGetState(generator, state, length); 48 | lua_pushstring(L, state); 49 | return 1; 50 | } 51 | 52 | static int l_lovrRandomGeneratorSetState(lua_State* L) { 53 | RandomGenerator* generator = luax_checktype(L, 1, RandomGenerator); 54 | const char* state = luaL_checklstring(L, 2, NULL); 55 | if (lovrRandomGeneratorSetState(generator, state)) { 56 | luaL_error(L, "invalid random state %s", state); 57 | } 58 | return 0; 59 | } 60 | 61 | int l_lovrRandomGeneratorRandom(lua_State* L) { 62 | RandomGenerator* generator = luax_checktype(L, 1, RandomGenerator); 63 | double r = lovrRandomGeneratorRandom(generator); 64 | 65 | if (lua_gettop(L) >= 3) { 66 | double lower = luaL_checknumber(L, 2); 67 | double upper = luaL_checknumber(L, 3); 68 | lua_pushnumber(L, floor(r * (upper - lower + 1)) + lower); 69 | } else if (lua_gettop(L) >= 2) { 70 | double upper = luaL_checknumber(L, 2); 71 | lua_pushnumber(L, floor(r * upper) + 1); 72 | } else { 73 | lua_pushnumber(L, r); 74 | } 75 | 76 | return 1; 77 | } 78 | 79 | int l_lovrRandomGeneratorRandomNormal(lua_State* L) { 80 | RandomGenerator* generator = luax_checktype(L, 1, RandomGenerator); 81 | float sigma = luax_optfloat(L, 2, 1.f); 82 | float mu = luax_optfloat(L, 3, 0.f); 83 | lua_pushnumber(L, mu + lovrRandomGeneratorRandomNormal(generator) * sigma); 84 | return 1; 85 | } 86 | 87 | const luaL_Reg lovrRandomGenerator[] = { 88 | { "getSeed", l_lovrRandomGeneratorGetSeed }, 89 | { "setSeed", l_lovrRandomGeneratorSetSeed }, 90 | { "getState", l_lovrRandomGeneratorGetState }, 91 | { "setState", l_lovrRandomGeneratorSetState }, 92 | { "random", l_lovrRandomGeneratorRandom }, 93 | { "randomNormal", l_lovrRandomGeneratorRandomNormal }, 94 | { NULL, NULL } 95 | }; 96 | -------------------------------------------------------------------------------- /src/api/l_physics_contact.c: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include "physics/physics.h" 3 | #include "util.h" 4 | 5 | static Contact* luax_checkcontact(lua_State* L, int index) { 6 | Contact* contact = luax_checktype(L, index, Contact); 7 | luax_check(L, lovrContactIsValid(contact), "Attempt to use a Contact outside of a World callback!"); 8 | return contact; 9 | } 10 | 11 | static int l_lovrContactGetColliders(lua_State* L) { 12 | Contact* contact = luax_checkcontact(L, 1); 13 | Collider* a = lovrContactGetColliderA(contact); 14 | Collider* b = lovrContactGetColliderB(contact); 15 | luax_pushtype(L, Collider, a); 16 | luax_pushtype(L, Collider, b); 17 | return 2; 18 | } 19 | 20 | static int l_lovrContactGetShapes(lua_State* L) { 21 | Contact* contact = luax_checkcontact(L, 1); 22 | Shape* a = lovrContactGetShapeA(contact); 23 | Shape* b = lovrContactGetShapeB(contact); 24 | luax_pushshape(L, a); 25 | luax_pushshape(L, b); 26 | return 2; 27 | } 28 | 29 | static int l_lovrContactGetNormal(lua_State* L) { 30 | Contact* contact = luax_checkcontact(L, 1); 31 | float normal[3]; 32 | lovrContactGetNormal(contact, normal); 33 | lua_pushnumber(L, normal[0]); 34 | lua_pushnumber(L, normal[1]); 35 | lua_pushnumber(L, normal[2]); 36 | return 3; 37 | } 38 | 39 | static int l_lovrContactGetOverlap(lua_State* L) { 40 | Contact* contact = luax_checkcontact(L, 1); 41 | float overlap = lovrContactGetOverlap(contact); 42 | lua_pushnumber(L, overlap); 43 | return 1; 44 | } 45 | 46 | static int l_lovrContactGetPoints(lua_State* L) { 47 | Contact* contact = luax_checkcontact(L, 1); 48 | uint32_t count = lovrContactGetPointCount(contact); 49 | lua_checkstack(L, count * 3); 50 | float point[3]; 51 | for (uint32_t i = 0; i < count; i++) { 52 | lovrContactGetPoint(contact, i, point); 53 | lua_pushnumber(L, point[0]); 54 | lua_pushnumber(L, point[1]); 55 | lua_pushnumber(L, point[2]); 56 | } 57 | return count * 3; 58 | } 59 | 60 | static int l_lovrContactGetFriction(lua_State* L) { 61 | Contact* contact = luax_checkcontact(L, 1); 62 | float friction = lovrContactGetFriction(contact); 63 | lua_pushnumber(L, friction); 64 | return 1; 65 | } 66 | 67 | static int l_lovrContactSetFriction(lua_State* L) { 68 | Contact* contact = luax_checkcontact(L, 1); 69 | float friction = luax_checkfloat(L, 2); 70 | lovrContactSetFriction(contact, friction); 71 | return 0; 72 | } 73 | 74 | static int l_lovrContactGetRestitution(lua_State* L) { 75 | Contact* contact = luax_checkcontact(L, 1); 76 | float restitution = lovrContactGetRestitution(contact); 77 | lua_pushnumber(L, restitution); 78 | return 1; 79 | } 80 | 81 | static int l_lovrContactSetRestitution(lua_State* L) { 82 | Contact* contact = luax_checkcontact(L, 1); 83 | float restitution = luax_checkfloat(L, 2); 84 | lovrContactSetRestitution(contact, restitution); 85 | return 0; 86 | } 87 | 88 | static int l_lovrContactIsEnabled(lua_State* L) { 89 | Contact* contact = luax_checkcontact(L, 1); 90 | bool enabled = lovrContactIsEnabled(contact); 91 | lua_pushboolean(L, enabled); 92 | return 1; 93 | } 94 | 95 | static int l_lovrContactSetEnabled(lua_State* L) { 96 | Contact* contact = luax_checkcontact(L, 1); 97 | lovrContactSetEnabled(contact, lua_toboolean(L, 2)); 98 | return 0; 99 | } 100 | 101 | static int l_lovrContactGetSurfaceVelocity(lua_State* L) { 102 | Contact* contact = luax_checkcontact(L, 1); 103 | float velocity[3]; 104 | lovrContactGetSurfaceVelocity(contact, velocity); 105 | lua_pushnumber(L, velocity[0]); 106 | lua_pushnumber(L, velocity[1]); 107 | lua_pushnumber(L, velocity[2]); 108 | return 3; 109 | } 110 | 111 | static int l_lovrContactSetSurfaceVelocity(lua_State* L) { 112 | Contact* contact = luax_checkcontact(L, 1); 113 | float velocity[3]; 114 | luax_readvec3(L, 2, velocity, NULL); 115 | lovrContactSetSurfaceVelocity(contact, velocity); 116 | return 0; 117 | } 118 | 119 | const luaL_Reg lovrContact[] = { 120 | { "getColliders", l_lovrContactGetColliders }, 121 | { "getShapes", l_lovrContactGetShapes }, 122 | { "getNormal", l_lovrContactGetNormal }, 123 | { "getOverlap", l_lovrContactGetOverlap }, 124 | { "getPoints", l_lovrContactGetPoints }, 125 | { "getFriction", l_lovrContactGetFriction }, 126 | { "setFriction", l_lovrContactSetFriction }, 127 | { "getRestitution", l_lovrContactGetRestitution }, 128 | { "setRestitution", l_lovrContactSetRestitution }, 129 | { "isEnabled", l_lovrContactIsEnabled }, 130 | { "setEnabled", l_lovrContactSetEnabled }, 131 | { "getSurfaceVelocity", l_lovrContactGetSurfaceVelocity }, 132 | { "setSurfaceVelocity", l_lovrContactSetSurfaceVelocity }, 133 | { NULL, NULL } 134 | }; 135 | -------------------------------------------------------------------------------- /src/api/l_thread.c: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include "data/blob.h" 3 | #include "event/event.h" 4 | #include "thread/thread.h" 5 | #include "core/os.h" 6 | #include "util.h" 7 | #include 8 | #include 9 | #include 10 | 11 | static char* threadRunner(Thread* thread, Blob* body, Variant* arguments, uint32_t argumentCount) { 12 | lua_State* L = luaL_newstate(); 13 | luaL_openlibs(L); 14 | luax_preload(L); 15 | 16 | lua_pushcfunction(L, luax_getstack); 17 | int errhandler = lua_gettop(L); 18 | 19 | if (!luaL_loadbuffer(L, body->data, body->size, "thread")) { 20 | for (uint32_t i = 0; i < argumentCount; i++) { 21 | luax_pushvariant(L, &arguments[i]); 22 | } 23 | 24 | if (!lua_pcall(L, argumentCount, 0, errhandler)) { 25 | lua_close(L); 26 | return NULL; 27 | } 28 | } 29 | 30 | // Error handling 31 | if (lua_type(L, -1) == LUA_TSTRING) { 32 | const char* message = lua_tostring(L, -1); 33 | char* error = lovrStrdup(message); 34 | lua_close(L); 35 | return error; 36 | } 37 | 38 | lua_close(L); 39 | return NULL; 40 | } 41 | 42 | static int l_lovrThreadNewThread(lua_State* L) { 43 | Blob* blob = luax_totype(L, 1, Blob); 44 | if (!blob) { 45 | size_t length; 46 | const char* str = luaL_checklstring(L, 1, &length); 47 | if (memchr(str, '\n', MIN(1024, length))) { 48 | void* data = lovrMalloc(length + 1); 49 | memcpy(data, str, length + 1); 50 | blob = lovrBlobCreate(data, length, "thread code"); 51 | } else { 52 | void* code = luax_readfile(str, &length); 53 | if (!code) luaL_error(L, "Could not read thread code from file '%s'", str); 54 | blob = lovrBlobCreate(code, length, str); 55 | } 56 | } else { 57 | lovrRetain(blob); 58 | } 59 | Thread* thread = lovrThreadCreate(threadRunner, blob); 60 | luax_pushtype(L, Thread, thread); 61 | lovrRelease(thread, lovrThreadDestroy); 62 | lovrRelease(blob, lovrBlobDestroy); 63 | return 1; 64 | } 65 | 66 | static int l_lovrThreadNewChannel(lua_State* L) { 67 | Channel* channel = lovrChannelCreate(0); 68 | luax_pushtype(L, Channel, channel); 69 | lovrRelease(channel, lovrChannelDestroy); 70 | return 1; 71 | } 72 | 73 | static int l_lovrThreadGetChannel(lua_State* L) { 74 | const char* name = luaL_checkstring(L, 1); 75 | Channel* channel = lovrThreadGetChannel(name); 76 | luax_pushtype(L, Channel, channel); 77 | // Note: Channels are intentionally not released here (see thread.h) 78 | return 1; 79 | } 80 | 81 | static const luaL_Reg lovrThreadModule[] = { 82 | { "newThread", l_lovrThreadNewThread }, 83 | { "newChannel", l_lovrThreadNewChannel }, 84 | { "getChannel", l_lovrThreadGetChannel }, 85 | { NULL, NULL } 86 | }; 87 | 88 | extern const luaL_Reg lovrThread[]; 89 | extern const luaL_Reg lovrChannel[]; 90 | 91 | int luaopen_lovr_thread(lua_State* L) { 92 | lua_newtable(L); 93 | luax_register(L, lovrThreadModule); 94 | luax_registertype(L, Thread); 95 | luax_registertype(L, Channel); 96 | 97 | int32_t workers = -1; 98 | 99 | luax_pushconf(L); 100 | if (lua_istable(L, -1)) { 101 | lua_getfield(L, -1, "thread"); 102 | if (lua_istable(L, -1)) { 103 | lua_getfield(L, -1, "workers"); 104 | if (lua_type(L, -1) == LUA_TNUMBER) { 105 | workers = lua_tointeger(L, -1); 106 | } 107 | lua_pop(L, 1); 108 | } 109 | lua_pop(L, 1); 110 | } 111 | lua_pop(L, 1); 112 | 113 | lovrThreadModuleInit(workers); 114 | luax_atexit(L, lovrThreadModuleDestroy); 115 | return 1; 116 | } 117 | -------------------------------------------------------------------------------- /src/api/l_thread_channel.c: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include "thread/thread.h" 3 | #include "event/event.h" 4 | #include "util.h" 5 | #include 6 | 7 | static void luax_checktimeout(lua_State* L, int index, double* timeout) { 8 | switch (lua_type(L, index)) { 9 | case LUA_TNONE: 10 | case LUA_TNIL: 11 | *timeout = NAN; 12 | break; 13 | case LUA_TBOOLEAN: 14 | *timeout = lua_toboolean(L, index) ? INFINITY : NAN; 15 | break; 16 | default: 17 | *timeout = luax_checkfloat(L, index); 18 | break; 19 | } 20 | } 21 | 22 | static int l_lovrChannelPush(lua_State* L) { 23 | Variant variant; 24 | double timeout; 25 | Channel* channel = luax_checktype(L, 1, Channel); 26 | luax_checkvariant(L, 2, &variant); 27 | luax_checktimeout(L, 3, &timeout); 28 | uint64_t id; 29 | bool read = lovrChannelPush(channel, &variant, timeout, &id); 30 | lua_pushnumber(L, id); 31 | lua_pushboolean(L, read); 32 | return 2; 33 | } 34 | 35 | static int l_lovrChannelPop(lua_State* L) { 36 | Variant variant; 37 | double timeout; 38 | Channel* channel = luax_checktype(L, 1, Channel); 39 | luax_checktimeout(L, 2, &timeout); 40 | if (lovrChannelPop(channel, &variant, timeout)) { 41 | luax_pushvariant(L, &variant); 42 | lovrVariantDestroy(&variant); 43 | return 1; 44 | } 45 | lua_pushnil(L); 46 | return 1; 47 | } 48 | 49 | static int l_lovrChannelPeek(lua_State* L) { 50 | Variant variant; 51 | Channel* channel = luax_checktype(L, 1, Channel); 52 | if (lovrChannelPeek(channel, &variant)) { 53 | luax_pushvariant(L, &variant); 54 | lua_pushboolean(L, true); 55 | return 2; 56 | } 57 | lua_pushnil(L); 58 | lua_pushboolean(L, false); 59 | return 2; 60 | } 61 | 62 | static int l_lovrChannelClear(lua_State* L) { 63 | Channel* channel = luax_checktype(L, 1, Channel); 64 | lovrChannelClear(channel); 65 | return 0; 66 | } 67 | 68 | static int l_lovrChannelGetCount(lua_State* L) { 69 | Channel* channel = luax_checktype(L, 1, Channel); 70 | lua_pushinteger(L, lovrChannelGetCount(channel)); 71 | return 1; 72 | } 73 | 74 | static int l_lovrChannelHasRead(lua_State* L) { 75 | Channel* channel = luax_checktype(L, 1, Channel); 76 | uint64_t id = luaL_checkinteger(L, 2); 77 | lua_pushboolean(L, lovrChannelHasRead(channel, id)); 78 | return 1; 79 | } 80 | 81 | const luaL_Reg lovrChannel[] = { 82 | { "push", l_lovrChannelPush }, 83 | { "pop", l_lovrChannelPop }, 84 | { "peek", l_lovrChannelPeek }, 85 | { "clear", l_lovrChannelClear }, 86 | { "getCount", l_lovrChannelGetCount }, 87 | { "hasRead", l_lovrChannelHasRead }, 88 | { NULL, NULL } 89 | }; 90 | -------------------------------------------------------------------------------- /src/api/l_thread_thread.c: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include "event/event.h" 3 | #include "thread/thread.h" 4 | #include "util.h" 5 | 6 | static int l_lovrThreadStart(lua_State* L) { 7 | Thread* thread = luax_checktype(L, 1, Thread); 8 | Variant arguments[MAX_THREAD_ARGUMENTS]; 9 | uint32_t argumentCount = MIN(MAX_THREAD_ARGUMENTS, lua_gettop(L) - 1); 10 | for (uint32_t i = 0; i < argumentCount; i++) { 11 | luax_checkvariant(L, 2 + i, &arguments[i]); 12 | } 13 | luax_assert(L, lovrThreadStart(thread, arguments, argumentCount)); 14 | return 0; 15 | } 16 | 17 | static int l_lovrThreadWait(lua_State* L) { 18 | Thread* thread = luax_checktype(L, 1, Thread); 19 | lovrThreadWait(thread); 20 | return 0; 21 | } 22 | 23 | static int l_lovrThreadGetError(lua_State* L) { 24 | Thread* thread = luax_checktype(L, 1, Thread); 25 | const char* error = lovrThreadGetError(thread); 26 | if (error) { 27 | lua_pushstring(L, error); 28 | } else { 29 | lua_pushnil(L); 30 | } 31 | return 1; 32 | } 33 | 34 | static int l_lovrThreadIsRunning(lua_State* L) { 35 | Thread* thread = luax_checktype(L, 1, Thread); 36 | bool running = lovrThreadIsRunning(thread); 37 | lua_pushboolean(L, running); 38 | return 1; 39 | } 40 | 41 | const luaL_Reg lovrThread[] = { 42 | { "start", l_lovrThreadStart }, 43 | { "wait", l_lovrThreadWait }, 44 | { "getError", l_lovrThreadGetError }, 45 | { "isRunning", l_lovrThreadIsRunning }, 46 | { NULL, NULL } 47 | }; 48 | -------------------------------------------------------------------------------- /src/api/l_timer.c: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include "timer/timer.h" 3 | 4 | static int l_lovrTimerGetDelta(lua_State* L) { 5 | lua_pushnumber(L, lovrTimerGetDelta()); 6 | return 1; 7 | } 8 | 9 | static int l_lovrTimerGetAverageDelta(lua_State* L) { 10 | lua_pushnumber(L, lovrTimerGetAverageDelta()); 11 | return 1; 12 | } 13 | 14 | static int l_lovrTimerGetFPS(lua_State* L) { 15 | lua_pushinteger(L, lovrTimerGetFPS()); 16 | return 1; 17 | } 18 | 19 | static int l_lovrTimerGetTime(lua_State* L) { 20 | lua_pushnumber(L, lovrTimerGetTime()); 21 | return 1; 22 | } 23 | 24 | static int l_lovrTimerStep(lua_State* L) { 25 | lua_pushnumber(L, lovrTimerStep()); 26 | return 1; 27 | } 28 | 29 | static int l_lovrTimerSleep(lua_State* L) { 30 | double duration = luaL_checknumber(L, 1); 31 | lovrTimerSleep(duration); 32 | return 0; 33 | } 34 | 35 | static const luaL_Reg lovrTimer[] = { 36 | { "getDelta", l_lovrTimerGetDelta }, 37 | { "getAverageDelta", l_lovrTimerGetAverageDelta }, 38 | { "getFPS", l_lovrTimerGetFPS }, 39 | { "getTime", l_lovrTimerGetTime }, 40 | { "step", l_lovrTimerStep }, 41 | { "sleep", l_lovrTimerSleep }, 42 | { NULL, NULL } 43 | }; 44 | 45 | int luaopen_lovr_timer(lua_State* L) { 46 | lua_newtable(L); 47 | luax_register(L, lovrTimer); 48 | lovrTimerInit(); 49 | luax_atexit(L, lovrTimerDestroy); 50 | return 1; 51 | } 52 | -------------------------------------------------------------------------------- /src/core/fs.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #pragma once 6 | 7 | typedef enum { 8 | FS_OK, 9 | FS_UNKNOWN_ERROR, 10 | FS_PERMISSION, 11 | FS_READ_ONLY, 12 | FS_TOO_LONG, 13 | FS_NOT_FOUND, 14 | FS_EXISTS, 15 | FS_IS_DIR, 16 | FS_NOT_DIR, 17 | FS_NOT_EMPTY, 18 | FS_LOOP, 19 | FS_FULL, 20 | FS_BUSY, 21 | FS_IO 22 | } fs_error; 23 | 24 | typedef enum { 25 | FILE_DIRECTORY, 26 | FILE_REGULAR 27 | } fs_type; 28 | 29 | typedef struct { 30 | uint64_t size; 31 | uint64_t lastModified; 32 | fs_type type; 33 | } fs_info; 34 | 35 | typedef void fs_list_cb(void*, const char*); 36 | typedef union { int fd; void* handle; } fs_handle; 37 | 38 | fs_error fs_open(const char* path, char mode, fs_handle* file); 39 | fs_error fs_close(fs_handle file); 40 | fs_error fs_read(fs_handle file, void* data, size_t size, size_t* count); 41 | fs_error fs_write(fs_handle file, const void* data, size_t size, size_t* count); 42 | fs_error fs_seek(fs_handle file, uint64_t offset); 43 | fs_error fs_fstat(fs_handle file, fs_info* info); 44 | fs_error fs_map(const char* path, void** pointer, size_t* size); 45 | fs_error fs_unmap(void* data, size_t size); 46 | fs_error fs_stat(const char* path, fs_info* info); 47 | fs_error fs_remove(const char* path); 48 | fs_error fs_mkdir(const char* path); 49 | fs_error fs_list(const char* path, fs_list_cb* callback, void* context); 50 | -------------------------------------------------------------------------------- /src/core/job.c: -------------------------------------------------------------------------------- 1 | #include "job.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #define MAX_WORKERS 64 7 | #define MAX_JOBS 1024 8 | 9 | struct job { 10 | job* next; 11 | fn_job* fn; 12 | void* arg; 13 | atomic_uint done; 14 | }; 15 | 16 | static struct { 17 | job jobs[MAX_JOBS]; 18 | thrd_t workers[MAX_WORKERS]; 19 | uint32_t workerCount; 20 | job* head; 21 | job* tail; 22 | job* pool; 23 | cnd_t hasJob; 24 | mtx_t lock; 25 | bool quit; 26 | } state; 27 | 28 | // Must hold lock, this will unlock it, state.head must exist 29 | static void runJob(void) { 30 | job* job = state.head; 31 | state.head = job->next; 32 | if (!job->next) state.tail = NULL; 33 | mtx_unlock(&state.lock); 34 | 35 | job->fn(job->arg); 36 | job->done = true; 37 | } 38 | 39 | static int workerLoop(void* arg) { 40 | for (;;) { 41 | mtx_lock(&state.lock); 42 | 43 | while (!state.head && !state.quit) { 44 | cnd_wait(&state.hasJob, &state.lock); 45 | } 46 | 47 | if (state.quit) { 48 | break; 49 | } 50 | 51 | runJob(); 52 | } 53 | 54 | mtx_unlock(&state.lock); 55 | return 0; 56 | } 57 | 58 | bool job_init(uint32_t count) { 59 | mtx_init(&state.lock, mtx_plain); 60 | cnd_init(&state.hasJob); 61 | 62 | state.pool = state.jobs; 63 | for (uint32_t i = 0; i < MAX_JOBS - 1; i++) { 64 | state.jobs[i].next = &state.jobs[i + 1]; 65 | } 66 | 67 | if (count > MAX_WORKERS) count = MAX_WORKERS; 68 | for (uint32_t i = 0; i < count; i++, state.workerCount++) { 69 | if (thrd_create(&state.workers[i], workerLoop, (void*) (uintptr_t) i) != thrd_success) { 70 | return false; 71 | } 72 | } 73 | 74 | return true; 75 | } 76 | 77 | void job_destroy(void) { 78 | state.quit = true; 79 | cnd_broadcast(&state.hasJob); 80 | for (uint32_t i = 0; i < state.workerCount; i++) { 81 | thrd_join(state.workers[i], NULL); 82 | } 83 | cnd_destroy(&state.hasJob); 84 | mtx_destroy(&state.lock); 85 | memset(&state, 0, sizeof(state)); 86 | } 87 | 88 | job* job_start(fn_job* fn, void* arg) { 89 | mtx_lock(&state.lock); 90 | 91 | if (!state.pool) { 92 | mtx_unlock(&state.lock); 93 | fn(arg); 94 | return NULL; 95 | } 96 | 97 | job* job = state.pool; 98 | state.pool = job->next; 99 | 100 | if (state.tail) { 101 | state.tail->next = job; 102 | state.tail = job; 103 | } else { 104 | state.head = job; 105 | state.tail = job; 106 | cnd_signal(&state.hasJob); 107 | } 108 | 109 | job->next = NULL; 110 | job->done = false; 111 | job->fn = fn; 112 | job->arg = arg; 113 | 114 | mtx_unlock(&state.lock); 115 | return job; 116 | } 117 | 118 | void job_wait(job* job) { 119 | if (!job) return; 120 | 121 | while (!job->done) { 122 | mtx_lock(&state.lock); 123 | 124 | if (state.head) { 125 | runJob(); 126 | } else { 127 | mtx_unlock(&state.lock); 128 | thrd_yield(); 129 | } 130 | } 131 | 132 | mtx_lock(&state.lock); 133 | job->next = state.pool; 134 | state.pool = job; 135 | mtx_unlock(&state.lock); 136 | } 137 | -------------------------------------------------------------------------------- /src/core/job.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #pragma once 6 | 7 | typedef struct job job; 8 | typedef void fn_job(void* arg); 9 | 10 | bool job_init(uint32_t workerCount); 11 | void job_destroy(void); 12 | job* job_start(fn_job* fn, void* arg); 13 | void job_wait(job* job); 14 | -------------------------------------------------------------------------------- /src/core/os.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #pragma once 6 | 7 | typedef struct os_window_config { 8 | uint32_t width; 9 | uint32_t height; 10 | bool fullscreen; 11 | bool resizable; 12 | const char* title; 13 | struct { 14 | void* data; 15 | uint32_t width; 16 | uint32_t height; 17 | } icon; 18 | } os_window_config; 19 | 20 | typedef enum { 21 | MOUSE_LEFT, 22 | MOUSE_RIGHT 23 | } os_mouse_button; 24 | 25 | typedef enum { 26 | MOUSE_MODE_NORMAL, 27 | MOUSE_MODE_GRABBED 28 | } os_mouse_mode; 29 | 30 | typedef enum { 31 | OS_KEY_A, 32 | OS_KEY_B, 33 | OS_KEY_C, 34 | OS_KEY_D, 35 | OS_KEY_E, 36 | OS_KEY_F, 37 | OS_KEY_G, 38 | OS_KEY_H, 39 | OS_KEY_I, 40 | OS_KEY_J, 41 | OS_KEY_K, 42 | OS_KEY_L, 43 | OS_KEY_M, 44 | OS_KEY_N, 45 | OS_KEY_O, 46 | OS_KEY_P, 47 | OS_KEY_Q, 48 | OS_KEY_R, 49 | OS_KEY_S, 50 | OS_KEY_T, 51 | OS_KEY_U, 52 | OS_KEY_V, 53 | OS_KEY_W, 54 | OS_KEY_X, 55 | OS_KEY_Y, 56 | OS_KEY_Z, 57 | OS_KEY_0, 58 | OS_KEY_1, 59 | OS_KEY_2, 60 | OS_KEY_3, 61 | OS_KEY_4, 62 | OS_KEY_5, 63 | OS_KEY_6, 64 | OS_KEY_7, 65 | OS_KEY_8, 66 | OS_KEY_9, 67 | OS_KEY_SPACE, 68 | OS_KEY_ENTER, 69 | OS_KEY_TAB, 70 | OS_KEY_ESCAPE, 71 | OS_KEY_BACKSPACE, 72 | OS_KEY_UP, 73 | OS_KEY_DOWN, 74 | OS_KEY_LEFT, 75 | OS_KEY_RIGHT, 76 | OS_KEY_HOME, 77 | OS_KEY_END, 78 | OS_KEY_PAGE_UP, 79 | OS_KEY_PAGE_DOWN, 80 | OS_KEY_INSERT, 81 | OS_KEY_DELETE, 82 | OS_KEY_F1, 83 | OS_KEY_F2, 84 | OS_KEY_F3, 85 | OS_KEY_F4, 86 | OS_KEY_F5, 87 | OS_KEY_F6, 88 | OS_KEY_F7, 89 | OS_KEY_F8, 90 | OS_KEY_F9, 91 | OS_KEY_F10, 92 | OS_KEY_F11, 93 | OS_KEY_F12, 94 | OS_KEY_BACKTICK, 95 | OS_KEY_MINUS, 96 | OS_KEY_EQUALS, 97 | OS_KEY_LEFT_BRACKET, 98 | OS_KEY_RIGHT_BRACKET, 99 | OS_KEY_BACKSLASH, 100 | OS_KEY_SEMICOLON, 101 | OS_KEY_APOSTROPHE, 102 | OS_KEY_COMMA, 103 | OS_KEY_PERIOD, 104 | OS_KEY_SLASH, 105 | OS_KEY_KP_0, 106 | OS_KEY_KP_1, 107 | OS_KEY_KP_2, 108 | OS_KEY_KP_3, 109 | OS_KEY_KP_4, 110 | OS_KEY_KP_5, 111 | OS_KEY_KP_6, 112 | OS_KEY_KP_7, 113 | OS_KEY_KP_8, 114 | OS_KEY_KP_9, 115 | OS_KEY_KP_DECIMAL, 116 | OS_KEY_KP_DIVIDE, 117 | OS_KEY_KP_MULTIPLY, 118 | OS_KEY_KP_SUBTRACT, 119 | OS_KEY_KP_ADD, 120 | OS_KEY_KP_ENTER, 121 | OS_KEY_KP_EQUALS, 122 | OS_KEY_LEFT_CONTROL, 123 | OS_KEY_LEFT_SHIFT, 124 | OS_KEY_LEFT_ALT, 125 | OS_KEY_LEFT_OS, 126 | OS_KEY_RIGHT_CONTROL, 127 | OS_KEY_RIGHT_SHIFT, 128 | OS_KEY_RIGHT_ALT, 129 | OS_KEY_RIGHT_OS, 130 | OS_KEY_CAPS_LOCK, 131 | OS_KEY_SCROLL_LOCK, 132 | OS_KEY_NUM_LOCK, 133 | OS_KEY_COUNT 134 | } os_key; 135 | 136 | typedef enum { 137 | BUTTON_PRESSED, 138 | BUTTON_RELEASED 139 | } os_button_action; 140 | 141 | typedef enum { 142 | OS_PERMISSION_AUDIO_CAPTURE 143 | } os_permission; 144 | 145 | typedef void fn_quit(void); 146 | typedef void fn_visible(bool visible); 147 | typedef void fn_focus(bool focused); 148 | typedef void fn_resize(uint32_t width, uint32_t height); 149 | typedef void fn_key(os_button_action action, os_key key, uint32_t scancode, bool repeat); 150 | typedef void fn_text(uint32_t codepoint); 151 | typedef void fn_mouse_button(int button, bool pressed); 152 | typedef void fn_mouse_move(double x, double y); 153 | typedef void fn_mousewheel_move(double deltaX, double deltaY); 154 | typedef void fn_permission(os_permission permission, bool granted); 155 | 156 | bool os_init(void); 157 | void os_destroy(void); 158 | const char* os_get_name(void); 159 | uint32_t os_get_core_count(void); 160 | void os_open_console(void); 161 | double os_get_time(void); 162 | void os_sleep(double seconds); 163 | void os_request_permission(os_permission permission); 164 | const char* os_get_clipboard_text(void); 165 | void os_set_clipboard_text(const char* text); 166 | 167 | void* os_vm_init(size_t size); 168 | bool os_vm_free(void* p, size_t size); 169 | bool os_vm_commit(void* p, size_t size); 170 | bool os_vm_release(void* p, size_t size); 171 | 172 | void os_thread_attach(void); 173 | void os_thread_detach(void); 174 | 175 | void os_poll_events(void); 176 | void os_on_quit(fn_quit* callback); 177 | void os_on_visible(fn_visible* callback); 178 | void os_on_focus(fn_focus* callback); 179 | void os_on_resize(fn_resize* callback); 180 | void os_on_key(fn_key* callback); 181 | void os_on_text(fn_text* callback); 182 | void os_on_mouse_button(fn_mouse_button* callback); 183 | void os_on_mouse_move(fn_mouse_move* callback); 184 | void os_on_mousewheel_move(fn_mousewheel_move* callback); 185 | void os_on_permission(fn_permission* callback); 186 | 187 | bool os_window_open(const os_window_config* config); 188 | bool os_window_is_open(void); 189 | bool os_window_is_visible(void); 190 | bool os_window_is_focused(void); 191 | void os_window_get_size(uint32_t* width, uint32_t* height); 192 | float os_window_get_pixel_density(void); 193 | void os_window_message_box(const char* message); 194 | 195 | void os_get_mouse_position(double* x, double* y); 196 | void os_set_mouse_mode(os_mouse_mode mode); 197 | bool os_is_mouse_down(os_mouse_button button); 198 | bool os_is_key_down(os_key key); 199 | 200 | size_t os_get_home_directory(char* buffer, size_t size); 201 | size_t os_get_data_directory(char* buffer, size_t size); 202 | size_t os_get_working_directory(char* buffer, size_t size); 203 | size_t os_get_executable_path(char* buffer, size_t size); 204 | size_t os_get_bundle_path(char* buffer, size_t size, const char** root); 205 | 206 | uintptr_t os_get_win32_window(void); 207 | uintptr_t os_get_win32_instance(void); 208 | 209 | uintptr_t os_get_ca_metal_layer(void); 210 | 211 | uintptr_t os_get_xcb_connection(void); 212 | uintptr_t os_get_xcb_window(void); 213 | -------------------------------------------------------------------------------- /src/core/os_macos.c: -------------------------------------------------------------------------------- 1 | #include "os.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define cls(T) ((id) objc_getClass(#T)) 16 | #define msg(ret, obj, fn) ((ret(*)(id, SEL)) objc_msgSend)(obj, sel_getUid(fn)) 17 | #define msg1(ret, obj, fn, T1, A1) ((ret(*)(id, SEL, T1)) objc_msgSend)(obj, sel_getUid(fn), A1) 18 | #define msg2(ret, obj, fn, T1, A1, T2, A2) ((ret(*)(id, SEL, T1, T2)) objc_msgSend)(obj, sel_getUid(fn), A1, A2) 19 | #define msg3(ret, obj, fn, T1, A1, T2, A2, T3, A3) ((ret(*)(id, SEL, T1, T2, T3)) objc_msgSend)(obj, sel_getUid(fn), A1, A2, A3) 20 | 21 | #include "os_glfw.h" 22 | 23 | static struct { 24 | uint64_t frequency; 25 | fn_permission* onPermissionEvent; 26 | } state; 27 | 28 | bool os_init(void) { 29 | mach_timebase_info_data_t info; 30 | mach_timebase_info(&info); 31 | state.frequency = (info.denom * 1e9) / info.numer; 32 | return true; 33 | } 34 | 35 | void os_destroy(void) { 36 | glfwTerminate(); 37 | } 38 | 39 | const char* os_get_name(void) { 40 | return "macOS"; 41 | } 42 | 43 | uint32_t os_get_core_count(void) { 44 | uint32_t count; 45 | size_t size = sizeof(count); 46 | sysctlbyname("hw.logicalcpu", &count, &size, NULL, 0); 47 | return count; 48 | } 49 | 50 | void os_open_console(void) { 51 | // 52 | } 53 | 54 | double os_get_time(void) { 55 | return mach_absolute_time() / (double) state.frequency; 56 | } 57 | 58 | void os_sleep(double seconds) { 59 | seconds += .5e-9; 60 | struct timespec t; 61 | t.tv_sec = seconds; 62 | t.tv_nsec = (seconds - t.tv_sec) * 1e9; 63 | while (nanosleep(&t, &t)); 64 | } 65 | 66 | typedef void (^PermissionHandler)(BOOL granted); 67 | 68 | void os_request_permission(os_permission permission) { 69 | if (permission == OS_PERMISSION_AUDIO_CAPTURE) { 70 | id AVCaptureDevice = cls(AVCaptureDevice); 71 | 72 | // If on old OS without permissions check, permission is implicitly granted 73 | if (!class_respondsToSelector(AVCaptureDevice, sel_getUid("authorizationStatusForMediaType:"))) { 74 | if (state.onPermissionEvent) state.onPermissionEvent(permission, true); 75 | return; 76 | } 77 | 78 | #if TARGET_OS_IOS || (defined(MAC_OS_X_VERSION_10_14) && __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14) 79 | __block fn_permission* callback = state.onPermissionEvent; 80 | PermissionHandler handler = ^(BOOL granted) { 81 | dispatch_async(dispatch_get_main_queue(), ^{ 82 | if (callback) callback(OS_PERMISSION_AUDIO_CAPTURE, granted); 83 | }); 84 | }; 85 | 86 | switch (msg1(AVAuthorizationStatus, AVCaptureDevice, "authorizationStatusForMediaType:", AVMediaType, AVMediaTypeAudio)) { 87 | case AVAuthorizationStatusAuthorized: 88 | if (state.onPermissionEvent) state.onPermissionEvent(permission, true); 89 | break; 90 | case AVAuthorizationStatusNotDetermined: 91 | msg2(void, AVCaptureDevice, "requestAccessForMediaType:completionHandler:", AVMediaType, AVMediaTypeAudio, PermissionHandler, handler); 92 | break; 93 | case AVAuthorizationStatusDenied: 94 | if (state.onPermissionEvent) state.onPermissionEvent(permission, false); 95 | break; 96 | case AVAuthorizationStatusRestricted: 97 | if (state.onPermissionEvent) state.onPermissionEvent(permission, false); 98 | break; 99 | } 100 | #else 101 | #error "Unsupported macOS target" 102 | #endif 103 | } 104 | } 105 | 106 | void* os_vm_init(size_t size) { 107 | return mmap(NULL, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 108 | } 109 | 110 | bool os_vm_free(void* p, size_t size) { 111 | return !munmap(p, size); 112 | } 113 | 114 | bool os_vm_commit(void* p, size_t size) { 115 | return !mprotect(p, size, PROT_READ | PROT_WRITE); 116 | } 117 | 118 | void os_on_permission(fn_permission* callback) { 119 | state.onPermissionEvent = callback; 120 | } 121 | 122 | void os_thread_attach(void) { 123 | // 124 | } 125 | 126 | void os_thread_detach(void) { 127 | // 128 | } 129 | 130 | size_t os_get_home_directory(char* buffer, size_t size) { 131 | const char* path = getenv("HOME"); 132 | 133 | if (!path) { 134 | struct passwd* entry = getpwuid(getuid()); 135 | if (!entry) { 136 | return 0; 137 | } 138 | path = entry->pw_dir; 139 | } 140 | 141 | size_t length = strlen(path); 142 | if (length >= size) { return 0; } 143 | memcpy(buffer, path, length); 144 | buffer[length] = '\0'; 145 | return length; 146 | } 147 | 148 | size_t os_get_data_directory(char* buffer, size_t size) { 149 | size_t cursor = os_get_home_directory(buffer, size); 150 | 151 | if (cursor > 0) { 152 | buffer += cursor; 153 | size -= cursor; 154 | const char* suffix = "/Library/Application Support"; 155 | size_t length = strlen(suffix); 156 | if (length < size) { 157 | memcpy(buffer, suffix, length); 158 | buffer[length] = '\0'; 159 | return cursor + length; 160 | } 161 | } 162 | 163 | return 0; 164 | } 165 | 166 | size_t os_get_working_directory(char* buffer, size_t size) { 167 | return getcwd(buffer, size) ? strlen(buffer) : 0; 168 | } 169 | 170 | size_t os_get_executable_path(char* buffer, size_t size) { 171 | uint32_t size32 = size; 172 | return _NSGetExecutablePath(buffer, &size32) ? 0 : size32; 173 | } 174 | 175 | size_t os_get_bundle_path(char* buffer, size_t size, const char** root) { 176 | id extension = msg1(id, cls(NSString), "stringWithUTF8String:", char*, "lovr"); 177 | id bundle = msg(id, cls(NSBundle), "mainBundle"); 178 | id path = msg2(id, bundle, "pathForResource:ofType:", id, nil, id, extension); 179 | if (path == nil) { 180 | *root = NULL; 181 | return os_get_executable_path(buffer, size); 182 | } 183 | 184 | const char* cpath = msg(const char*, path, "UTF8String"); 185 | if (!cpath) { 186 | return 0; 187 | } 188 | 189 | size_t length = strlen(cpath); 190 | if (length >= size) { 191 | return 0; 192 | } 193 | 194 | memcpy(buffer, cpath, length); 195 | buffer[length] = '\0'; 196 | *root = NULL; 197 | return length; 198 | } 199 | -------------------------------------------------------------------------------- /src/core/spv.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #pragma once 5 | 6 | typedef enum { 7 | SPV_B32, 8 | SPV_I32, 9 | SPV_I32x2, 10 | SPV_I32x3, 11 | SPV_I32x4, 12 | SPV_U32, 13 | SPV_U32x2, 14 | SPV_U32x3, 15 | SPV_U32x4, 16 | SPV_F32, 17 | SPV_F32x2, 18 | SPV_F32x3, 19 | SPV_F32x4, 20 | SPV_MAT2x2, 21 | SPV_MAT2x3, 22 | SPV_MAT2x4, 23 | SPV_MAT3x2, 24 | SPV_MAT3x3, 25 | SPV_MAT3x4, 26 | SPV_MAT4x2, 27 | SPV_MAT4x3, 28 | SPV_MAT4x4, 29 | SPV_STRUCT 30 | } spv_type; 31 | 32 | typedef struct { 33 | const char* name; 34 | uint32_t id; 35 | spv_type type; 36 | } spv_spec_constant; 37 | 38 | typedef struct spv_field { 39 | const char* name; 40 | spv_type type; 41 | uint32_t offset; 42 | uint32_t arrayLength; 43 | uint32_t arrayStride; 44 | uint32_t elementSize; 45 | uint16_t fieldCount; 46 | uint16_t totalFieldCount; 47 | struct spv_field* fields; 48 | } spv_field; 49 | 50 | typedef struct { 51 | const char* name; 52 | uint32_t location; 53 | } spv_attribute; 54 | 55 | typedef enum { 56 | SPV_UNIFORM_BUFFER, 57 | SPV_STORAGE_BUFFER, 58 | SPV_SAMPLED_TEXTURE, 59 | SPV_STORAGE_TEXTURE, 60 | SPV_SAMPLER, 61 | SPV_COMBINED_TEXTURE_SAMPLER, 62 | SPV_UNIFORM_TEXEL_BUFFER, 63 | SPV_STORAGE_TEXEL_BUFFER, 64 | SPV_INPUT_ATTACHMENT 65 | } spv_resource_type; 66 | 67 | typedef enum { 68 | SPV_TEXTURE_1D, 69 | SPV_TEXTURE_2D, 70 | SPV_TEXTURE_3D 71 | } spv_texture_dimension; 72 | 73 | enum { 74 | SPV_TEXTURE_CUBE = (1 << 0), 75 | SPV_TEXTURE_ARRAY = (1 << 1), 76 | SPV_TEXTURE_SHADOW = (1 << 2), 77 | SPV_TEXTURE_MULTISAMPLE = (1 << 3), 78 | SPV_TEXTURE_INTEGER = (1 << 4) 79 | }; 80 | 81 | typedef struct { 82 | const uint32_t* set; 83 | const uint32_t* binding; 84 | const char* name; 85 | uint32_t arraySize; 86 | spv_resource_type type; 87 | spv_texture_dimension dimension; 88 | uint16_t textureFlags; 89 | spv_field* bufferFields; 90 | } spv_resource; 91 | 92 | typedef struct { 93 | uint32_t version; 94 | uint32_t workgroupSize[3]; 95 | uint32_t featureCount; 96 | uint32_t specConstantCount; 97 | uint32_t attributeCount; 98 | uint32_t resourceCount; 99 | uint32_t fieldCount; 100 | uint32_t* features; 101 | spv_spec_constant* specConstants; 102 | spv_field* pushConstants; 103 | spv_attribute* attributes; 104 | spv_resource* resources; 105 | spv_field* fields; 106 | } spv_info; 107 | 108 | typedef enum { 109 | SPV_OK, 110 | SPV_INVALID, 111 | SPV_TOO_BIG, 112 | SPV_UNSUPPORTED_SPEC_CONSTANT_TYPE, 113 | SPV_UNSUPPORTED_DATA_TYPE 114 | } spv_result; 115 | 116 | spv_result spv_parse(const void* source, size_t size, spv_info* info); 117 | const char* spv_result_to_string(spv_result); 118 | -------------------------------------------------------------------------------- /src/lib/dmon/dmon.c: -------------------------------------------------------------------------------- 1 | #define DMON_IMPL 2 | #include "dmon.h" 3 | -------------------------------------------------------------------------------- /src/lib/jsmn/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Serge A. Zaitsev 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/lib/jsmn/jsmn.h: -------------------------------------------------------------------------------- 1 | #ifndef __JSMN_H_ 2 | #define __JSMN_H_ 3 | 4 | #define JSMN_PARENT_LINKS 5 | #define JSMN_STRICT 6 | 7 | #include 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | /** 14 | * JSON type identifier. Basic types are: 15 | * o Object 16 | * o Array 17 | * o String 18 | * o Other primitive: number, boolean (true/false) or null 19 | */ 20 | typedef enum { 21 | JSMN_UNDEFINED = 0, 22 | JSMN_OBJECT = 1, 23 | JSMN_ARRAY = 2, 24 | JSMN_STRING = 3, 25 | JSMN_PRIMITIVE = 4 26 | } jsmntype_t; 27 | 28 | enum jsmnerr { 29 | /* Not enough tokens were provided */ 30 | JSMN_ERROR_NOMEM = -1, 31 | /* Invalid character inside JSON string */ 32 | JSMN_ERROR_INVAL = -2, 33 | /* The string is not a full JSON packet, more bytes expected */ 34 | JSMN_ERROR_PART = -3 35 | }; 36 | 37 | /** 38 | * JSON token description. 39 | * type type (object, array, string etc.) 40 | * start start position in JSON data string 41 | * end end position in JSON data string 42 | */ 43 | typedef struct { 44 | jsmntype_t type; 45 | int start; 46 | int end; 47 | int size; 48 | #ifdef JSMN_PARENT_LINKS 49 | int parent; 50 | #endif 51 | } jsmntok_t; 52 | 53 | /** 54 | * JSON parser. Contains an array of token blocks available. Also stores 55 | * the string being parsed now and current position in that string 56 | */ 57 | typedef struct { 58 | unsigned int pos; /* offset in the JSON string */ 59 | unsigned int toknext; /* next token to allocate */ 60 | int toksuper; /* superior token node, e.g parent object or array */ 61 | } jsmn_parser; 62 | 63 | /** 64 | * Create JSON parser over an array of tokens 65 | */ 66 | void jsmn_init(jsmn_parser *parser); 67 | 68 | /** 69 | * Run JSON parser. It parses a JSON data string into and array of tokens, each describing 70 | * a single JSON object. 71 | */ 72 | int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, 73 | jsmntok_t *tokens, unsigned int num_tokens); 74 | 75 | #ifdef __cplusplus 76 | } 77 | #endif 78 | 79 | #endif /* __JSMN_H_ */ 80 | -------------------------------------------------------------------------------- /src/lib/luax/lutf8lib.h: -------------------------------------------------------------------------------- 1 | struct lua_State; 2 | 3 | int luaopen_utf8(struct lua_State *L); 4 | -------------------------------------------------------------------------------- /src/lib/miniaudio/miniaudio.c: -------------------------------------------------------------------------------- 1 | #define MINIAUDIO_IMPLEMENTATION 2 | #define MA_ENABLE_ONLY_SPECIFIC_BACKENDS 3 | #define MA_ENABLE_WASAPI 4 | #define MA_ENABLE_PULSEAUDIO 5 | #define MA_ENABLE_COREAUDIO 6 | #define MA_ENABLE_OPENSL 7 | #define MA_ENABLE_WEBAUDIO 8 | #define MA_NO_DECODING 9 | #define MA_NO_ENCODING 10 | #define MA_NO_GENERATION 11 | #define MA_NO_ENGINE 12 | #define MA_NO_RESOURCE_MANAGER 13 | #define MA_NO_NODE_GRAPH 14 | #include "miniaudio.h" 15 | -------------------------------------------------------------------------------- /src/lib/minimp3/minimp3.c: -------------------------------------------------------------------------------- 1 | #define MINIMP3_IMPLEMENTATION 2 | #define MINIMP3_ONLY_MP3 3 | #define MINIMP3_NONSTANDARD_BUT_LOGICAL 4 | #define MINIMP3_FLOAT_OUTPUT 5 | #define MINIMP3_NO_STDIO 6 | #include "minimp3_ex.h" 7 | -------------------------------------------------------------------------------- /src/lib/noise/simplexnoise1234.h: -------------------------------------------------------------------------------- 1 | /* SimplexNoise1234, Simplex noise with true analytic 2 | * derivative in 1D to 4D. 3 | * 4 | * Author: Stefan Gustavson, 2003-2005 5 | * Contact: stegu@itn.liu.se 6 | * 7 | * This code was GPL licensed until February 2011. 8 | * As the original author of this code, I hereby 9 | * release it into the public domain. 10 | * Please feel free to use it for whatever you want. 11 | * Credit is appreciated where appropriate, and I also 12 | * appreciate being told where this code finds any use, 13 | * but you may do as you like. 14 | */ 15 | 16 | /* 17 | * This is a clean, fast, modern and free Perlin Simplex noise function. 18 | * It is a stand-alone compilation unit with no external dependencies, 19 | * highly reusable without source code modifications. 20 | * 21 | * Note: 22 | * Replacing the "float" type with "double" can actually make this run faster 23 | * on some platforms. Having both versions could be useful. 24 | */ 25 | 26 | /** 1D, 2D, 3D and 4D float Perlin simplex noise 27 | */ 28 | double snoise1( double x ); 29 | double snoise2( double x, double y ); 30 | double snoise3( double x, double y, double z ); 31 | double snoise4( double x, double y, double z, double w ); 32 | -------------------------------------------------------------------------------- /src/lib/stb/stb_image.c: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_IMPLEMENTATION 2 | #define STBI_NO_STDIO 3 | #define STBI_ONLY_JPEG 4 | #define STBI_ONLY_PNG 5 | #define STBI_ONLY_HDR 6 | #define STBI_ASSERT(x) 7 | #define STBI_MALLOC lovrMalloc 8 | #define STBI_REALLOC lovrRealloc 9 | #define STBI_FREE lovrFree 10 | #include "util.h" 11 | #include "stb_image.h" 12 | -------------------------------------------------------------------------------- /src/lib/stb/stb_truetype.c: -------------------------------------------------------------------------------- 1 | #define STB_TRUETYPE_IMPLEMENTATION 2 | #include "stb_truetype.h" 3 | -------------------------------------------------------------------------------- /src/lib/std/stdatomic.h: -------------------------------------------------------------------------------- 1 | // I hacked this together from a C11 draft spec and clang/gcc atomics documentation 2 | // DO NOT TRUST IT 3 | 4 | #pragma once 5 | 6 | #ifndef _MSC_VER 7 | 8 | #include 9 | #include 10 | 11 | // 7.17.1 12 | 13 | #define ATOMIC_BOOL_LOCK_FREE __GCC_ATOMIC_BOOL_LOCK_FREE 14 | #define ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE 15 | #define ATOMIC_CHAR16_T_LOCK_FREE __GCC_ATOMIC_CHAR16_T_LOCK_FREE 16 | #define ATOMIC_CHAR32_T_LOCK_FREE __GCC_ATOMIC_CHAR32_T_LOCK_FREE 17 | #define ATOMIC_WCHAR_T_LOCK_FREE __GCC_ATOMIC_WCHAR_T_LOCK_FREE 18 | #define ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE 19 | #define ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE 20 | #define ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE 21 | #define ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE 22 | #define ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE 23 | 24 | // 7.17.2 25 | 26 | #define ATOMIC_VAR_INIT(x) (x) 27 | #define atomic_init(p, x) atomic_store_explicit(p, x, memory_order_relaxed) 28 | 29 | // 7.17.3 30 | 31 | typedef enum memory_order { 32 | memory_order_relaxed = __ATOMIC_RELAXED, 33 | memory_order_consume = __ATOMIC_CONSUME, 34 | memory_order_acquire = __ATOMIC_ACQUIRE, 35 | memory_order_release = __ATOMIC_RELEASE, 36 | memory_order_acq_rel = __ATOMIC_ACQ_REL, 37 | memory_order_seq_cst = __ATOMIC_SEQ_CST 38 | } memory_order; 39 | 40 | #define kill_dependency(x) (x) 41 | 42 | // 7.17.4 43 | 44 | void atomic_thread_fence(memory_order); 45 | void atomic_signal_fence(memory_order); 46 | #define atomic_thread_fence(order) __atomic_thread_fence(order) 47 | #define atomic_signal_fence(order) __atomic_signal_fence(order) 48 | 49 | // 7.17.5 50 | 51 | #define atomic_is_lock_free(x) __atomic_is_lock_free(sizeof(*(x)), x) 52 | 53 | // 7.17.6 54 | 55 | typedef _Bool atomic_bool; 56 | typedef char atomic_char; 57 | typedef signed char atomic_schar; 58 | typedef unsigned char atomic_uchar; 59 | typedef short atomic_short; 60 | typedef unsigned short atomic_ushort; 61 | typedef int atomic_int; 62 | typedef unsigned int atomic_uint; 63 | typedef long atomic_long; 64 | typedef unsigned long atomic_ulong; 65 | typedef long long atomic_llong; 66 | typedef unsigned long long atomic_ullong; 67 | typedef uint_least16_t atomic_char16_t; 68 | typedef uint_least32_t atomic_char32_t; 69 | typedef wchar_t atomic_wchar_t; 70 | typedef int_least8_t atomic_int_least8_t; 71 | typedef uint_least8_t atomic_uint_least8_t; 72 | typedef int_least16_t atomic_int_least16_t; 73 | typedef uint_least16_t atomic_uint_least16_t; 74 | typedef int_least32_t atomic_int_least32_t; 75 | typedef uint_least32_t atomic_uint_least32_t; 76 | typedef int_least64_t atomic_int_least64_t; 77 | typedef uint_least64_t atomic_uint_least64_t; 78 | typedef int_fast8_t atomic_int_fast8_t; 79 | typedef uint_fast8_t atomic_uint_fast8_t; 80 | typedef int_fast16_t atomic_int_fast16_t; 81 | typedef uint_fast16_t atomic_uint_fast16_t; 82 | typedef int_fast32_t atomic_int_fast32_t; 83 | typedef uint_fast32_t atomic_uint_fast32_t; 84 | typedef int_fast64_t atomic_int_fast64_t; 85 | typedef uint_fast64_t atomic_uint_fast64_t; 86 | typedef intptr_t atomic_intptr_t; 87 | typedef uintptr_t atomic_uintptr_t; 88 | typedef size_t atomic_size_t; 89 | typedef ptrdiff_t atomic_ptrdiff_t; 90 | typedef intmax_t atomic_intmax_t; 91 | typedef uintmax_t atomic_uintmax_t; 92 | 93 | // 7.17.7 94 | 95 | #define atomic_store_explicit __atomic_store_n 96 | #define atomic_store(p, x) atomic_store_explicit(p, x, __ATOMIC_SEQ_CST) 97 | 98 | #define atomic_load_explicit __atomic_load_n 99 | #define atomic_load(p) atomic_load_explicit(p, __ATOMIC_SEQ_CST) 100 | 101 | #define atomic_exchange_explicit __atomic_exchange_n 102 | #define atomic_exchange(p, x) atomic_exchange_explicit(p, x, __ATOMIC_SEQ_CST) 103 | 104 | #define atomic_compare_exchange_strong_explicit(p, x, y, o1, o2) __atomic_compare_exchange_n(p, x, y, false, o1, o2) 105 | #define atomic_compare_exchange_strong(p, x, y) atomic_compare_exchange_strong_explicit(p, x, y, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) 106 | 107 | #define atomic_compare_exchange_weak_explicit(p, x, y, o1, o2) __atomic_compare_exchange_n(p, x, y, true, o1, o2) 108 | #define atomic_compare_exchange_weak(p, x, y) atomic_compare_exchange_weak_explicit(p, x, y, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) 109 | 110 | #define atomic_fetch_add_explicit __atomic_fetch_add 111 | #define atomic_fetch_add(p, x) atomic_fetch_add_explicit(p, x, __ATOMIC_SEQ_CST) 112 | 113 | #define atomic_fetch_sub_explicit __atomic_fetch_sub 114 | #define atomic_fetch_sub(p, x) atomic_fetch_sub_explicit(p, x, __ATOMIC_SEQ_CST) 115 | 116 | #define atomic_fetch_or_explicit __atomic_fetch_or 117 | #define atomic_fetch_or(p, x) atomic_fetch_or_explicit(p, x, __ATOMIC_SEQ_CST) 118 | 119 | #define atomic_fetch_xor_explicit __atomic_fetch_xor 120 | #define atomic_fetch_xor(p, x) atomic_fetch_xor_explicit(p, x, __ATOMIC_SEQ_CST) 121 | 122 | #define atomic_fetch_and_explicit __atomic_fetch_and 123 | #define atomic_fetch_and(p, x) atomic_fetch_and_explicit(p, x, __ATOMIC_SEQ_CST) 124 | 125 | // 7.17.8 126 | 127 | #define ATOMIC_FLAG_INIT { 0 } 128 | typedef struct atomic_flag { atomic_bool value; } atomic_flag; 129 | 130 | _Bool atomic_flag_test_and_set(volatile atomic_flag*); 131 | _Bool atomic_flag_test_and_set_explicit(volatile atomic_flag*, memory_order); 132 | 133 | #define atomic_flag_test_and_set(f) __atomic_test_and_set(f, __ATOMIC_SEQ_CST) 134 | #define atomic_flag_test_and_set_explicit __atomic_test_and_set 135 | 136 | _Bool atomic_flag_clear(volatile atomic_flag*); 137 | _Bool atomic_flag_clear_explicit(volatile atomic_flag*, memory_order); 138 | 139 | #define atomic_flag_clear(f) __atomic_clear(f, __ATOMIC_SEQ_CST) 140 | #define atomic_flag_clear_explicit __atomic_clear 141 | 142 | #else 143 | 144 | #include 145 | 146 | typedef volatile long atomic_uint; 147 | 148 | #define atomic_store(p, x) *(p) = (x); 149 | #define atomic_store_explicit(p, x, o) atomic_store(p, x) 150 | 151 | #define atomic_load(p) *(p) 152 | #define atomic_load_explicit(p, o) atomic_load(p) 153 | 154 | #define atomic_fetch_add(p, x) _InterlockedExchangeAdd(p, x) 155 | #define atomic_fetch_add_explicit(p, x, o) atomic_fetch_add(p, x) 156 | 157 | #define atomic_fetch_sub(p, x) _InterlockedExchangeAdd(p, -(x)) 158 | #define atomic_fetch_sub_explicit(p, x, o) atomic_fetch_sub(p, x) 159 | 160 | #define atomic_fetch_or(p, x) InterlockedOr(p, x) 161 | #define atomic_fetch_or_explicit(p, x, o) atomic_fetch_or(p, x) 162 | 163 | #define atomic_fetch_xor(p, x) InterlockedXor(p, x) 164 | #define atomic_fetch_xor_explicit(p, x, o) atomic_fetch_xor(p, x) 165 | 166 | #define atomic_fetch_and(p, x) InterlockedAnd(p, x) 167 | #define atomic_fetch_and_explicit(p, x, o) atomic_fetch_and(p, x) 168 | 169 | static inline bool atomic_compare_exchange_strong(void** p, void** x, void* y) { 170 | void* old = *x; 171 | *x = _InterlockedCompareExchangePointer(p, y, old); 172 | return *x == old; 173 | } 174 | 175 | #define ATOMIC_INT_LOCK_FREE 2 176 | 177 | #endif 178 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include "api/api.h" 2 | #include "event/event.h" 3 | #include "core/os.h" 4 | #include "util.h" 5 | #include "boot.lua.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int main(int argc, char** argv) { 12 | os_init(); 13 | 14 | for (;;) { 15 | lua_State* L = luaL_newstate(); 16 | luax_setmainthread(L); 17 | luaL_openlibs(L); 18 | luax_preload(L); 19 | 20 | lua_newtable(L); 21 | static Variant cookie; 22 | luax_pushvariant(L, &cookie); 23 | lua_setfield(L, -2, "restart"); 24 | for (int i = 0; i < argc; i++) { 25 | lua_pushstring(L, argv[i]); 26 | lua_rawseti(L, -2, i); 27 | } 28 | lua_setglobal(L, "arg"); 29 | 30 | lua_pushcfunction(L, luax_getstack); 31 | if (luaL_loadbuffer(L, (const char*) etc_boot_lua, etc_boot_lua_len, "@boot.lua") || lua_pcall(L, 0, 1, -2)) { 32 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 33 | os_destroy(); 34 | return 1; 35 | } 36 | 37 | lua_State* T = lua_tothread(L, -1); 38 | lovrSetLogCallback(luax_vlog, T); 39 | 40 | while (luax_resume(T, 0) == LUA_YIELD) { 41 | os_sleep(0.); 42 | } 43 | 44 | if (lua_type(T, 1) == LUA_TSTRING && !strcmp(lua_tostring(T, 1), "restart")) { 45 | luax_checkvariant(T, 2, &cookie); 46 | if (cookie.type == TYPE_OBJECT) memset(&cookie, 0, sizeof(cookie)); 47 | lua_close(L); 48 | continue; 49 | } else { 50 | int status = lua_tointeger(T, 1); 51 | lua_close(L); 52 | os_destroy(); 53 | return status; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/modules/audio/audio.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #pragma once 6 | 7 | #define BUFFER_SIZE 256 8 | #define MAX_SOURCES 64 9 | 10 | struct Sound; 11 | 12 | typedef struct Source Source; 13 | 14 | typedef enum { 15 | EFFECT_ABSORPTION, 16 | EFFECT_ATTENUATION, 17 | EFFECT_OCCLUSION, 18 | EFFECT_REVERB, 19 | EFFECT_SPATIALIZATION, 20 | EFFECT_TRANSMISSION 21 | } Effect; 22 | 23 | typedef enum { 24 | MATERIAL_GENERIC, 25 | MATERIAL_BRICK, 26 | MATERIAL_CARPET, 27 | MATERIAL_CERAMIC, 28 | MATERIAL_CONCRETE, 29 | MATERIAL_GLASS, 30 | MATERIAL_GRAVEL, 31 | MATERIAL_METAL, 32 | MATERIAL_PLASTER, 33 | MATERIAL_ROCK, 34 | MATERIAL_WOOD 35 | } AudioMaterial; 36 | 37 | typedef enum { 38 | AUDIO_SHARED, 39 | AUDIO_EXCLUSIVE 40 | } AudioShareMode; 41 | 42 | typedef struct { 43 | size_t idSize; 44 | const void* id; 45 | const char* name; 46 | bool isDefault; 47 | } AudioDevice; 48 | 49 | typedef enum { 50 | AUDIO_PLAYBACK, 51 | AUDIO_CAPTURE 52 | } AudioType; 53 | 54 | typedef enum { 55 | UNIT_SECONDS, 56 | UNIT_FRAMES 57 | } TimeUnit; 58 | 59 | typedef enum { 60 | UNIT_LINEAR, 61 | UNIT_DECIBELS 62 | } VolumeUnit; 63 | 64 | typedef void AudioDeviceCallback(AudioDevice* device, void* userdata); 65 | 66 | bool lovrAudioInit(const char* spatializer, uint32_t sampleRate); 67 | void lovrAudioDestroy(void); 68 | void lovrAudioEnumerateDevices(AudioType type, AudioDeviceCallback* callback, void* userdata); 69 | bool lovrAudioGetDevice(AudioType type, AudioDevice* device); 70 | bool lovrAudioSetDevice(AudioType type, void* id, size_t size, struct Sound* sink, AudioShareMode shareMode); 71 | bool lovrAudioStart(AudioType type); 72 | bool lovrAudioStop(AudioType type); 73 | bool lovrAudioIsStarted(AudioType type); 74 | float lovrAudioGetVolume(VolumeUnit units); 75 | void lovrAudioSetVolume(float volume, VolumeUnit units); 76 | void lovrAudioGetPose(float position[3], float orientation[4]); 77 | void lovrAudioSetPose(float position[3], float orientation[4]); 78 | bool lovrAudioSetGeometry(float* vertices, uint32_t* indices, uint32_t vertexCount, uint32_t indexCount, AudioMaterial material); 79 | const char* lovrAudioGetSpatializer(void); 80 | uint32_t lovrAudioGetSampleRate(void); 81 | void lovrAudioGetAbsorption(float absorption[3]); 82 | void lovrAudioSetAbsorption(float absorption[3]); 83 | 84 | // Source 85 | 86 | Source* lovrSourceCreate(struct Sound* sound, bool pitch, bool spatial, uint32_t effects); 87 | Source* lovrSourceClone(Source* source); 88 | void lovrSourceDestroy(void* ref); 89 | struct Sound* lovrSourceGetSound(Source* source); 90 | bool lovrSourcePlay(Source* source); 91 | void lovrSourcePause(Source* source); 92 | void lovrSourceStop(Source* source); 93 | bool lovrSourceIsPlaying(Source* source); 94 | bool lovrSourceIsLooping(Source* source); 95 | bool lovrSourceSetLooping(Source* source, bool loop); 96 | float lovrSourceGetPitch(Source* source); 97 | bool lovrSourceSetPitch(Source* source, float pitch); 98 | float lovrSourceGetVolume(Source* source, VolumeUnit units); 99 | void lovrSourceSetVolume(Source* source, float volume, VolumeUnit units); 100 | void lovrSourceSeek(Source* source, double time, TimeUnit units); 101 | double lovrSourceTell(Source* source, TimeUnit units); 102 | double lovrSourceGetDuration(Source* source, TimeUnit units); 103 | bool lovrSourceIsPitchable(Source* source); 104 | bool lovrSourceIsSpatial(Source* source); 105 | void lovrSourceGetPose(Source* source, float position[3], float orientation[4]); 106 | void lovrSourceSetPose(Source* source, float position[3], float orientation[4]); 107 | float lovrSourceGetRadius(Source* source); 108 | void lovrSourceSetRadius(Source* source, float radius); 109 | void lovrSourceGetDirectivity(Source* source, float* weight, float* power); 110 | void lovrSourceSetDirectivity(Source* source, float weight, float power); 111 | bool lovrSourceIsEffectEnabled(Source* source, Effect effect); 112 | bool lovrSourceSetEffectEnabled(Source* Source, Effect effect, bool enabled); 113 | -------------------------------------------------------------------------------- /src/modules/audio/spatializer.h: -------------------------------------------------------------------------------- 1 | #include "audio.h" 2 | 3 | // Private Source functions for spatializer use 4 | intptr_t* lovrSourceGetSpatializerMemoField(Source* source); 5 | uint32_t lovrSourceGetIndex(Source* source); 6 | 7 | typedef struct { 8 | bool (*init)(void); 9 | void (*destroy)(void); 10 | // input is mono, output is interleaved stereo, framesIn is mono frames, framesOut is stereo frames. 11 | // Safe to assume framesIn == framesOut unless spatializer requests needFixedBuffer. 12 | // Return value is number of samples written into output. 13 | uint32_t (*apply)(Source* source, const float* input, float* output, uint32_t framesIn, uint32_t framesOut); 14 | // called at end of frame for any "additional noise", like echo. 15 | // output is stereo, frames is stereo frames, scratch is a buffer the length of output (in case that helps) 16 | // return value is number of stereo frames written. 17 | uint32_t (*tail)(float* scratch, float* output, uint32_t frames); 18 | void (*setListenerPose)(float position[3], float orientation[4]); 19 | bool (*setGeometry)(float* vertices, uint32_t* indices, uint32_t vertexCount, uint32_t indexCount, AudioMaterial material); 20 | void (*sourceCreate)(Source* source); 21 | void (*sourceDestroy)(Source* source); 22 | const char* name; 23 | } Spatializer; 24 | 25 | #ifdef LOVR_ENABLE_PHONON_SPATIALIZER 26 | extern Spatializer phononSpatializer; 27 | #endif 28 | #ifdef LOVR_ENABLE_OCULUS_SPATIALIZER 29 | extern Spatializer oculusSpatializer; 30 | #endif 31 | extern Spatializer simpleSpatializer; 32 | -------------------------------------------------------------------------------- /src/modules/audio/spatializer_simple.c: -------------------------------------------------------------------------------- 1 | #include "spatializer.h" 2 | #include "core/maf.h" 3 | #include "util.h" 4 | #include 5 | 6 | static struct { 7 | float listener[16]; 8 | float gain[MAX_SOURCES][2]; 9 | } state; 10 | 11 | static bool simple_init(void) { 12 | mat4_identity(state.listener); 13 | return true; 14 | } 15 | 16 | static void simple_destroy(void) { 17 | // 18 | } 19 | 20 | static uint32_t simple_apply(Source* source, const float* input, float* output, uint32_t frames, uint32_t _frames) { 21 | float sourcePos[3], sourceOrientation[4]; 22 | lovrSourceGetPose(source, sourcePos, sourceOrientation); 23 | 24 | float listenerPos[3]; 25 | mat4_getPosition(state.listener, listenerPos); 26 | 27 | float target[2] = { 1.f, 1.f }; 28 | if (lovrSourceIsEffectEnabled(source, EFFECT_SPATIALIZATION)) { 29 | float leftEar[3] = { -0.1f, 0.0f, 0.0f }; 30 | float rightEar[3] = { 0.1f, 0.0f, 0.0f }; 31 | mat4_mulPoint(state.listener, leftEar); 32 | mat4_mulPoint(state.listener, rightEar); 33 | float ldistance = vec3_distance(sourcePos, leftEar); 34 | float rdistance = vec3_distance(sourcePos, rightEar); 35 | target[0] = .5f + (rdistance - ldistance) * 2.5f; 36 | target[1] = .5f + (ldistance - rdistance) * 2.5f; 37 | } 38 | 39 | float weight, power; 40 | lovrSourceGetDirectivity(source, &weight, &power); 41 | if (weight > 0.f && power > 0.f) { 42 | float sourceDirection[3]; 43 | float sourceToListener[3]; 44 | quat_getDirection(sourceOrientation, sourceDirection); 45 | vec3_normalize(vec3_sub(vec3_init(sourceToListener, listenerPos), sourcePos)); 46 | float dot = vec3_dot(sourceToListener, sourceDirection); 47 | float factor = powf(fabsf(1.f - weight + weight * dot), power); 48 | target[0] *= factor; 49 | target[1] *= factor; 50 | } 51 | 52 | if (lovrSourceIsEffectEnabled(source, EFFECT_ATTENUATION)) { 53 | float distance = vec3_distance(sourcePos, listenerPos); 54 | float attenuation = 1.f / MAX(distance, 1.f); 55 | target[0] *= attenuation; 56 | target[1] *= attenuation; 57 | } 58 | 59 | uint32_t index = lovrSourceGetIndex(source); 60 | float* gain = state.gain[index]; 61 | 62 | float lerpDuration = .05f; 63 | float lerpFrames = lovrAudioGetSampleRate() * lerpDuration; 64 | float lerpRate = 1.f / lerpFrames; 65 | 66 | for (uint32_t c = 0; c < 2; c++) { 67 | float sign = target[c] > gain[c] ? 1.f : -1.f; 68 | 69 | uint32_t lerpCount = fabsf(target[c] - gain[c]) / lerpRate; 70 | lerpCount = MIN(lerpCount, frames); 71 | 72 | for (uint32_t i = 0; i < lerpCount; i++) { 73 | output[i * 2 + c] = input[i] * gain[c]; 74 | gain[c] += lerpRate * sign; 75 | } 76 | 77 | for (uint32_t i = lerpCount; i < frames; i++) { 78 | output[i * 2 + c] = input[i] * gain[c]; 79 | } 80 | } 81 | 82 | return frames; 83 | } 84 | 85 | static uint32_t simple_tail(float* scratch, float* output, uint32_t frames) { 86 | return 0; 87 | } 88 | 89 | static void simple_setListenerPose(float position[3], float orientation[4]) { 90 | mat4_identity(state.listener); 91 | mat4_translate(state.listener, position[0], position[1], position[2]); 92 | mat4_rotateQuat(state.listener, orientation); 93 | } 94 | 95 | static bool simple_setGeometry(float* vertices, uint32_t* indices, uint32_t vertexCount, uint32_t indexCount, AudioMaterial material) { 96 | return false; 97 | } 98 | 99 | static void simple_sourceCreate(Source* source) { 100 | uint32_t index = lovrSourceGetIndex(source); 101 | state.gain[index][0] = 0.f; 102 | state.gain[index][1] = 0.f; 103 | } 104 | 105 | static void simple_sourceDestroy(Source* source) { 106 | // 107 | } 108 | 109 | Spatializer simpleSpatializer = { 110 | .init = simple_init, 111 | .destroy = simple_destroy, 112 | .apply = simple_apply, 113 | .tail = simple_tail, 114 | .setListenerPose = simple_setListenerPose, 115 | .setGeometry = simple_setGeometry, 116 | .sourceCreate = simple_sourceCreate, 117 | .sourceDestroy = simple_sourceDestroy, 118 | .name = "simple" 119 | }; 120 | -------------------------------------------------------------------------------- /src/modules/data/blob.c: -------------------------------------------------------------------------------- 1 | #include "data/blob.h" 2 | #include "util.h" 3 | #include 4 | #include 5 | 6 | Blob* lovrBlobCreate(void* data, size_t size, const char* name) { 7 | Blob* blob = lovrCalloc(sizeof(Blob)); 8 | blob->ref = 1; 9 | blob->data = data; 10 | blob->size = size; 11 | blob->name = lovrStrdup(name); 12 | return blob; 13 | } 14 | 15 | void lovrBlobDestroy(void* ref) { 16 | Blob* blob = ref; 17 | lovrFree(blob->data); 18 | lovrFree(blob->name); 19 | lovrFree(blob); 20 | } 21 | -------------------------------------------------------------------------------- /src/modules/data/blob.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #pragma once 5 | 6 | typedef struct Blob { 7 | uint32_t ref; 8 | void* data; 9 | size_t size; 10 | char* name; 11 | } Blob; 12 | 13 | Blob* lovrBlobCreate(void* data, size_t size, const char* name); 14 | void lovrBlobDestroy(void* ref); 15 | -------------------------------------------------------------------------------- /src/modules/data/image.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #pragma once 6 | 7 | struct Blob; 8 | 9 | typedef enum { 10 | FORMAT_R8, 11 | FORMAT_RG8, 12 | FORMAT_RGBA8, 13 | FORMAT_BGRA8, 14 | FORMAT_R16, 15 | FORMAT_RG16, 16 | FORMAT_RGBA16, 17 | FORMAT_R16F, 18 | FORMAT_RG16F, 19 | FORMAT_RGBA16F, 20 | FORMAT_R32F, 21 | FORMAT_RG32F, 22 | FORMAT_RGBA32F, 23 | FORMAT_RGB565, 24 | FORMAT_RGB5A1, 25 | FORMAT_RGB10A2, 26 | FORMAT_RG11B10F, 27 | FORMAT_D16, 28 | FORMAT_D24, 29 | FORMAT_D32F, 30 | FORMAT_D24S8, 31 | FORMAT_D32FS8, 32 | FORMAT_BC1, 33 | FORMAT_BC2, 34 | FORMAT_BC3, 35 | FORMAT_BC4U, 36 | FORMAT_BC4S, 37 | FORMAT_BC5U, 38 | FORMAT_BC5S, 39 | FORMAT_BC6UF, 40 | FORMAT_BC6SF, 41 | FORMAT_BC7, 42 | FORMAT_ASTC_4x4, 43 | FORMAT_ASTC_5x4, 44 | FORMAT_ASTC_5x5, 45 | FORMAT_ASTC_6x5, 46 | FORMAT_ASTC_6x6, 47 | FORMAT_ASTC_8x5, 48 | FORMAT_ASTC_8x6, 49 | FORMAT_ASTC_8x8, 50 | FORMAT_ASTC_10x5, 51 | FORMAT_ASTC_10x6, 52 | FORMAT_ASTC_10x8, 53 | FORMAT_ASTC_10x10, 54 | FORMAT_ASTC_12x10, 55 | FORMAT_ASTC_12x12 56 | } TextureFormat; 57 | 58 | typedef void MapPixelCallback(void* userdata, uint32_t x, uint32_t y, float pixel[4]); 59 | 60 | typedef struct Image Image; 61 | 62 | Image* lovrImageCreateRaw(uint32_t width, uint32_t height, TextureFormat format, bool srgb); 63 | Image* lovrImageCreateFromFile(struct Blob* blob); 64 | void lovrImageDestroy(void* ref); 65 | bool lovrImageIsSRGB(Image* image); 66 | bool lovrImageIsPremultiplied(Image* image); 67 | bool lovrImageIsCube(Image* image); 68 | bool lovrImageIsDepth(Image* image); 69 | bool lovrImageIsCompressed(Image* image); 70 | struct Blob* lovrImageGetBlob(Image* image); 71 | uint32_t lovrImageGetWidth(Image* image, uint32_t level); 72 | uint32_t lovrImageGetHeight(Image* image, uint32_t level); 73 | uint32_t lovrImageGetLayerCount(Image* image); 74 | uint32_t lovrImageGetLevelCount(Image* image); 75 | TextureFormat lovrImageGetFormat(Image* image); 76 | size_t lovrImageGetLayerSize(Image* image, uint32_t level); 77 | void* lovrImageGetLayerData(Image* image, uint32_t level, uint32_t layer); 78 | bool lovrImageGetPixel(Image* image, uint32_t x, uint32_t y, float pixel[4]); 79 | bool lovrImageSetPixel(Image* image, uint32_t x, uint32_t y, float pixel[4]); 80 | bool lovrImageMapPixel(Image* image, uint32_t x, uint32_t y, uint32_t w, uint32_t h, MapPixelCallback* callback, void* userdata); 81 | bool lovrImageCopy(Image* src, Image* dst, uint32_t srcOffset[2], uint32_t dstOffset[2], uint32_t extent[2]); 82 | struct Blob* lovrImageEncode(Image* image); 83 | -------------------------------------------------------------------------------- /src/modules/data/modelData.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #pragma once 6 | 7 | struct Blob; 8 | struct Image; 9 | 10 | typedef enum { 11 | META_GLTF_JSON, 12 | META_HANDTRACKING_FB, 13 | META_CONTROLLER_MSFT 14 | } MetadataType; 15 | 16 | typedef struct { 17 | uint32_t blob; 18 | size_t offset; 19 | size_t size; 20 | size_t stride; 21 | char* data; 22 | } ModelBuffer; 23 | 24 | typedef enum { 25 | ATTR_POSITION, 26 | ATTR_NORMAL, 27 | ATTR_UV, 28 | ATTR_COLOR, 29 | ATTR_TANGENT, 30 | ATTR_JOINTS, 31 | ATTR_WEIGHTS, 32 | MAX_DEFAULT_ATTRIBUTES 33 | } DefaultAttribute; 34 | 35 | typedef enum { I8, U8, I16, U16, I32, U32, F32, SN10x3 } AttributeType; 36 | 37 | typedef union { 38 | void* raw; 39 | int8_t* i8; 40 | uint8_t* u8; 41 | int16_t* i16; 42 | uint16_t* u16; 43 | int32_t* i32; 44 | uint32_t* u32; 45 | float* f32; 46 | } AttributeData; 47 | 48 | typedef struct { 49 | uint32_t offset; 50 | uint32_t buffer; 51 | size_t stride; 52 | uint32_t count; 53 | AttributeType type; 54 | unsigned components : 3; 55 | unsigned normalized : 1; 56 | unsigned matrix : 1; 57 | unsigned hasMin : 1; 58 | unsigned hasMax : 1; 59 | float min[4]; 60 | float max[4]; 61 | } ModelAttribute; 62 | 63 | typedef struct { 64 | ModelAttribute* positions; 65 | ModelAttribute* normals; 66 | ModelAttribute* tangents; 67 | } ModelBlendData; 68 | 69 | typedef enum { 70 | DRAW_POINT_LIST, 71 | DRAW_LINE_LIST, 72 | DRAW_LINE_LOOP, 73 | DRAW_LINE_STRIP, 74 | DRAW_TRIANGLE_LIST, 75 | DRAW_TRIANGLE_STRIP, 76 | DRAW_TRIANGLE_FAN 77 | } ModelDrawMode; 78 | 79 | typedef struct { 80 | ModelAttribute* attributes[MAX_DEFAULT_ATTRIBUTES]; 81 | ModelAttribute* indices; 82 | ModelBlendData* blendShapes; 83 | uint32_t blendShapeCount; 84 | ModelDrawMode mode; 85 | uint32_t material; 86 | uint32_t skin; 87 | } ModelPrimitive; 88 | 89 | typedef struct { 90 | float color[4]; 91 | float glow[4]; 92 | float uvShift[2]; 93 | float uvScale[2]; 94 | float sdfRange[2]; 95 | float metalness; 96 | float roughness; 97 | float clearcoat; 98 | float clearcoatRoughness; 99 | float occlusionStrength; 100 | float normalScale; 101 | float alphaCutoff; 102 | uint32_t texture; 103 | uint32_t glowTexture; 104 | uint32_t metalnessTexture; 105 | uint32_t roughnessTexture; 106 | uint32_t clearcoatTexture; 107 | uint32_t occlusionTexture; 108 | uint32_t normalTexture; 109 | const char* name; 110 | } ModelMaterial; 111 | 112 | typedef struct { 113 | const char* name; 114 | uint32_t node; 115 | float weight; 116 | } ModelBlendShape; 117 | 118 | typedef enum { 119 | PROP_TRANSLATION, 120 | PROP_ROTATION, 121 | PROP_SCALE, 122 | PROP_WEIGHTS 123 | } AnimationProperty; 124 | 125 | typedef enum { 126 | SMOOTH_STEP, 127 | SMOOTH_LINEAR, 128 | SMOOTH_CUBIC 129 | } SmoothMode; 130 | 131 | typedef struct { 132 | uint32_t nodeIndex; 133 | AnimationProperty property; 134 | SmoothMode smoothing; 135 | uint32_t keyframeCount; 136 | float* times; 137 | float* data; 138 | } ModelAnimationChannel; 139 | 140 | typedef struct { 141 | const char* name; 142 | ModelAnimationChannel* channels; 143 | uint32_t channelCount; 144 | float duration; 145 | } ModelAnimation; 146 | 147 | typedef struct { 148 | uint32_t* joints; 149 | uint32_t jointCount; 150 | uint32_t vertexCount; 151 | uint32_t blendedVertexCount; 152 | float* inverseBindMatrices; 153 | } ModelSkin; 154 | 155 | typedef struct { 156 | const char* name; 157 | union { 158 | float matrix[16]; 159 | struct { 160 | float translation[3]; 161 | float rotation[4]; 162 | float scale[3]; 163 | }; 164 | } transform; 165 | uint32_t* children; 166 | uint32_t childCount; 167 | uint32_t parent; 168 | uint32_t primitiveIndex; 169 | uint32_t primitiveCount; 170 | uint32_t blendShapeIndex; 171 | uint32_t blendShapeCount; 172 | uint32_t skin; 173 | bool hasMatrix; 174 | } ModelNode; 175 | 176 | typedef struct ModelData { 177 | uint32_t ref; 178 | void* data; 179 | 180 | void* metadata; 181 | size_t metadataSize; 182 | MetadataType metadataType; 183 | 184 | struct Blob** blobs; 185 | struct Image** images; 186 | ModelBuffer* buffers; 187 | ModelAttribute* attributes; 188 | ModelPrimitive* primitives; 189 | ModelMaterial* materials; 190 | ModelBlendShape* blendShapes; 191 | ModelAnimation* animations; 192 | ModelSkin* skins; 193 | ModelNode* nodes; 194 | uint32_t rootNode; 195 | 196 | uint32_t blobCount; 197 | uint32_t imageCount; 198 | uint32_t bufferCount; 199 | uint32_t attributeCount; 200 | uint32_t primitiveCount; 201 | uint32_t materialCount; 202 | uint32_t blendShapeCount; 203 | uint32_t animationCount; 204 | uint32_t skinCount; 205 | uint32_t nodeCount; 206 | 207 | ModelAnimationChannel* channels; 208 | ModelBlendData* blendData; 209 | uint32_t* children; 210 | uint32_t* joints; 211 | char* chars; 212 | uint32_t channelCount; 213 | uint32_t blendDataCount; 214 | uint32_t childCount; 215 | uint32_t jointCount; 216 | uint32_t charCount; 217 | 218 | // Computed properties (loaders don't need to fill these out) 219 | 220 | uint32_t vertexCount; 221 | uint32_t skinnedVertexCount; 222 | uint32_t blendShapeVertexCount; 223 | uint32_t dynamicVertexCount; 224 | uint32_t indexCount; 225 | AttributeType indexType; 226 | 227 | float boundingBox[6]; 228 | float boundingSphere[4]; 229 | 230 | float* vertices; 231 | uint32_t* indices; 232 | uint32_t totalVertexCount; 233 | uint32_t totalIndexCount; 234 | 235 | // Lookups 236 | 237 | void* blendShapeMap; 238 | void* animationMap; 239 | void* materialMap; 240 | void* nodeMap; 241 | } ModelData; 242 | 243 | typedef void* ModelDataIO(const char* filename, size_t* bytesRead); 244 | 245 | ModelData* lovrModelDataCreate(struct Blob* blob, ModelDataIO* io); 246 | bool lovrModelDataInitGltf(ModelData** model, struct Blob* blob, ModelDataIO* io); 247 | bool lovrModelDataInitObj(ModelData** model, struct Blob* blob, ModelDataIO* io); 248 | bool lovrModelDataInitStl(ModelData** model, struct Blob* blob, ModelDataIO* io); 249 | void lovrModelDataDestroy(void* ref); 250 | void lovrModelDataAllocate(ModelData* model); 251 | bool lovrModelDataFinalize(ModelData* model); 252 | void lovrModelDataCopyAttribute(ModelData* data, ModelAttribute* attribute, char* dst, AttributeType type, uint32_t components, bool normalized, uint32_t count, size_t stride, uint8_t clear); 253 | void lovrModelDataGetBoundingBox(ModelData* data, float box[6]); 254 | void lovrModelDataGetBoundingSphere(ModelData* data, float sphere[4]); 255 | void lovrModelDataGetTriangles(ModelData* data, float** vertices, uint32_t** indices, uint32_t* vertexCount, uint32_t* indexCount); 256 | -------------------------------------------------------------------------------- /src/modules/data/modelData_stl.c: -------------------------------------------------------------------------------- 1 | #include "data/modelData.h" 2 | #include "data/blob.h" 3 | #include "core/maf.h" 4 | #include "util.h" 5 | #include 6 | #include 7 | 8 | static bool lovrModelDataInitStlAscii(ModelData** result, Blob* source, ModelDataIO* io) { 9 | return lovrSetError("ASCII STL files are not supported yet"); 10 | } 11 | 12 | // The binary format has an 80 byte header, followed by a u32 triangle count, followed by 50 byte 13 | // triangles. Each triangle has a vec3 normal, 3 vec3 vertices, and 2 bytes of padding. 14 | static bool lovrModelDataInitStlBinary(ModelData** result, Blob* source, ModelDataIO* io, uint32_t triangleCount) { 15 | char* data = (char*) source->data + 84; 16 | 17 | uint32_t vertexCount = triangleCount * 3; 18 | size_t vertexBufferSize = vertexCount * 6 * sizeof(float); 19 | float* vertices = lovrMalloc(vertexBufferSize); 20 | 21 | ModelData* model = lovrCalloc(sizeof(ModelData)); 22 | model->ref = 1; 23 | model->blobCount = 1; 24 | model->bufferCount = 1; 25 | model->attributeCount = 2; 26 | model->primitiveCount = 1; 27 | model->nodeCount = 1; 28 | 29 | lovrModelDataAllocate(model); 30 | 31 | model->blobs[0] = lovrBlobCreate(vertices, vertexBufferSize, "stl vertex data"); 32 | model->buffers[0] = (ModelBuffer) { .data = (char*) vertices, .size = vertexBufferSize, .stride = 6 * sizeof(float) }; 33 | model->attributes[0] = (ModelAttribute) { .count = vertexCount, .components = 3, .type = F32, .offset = 0 * sizeof(float) }; 34 | model->attributes[1] = (ModelAttribute) { .count = vertexCount, .components = 3, .type = F32, .offset = 3 * sizeof(float) }; 35 | model->primitives[0] = (ModelPrimitive) { 36 | .mode = DRAW_TRIANGLE_LIST, 37 | .material = ~0u, 38 | .attributes = { 39 | [ATTR_POSITION] = &model->attributes[0], 40 | [ATTR_NORMAL] = &model->attributes[1] 41 | } 42 | }; 43 | model->nodes[0] = (ModelNode) { 44 | .hasMatrix = true, 45 | .transform.matrix = MAT4_IDENTITY, 46 | .primitiveCount = 1, 47 | .skin = ~0u 48 | }; 49 | 50 | for (uint32_t i = 0; i < triangleCount; i++) { 51 | memcpy(vertices + 3, data, 12); 52 | memcpy(vertices + 9, data, 12); 53 | memcpy(vertices + 15, data, 12), data += 12; 54 | memcpy(vertices + 0, data, 12), data += 12; 55 | memcpy(vertices + 6, data, 12), data += 12; 56 | memcpy(vertices + 12, data, 12), data += 12; 57 | vertices += 18; 58 | data += 2; 59 | } 60 | 61 | *result = model; 62 | return true; 63 | } 64 | 65 | bool lovrModelDataInitStl(ModelData** result, Blob* source, ModelDataIO* io) { 66 | if (source->size > strlen("solid ") && !memcmp(source->data, "solid ", strlen("solid "))) { 67 | return lovrModelDataInitStlAscii(result, source, io); 68 | } else if (source->size > 84) { 69 | uint32_t triangleCount; 70 | memcpy(&triangleCount, (char*) source->data + 80, sizeof(triangleCount)); 71 | if (source->size == 84 + 50 * triangleCount) { 72 | return lovrModelDataInitStlBinary(result, source, io, triangleCount); 73 | } 74 | } 75 | 76 | return true; 77 | } 78 | -------------------------------------------------------------------------------- /src/modules/data/rasterizer.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #pragma once 6 | 7 | struct Blob; 8 | struct Image; 9 | 10 | typedef enum { 11 | RASTERIZER_TTF, 12 | RASTERIZER_BMF 13 | } RasterizerType; 14 | 15 | typedef void* RasterizerIO(const char* filename, size_t* bytesRead); 16 | 17 | typedef struct Rasterizer Rasterizer; 18 | 19 | Rasterizer* lovrRasterizerCreate(struct Blob* blob, float size, RasterizerIO* io); 20 | void lovrRasterizerDestroy(void* ref); 21 | RasterizerType lovrRasterizerGetType(Rasterizer* rasterizer); 22 | float lovrRasterizerGetFontSize(Rasterizer* rasterizer); 23 | size_t lovrRasterizerGetGlyphCount(Rasterizer* rasterizer); 24 | bool lovrRasterizerHasGlyph(Rasterizer* rasterizer, uint32_t codepoint); 25 | bool lovrRasterizerHasGlyphs(Rasterizer* rasterizer, const char* str, size_t length); 26 | bool lovrRasterizerIsGlyphEmpty(Rasterizer* rasterizer, uint32_t codepoint); 27 | float lovrRasterizerGetAscent(Rasterizer* rasterizer); 28 | float lovrRasterizerGetDescent(Rasterizer* rasterizer); 29 | float lovrRasterizerGetLeading(Rasterizer* rasterizer); 30 | float lovrRasterizerGetAdvance(Rasterizer* rasterizer, uint32_t codepoint); 31 | float lovrRasterizerGetBearing(Rasterizer* rasterizer, uint32_t codepoint); 32 | float lovrRasterizerGetKerning(Rasterizer* rasterizer, uint32_t first, uint32_t second); 33 | void lovrRasterizerGetBoundingBox(Rasterizer* rasterizer, float box[4]); 34 | void lovrRasterizerGetGlyphBoundingBox(Rasterizer* rasterizer, uint32_t codepoint, float box[4]); 35 | bool lovrRasterizerGetCurves(Rasterizer* rasterizer, uint32_t codepoint, void (*fn)(void* context, uint32_t degree, float* points), void* context); 36 | bool lovrRasterizerGetPixels(Rasterizer* rasterizer, uint32_t codepoint, float* pixels, uint32_t width, uint32_t height, double spread); 37 | struct Image* lovrRasterizerGetAtlas(Rasterizer* rasterizer); 38 | uint32_t lovrRasterizerGetAtlasGlyph(Rasterizer* rasterizer, size_t index, uint16_t* x, uint16_t* y); 39 | -------------------------------------------------------------------------------- /src/modules/data/sound.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #pragma once 6 | 7 | struct Blob; 8 | 9 | // Can pass as the maxFrames argument to lovrSoundCreateFromCallback 10 | #define LOVR_SOUND_ENDLESS 0xFFFFFFFF 11 | 12 | typedef enum { 13 | SAMPLE_F32, 14 | SAMPLE_I16 15 | } SampleFormat; 16 | 17 | typedef enum { 18 | CHANNEL_MONO, 19 | CHANNEL_STEREO, 20 | CHANNEL_AMBISONIC 21 | } ChannelLayout; 22 | 23 | typedef struct Sound Sound; 24 | 25 | typedef uint32_t SoundCallback(Sound* sound, uint32_t offset, uint32_t count, void* data); 26 | typedef void SoundDestroyCallback(Sound* sound); 27 | 28 | Sound* lovrSoundCreateRaw(uint32_t frames, SampleFormat format, ChannelLayout channels, uint32_t sampleRate, struct Blob* data); 29 | Sound* lovrSoundCreateStream(uint32_t frames, SampleFormat format, ChannelLayout channels, uint32_t sampleRate); 30 | Sound* lovrSoundCreateFromFile(struct Blob* blob, bool decode); 31 | Sound* lovrSoundCreateFromCallback(SoundCallback read, void *callbackMemo, SoundDestroyCallback callbackDataDestroy, SampleFormat format, uint32_t sampleRate, ChannelLayout channels, uint32_t maxFrames); 32 | void lovrSoundDestroy(void* ref); 33 | struct Blob* lovrSoundGetBlob(Sound* sound); 34 | SampleFormat lovrSoundGetFormat(Sound* sound); 35 | ChannelLayout lovrSoundGetChannelLayout(Sound* sound); 36 | uint32_t lovrSoundGetChannelCount(Sound* sound); 37 | uint32_t lovrSoundGetSampleRate(Sound* sound); 38 | uint32_t lovrSoundGetFrameCount(Sound* sound); 39 | uint32_t lovrSoundGetCapacity(Sound* sound); 40 | size_t lovrSoundGetStride(Sound* sound); 41 | bool lovrSoundIsCompressed(Sound* sound); 42 | bool lovrSoundIsStream(Sound* sound); 43 | uint32_t lovrSoundRead(Sound* sound, uint32_t offset, uint32_t count, void* data); 44 | bool lovrSoundWrite(Sound* sound, uint32_t offset, uint32_t count, const void* data, uint32_t* framesWritten); 45 | bool lovrSoundCopy(Sound* src, Sound* dst, uint32_t frames, uint32_t srcOffset, uint32_t dstOffset, uint32_t* framesCopied); 46 | void *lovrSoundGetCallbackMemo(Sound* sound); 47 | -------------------------------------------------------------------------------- /src/modules/event/event.c: -------------------------------------------------------------------------------- 1 | #include "event/event.h" 2 | #include "thread/thread.h" 3 | #include "util.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static struct { 10 | uint32_t ref; 11 | arr_t(Event) events; 12 | size_t head; 13 | mtx_t lock; 14 | } state; 15 | 16 | void lovrVariantDestroy(Variant* variant) { 17 | switch (variant->type) { 18 | case TYPE_STRING: lovrFree(variant->value.string.pointer); return; 19 | case TYPE_OBJECT: lovrRelease(variant->value.object.pointer, variant->value.object.destructor); return; 20 | case TYPE_MATRIX: lovrFree(variant->value.matrix.data); return; 21 | case TYPE_TABLE: 22 | for (size_t i = 0; i < variant->value.table.length; i++) { 23 | lovrVariantDestroy(&variant->value.table.keys[i]); 24 | lovrVariantDestroy(&variant->value.table.vals[i]); 25 | } 26 | lovrFree(variant->value.table.keys); 27 | lovrFree(variant->value.table.vals); 28 | return; 29 | default: return; 30 | } 31 | } 32 | 33 | bool lovrEventInit(void) { 34 | if (atomic_fetch_add(&state.ref, 1)) return true; 35 | arr_init(&state.events); 36 | mtx_init(&state.lock, mtx_plain); 37 | return true; 38 | } 39 | 40 | void lovrEventDestroy(void) { 41 | if (atomic_fetch_sub(&state.ref, 1) != 1) return; 42 | mtx_lock(&state.lock); 43 | for (size_t i = state.head; i < state.events.length; i++) { 44 | Event* event = &state.events.data[i]; 45 | switch (event->type) { 46 | #ifndef LOVR_DISABLE_THREAD 47 | case EVENT_THREAD_ERROR: lovrRelease(event->data.thread.thread, lovrThreadDestroy); break; 48 | #endif 49 | case EVENT_CUSTOM: 50 | for (uint32_t j = 0; j < event->data.custom.count; j++) { 51 | lovrVariantDestroy(&event->data.custom.data[j]); 52 | } 53 | break; 54 | default: break; 55 | } 56 | } 57 | arr_free(&state.events); 58 | mtx_unlock(&state.lock); 59 | mtx_destroy(&state.lock); 60 | memset(&state, 0, sizeof(state)); 61 | } 62 | 63 | void lovrEventPush(Event event) { 64 | if (state.ref == 0) return; 65 | 66 | #ifndef LOVR_DISABLE_THREAD 67 | if (event.type == EVENT_THREAD_ERROR) { 68 | lovrRetain(event.data.thread.thread); 69 | event.data.thread.error = lovrStrdup(event.data.thread.error); 70 | } 71 | #endif 72 | 73 | if (event.type == EVENT_FILECHANGED) { 74 | event.data.file.path = lovrStrdup(event.data.file.path); 75 | event.data.file.oldpath = lovrStrdup(event.data.file.oldpath); 76 | } 77 | 78 | mtx_lock(&state.lock); 79 | arr_push(&state.events, event); 80 | mtx_unlock(&state.lock); 81 | } 82 | 83 | bool lovrEventPoll(Event* event) { 84 | mtx_lock(&state.lock); 85 | if (state.head == state.events.length) { 86 | state.head = state.events.length = 0; 87 | mtx_unlock(&state.lock); 88 | return false; 89 | } 90 | 91 | *event = state.events.data[state.head++]; 92 | mtx_unlock(&state.lock); 93 | return true; 94 | } 95 | 96 | void lovrEventClear(void) { 97 | mtx_lock(&state.lock); 98 | arr_clear(&state.events); 99 | state.head = 0; 100 | mtx_unlock(&state.lock); 101 | } 102 | -------------------------------------------------------------------------------- /src/modules/event/event.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #pragma once 6 | 7 | #define MAX_EVENT_NAME_LENGTH 32 8 | 9 | struct Thread; 10 | struct Variant; 11 | 12 | typedef enum { 13 | DISPLAY_HEADSET, 14 | DISPLAY_WINDOW 15 | } DisplayType; 16 | 17 | typedef enum { 18 | EVENT_QUIT, 19 | EVENT_RESTART, 20 | EVENT_VISIBLE, 21 | EVENT_FOCUS, 22 | EVENT_MOUNT, 23 | EVENT_RECENTER, 24 | EVENT_RESIZE, 25 | EVENT_KEYPRESSED, 26 | EVENT_KEYRELEASED, 27 | EVENT_TEXTINPUT, 28 | EVENT_MOUSEPRESSED, 29 | EVENT_MOUSERELEASED, 30 | EVENT_MOUSEMOVED, 31 | EVENT_MOUSEWHEELMOVED, 32 | #ifndef LOVR_DISABLE_THREAD 33 | EVENT_THREAD_ERROR, 34 | #endif 35 | EVENT_FILECHANGED, 36 | EVENT_PERMISSION, 37 | EVENT_CUSTOM 38 | } EventType; 39 | 40 | typedef enum { 41 | TYPE_NIL, 42 | TYPE_BOOLEAN, 43 | TYPE_NUMBER, 44 | TYPE_STRING, 45 | TYPE_MINISTRING, 46 | TYPE_POINTER, 47 | TYPE_OBJECT, 48 | TYPE_VECTOR, 49 | TYPE_MATRIX, 50 | TYPE_TABLE 51 | } VariantType; 52 | 53 | typedef union { 54 | bool boolean; 55 | double number; 56 | void* pointer; 57 | struct { 58 | char* pointer; 59 | size_t length; 60 | } string; 61 | struct { 62 | uint8_t length; 63 | char data[23]; 64 | } ministring; 65 | struct { 66 | void* pointer; 67 | const char* type; 68 | void (*destructor)(void*); 69 | } object; 70 | struct { 71 | int type; 72 | float data[4]; 73 | } vector; 74 | struct { 75 | float* data; 76 | } matrix; 77 | struct { 78 | struct Variant* keys; 79 | struct Variant* vals; 80 | size_t length; 81 | } table; 82 | } VariantValue; 83 | 84 | typedef struct Variant { 85 | VariantType type; 86 | VariantValue value; 87 | } Variant; 88 | 89 | typedef struct { 90 | int exitCode; 91 | } QuitEvent; 92 | 93 | typedef struct { 94 | bool visible; 95 | DisplayType display; 96 | } VisibleEvent; 97 | 98 | typedef struct { 99 | bool focused; 100 | DisplayType display; 101 | } FocusEvent; 102 | 103 | typedef struct { 104 | bool mounted; 105 | } MountEvent; 106 | 107 | typedef struct { 108 | uint32_t width; 109 | uint32_t height; 110 | } ResizeEvent; 111 | 112 | typedef struct { 113 | uint32_t code; 114 | uint32_t scancode; 115 | bool repeat; 116 | } KeyEvent; 117 | 118 | typedef struct { 119 | char utf8[4]; 120 | uint32_t codepoint; 121 | } TextEvent; 122 | 123 | typedef struct { 124 | double x; 125 | double y; 126 | double dx; 127 | double dy; 128 | int button; 129 | } MouseEvent; 130 | 131 | typedef struct { 132 | double x; 133 | double y; 134 | } MouseWheelEvent; 135 | 136 | typedef struct { 137 | struct Thread* thread; 138 | char* error; 139 | } ThreadEvent; 140 | 141 | typedef struct { 142 | char* path; 143 | char* oldpath; 144 | int action; 145 | } FileEvent; 146 | 147 | typedef struct { 148 | uint32_t permission; 149 | bool granted; 150 | } PermissionEvent; 151 | 152 | typedef struct { 153 | char name[MAX_EVENT_NAME_LENGTH]; 154 | Variant data[4]; 155 | uint32_t count; 156 | } CustomEvent; 157 | 158 | typedef union { 159 | QuitEvent quit; 160 | VisibleEvent visible; 161 | FocusEvent focus; 162 | MountEvent mount; 163 | ResizeEvent resize; 164 | KeyEvent key; 165 | TextEvent text; 166 | MouseEvent mouse; 167 | MouseWheelEvent wheel; 168 | ThreadEvent thread; 169 | FileEvent file; 170 | PermissionEvent permission; 171 | CustomEvent custom; 172 | } EventData; 173 | 174 | typedef struct { 175 | EventType type; 176 | EventData data; 177 | } Event; 178 | 179 | void lovrVariantDestroy(Variant* variant); 180 | 181 | bool lovrEventInit(void); 182 | void lovrEventDestroy(void); 183 | void lovrEventPush(Event event); 184 | bool lovrEventPoll(Event* event); 185 | void lovrEventClear(void); 186 | -------------------------------------------------------------------------------- /src/modules/filesystem/filesystem.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #pragma once 6 | 7 | #define LOVR_PATH_MAX 1024 8 | 9 | typedef struct Archive Archive; 10 | typedef struct File File; 11 | 12 | typedef enum { 13 | FILE_CREATE, 14 | FILE_DELETE, 15 | FILE_MODIFY, 16 | FILE_RENAME 17 | } FileAction; 18 | 19 | bool lovrFilesystemInit(void); 20 | void lovrFilesystemDestroy(void); 21 | bool lovrFilesystemSetSource(const char* source); 22 | const char* lovrFilesystemGetSource(void); 23 | bool lovrFilesystemIsFused(void); 24 | void lovrFilesystemWatch(void); 25 | void lovrFilesystemUnwatch(void); 26 | bool lovrFilesystemMount(const char* path, const char* mountpoint, bool append, const char *root); 27 | bool lovrFilesystemUnmount(const char* path); 28 | const char* lovrFilesystemGetRealDirectory(const char* path); 29 | bool lovrFilesystemIsFile(const char* path); 30 | bool lovrFilesystemIsDirectory(const char* path); 31 | bool lovrFilesystemGetSize(const char* path, uint64_t* size); 32 | bool lovrFilesystemGetLastModified(const char* path, uint64_t* modtime); 33 | void* lovrFilesystemRead(const char* path, size_t* size); 34 | void lovrFilesystemGetDirectoryItems(const char* path, void (*callback)(void* context, const char* path), void* context); 35 | const char* lovrFilesystemGetIdentity(void); 36 | bool lovrFilesystemSetIdentity(const char* identity, bool precedence); 37 | const char* lovrFilesystemGetSaveDirectory(void); 38 | bool lovrFilesystemCreateDirectory(const char* path); 39 | bool lovrFilesystemRemove(const char* path); 40 | bool lovrFilesystemWrite(const char* path, const char* content, size_t size, bool append); 41 | size_t lovrFilesystemGetAppdataDirectory(char* buffer, size_t size); 42 | size_t lovrFilesystemGetBundlePath(char* buffer, size_t size, const char** root); 43 | size_t lovrFilesystemGetExecutablePath(char* buffer, size_t size); 44 | size_t lovrFilesystemGetUserDirectory(char* buffer, size_t size); 45 | size_t lovrFilesystemGetWorkingDirectory(char* buffer, size_t size); 46 | const char* lovrFilesystemGetRequirePath(void); 47 | void lovrFilesystemSetRequirePath(const char* requirePath); 48 | 49 | // Archive 50 | 51 | Archive* lovrArchiveCreate(const char* path, const char* mountpoint, const char* root); 52 | void lovrArchiveDestroy(void* ref); 53 | 54 | // File 55 | 56 | typedef enum { 57 | OPEN_READ, 58 | OPEN_WRITE, 59 | OPEN_APPEND 60 | } OpenMode; 61 | 62 | File* lovrFileCreate(const char* path, OpenMode mode); 63 | void lovrFileDestroy(void* ref); 64 | const char* lovrFileGetPath(File* file); 65 | OpenMode lovrFileGetMode(File* file); 66 | bool lovrFileGetSize(File* file, uint64_t* size); 67 | bool lovrFileRead(File* file, void* data, size_t size, size_t* count); 68 | bool lovrFileWrite(File* file, const void* data, size_t size, size_t* count); 69 | bool lovrFileSeek(File* file, uint64_t offset); 70 | uint64_t lovrFileTell(File* file); 71 | -------------------------------------------------------------------------------- /src/modules/headset/headset.c: -------------------------------------------------------------------------------- 1 | #include "headset/headset.h" 2 | #include "util.h" 3 | #include 4 | 5 | HeadsetInterface* lovrHeadsetInterface = NULL; 6 | static uint32_t ref; 7 | 8 | bool lovrHeadsetInit(HeadsetConfig* config) { 9 | if (atomic_fetch_add(&ref, 1)) return true; 10 | 11 | for (size_t i = 0; i < config->driverCount; i++) { 12 | HeadsetInterface* interface = NULL; 13 | 14 | switch (config->drivers[i]) { 15 | #ifdef LOVR_USE_SIMULATOR 16 | case DRIVER_SIMULATOR: interface = &lovrHeadsetSimulatorDriver; break; 17 | #endif 18 | #ifdef LOVR_USE_OPENXR 19 | case DRIVER_OPENXR: interface = &lovrHeadsetOpenXRDriver; break; 20 | #endif 21 | #ifdef LOVR_USE_WEBXR 22 | case DRIVER_WEBXR: interface = &lovrHeadsetWebXRDriver; break; 23 | #endif 24 | default: continue; 25 | } 26 | 27 | if (interface->init(config)) { 28 | lovrHeadsetInterface = interface; 29 | break; 30 | } 31 | } 32 | 33 | lovrAssert(lovrHeadsetInterface, "No headset display driver available, check t.headset.drivers in conf.lua"); 34 | return true; 35 | } 36 | 37 | void lovrHeadsetDestroy(void) { 38 | if (atomic_fetch_sub(&ref, 1) != 1) return; 39 | if (lovrHeadsetInterface) { 40 | lovrHeadsetInterface->destroy(); 41 | lovrHeadsetInterface = NULL; 42 | } 43 | ref = 0; 44 | } 45 | 46 | void lovrLayerDestroy(void* ref) { 47 | lovrHeadsetInterface->destroyLayer(ref); 48 | } 49 | -------------------------------------------------------------------------------- /src/modules/headset/headset_webxr.c: -------------------------------------------------------------------------------- 1 | #include "headset/headset.h" 2 | 3 | extern bool webxr_init(HeadsetConfig* config); 4 | extern bool webxr_start(void); 5 | extern void webxr_stop(void); 6 | extern void webxr_destroy(void); 7 | extern bool webxr_getDriverName(char* name, size_t length); 8 | extern void webxr_getFeatures(HeadsetFeatures* features); 9 | extern bool webxr_getName(char* name, size_t length); 10 | extern bool webxr_isSeated(void); 11 | extern void webxr_getDisplayDimensions(uint32_t* width, uint32_t* height); 12 | extern float webxr_getRefreshRate(void); 13 | extern bool webxr_setRefreshRate(float refreshRate); 14 | extern const float* webxr_getRefreshRates(uint32_t* count); 15 | extern void webxr_getFoveation(FoveationLevel* level, bool* dynamic); 16 | extern bool webxr_setFoveation(FoveationLevel level, bool dynamic); 17 | PassthroughMode webxr_getPassthrough(void); 18 | extern bool webxr_setPassthrough(PassthroughMode mode); 19 | extern bool webxr_isPassthroughSupported(PassthroughMode mode); 20 | extern double webxr_getDisplayTime(void); 21 | extern double webxr_getDeltaTime(void); 22 | extern uint32_t webxr_getViewCount(void); 23 | extern bool webxr_getViewPose(uint32_t view, float* position, float* orientation); 24 | extern bool webxr_getViewAngles(uint32_t view, float* left, float* right, float* up, float* down); 25 | extern void webxr_getClipDistance(float* near, float* far); 26 | extern void webxr_setClipDistance(float near, float far); 27 | extern void webxr_getBoundsDimensions(float* width, float* depth); 28 | extern const float* webxr_getBoundsGeometry(uint32_t* count); 29 | extern bool webxr_getPose(Device device, float* position, float* orientation); 30 | extern bool webxr_getVelocity(Device device, float* velocity, float* angularVelocity); 31 | extern bool webxr_isDown(Device device, DeviceButton button, bool* down, bool* changed); 32 | extern bool webxr_isTouched(Device device, DeviceButton button, bool* touched); 33 | extern bool webxr_getAxis(Device device, DeviceAxis axis, float* value); 34 | extern bool webxr_getSkeleton(Device device, float* poses, SkeletonSource* source); 35 | extern bool webxr_vibrate(Device device, float strength, float duration, float frequency); 36 | extern void webxr_stopVibration(Device device); 37 | extern struct ModelData* webxr_newModelData(Device device, bool animated); 38 | extern bool webxr_animate(struct Model* model); 39 | extern bool webxr_getTexture(struct Texture** texture); 40 | extern bool webxr_getPass(struct Pass** pass); 41 | extern bool webxr_submit(void); 42 | extern bool webxr_isActive(void); 43 | extern bool webxr_isVisible(void); 44 | extern bool webxr_isFocused(void); 45 | extern bool webxr_isMounted(void); 46 | extern bool webxr_update(double* dt); 47 | 48 | static bool webxrAttached = false; 49 | static HeadsetInterface* previousHeadsetDriver; 50 | 51 | void webxr_attach(void) { 52 | if (webxrAttached || lovrHeadsetInterface == &lovrHeadsetWebXRDriver) { 53 | return; 54 | } 55 | 56 | previousHeadsetDriver = lovrHeadsetInterface; 57 | lovrHeadsetInterface = &lovrHeadsetWebXRDriver; 58 | webxrAttached = true; 59 | } 60 | 61 | void webxr_detach(void) { 62 | if (!webxrAttached) { 63 | return; 64 | } 65 | 66 | lovrHeadsetInterface = previousHeadsetDriver; 67 | previousHeadsetDriver = NULL; 68 | webxrAttached = false; 69 | } 70 | 71 | HeadsetInterface lovrHeadsetWebXRDriver = { 72 | .driverType = DRIVER_WEBXR, 73 | .init = webxr_init, 74 | .start = webxr_start, 75 | .stop = webxr_stop, 76 | .destroy = webxr_destroy, 77 | .getFeatures = webxr_getFeatures, 78 | .getDriverName = webxr_getDriverName, 79 | .getName = webxr_getName, 80 | .isSeated = webxr_isSeated, 81 | .getDisplayDimensions = webxr_getDisplayDimensions, 82 | .getRefreshRate = webxr_getRefreshRate, 83 | .setRefreshRate = webxr_setRefreshRate, 84 | .getRefreshRates = webxr_getRefreshRates, 85 | .getPassthrough = webxr_getPassthrough, 86 | .setPassthrough = webxr_setPassthrough, 87 | .isPassthroughSupported = webxr_isPassthroughSupported, 88 | .getDisplayTime = webxr_getDisplayTime, 89 | .getDeltaTime = webxr_getDeltaTime, 90 | .getViewCount = webxr_getViewCount, 91 | .getViewPose = webxr_getViewPose, 92 | .getViewAngles = webxr_getViewAngles, 93 | .getClipDistance = webxr_getClipDistance, 94 | .setClipDistance = webxr_setClipDistance, 95 | .getBoundsDimensions = webxr_getBoundsDimensions, 96 | .getBoundsGeometry = webxr_getBoundsGeometry, 97 | .getPose = webxr_getPose, 98 | .getVelocity = webxr_getVelocity, 99 | .isDown = webxr_isDown, 100 | .isTouched = webxr_isTouched, 101 | .getAxis = webxr_getAxis, 102 | .getSkeleton = webxr_getSkeleton, 103 | .vibrate = webxr_vibrate, 104 | .stopVibration = webxr_stopVibration, 105 | .newModelData = webxr_newModelData, 106 | .animate = webxr_animate, 107 | .getTexture = webxr_getTexture, 108 | .getPass = webxr_getPass, 109 | .submit = webxr_submit, 110 | .isVisible = webxr_isVisible, 111 | .isFocused = webxr_isFocused, 112 | .isMounted = webxr_isMounted, 113 | .update = webxr_update 114 | }; 115 | -------------------------------------------------------------------------------- /src/modules/math/math.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #pragma once 6 | 7 | typedef struct Curve Curve; 8 | typedef struct Pool Pool; 9 | typedef struct RandomGenerator RandomGenerator; 10 | 11 | bool lovrMathInit(void); 12 | void lovrMathDestroy(void); 13 | float lovrMathGammaToLinear(float x); 14 | float lovrMathLinearToGamma(float x); 15 | double lovrMathNoise1(double x); 16 | double lovrMathNoise2(double x, double y); 17 | double lovrMathNoise3(double x, double y, double z); 18 | double lovrMathNoise4(double x, double y, double z, double w); 19 | RandomGenerator* lovrMathGetRandomGenerator(void); 20 | 21 | // Curve 22 | 23 | Curve* lovrCurveCreate(void); 24 | void lovrCurveDestroy(void* ref); 25 | bool lovrCurveEvaluate(Curve* curve, float t, float* point); 26 | void lovrCurveGetTangent(Curve* curve, float t, float* point); 27 | Curve* lovrCurveSlice(Curve* curve, float t1, float t2); 28 | size_t lovrCurveGetPointCount(Curve* curve); 29 | void lovrCurveGetPoint(Curve* curve, size_t index, float* point); 30 | void lovrCurveSetPoint(Curve* curve, size_t index, float* point); 31 | void lovrCurveAddPoint(Curve* curve, float* point, size_t index); 32 | void lovrCurveRemovePoint(Curve* curve, size_t index); 33 | 34 | // Pool 35 | 36 | typedef enum { 37 | V_NONE, 38 | V_VEC2, 39 | V_VEC3, 40 | V_VEC4, 41 | V_QUAT, 42 | V_MAT4, 43 | MAX_VECTOR_TYPES 44 | } VectorType; 45 | 46 | typedef union { 47 | void* pointer; 48 | struct { 49 | unsigned type : 4; 50 | unsigned generation : 4; 51 | unsigned index : 24; 52 | unsigned padding : 32; 53 | } handle; 54 | } Vector; 55 | 56 | Pool* lovrPoolCreate(void); 57 | void lovrPoolDestroy(void* ref); 58 | bool lovrPoolGrow(Pool* pool, size_t count); 59 | Vector lovrPoolAllocate(Pool* pool, VectorType type, float** data); 60 | float* lovrPoolResolve(Pool* pool, Vector vector); 61 | void lovrPoolDrain(Pool* pool); 62 | 63 | // RandomGenerator 64 | 65 | typedef union { 66 | uint64_t b64; 67 | struct { 68 | uint32_t lo; 69 | uint32_t hi; 70 | } b32; 71 | } Seed; 72 | 73 | RandomGenerator* lovrRandomGeneratorCreate(void); 74 | void lovrRandomGeneratorDestroy(void* ref); 75 | Seed lovrRandomGeneratorGetSeed(RandomGenerator* generator); 76 | void lovrRandomGeneratorSetSeed(RandomGenerator* generator, Seed seed); 77 | void lovrRandomGeneratorGetState(RandomGenerator* generator, char* state, size_t length); 78 | int lovrRandomGeneratorSetState(RandomGenerator* generator, const char* state); 79 | double lovrRandomGeneratorRandom(RandomGenerator* generator); 80 | double lovrRandomGeneratorRandomNormal(RandomGenerator* generator); 81 | -------------------------------------------------------------------------------- /src/modules/system/system.c: -------------------------------------------------------------------------------- 1 | #include "system/system.h" 2 | #include "event/event.h" 3 | #include "core/os.h" 4 | #include "util.h" 5 | #include 6 | #include 7 | 8 | static struct { 9 | uint32_t ref; 10 | bool keyRepeat; 11 | bool prevKeyState[OS_KEY_COUNT]; 12 | bool keyState[OS_KEY_COUNT]; 13 | bool prevMouseState[8]; 14 | bool mouseState[8]; 15 | double mouseX; 16 | double mouseY; 17 | double scrollDelta; 18 | } state; 19 | 20 | static void onKey(os_button_action action, os_key key, uint32_t scancode, bool repeat) { 21 | if (repeat && !state.keyRepeat) return; 22 | state.keyState[key] = (action == BUTTON_PRESSED); 23 | lovrEventPush((Event) { 24 | .type = action == BUTTON_PRESSED ? EVENT_KEYPRESSED : EVENT_KEYRELEASED, 25 | .data.key.code = key, 26 | .data.key.scancode = scancode, 27 | .data.key.repeat = repeat 28 | }); 29 | } 30 | 31 | static void onText(uint32_t codepoint) { 32 | Event event; 33 | event.type = EVENT_TEXTINPUT; 34 | event.data.text.codepoint = codepoint; 35 | memset(&event.data.text.utf8, 0, sizeof(event.data.text.utf8)); 36 | utf8_encode(codepoint, event.data.text.utf8); 37 | lovrEventPush(event); 38 | } 39 | 40 | static void onMouseButton(int button, bool pressed) { 41 | if ((size_t) button < COUNTOF(state.mouseState)) state.mouseState[button] = pressed; 42 | lovrEventPush((Event) { 43 | .type = pressed ? EVENT_MOUSEPRESSED : EVENT_MOUSERELEASED, 44 | .data.mouse.x = state.mouseX, 45 | .data.mouse.y = state.mouseY, 46 | .data.mouse.button = button 47 | }); 48 | } 49 | 50 | static void onMouseMove(double x, double y) { 51 | lovrEventPush((Event) { 52 | .type = EVENT_MOUSEMOVED, 53 | .data.mouse.x = x, 54 | .data.mouse.y = y, 55 | .data.mouse.dx = x - state.mouseX, 56 | .data.mouse.dy = y - state.mouseY 57 | }); 58 | 59 | state.mouseX = x; 60 | state.mouseY = y; 61 | } 62 | 63 | static void onWheelMove(double deltaX, double deltaY) { 64 | state.scrollDelta += deltaY; 65 | lovrEventPush((Event) { 66 | .type = EVENT_MOUSEWHEELMOVED, 67 | .data.wheel.x = deltaX, 68 | .data.wheel.y = deltaY, 69 | }); 70 | } 71 | 72 | static void onPermission(os_permission permission, bool granted) { 73 | lovrEventPush((Event) { 74 | .type = EVENT_PERMISSION, 75 | .data.permission.permission = permission, 76 | .data.permission.granted = granted 77 | }); 78 | } 79 | 80 | static void onQuit(void) { 81 | lovrEventPush((Event) { 82 | .type = EVENT_QUIT, 83 | .data.quit.exitCode = 0 84 | }); 85 | } 86 | 87 | static void onVisible(bool visible) { 88 | lovrEventPush((Event) { 89 | .type = EVENT_VISIBLE, 90 | .data.visible.visible = visible, 91 | .data.visible.display = DISPLAY_WINDOW 92 | }); 93 | } 94 | 95 | static void onFocus(bool focused) { 96 | lovrEventPush((Event) { 97 | .type = EVENT_FOCUS, 98 | .data.focus.focused = focused, 99 | .data.focus.display = DISPLAY_WINDOW 100 | }); 101 | } 102 | 103 | bool lovrSystemInit(void) { 104 | if (atomic_fetch_add(&state.ref, 1)) return false; 105 | os_on_key(onKey); 106 | os_on_text(onText); 107 | os_on_mouse_button(onMouseButton); 108 | os_on_mouse_move(onMouseMove); 109 | os_on_mousewheel_move(onWheelMove); 110 | os_on_permission(onPermission); 111 | os_get_mouse_position(&state.mouseX, &state.mouseY); 112 | return true; 113 | } 114 | 115 | void lovrSystemDestroy(void) { 116 | if (atomic_fetch_sub(&state.ref, 1) != 1) return; 117 | os_on_key(NULL); 118 | os_on_text(NULL); 119 | os_on_permission(NULL); 120 | memset(&state, 0, sizeof(state)); 121 | } 122 | 123 | const char* lovrSystemGetOS(void) { 124 | return os_get_name(); 125 | } 126 | 127 | void lovrSystemOpenConsole(void) { 128 | os_open_console(); 129 | } 130 | 131 | uint32_t lovrSystemGetCoreCount(void) { 132 | return os_get_core_count(); 133 | } 134 | 135 | void lovrSystemRequestPermission(Permission permission) { 136 | os_request_permission((os_permission) permission); 137 | } 138 | 139 | bool lovrSystemOpenWindow(os_window_config* window) { 140 | lovrAssert(os_window_open(window), "Could not open window"); 141 | os_on_quit(onQuit); 142 | os_on_visible(onVisible); 143 | os_on_focus(onFocus); 144 | return true; 145 | } 146 | 147 | bool lovrSystemIsWindowOpen(void) { 148 | return os_window_is_open(); 149 | } 150 | 151 | bool lovrSystemIsWindowVisible(void) { 152 | return os_window_is_visible(); 153 | } 154 | 155 | bool lovrSystemIsWindowFocused(void) { 156 | return os_window_is_focused(); 157 | } 158 | 159 | void lovrSystemGetWindowSize(uint32_t* width, uint32_t* height) { 160 | os_window_get_size(width, height); 161 | } 162 | 163 | float lovrSystemGetWindowDensity(void) { 164 | return os_window_get_pixel_density(); 165 | } 166 | 167 | void lovrSystemPollEvents(void) { 168 | memcpy(state.prevKeyState, state.keyState, sizeof(state.keyState)); 169 | memcpy(state.prevMouseState, state.mouseState, sizeof(state.mouseState)); 170 | state.scrollDelta = 0.; 171 | os_poll_events(); 172 | } 173 | 174 | bool lovrSystemIsKeyDown(int keycode) { 175 | return state.keyState[keycode]; 176 | } 177 | 178 | bool lovrSystemWasKeyPressed(int keycode) { 179 | return !state.prevKeyState[keycode] && state.keyState[keycode]; 180 | } 181 | 182 | bool lovrSystemWasKeyReleased(int keycode) { 183 | return state.prevKeyState[keycode] && !state.keyState[keycode]; 184 | } 185 | 186 | bool lovrSystemHasKeyRepeat(void) { 187 | return state.keyRepeat; 188 | } 189 | 190 | void lovrSystemSetKeyRepeat(bool repeat) { 191 | state.keyRepeat = repeat; 192 | } 193 | 194 | void lovrSystemGetMousePosition(double* x, double* y) { 195 | *x = state.mouseX; 196 | *y = state.mouseY; 197 | } 198 | 199 | bool lovrSystemIsMouseDown(int button) { 200 | if ((size_t) button > COUNTOF(state.mouseState)) return false; 201 | return state.mouseState[button]; 202 | } 203 | 204 | bool lovrSystemWasMousePressed(int button) { 205 | if ((size_t) button > COUNTOF(state.mouseState)) return false; 206 | return !state.prevMouseState[button] && state.mouseState[button]; 207 | } 208 | 209 | bool lovrSystemWasMouseReleased(int button) { 210 | if ((size_t) button > COUNTOF(state.mouseState)) return false; 211 | return state.prevMouseState[button] && !state.mouseState[button]; 212 | } 213 | 214 | // This is kind of a hacky thing for the simulator, since we're kinda bad at event dispatch 215 | float lovrSystemGetScrollDelta(void) { 216 | return state.scrollDelta; 217 | } 218 | 219 | const char* lovrSystemGetClipboardText(void) { 220 | return os_get_clipboard_text(); 221 | } 222 | 223 | void lovrSystemSetClipboardText(const char* text) { 224 | os_set_clipboard_text(text); 225 | } 226 | -------------------------------------------------------------------------------- /src/modules/system/system.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #pragma once 5 | 6 | struct os_window_config; 7 | 8 | typedef enum { 9 | PERMISSION_AUDIO_CAPTURE 10 | } Permission; 11 | 12 | bool lovrSystemInit(void); 13 | void lovrSystemDestroy(void); 14 | const char* lovrSystemGetOS(void); 15 | uint32_t lovrSystemGetCoreCount(void); 16 | void lovrSystemOpenConsole(void); 17 | void lovrSystemRequestPermission(Permission permission); 18 | bool lovrSystemOpenWindow(struct os_window_config* config); 19 | bool lovrSystemIsWindowOpen(void); 20 | bool lovrSystemIsWindowVisible(void); 21 | bool lovrSystemIsWindowFocused(void); 22 | void lovrSystemGetWindowSize(uint32_t* width, uint32_t* height); 23 | float lovrSystemGetWindowDensity(void); 24 | void lovrSystemPollEvents(void); 25 | bool lovrSystemIsKeyDown(int keycode); 26 | bool lovrSystemWasKeyPressed(int keycode); 27 | bool lovrSystemWasKeyReleased(int keycode); 28 | bool lovrSystemHasKeyRepeat(void); 29 | void lovrSystemSetKeyRepeat(bool repeat); 30 | void lovrSystemGetMousePosition(double* x, double* y); 31 | bool lovrSystemIsMouseDown(int button); 32 | bool lovrSystemWasMousePressed(int button); 33 | bool lovrSystemWasMouseReleased(int button); 34 | float lovrSystemGetScrollDelta(void); 35 | const char* lovrSystemGetClipboardText(void); 36 | void lovrSystemSetClipboardText(const char* text); 37 | -------------------------------------------------------------------------------- /src/modules/thread/thread.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Note: Channels retrieved with lovrThreadGetChannel don't need to be released. They're just all 5 | // cleaned up when the thread module is destroyed. 6 | 7 | #pragma once 8 | 9 | #define MAX_THREAD_ARGUMENTS 4 10 | 11 | struct Blob; 12 | struct Variant; 13 | 14 | typedef struct Thread Thread; 15 | typedef struct Channel Channel; 16 | 17 | bool lovrThreadModuleInit(int32_t workers); 18 | void lovrThreadModuleDestroy(void); 19 | uint32_t lovrThreadGetWorkerCount(void); 20 | struct Channel* lovrThreadGetChannel(const char* name); 21 | 22 | // Thread 23 | 24 | typedef char* ThreadFunction(Thread* thread, struct Blob* body, struct Variant* arguments, uint32_t argumentCount); 25 | 26 | Thread* lovrThreadCreate(ThreadFunction* function, struct Blob* body); 27 | void lovrThreadDestroy(void* ref); 28 | bool lovrThreadStart(Thread* thread, struct Variant* arguments, uint32_t argumentCount); 29 | void lovrThreadWait(Thread* thread); 30 | bool lovrThreadIsRunning(Thread* thread); 31 | const char* lovrThreadGetError(Thread* thread); 32 | 33 | // Channel 34 | 35 | Channel* lovrChannelCreate(uint64_t hash); 36 | void lovrChannelDestroy(void* ref); 37 | bool lovrChannelPush(Channel* channel, struct Variant* variant, double timeout, uint64_t* id); 38 | bool lovrChannelPop(Channel* channel, struct Variant* variant, double timeout); 39 | bool lovrChannelPeek(Channel* channel, struct Variant* variant); 40 | void lovrChannelClear(Channel* channel); 41 | uint64_t lovrChannelGetCount(Channel* channel); 42 | bool lovrChannelHasRead(Channel* channel, uint64_t id); 43 | -------------------------------------------------------------------------------- /src/modules/timer/timer.c: -------------------------------------------------------------------------------- 1 | #include "timer/timer.h" 2 | #include "core/os.h" 3 | #include 4 | #include 5 | 6 | static struct { 7 | uint32_t ref; 8 | double epoch; 9 | double lastTime; 10 | double time; 11 | double dt; 12 | int tickIndex; 13 | double tickSum; 14 | double tickBuffer[TICK_SAMPLES]; 15 | } state; 16 | 17 | bool lovrTimerInit(void) { 18 | if (atomic_fetch_add(&state.ref, 1)) return false; 19 | state.epoch = os_get_time(); 20 | return true; 21 | } 22 | 23 | void lovrTimerDestroy(void) { 24 | if (atomic_fetch_sub(&state.ref, 1) != 1) return; 25 | memset(&state, 0, sizeof(state)); 26 | } 27 | 28 | double lovrTimerGetDelta(void) { 29 | return state.dt; 30 | } 31 | 32 | double lovrTimerGetTime(void) { 33 | return os_get_time() - state.epoch; 34 | } 35 | 36 | double lovrTimerStep(void) { 37 | state.lastTime = state.time; 38 | state.time = os_get_time(); 39 | state.dt = state.time - state.lastTime; 40 | state.tickSum -= state.tickBuffer[state.tickIndex]; 41 | state.tickSum += state.dt; 42 | state.tickBuffer[state.tickIndex] = state.dt; 43 | if (++state.tickIndex == TICK_SAMPLES) { 44 | state.tickIndex = 0; 45 | } 46 | return state.dt; 47 | } 48 | 49 | double lovrTimerGetAverageDelta(void) { 50 | return state.tickSum / TICK_SAMPLES; 51 | } 52 | 53 | int lovrTimerGetFPS(void) { 54 | return (int) (1 / (state.tickSum / TICK_SAMPLES) + .5); 55 | } 56 | 57 | void lovrTimerSleep(double seconds) { 58 | os_sleep(seconds); 59 | } 60 | -------------------------------------------------------------------------------- /src/modules/timer/timer.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #pragma once 4 | 5 | #define TICK_SAMPLES 90 6 | 7 | bool lovrTimerInit(void); 8 | void lovrTimerDestroy(void); 9 | double lovrTimerGetDelta(void); 10 | double lovrTimerGetTime(void); 11 | double lovrTimerStep(void); 12 | double lovrTimerGetAverageDelta(void); 13 | int lovrTimerGetFPS(void); 14 | void lovrTimerSleep(double seconds); 15 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #pragma once 6 | 7 | #define LOVR_VERSION_MAJOR 0 8 | #define LOVR_VERSION_MINOR 18 9 | #define LOVR_VERSION_PATCH 0 10 | #define LOVR_VERSION_ALIAS "Dream Eater" 11 | 12 | #ifndef M_PI 13 | #define M_PI 3.14159265358979 14 | #endif 15 | 16 | #define MAX(a, b) (a > b ? a : b) 17 | #define MIN(a, b) (a < b ? a : b) 18 | #define CLAMP(x, min, max) MAX(min, MIN(max, x)) 19 | #define ALIGN(p, n) (((uintptr_t) (p) + (n - 1)) & ~(n - 1)) 20 | #define COUNTOF(x) (sizeof(x) / sizeof(x[0])) 21 | #define CHECK_SIZEOF(T) int(*_o)[sizeof(T)]=1 22 | #define BREAK() __asm("int $3") 23 | 24 | // Allocation 25 | void* lovrMalloc(size_t size); 26 | void* lovrCalloc(size_t size); 27 | void* lovrRealloc(void* data, size_t size); 28 | void lovrFree(void* data); 29 | 30 | #define lovrStrdup(s) (s ? memcpy(lovrMalloc(strlen(s) + 1), s, strlen(s) + 1) : NULL) 31 | 32 | // Refcounting (to be refcounted, a struct must have a uint32_t refcount as its first field) 33 | void lovrRetain(void* ref); 34 | void lovrRelease(void* ref, void (*destructor)(void*)); 35 | 36 | // Errors 37 | 38 | const char* lovrGetError(void); 39 | int lovrSetError(const char* format, ...); 40 | 41 | #define lovrUnreachable() abort() 42 | #define lovrAssert(c, ...) do { if (!(c)) { lovrSetError(__VA_ARGS__); return 0; } } while (0) 43 | #define lovrAssertGoto(label, c, ...) do { if (!(c)) { lovrSetError(__VA_ARGS__); goto label; } } while (0) 44 | #ifdef LOVR_UNCHECKED 45 | #define lovrCheck(c, ...) ((void) 0) 46 | #define lovrCheckGoto(label, c, ...) ((void) 0) 47 | #else 48 | #define lovrCheck lovrAssert 49 | #define lovrCheckGoto lovrAssertGoto 50 | #endif 51 | 52 | // Logging 53 | typedef void fn_log(void*, int, const char*, const char*, va_list); 54 | enum { LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR }; 55 | void lovrSetLogCallback(fn_log* callback, void* userdata); 56 | void lovrLog(int level, const char* tag, const char* format, ...); 57 | 58 | // Profiling 59 | #ifdef LOVR_PROFILE 60 | #include 61 | #define lovrProfileMarkFrame() TracyCFrameMark 62 | #define lovrProfileStart(id, label) TracyCZoneN(id, label, true) 63 | #define lovrProfileEnd(id) TracyCZoneEnd(id) 64 | #define lovrProfileAlloc(p, size) TracyCAlloc(p, size) 65 | #define lovrProfileFree(p) TracyCFree(p) 66 | #else 67 | #define lovrProfileMarkFrame() ((void) 0) 68 | #define lovrProfileStart(id, label) ((void) 0) 69 | #define lovrProfileEnd(id) ((void) 0) 70 | #define lovrProfileAlloc(p, size) ((void) 0) 71 | #define lovrProfileFree(p) ((void) 0) 72 | #endif 73 | 74 | // Dynamic Array 75 | #define arr_t(T) struct { T* data; size_t length, capacity; } 76 | #define arr_init(a) (a)->data = NULL, (a)->length = 0, (a)->capacity = 0 77 | #define arr_free(a) if ((a)->data) lovrFree((a)->data) 78 | #define arr_reserve(a, n) _arr_reserve((void**) &((a)->data), n, &(a)->capacity, sizeof(*(a)->data)) 79 | #define arr_expand(a, n) arr_reserve(a, (a)->length + n) 80 | #define arr_push(a, x) arr_reserve(a, (a)->length + 1), (a)->data[(a)->length] = x, (a)->length++ 81 | #define arr_pop(a) (a)->data[--(a)->length] 82 | #define arr_append(a, p, n) arr_reserve(a, (a)->length + n), memcpy((a)->data + (a)->length, p, n * sizeof(*(p))), (a)->length += n 83 | #define arr_splice(a, i, n) memmove((a)->data + (i), (a)->data + ((i) + n), ((a)->length - (i) - (n)) * sizeof(*(a)->data)), (a)->length -= n 84 | #define arr_clear(a) (a)->length = 0 85 | 86 | static inline void _arr_reserve(void** data, size_t n, size_t* capacity, size_t stride) { 87 | if (*capacity >= n) return; 88 | if (*capacity == 0) *capacity = 1; 89 | while (*capacity < n) *capacity *= 2; 90 | *data = lovrRealloc(*data, *capacity * stride); 91 | } 92 | 93 | // Hash function (FNV1a) 94 | static inline uint64_t hash64(const void* data, size_t length) { 95 | const uint8_t* bytes = (const uint8_t*) data; 96 | uint64_t hash = 0xcbf29ce484222325; 97 | for (size_t i = 0; i < length; i++) { 98 | hash = (hash ^ bytes[i]) * 0x100000001b3; 99 | } 100 | return hash; 101 | } 102 | 103 | // Hashmap (does not support removal) 104 | typedef struct { 105 | uint64_t* hashes; 106 | uint64_t* values; 107 | uint32_t size; 108 | uint32_t used; 109 | } map_t; 110 | 111 | #define MAP_NIL UINT64_MAX 112 | 113 | void map_init(map_t* map, uint32_t n); 114 | void map_free(map_t* map); 115 | uint64_t map_get(map_t* map, uint64_t hash); 116 | void map_set(map_t* map, uint64_t hash, uint64_t value); 117 | 118 | // UTF-8 119 | size_t utf8_decode(const char *s, const char *e, unsigned *pch); 120 | void utf8_encode(uint32_t codepoint, char str[4]); 121 | 122 | // f16 123 | typedef float float32; 124 | typedef uint16_t float16; 125 | void float16Init(void); 126 | float16 float32to16(float32 f); 127 | float32 float16to32(float16 f); 128 | -------------------------------------------------------------------------------- /test/conf.lua: -------------------------------------------------------------------------------- 1 | function lovr.conf(t) 2 | t.identity = 'test' 3 | t.modules.graphics = not os.getenv('CI') 4 | t.window = nil 5 | end 6 | -------------------------------------------------------------------------------- /test/lovr/data.lua: -------------------------------------------------------------------------------- 1 | group('data', function() 2 | group('Blob', function() 3 | test(':getName', function() 4 | -- Test that Blob copies its name instead of relying on Lua string staying live 5 | blob = lovr.data.newBlob('foo', 'b' .. 'ar') 6 | collectgarbage() 7 | expect(blob:getName()).to.equal('b' .. 'ar') 8 | end) 9 | 10 | test(':set* byte range', function() 11 | blob = lovr.data.newBlob(1) 12 | expect(function() blob:setU8(-10, 7) end).to.fail() 13 | expect(function() blob:setU8(10, 7) end).to.fail() 14 | expect(function() blob:setF32(0, 7) end).to.fail() 15 | end) 16 | 17 | test(':setI8', function() 18 | blob = lovr.data.newBlob(16) 19 | for i = 1, 16 do blob:setI8(i - 1, i - 8) end 20 | for i = 1, 16 do expect(blob:getI8(i - 1)).to.equal(i - 8) end 21 | end) 22 | 23 | test(':setU8', function() 24 | blob = lovr.data.newBlob(16) 25 | for i = 1, 16 do blob:setU8(i - 1, i + 150) end 26 | for i = 1, 16 do expect(blob:getU8(i - 1)).to.equal(i + 150) end 27 | end) 28 | 29 | test(':setI16', function() 30 | blob = lovr.data.newBlob(4) 31 | blob:setI16(0, -5000, 5000) 32 | expect(blob:getI16(0)).to.equal(-5000) 33 | expect(blob:getI16(2)).to.equal(5000) 34 | end) 35 | 36 | test(':setU16', function() 37 | blob = lovr.data.newBlob(6) 38 | blob:setU16(0, 0, 1, 60000) 39 | expect(blob:getU16()).to.equal(0) 40 | expect({ blob:getU16(2, 2) }).to.equal({ 1, 60000 }) 41 | end) 42 | 43 | test(':setI32', function() 44 | blob = lovr.data.newBlob(8) 45 | blob:setI32(4, -12345678) 46 | expect(blob:getI32(4)).to.equal(-12345678) 47 | end) 48 | 49 | test(':setU32', function() 50 | blob = lovr.data.newBlob(4) 51 | blob:setU32(0, 0xaabbccdd) 52 | expect(blob:getU32()).to.equal(0xaabbccdd) 53 | expect(blob:getU8(0)).to.equal(0xdd) 54 | expect(blob:getU8(1)).to.equal(0xcc) 55 | expect(blob:getU8(2)).to.equal(0xbb) 56 | expect(blob:getU8(3)).to.equal(0xaa) 57 | end) 58 | 59 | test(':setF32', function() 60 | blob = lovr.data.newBlob(12) 61 | blob:setF32(0, 1, -1000, 1000000) 62 | expect({ blob:getF32(0, 3) }).to.equal({ 1, -1000, 1000000 }) 63 | end) 64 | 65 | test(':setF64', function() 66 | blob = lovr.data.newBlob(8) 67 | blob:setF64(0, 2 ^ 53) 68 | expect(blob:getF64(0)).to.equal(2 ^ 53) 69 | end) 70 | end) 71 | 72 | group('Image', function() 73 | test(':setPixel', function() 74 | local image = lovr.data.newImage(4, 4) 75 | image:setPixel(0, 0, 1, 0, 0, 1) 76 | expect({ image:getPixel(0, 0) }).to.equal({ 1, 0, 0, 1 }) 77 | 78 | -- Default alpha 79 | image:setPixel(1, 1, 0, 1, 0) 80 | expect({ image:getPixel(1, 1) }).to.equal({ 0, 1, 0, 1 }) 81 | 82 | -- Out of bounds 83 | expect(function() image:setPixel(4, 4, 0, 0, 0, 0) end).to.fail() 84 | expect(function() image:setPixel(-4, -4, 0, 0, 0, 0) end).to.fail() 85 | 86 | -- f16 87 | image = lovr.data.newImage(4, 4, 'rg16f') 88 | image:setPixel(0, 0, 1, 2, 3, 4) 89 | image:setPixel(3, 3, 9, 8, 7, 6) 90 | expect({ image:getPixel(0, 0) }).to.equal({ 1, 2, 0, 1 }) 91 | expect({ image:getPixel(3, 3) }).to.equal({ 9, 8, 0, 1 }) 92 | end) 93 | end) 94 | end) 95 | -------------------------------------------------------------------------------- /test/lovr/filesystem.lua: -------------------------------------------------------------------------------- 1 | group('filesystem', function() 2 | test('File', function() 3 | local long = string.rep('a', 2000) .. '.txt' 4 | local file, err = lovr.filesystem.newFile(long, 'r') 5 | assert(not file and err) 6 | 7 | local missing = 'does/not/exist.txt' 8 | local file, err = lovr.filesystem.newFile(missing, 'r') 9 | assert(not file and err) 10 | local file, err = lovr.filesystem.newFile(missing, 'w') 11 | assert(not file and err) 12 | 13 | local file, err = lovr.filesystem.newFile('test.txt', 'w') 14 | assert(file and not err) 15 | assert(file:write('hi')) 16 | file:release() 17 | assert(lovr.filesystem.read('test.txt') == 'hi') 18 | 19 | assert(lovr.filesystem.write('test.txt', 'hi')) 20 | local file, err = lovr.filesystem.newFile('test.txt', 'r') 21 | assert(file and not err) 22 | assert(file:getMode() == 'r') 23 | assert(file:getPath() == 'test.txt') 24 | assert(file:getSize() == 2) 25 | local data, size = file:read() 26 | assert(data == 'hi' and size == 2) 27 | file:release() 28 | 29 | assert(lovr.filesystem.isDirectory('dir') or lovr.filesystem.createDirectory('dir')) 30 | local file, err = lovr.filesystem.newFile('dir', 'r') 31 | assert(not file and err) 32 | local file, err = lovr.filesystem.newFile('dir', 'w') 33 | assert(not file and err) 34 | assert(lovr.filesystem.remove('dir')) 35 | 36 | assert(lovr.filesystem.write('test.txt', 'hi')) 37 | local file, err = lovr.filesystem.newFile('test.txt', 'r') 38 | assert(file and not err) 39 | assert(file:isEOF() == false) 40 | assert(file:tell() == 0) 41 | assert(file:seek(1)) 42 | assert(file:tell() == 1) 43 | assert(file:read(1) == 'i') 44 | assert(file:tell() == 2) 45 | assert(file:isEOF() == true) 46 | 47 | assert(file:seek(1000000)) 48 | assert(file:tell() == 1000000) 49 | assert(file:read(1000000) == '') 50 | assert(file:isEOF() == true) 51 | 52 | assert(pcall(file.seek, file, -1) == false) 53 | assert(pcall(file.seek, file, 2^53 - 1) == true) 54 | assert(pcall(file.seek, file, 2^53) == false) 55 | 56 | assert(file:seek(0)) 57 | assert(file:read(2) == 'hi') 58 | assert(file:tell() == 2) 59 | end) 60 | end) 61 | -------------------------------------------------------------------------------- /test/lovr/headset.lua: -------------------------------------------------------------------------------- 1 | group('headset', function() 2 | test('isVisible', function() 3 | expect(lovr.headset.isVisible()).to.be.a('boolean') 4 | end) 5 | 6 | test('isFocused', function() 7 | expect(lovr.headset.isFocused()).to.be.a('boolean') 8 | end) 9 | 10 | test('isMounted', function() 11 | expect(lovr.headset.isMounted()).to.be.a('boolean') 12 | end) 13 | end) 14 | -------------------------------------------------------------------------------- /test/lovr/init.lua: -------------------------------------------------------------------------------- 1 | group('lovr', function() 2 | for i, file in ipairs(lovr.filesystem.getDirectoryItems('lovr')) do 3 | local module = file:match('%a+') 4 | if lovr[module] then 5 | require('lovr/' .. module) 6 | end 7 | end 8 | end) 9 | -------------------------------------------------------------------------------- /test/lovr/math.lua: -------------------------------------------------------------------------------- 1 | group('math', function() 2 | group('Curve', function() 3 | test(':slice', function() 4 | local points = { 5 | vec3(0, 0, 0), 6 | vec3(0, 1, 0), 7 | vec3(1, 2, 0), 8 | vec3(2, 1, 0), 9 | vec3(2, 0, 0) 10 | } 11 | 12 | curve = lovr.math.newCurve(points) 13 | slice = curve:slice(0, 1) 14 | for i = 1, #points do 15 | expect({ curve:getPoint(i) }).to.equal({ slice:getPoint(i) }) 16 | end 17 | end) 18 | end) 19 | 20 | group('vectors', function() 21 | test('temporary vector generation errors', function() 22 | local v = vec3() 23 | lovr.math.drain() 24 | expect(function() v:length() end).to.fail.with('Attempt to use a temporary vector from a previous frame') 25 | end) 26 | end) 27 | end) 28 | -------------------------------------------------------------------------------- /test/lovr/physics.lua: -------------------------------------------------------------------------------- 1 | group('physics', function() 2 | local world 3 | before(function() world = lovr.physics.newWorld() end) 4 | 5 | group('World', function() 6 | test('distant colliders', function() 7 | local c1 = world:newBoxCollider(1e8, 0, 0) 8 | local c2 = world:newBoxCollider(1e8, 0, 0) 9 | world:update(1) 10 | end) 11 | 12 | group(':raycast', function() 13 | test('zero-shape Collider', function() 14 | collider = world:newCollider(0, 0, 0) 15 | world:raycast(0, 10, 0, 0, -10, 0) 16 | end) 17 | end) 18 | end) 19 | 20 | group('Collider', function() 21 | test(':setEnabled', function() 22 | local c = world:newCollider() 23 | expect(c:isEnabled()).to.equal(true) 24 | c:setEnabled() 25 | expect(c:isEnabled()).to.equal(false) 26 | c:setEnabled() 27 | expect(c:isEnabled()).to.equal(false) 28 | 29 | expect(c:getJoints()).to.equal({}) 30 | expect(c:getShapes()).to.equal({}) 31 | 32 | c:setUserData(7) 33 | expect(c:getUserData()).to.equal(7) 34 | 35 | expect(c:isKinematic()).to.equal(false) 36 | c:setKinematic(true) 37 | expect(c:isKinematic()).to.equal(true) 38 | c:setKinematic(false) 39 | 40 | expect(c:isSensor()).to.equal(false) 41 | c:setSensor(true) 42 | expect(c:isSensor()).to.equal(true) 43 | 44 | expect(c:isContinuous()).to.equal(false) 45 | c:setContinuous(true) 46 | expect(c:isContinuous()).to.equal(true) 47 | 48 | expect(c:getGravityScale()).to.equal(1.0) 49 | c:setGravityScale(2.0) 50 | expect(c:getGravityScale()).to.equal(2.0) 51 | 52 | expect(c:isSleepingAllowed()).to.equal(true) 53 | c:setSleepingAllowed(false) 54 | expect(c:isSleepingAllowed()).to.equal(false) 55 | 56 | expect(c:isAwake()).to.equal(false) 57 | c:setAwake(true) 58 | expect(c:isAwake()).to.equal(false) 59 | 60 | c:setMass(10) 61 | expect(c:getMass()).to.equal(10) 62 | c:resetMassData() 63 | 64 | c:setDegreesOfFreedom('z', 'x') 65 | expect({ c:getDegreesOfFreedom() }).to.equal({ 'z', 'x' }) 66 | end) 67 | end) 68 | 69 | group('Shape', function() 70 | test(':raycast', function() 71 | shape = lovr.physics.newBoxShape(2, 10, 2) 72 | expect(shape:raycast(-10, 10, 0, 10, 10, 0)).to_not.be.truthy() 73 | expect({ shape:raycast(-10, 0, 0, 10, 0, 0) }).to.equal({ -1, 0, 0, -1, 0, 0 }, 1e-6) 74 | expect({ shape:raycast(-10, 4, 0, 10, 4, 0) }).to.equal({ -1, 4, 0, -1, 0, 0 }, 1e-6) 75 | shape:setOffset(0, 0, 0, math.pi / 2, 0, 0, 1) 76 | expect({ shape:raycast(-10, 0, 0, 10, 0, 0) }).to.equal({ -5, 0, 0, -1, 0, 0 }, 1e-6) 77 | expect(shape:raycast(-10, 4, 0, 10, 4, 0)).to.equal(nil) 78 | 79 | collider = world:newCollider() 80 | collider:setPosition(100, 100, 100) 81 | collider:setOrientation(math.pi, 0, 1, 0) 82 | collider:addShape(shape) 83 | expect(shape:raycast(-10, 0, 0, 10, 0, 0)).to.equal(nil) 84 | expect({ shape:raycast(-500, 100, 100, 500, 100, 100) }).to.equal({ 95, 100, 100, -1, 0, 0 }, 1e-6) 85 | end) 86 | 87 | group('ConvexShape', function() 88 | test('tetrahedron', function() 89 | shape = lovr.physics.newConvexShape({ 1, 1, 0, -1, 1, 0, 0, 0, 0, 0, 0, 1 }) 90 | expect(shape:getPointCount()).to.equal(4) 91 | expect(shape:getFaceCount()).to.equal(4) 92 | expect(shape:getFace(1)).to.equal({ 1, 2, 3 }) 93 | expect(shape:getFace(2)).to.equal({ 2, 4, 3 }) 94 | expect(shape:getFace(3)).to.equal({ 4, 1, 3 }) 95 | expect(shape:getFace(4)).to.equal({ 1, 4, 2 }) 96 | end) 97 | 98 | if lovr.graphics then 99 | test('from Mesh', function() 100 | mesh = lovr.graphics.newMesh({ 101 | { 1, 1, 0 }, 102 | { -1, 1, 0 }, 103 | { 0, 0, 0 }, 104 | { 0, 0, 1 } 105 | }) 106 | mesh:setIndices({ 1, 2, 3, 1, 3, 4, 1, 2, 4, 2, 3, 4 }) 107 | shape = lovr.physics.newConvexShape(mesh) 108 | expect(shape:getPointCount()).to.equal(4) 109 | end) 110 | end 111 | 112 | test('scale', function() 113 | shape = lovr.physics.newConvexShape({ 1, 1, 0, -1, 1, 0, 0, 0, 0, 0, 0, 1 }, 2) 114 | expect(shape:getScale()).to.equal(2) 115 | end) 116 | end) 117 | 118 | group('MeshShape', function() 119 | if lovr.graphics then 120 | test('from Mesh', function() 121 | mesh = lovr.graphics.newMesh({ 122 | { 0, .4, 0 }, 123 | { -.5, -.4, 0 }, 124 | { .5, -.4, 0 } 125 | }) 126 | 127 | shape = lovr.physics.newMeshShape(mesh) 128 | end) 129 | end 130 | 131 | test('scale', function() 132 | shape = lovr.physics.newMeshShape({ 133 | { 0, .4, 0 }, 134 | { -.5, -.4, 0 }, 135 | { .5, -.4, 0 } 136 | }, { 1, 2, 3 }, 5) 137 | 138 | expect(shape:getScale()).to.equal(5) 139 | end) 140 | end) 141 | end) 142 | end) 143 | -------------------------------------------------------------------------------- /test/lovr/thread.lua: -------------------------------------------------------------------------------- 1 | group('thread', function() 2 | group('Thread', function() 3 | test(':start', function() 4 | local thread = lovr.thread.newThread([[ 5 | require('lovr.data') 6 | assert((...):type() == 'Blob') 7 | ]]) 8 | 9 | thread:start(lovr.data.newBlob(1)) 10 | thread:wait() 11 | end) 12 | end) 13 | 14 | group('Channel', function() 15 | test('push/pop', function() 16 | local channel = lovr.thread.getChannel('test') 17 | local data = { 123, a = 1, b = channel, c = { 321, a = channel }, d = {} } 18 | channel:push(data) 19 | expect(channel:pop()).to.equal(data) 20 | 21 | local t = { 123, a = 1 } 22 | t.t = t 23 | expect(function() channel:push(t) end).to.fail() 24 | end) 25 | end) 26 | end) 27 | -------------------------------------------------------------------------------- /test/lovr/timer.lua: -------------------------------------------------------------------------------- 1 | group('timer', function() 2 | test('getTime', function() 3 | expect(lovr.timer.getTime()).to.be.a('number') 4 | end) 5 | end) 6 | -------------------------------------------------------------------------------- /test/main.lua: -------------------------------------------------------------------------------- 1 | local lust = require 'lust' 2 | 3 | group, test, expect, before = lust.describe, lust.it, lust.expect, lust.before 4 | 5 | function lovr.load() 6 | require('lovr/' .. (arg[1] or 'init')) 7 | lovr.event.quit(lust.errors > 0 and 1 or 0) 8 | end 9 | 10 | function lovr.errhand(message) 11 | print(message) 12 | end 13 | --------------------------------------------------------------------------------