├── .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 | [](https://github.com/bjornbytes/lovr/actions/workflows/build.yml)
10 | [](https://github.com/bjornbytes/lovr/releases)
11 | [](https://lovr.org/matrix)
12 | [](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 |
--------------------------------------------------------------------------------