Merge branch 'dev' into master

This commit is contained in:
Bjorn 2023-08-21 14:59:53 -07:00 committed by GitHub
commit f8f9c833b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
110 changed files with 12329 additions and 6559 deletions

View File

@ -13,7 +13,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
submodules: 'recursive'
- name: Configure
run: cmake -B build
shell: cmd
@ -31,16 +31,16 @@ jobs:
linux:
name: Linux
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
steps:
- name: Update Packages
run: sudo apt update
- name: Install Packages
run: sudo apt install -y xorg-dev libxcb-glx0-dev libfuse2
run: sudo apt install -y xorg-dev libxcb-glx0-dev libfuse2 libcurl4-openssl-dev libssl-dev
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
submodules: 'recursive'
- name: Init
run: cmake -B build -D LOVR_BUILD_BUNDLE=ON
- name: Build
@ -68,7 +68,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
submodules: 'recursive'
- name: Init
run: >
mkdir build &&
@ -116,7 +116,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
submodules: 'recursive'
- name: Init
run: cmake -B build -D LOVR_BUILD_BUNDLE=ON
- name: Build

6
.gitmodules vendored
View File

@ -16,9 +16,6 @@
[submodule "deps/openxr"]
path = deps/openxr
url = https://github.com/khronosgroup/openxr-sdk
[submodule "deps/oculus-openxr"]
path = deps/oculus-openxr
url = https://github.com/lovr-org/ovr_openxr_mobile_sdk
[submodule "deps/glslang"]
path = deps/glslang
url = https://github.com/bjornbytes/glslang
@ -28,3 +25,6 @@
[submodule "deps/pico-openxr"]
path = deps/pico-openxr
url = https://github.com/lovr-org/pico_openxr_sdk
[submodule "plugins/lua-enet"]
path = plugins/lua-enet
url = https://github.com/bjornbytes/lua-enet

View File

@ -1,5 +1,6 @@
cmake_minimum_required(VERSION 3.1.0)
cmake_policy(SET CMP0063 NEW)
cmake_policy(SET CMP0079 NEW)
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "Minimum macOS deployment version")
project(lovr)
@ -16,6 +17,8 @@ option(LOVR_ENABLE_SYSTEM "Enable the system module" ON)
option(LOVR_ENABLE_THREAD "Enable the thread module" ON)
option(LOVR_ENABLE_TIMER "Enable the timer module" ON)
option(LOVR_ENABLE_UTF8 "Enable the utf8 module" ON)
option(LOVR_USE_GLFW "Use GLFW for desktop windows" ON)
option(LOVR_USE_LUAJIT "Use LuaJIT instead of Lua" ON)
option(LOVR_USE_GLSLANG "Use glslang to compile GLSL shaders" ON)
@ -23,7 +26,7 @@ option(LOVR_USE_VULKAN "Use the Vulkan renderer" ON)
option(LOVR_USE_WEBGPU "Use the WebGPU renderer" OFF)
option(LOVR_USE_OPENXR "Enable the OpenXR backend for the headset module" ON)
option(LOVR_USE_WEBXR "Enable the WebXR backend for the headset module" OFF)
option(LOVR_USE_DESKTOP "Enable the keyboard/mouse backend for the headset module" ON)
option(LOVR_USE_SIMULATOR "Enable the keyboard/mouse backend for the headset module" ON)
option(LOVR_USE_STEAM_AUDIO "Enable the Steam Audio spatializer (be sure to also set LOVR_STEAM_AUDIO_PATH)" OFF)
option(LOVR_USE_OCULUS_AUDIO "Enable the Oculus Audio spatializer (be sure to also set LOVR_OCULUS_AUDIO_PATH)" OFF)
option(LOVR_SANITIZE "Enable Address Sanitizer" OFF)
@ -64,7 +67,7 @@ elseif(MSVC)
add_compile_options(/MP)
elseif(ANDROID)
find_package(Java REQUIRED)
set(LOVR_USE_DESKTOP OFF)
set(LOVR_USE_SIMULATOR OFF)
if(LOVR_BUILD_EXE)
set(LOVR_BUILD_SHARED ON)
endif()
@ -102,10 +105,6 @@ endif()
# Lua
if(LOVR_USE_LUAJIT AND NOT EMSCRIPTEN)
# Required to make luajit compile without breaking linker cache on macOS Big Sur. Also, is a better,
# newer GC so enable it on all builds.
set(LUAJIT_ENABLE_GC64 ON)
if(LOVR_SYSTEM_LUA)
pkg_search_module(LUAJIT REQUIRED luajit)
include_directories(${LUAJIT_INCLUDE_DIRS})
@ -183,20 +182,17 @@ endif()
if(LOVR_USE_GLSLANG)
set(ENABLE_HLSL OFF CACHE BOOL "")
set(ENABLE_SPVREMAPPER OFF CACHE BOOL "")
set(ENABLE_GLSLANG_BINARIES ON CACHE BOOL "")
set(ENABLE_GLSLANG_INSTALL OFF CACHE BOOL "")
set(ENABLE_OPT OFF CACHE BOOL "")
set(ENABLE_CTEST OFF CACHE BOOL "")
set(BUILD_EXTERNAL OFF CACHE BOOL "")
set(BUILD_SHARED_LIBS OFF)
include_directories(deps/glslang/glslang/Include deps/glslang/StandAlone)
if(NOT EMSCRIPTEN)
set(ENABLE_GLSLANG_BINARIES ON CACHE BOOL "")
endif()
include_directories(deps/glslang/glslang/Include deps/glslang/glslang/Public)
add_subdirectory(deps/glslang glslang)
add_library(lovr_glslang
"${CMAKE_CURRENT_SOURCE_DIR}/deps/glslang/StandAlone/ResourceLimits.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/deps/glslang/StandAlone/resource_limits_c.cpp"
)
target_link_libraries(lovr_glslang PUBLIC glslang)
set(LOVR_GLSLANG lovr_glslang SPIRV)
set(LOVR_GLSLANG glslang SPIRV glslang-default-resource-limits)
endif()
# Vulkan
@ -206,16 +202,13 @@ endif()
# OpenXR
if(LOVR_ENABLE_HEADSET AND LOVR_USE_OPENXR)
include_directories(deps/openxr/include)
if(LOVR_SYSTEM_OPENXR AND NOT ANDROID)
pkg_search_module(OPENXR openxr)
if(NOT OPENXR_FOUND)
message(FATAL_ERROR "OpenXR not found.")
endif()
pkg_search_module(OPENXR openxr REQUIRED)
include_directories(${OPENXR_INCLUDE_DIRS})
set(LOVR_OPENXR ${OPENXR_LIBRARIES})
else()
set(DYNAMIC_LOADER ON CACHE BOOL "")
include_directories(deps/openxr/include)
add_subdirectory(deps/openxr openxr)
set(LOVR_OPENXR openxr_loader)
endif()
@ -296,25 +289,6 @@ endif()
# LÖVR
# Plugins
set(LOVR 1)
link_libraries(${LOVR_LUA})
if(NOT DEFINED LOVR_PLUGINS)
file(GLOB LOVR_PLUGINS ${CMAKE_SOURCE_DIR}/plugins/*)
endif()
foreach(PLUGIN_PATH ${LOVR_PLUGINS})
if(IS_DIRECTORY ${PLUGIN_PATH} AND EXISTS ${PLUGIN_PATH}/CMakeLists.txt)
get_filename_component(PLUGIN "${PLUGIN_PATH}" NAME)
add_subdirectory(${PLUGIN_PATH} "${CMAKE_CURRENT_BINARY_DIR}/plugins/${PLUGIN}")
get_directory_property(PLUGIN_TARGETS DIRECTORY ${PLUGIN_PATH} DEFINITION LOVR_PLUGIN_TARGETS)
if(NOT PLUGIN_TARGETS)
get_directory_property(PLUGIN_TARGETS DIRECTORY ${PLUGIN_PATH} BUILDSYSTEM_TARGETS)
endif()
set_target_properties(${PLUGIN_TARGETS} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/plugins/${PLUGIN}")
list(APPEND ALL_PLUGIN_TARGETS ${PLUGIN_TARGETS})
endif()
endforeach()
set(LOVR_SRC
src/core/fs.c
src/core/zip.c
@ -329,7 +303,6 @@ endif()
if(LOVR_BUILD_SHARED)
add_library(lovr SHARED ${LOVR_SRC})
target_compile_definitions(lovr PRIVATE LOVR_BUILDING_SHARED)
elseif(LOVR_BUILD_EXE)
add_executable(lovr ${LOVR_SRC})
else()
@ -368,7 +341,7 @@ target_link_libraries(lovr
${EMSCRIPTEN_LINKER_FLAGS}
)
if (LOVR_SANITIZE)
if(LOVR_SANITIZE)
set(LOVR_SANITIZE_FLAGS "-fsanitize=address,undefined" "-O1" "-fno-omit-frame-pointer")
target_compile_options(lovr PRIVATE ${LOVR_SANITIZE_FLAGS})
target_link_options(lovr PRIVATE ${LOVR_SANITIZE_FLAGS})
@ -458,9 +431,9 @@ if(LOVR_ENABLE_GRAPHICS)
src/api/l_graphics_shader.c
src/api/l_graphics_material.c
src/api/l_graphics_font.c
src/api/l_graphics_mesh.c
src/api/l_graphics_model.c
src/api/l_graphics_readback.c
src/api/l_graphics_tally.c
src/api/l_graphics_pass.c
)
@ -474,8 +447,8 @@ if(LOVR_ENABLE_GRAPHICS)
endif()
if(LOVR_USE_WEBGPU)
target_compile_definitions(lovr PRIVATE LOVR_WGPU)
target_sources(lovr PRIVATE src/core/gpu_webgpu.c)
target_compile_definitions(lovr PRIVATE LOVR_WEBGPU)
target_sources(lovr PRIVATE src/core/gpu_web.c)
endif()
function(compile_shaders)
@ -489,13 +462,14 @@ if(LOVR_ENABLE_GRAPHICS)
message(FATAL_ERROR "Need glslangValidator installed or LOVR_USE_GLSLANG enabled")
endif()
endif()
set(LOVR_GLSL "${CMAKE_CURRENT_SOURCE_DIR}/etc/shaders/lovr.glsl")
file(GLOB shader_files "etc/shaders/*.${ARGV0}")
foreach(shader_file ${shader_files})
string(REGEX MATCH "([^\/]+)\.${ARGV0}" shader ${shader_file})
string(REPLACE ".${ARGV0}" "" shader ${shader})
add_custom_command(
OUTPUT ${shader_file}.h
DEPENDS ${shader_file}
DEPENDS ${shader_file} ${LOVR_GLSL}
COMMAND
${GLSLANG_VALIDATOR}
--quiet
@ -529,9 +503,9 @@ if(LOVR_ENABLE_HEADSET)
target_compile_definitions(lovr PRIVATE LOVR_USE_WEBXR)
target_sources(lovr PRIVATE src/modules/headset/headset_webxr.c)
endif()
if(LOVR_USE_DESKTOP)
target_compile_definitions(lovr PRIVATE LOVR_USE_DESKTOP)
target_sources(lovr PRIVATE src/modules/headset/headset_desktop.c)
if(LOVR_USE_SIMULATOR)
target_compile_definitions(lovr PRIVATE LOVR_USE_SIMULATOR)
target_sources(lovr PRIVATE src/modules/headset/headset_simulator.c)
endif()
else()
target_compile_definitions(lovr PRIVATE LOVR_DISABLE_HEADSET)
@ -540,9 +514,6 @@ endif()
if(LOVR_ENABLE_MATH)
target_sources(lovr PRIVATE
src/modules/math/math.c
src/modules/math/curve.c
src/modules/math/pool.c
src/modules/math/randomGenerator.c
src/api/l_math.c
src/api/l_math_curve.c
src/api/l_math_randomGenerator.c
@ -597,6 +568,43 @@ else()
target_compile_definitions(lovr PRIVATE LOVR_DISABLE_TIMER)
endif()
if(LOVR_ENABLE_UTF8)
target_sources(lovr PRIVATE src/lib/lua/lutf8lib.c)
else()
target_compile_definitions(lovr PRIVATE LOVR_DISABLE_UTF8)
endif()
# Plugins
if(NOT EMSCRIPTEN)
set(LOVR 1)
if(NOT DEFINED LOVR_PLUGINS)
file(GLOB LOVR_PLUGINS ${CMAKE_SOURCE_DIR}/plugins/*)
endif()
function(glob_targets dir targets)
get_directory_property(subdirectories DIRECTORY "${dir}" SUBDIRECTORIES)
foreach(subdirectory IN LISTS subdirectories)
glob_targets("${subdirectory}" ${targets})
endforeach()
get_directory_property(dir_targets DIRECTORY "${dir}" BUILDSYSTEM_TARGETS)
set(${targets} ${${targets}} ${dir_targets} PARENT_SCOPE)
endfunction()
foreach(PLUGIN_PATH ${LOVR_PLUGINS})
if(IS_DIRECTORY ${PLUGIN_PATH} AND EXISTS ${PLUGIN_PATH}/CMakeLists.txt)
get_filename_component(PLUGIN "${PLUGIN_PATH}" NAME)
add_subdirectory(${PLUGIN_PATH} "${CMAKE_CURRENT_BINARY_DIR}/plugins/${PLUGIN}")
get_directory_property(PLUGIN_TARGETS DIRECTORY ${PLUGIN_PATH} DEFINITION LOVR_PLUGIN_TARGETS)
if(NOT PLUGIN_TARGETS)
glob_targets(${PLUGIN_PATH} PLUGIN_TARGETS)
endif()
set_target_properties(${PLUGIN_TARGETS} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/plugins/${PLUGIN}")
foreach(PLUGIN_TARGET ${PLUGIN_TARGETS})
target_link_libraries(${PLUGIN_TARGET} ${LOVR_LUA})
endforeach()
list(APPEND ALL_PLUGIN_TARGETS ${PLUGIN_TARGETS})
endif()
endforeach()
endif()
# Resources
file(GLOB LOVR_RESOURCES "etc/*.ttf" "etc/*.lua" "etc/shaders/*.glsl")
foreach(path ${LOVR_RESOURCES})
@ -632,7 +640,7 @@ endfunction()
if(WIN32)
target_sources(lovr PRIVATE src/core/os_win32.c)
target_sources(lovr PRIVATE etc/lovr.rc)
if (MSVC)
if(MSVC)
set_target_properties(lovr PROPERTIES COMPILE_FLAGS /wd4244)
# Excuse anonymous union for type punning
set_source_files_properties(src/util.c src/modules/graphics/graphics.c PROPERTIES COMPILE_FLAGS /wd4116)
@ -770,11 +778,6 @@ elseif(ANDROID)
configure_file(${PICO_LOADER} "raw/lib/${ANDROID_ABI}/libopenxr_loader_pico.so" COPYONLY)
endif()
set(OCULUS_LOADER "${CMAKE_CURRENT_SOURCE_DIR}/deps/oculus-openxr/Libs/Android/${ANDROID_ABI}/Release/libopenxr_loader.so")
if(EXISTS ${OCULUS_LOADER})
configure_file(${OCULUS_LOADER} "raw/lib/${ANDROID_ABI}/libopenxr_loader_oculus.so" COPYONLY)
endif()
configure_file("${ANDROID_NDK}/toolchains/llvm/prebuilt/${ANDROID_HOST_TAG}/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so" "raw/lib/${ANDROID_ABI}/libc++_shared.so" COPYONLY)
endif()
@ -831,11 +834,20 @@ elseif(ANDROID)
# Copy plugin libraries to lib folder before packaging APK
foreach(target ${ALL_PLUGIN_TARGETS})
add_custom_command(TARGET move_files POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:${target}>
raw/lib/${ANDROID_ABI}/$<TARGET_FILE_NAME:${target}>
)
get_target_property(TARGET_TYPE ${target} TYPE)
if(${TARGET_TYPE} STREQUAL "SHARED_LIBRARY")
add_custom_command(TARGET move_files POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:${target}>
raw/lib/${ANDROID_ABI}/$<TARGET_SONAME_FILE_NAME:${target}>
)
elseif(${TARGET_TYPE} STREQUAL "MODULE_LIBRARY")
add_custom_command(TARGET move_files POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:${target}>
raw/lib/${ANDROID_ABI}/$<TARGET_FILE_NAME:${target}>
)
endif()
endforeach()
add_dependencies(buildAPK move_files)
endif()

View File

@ -8,6 +8,7 @@ config = {
glfw = true,
luajit = false,
glslang = true,
utf8 = true,
modules = {
audio = true,
data = true,
@ -22,7 +23,7 @@ config = {
timer = true
},
headsets = {
desktop = true,
simulator = true,
openxr = false,
webxr = false
},
@ -143,6 +144,8 @@ if target == 'linux' then
end
if target == 'wasm' then
cc = 'emcc'
cxx = 'em++'
cflags += '-std=gnu11'
cflags += '-D_POSIX_C_SOURCE=200809L'
lflags += '-s FORCE_FILESYSTEM'
@ -245,7 +248,7 @@ end
if config.modules.graphics and config.glslang then
cflags_graphics += '-Ideps/glslang/glslang/Include'
cflags_graphics += '-Ideps/glslang/StandAlone'
cflags_graphics += '-Ideps/glslang/glslang/Public'
cflags += '-DLOVR_USE_GLSLANG'
lflags += '-lglslang'
@ -254,7 +257,6 @@ if config.modules.graphics and config.glslang then
glslang_cflags += '-fno-rtti'
glslang_cflags += '-Ideps/glslang'
glslang_lflags += '-shared'
glslang_lflags += '-static-libstdc++'
glslang_src += 'deps/glslang/OGLCompilersDLL/*.cpp'
glslang_src += 'deps/glslang/glslang/CInterface/*.cpp'
glslang_src += 'deps/glslang/glslang/MachineIndependent/*.cpp'
@ -268,8 +270,8 @@ if config.modules.graphics and config.glslang then
glslang_src += 'deps/glslang/SPIRV/SpvPostProcess.cpp'
glslang_src += 'deps/glslang/SPIRV/InReadableOrder.cpp'
glslang_src += 'deps/glslang/SPIRV/CInterface/spirv_c_interface.cpp'
glslang_src += 'deps/glslang/StandAlone/resource_limits_c.cpp'
glslang_src += 'deps/glslang/StandAlone/ResourceLimits.cpp'
glslang_src += 'deps/glslang/glslang/ResourceLimits/resource_limits_c.cpp'
glslang_src += 'deps/glslang/glslang/ResourceLimits/ResourceLimits.cpp'
tup.foreach_rule(glslang_src, '^ CC glslang/%b^ $(cc) $(flags) $(glslang_cflags) -c %f -o %o', '.obj/glslang/%B.o')
tup.rule('.obj/glslang/*.o', '^ LD %o^ $(cxx) $(flags) -o %o %f $(glslang_lflags)', lib('glslang'))
@ -282,7 +284,7 @@ if config.modules.data then
msdfgen_cflags += '-fPIC'
msdfgen_src += 'deps/msdfgen/core/*.cpp'
tup.foreach_rule(msdfgen_src, '^ CC msdfgen/%b^ $(cxx) $(flags) $(msdfgen_cflags) -c %f -o %o', '.obj/msdfgen/%B.o')
tup.rule('.obj/msdfgen/*.o', '^ LD %o^ $(cxx) $(flags) -shared -static-libstdc++ -o %o %f', lib('msdfgen'))
tup.rule('.obj/msdfgen/*.o', '^ LD %o^ $(cxx) $(flags) -shared -o %o %f', lib('msdfgen'))
end
if config.modules.physics then
@ -321,6 +323,7 @@ if config.modules.physics then
-- ode
ode_cflags += '-fPIC'
ode_cflags += config.optimize and '-DdNODEBUG' or ''
ode_cflags += '-Wno-implicit-float-conversion'
ode_cflags += '-Wno-array-bounds'
ode_cflags += '-Wno-undefined-var-template'
@ -343,7 +346,7 @@ if config.modules.physics then
tup.foreach_rule(ode_c_src, '^ CC ode/%b^ $(cc) $(flags) $(ode_cflags) -c %f -o %o', '.obj/ode/%B.o')
tup.foreach_rule(ode_src, '^ CC ode/%b^ $(cxx) $(flags) $(ode_cflags) -c %f -o %o', '.obj/ode/%B.o')
tup.rule('.obj/ode/*.o', '^ LD %o^ $(cxx) $(flags) -shared -static-libstdc++ -o %o %f', lib('ode'))
tup.rule('.obj/ode/*.o', '^ LD %o^ $(cxx) $(flags) -shared -o %o %f', lib('ode'))
end
if config.headsets.openxr then
@ -416,9 +419,8 @@ end
for renderer, enabled in pairs(config.renderers) do
if enabled then
local code = renderer:gsub('vulkan', 'vk')
cflags += '-DLOVR_' .. code:upper()
src += 'src/core/gpu_' .. code .. '.c'
cflags += '-DLOVR_' .. ({ vulkan = 'VK', webgpu = 'WEBGPU' })[renderer]
src += 'src/core/gpu_' .. ({ vulkan = 'vk', webgpu = 'web' })[renderer] .. '.c'
end
end
@ -429,6 +431,12 @@ for spatializer, enabled in pairs(config.spatializers) do
end
end
if config.utf8 then
src += 'src/lib/lua/lutf8lib.c'
else
cflags += '-DLOVR_DISABLE_UTF8'
end
src += 'src/lib/stb/*.c'
src += (config.modules.audio or config.modules.data) and 'src/lib/miniaudio/*.c' or nil
src += config.modules.data and 'src/lib/jsmn/*.c' or nil

2
deps/glslang vendored

@ -1 +1 @@
Subproject commit de4daf201a2acd83ad523b4c6148e4d8e5b7dab4
Subproject commit cdafe1c6028e01d669a02576b291e111b5e08d78

2
deps/luajit vendored

@ -1 +1 @@
Subproject commit 05db1d35d8a35f74ce40d0ba5e387717ad91b24d
Subproject commit 113eb1d172dfb6b91f67e9d0478a113a3ccced80

1
deps/oculus-openxr vendored

@ -1 +0,0 @@
Subproject commit 56205f0f2c3358896fdb5c8da87736bf0f20705d

2
deps/ode vendored

@ -1 +1 @@
Subproject commit dd3e3fc1f16a6746c0bfaa343899cb3a418b0cab
Subproject commit 3f3b4da5dc81a623dc80315369093d0f1f9656f7

2
deps/openxr vendored

@ -1 +1 @@
Subproject commit 1ca7bec6b531185530c9b4f1e7a50e1fd55e7641
Subproject commit 58a00cf85c39ad5ec4dc43a769624e420c06179a

View File

@ -8,10 +8,7 @@ import android.os.Build;
public class Activity extends NativeActivity {
static {
if (Build.MANUFACTURER.contains("Oculus")) {
Log.d("LOVR", "Using Oculus OpenXR Loader");
System.loadLibrary("openxr_loader_oculus");
} else if (Build.MANUFACTURER.contains("Pico")) {
if (Build.MANUFACTURER.contains("Pico")) {
Log.d("LOVR", "Using Pico OpenXR Loader");
System.loadLibrary("openxr_loader_pico");
} else {

View File

@ -68,6 +68,8 @@ function lovr.boot()
if hasConf then confOk, confError = pcall(require, 'conf') end
if confOk and lovr.conf then confOk, confError = pcall(lovr.conf, conf) end
conf.graphics.debug = arg['--graphics-debug'] or conf.graphics.debug
lovr._setConf(conf)
lovr.filesystem.setIdentity(conf.identity, conf.saveprecedence)
@ -92,10 +94,6 @@ function lovr.boot()
if lovr.headset then
lovr.headset.start()
if lovr.headset.getDriver() == 'desktop' then
lovr.mirror = nil
end
end
lovr.handlers = setmetatable({}, { __index = lovr })
@ -108,8 +106,8 @@ function lovr.run()
if lovr.timer then lovr.timer.step() end
if lovr.load then lovr.load(arg) end
return function()
if lovr.system then lovr.system.pollEvents() end
if lovr.event then
lovr.event.pump()
for name, a, b, c, d in lovr.event.poll() do
if name == 'restart' then
local cookie = lovr.restart and lovr.restart()
@ -125,21 +123,12 @@ function lovr.run()
if lovr.headset then dt = lovr.headset.update() end
if lovr.update then lovr.update(dt) end
if lovr.graphics then
if lovr.headset then
local pass = lovr.headset.getPass()
if pass then
local skip = lovr.draw and lovr.draw(pass)
if not skip then lovr.graphics.submit(pass) end
end
end
if lovr.system.isWindowOpen() then
if lovr.mirror then
local pass = lovr.graphics.getWindowPass()
local skip = not pass or lovr.mirror(pass)
if not skip then lovr.graphics.submit(pass) end
end
lovr.graphics.present()
end
local headset = lovr.headset and lovr.headset.getPass()
if headset and (not lovr.draw or lovr.draw(headset)) then headset = nil end
local window = lovr.graphics.getWindowPass()
if window and (not lovr.mirror or lovr.mirror(window)) then window = nil end
lovr.graphics.submit(headset, window)
lovr.graphics.present()
end
if lovr.headset then lovr.headset.submit() end
if lovr.math then lovr.math.drain() end
@ -151,8 +140,6 @@ function lovr.mirror(pass)
local texture = lovr.headset.getTexture()
if texture then
pass:fill(texture)
else
return true
end
else
return lovr.draw and lovr.draw(pass)
@ -160,12 +147,13 @@ function lovr.mirror(pass)
end
local function formatTraceback(s)
return s:gsub('\n[^\n]+$', ''):gsub('\t', ''):gsub('stack traceback', '\nStack')
return s:gsub('\n[^\n]+$', ''):gsub('\t', ''):gsub('stack traceback:', '\nStack:\n')
end
function lovr.errhand(message)
message = tostring(message) .. formatTraceback(debug.traceback('', 4))
print('Error:\n' .. message)
message = 'Error:\n\n' .. tostring(message) .. formatTraceback(debug.traceback('', 4))
print(message)
if not lovr.graphics or not lovr.graphics.isInitialized() then
return function() return 1 end
@ -173,39 +161,43 @@ function lovr.errhand(message)
if lovr.audio then lovr.audio.stop() end
local scale = .35
local font = lovr.graphics.getDefaultFont()
local wrap = .7 * font:getPixelDensity()
local lines = font:getLines(message, wrap)
local width = math.min(font:getWidth(message), wrap) * scale
local height = .8 + #lines * font:getHeight() * scale
local x = -width / 2
local y = math.min(height / 2, 10)
local z = -10
lovr.graphics.setBackgroundColor(.11, .10, .14)
font:setPixelDensity()
local function render(pass)
pass:setColor(.95, .95, .95)
pass:text('Error', x, y, z, scale * 1.6, 0, 0, 0, 0, nil, 'left', 'top')
pass:text(message, x, y - .8, z, scale, 0, 0, 0, 0, wrap, 'left', 'top')
if not lovr.headset or lovr.headset.getPassthrough() == 'opaque' then
lovr.graphics.setBackgroundColor(.11, .10, .14)
else
lovr.graphics.setBackgroundColor(0, 0, 0, 0)
end
local font = lovr.graphics.getDefaultFont()
return function()
lovr.event.pump()
lovr.system.pollEvents()
for name, a in lovr.event.poll() do
if name == 'quit' then return a or 1
elseif name == 'restart' then return 'restart', lovr.restart and lovr.restart()
elseif name == 'keypressed' and a == 'f5' then lovr.event.restart() end
elseif name == 'keypressed' and a == 'f5' then lovr.event.restart()
elseif name == 'keypressed' and a == 'escape' then lovr.event.quit() end
end
if lovr.headset and lovr.headset.getDriver() ~= 'desktop' then
lovr.headset.update()
local pass = lovr.headset.getPass()
if pass then
render(pass)
font:setPixelDensity()
local scale = .35
local font = lovr.graphics.getDefaultFont()
local wrap = .7 * font:getPixelDensity()
local lines = font:getLines(message, wrap)
local width = math.min(font:getWidth(message), wrap) * scale
local height = .8 + #lines * font:getHeight() * scale
local x = -width / 2
local y = math.min(height / 2, 10)
local z = -10
pass:setColor(.95, .95, .95)
pass:text(message, x, y, z, scale, 0, 0, 0, 0, wrap, 'left', 'top')
lovr.graphics.submit(pass)
lovr.headset.submit()
end
@ -214,11 +206,24 @@ function lovr.errhand(message)
if lovr.system.isWindowOpen() then
local pass = lovr.graphics.getWindowPass()
if pass then
render(pass)
local w, h = lovr.system.getWindowDimensions()
pass:setProjection(1, lovr.math.mat4():orthographic(0, w, 0, h, -1, 1))
font:setPixelDensity(1)
local scale = .6
local wrap = w * .8 / scale
local width = math.min(font:getWidth(message), wrap) * scale
local x = w / 2 - width / 2
pass:setColor(.95, .95, .95)
pass:text(message, x, h / 2, 0, scale, 0, 0, 0, 0, wrap, 'left', 'middle')
lovr.graphics.submit(pass)
lovr.graphics.present()
end
end
lovr.math.drain()
end
end

View File

@ -1,4 +1,4 @@
float monkey_size[3] = { 1.367188f, 0.984375f, 0.851563f };
float monkey_bounds[6] = { 0.000000f, 0.000000f, 0.000000f, 0.683594f, 0.492188f, 0.425781f };
float monkey_offset[3] = { -0.683594f, -0.492188f, -0.425781f };
uint8_t monkey_vertices[] = {

View File

@ -11,14 +11,16 @@ for i = 1, model:getMeshVertexCount(1) do
max.x, max.y, max.z = math.max(x, max.x), math.max(y, max.y), math.max(z, max.z)
end
local size = lovr.math.newVec3(max - min)
local scale = .5
min:mul(scale)
max:mul(scale)
size:mul(scale)
io.write(('float monkey_size[3] = { %ff, %ff, %ff };\n'):format(size:unpack()))
local center = Vec3(max + min):mul(.5)
local extent = Vec3(max - min)
local halfExtent = extent / 2
local bounds = { center[1], center[2], center[3], halfExtent[1], halfExtent[2], halfExtent[3] }
io.write(('float monkey_bounds[6] = { %ff, %ff, %ff, %ff, %ff, %ff };\n'):format(unpack(bounds)))
io.write(('float monkey_offset[3] = { %ff, %ff, %ff };\n'):format(min:unpack()))
io.write('\n')
@ -28,7 +30,7 @@ for i = 1, model:getMeshVertexCount(1) do
local position = vec3(x, y, z):mul(scale)
local normal = vec3(nx, ny, nz)
local qx, qy, qz = ((position - min) / size * 255 + .5):unpack()
local qx, qy, qz = ((position - min) / extent * 255 + .5):unpack()
local qnx, qny, qnz = ((normal / 2 + .5) * 255 + .5):unpack()
qx, qy, qz = math.floor(qx), math.floor(qy), math.floor(qz)

View File

@ -12,7 +12,10 @@ function lovr.load()
return
end
lovr.graphics.setBackgroundColor(0x20232c)
if lovr.headset.getPassthrough() == 'opaque' then
lovr.graphics.setBackgroundColor(0x20232c)
end
logo = lovr.graphics.newShader('unlit', 'logo')
end
@ -23,7 +26,7 @@ function lovr.draw(pass)
local titlePosition = 1.5 - padding
local subtitlePosition = titlePosition - font:getHeight() * .25 - padding
pass:setCullMode('back')
pass:setFaceCull(true)
pass:setShader(logo)
pass:plane(0, 2, -3)
pass:setShader()
@ -33,3 +36,9 @@ function lovr.draw(pass)
pass:setColor(.9, .9, .9, fade)
pass:text('No game :(', -.005, subtitlePosition, -3, .15, 0, 0, 1, 0, nil, 'center', 'top')
end
function lovr.keypressed(key)
if key == 'escape' then
lovr.event.quit()
end
end

View File

@ -7,9 +7,9 @@
#include "shaders/equirect.frag.h"
#include "shaders/fill.vert.h"
#include "shaders/fill_array.frag.h"
#include "shaders/fill_layer.frag.h"
#include "shaders/animator.comp.h"
#include "shaders/timewizard.comp.h"
#include "shaders/blender.comp.h"
#include "shaders/tallymerge.comp.h"
#include "shaders/logo.frag.h"
#include "shaders/lovr.glsl.h"

View File

@ -23,8 +23,8 @@ struct SkinVertex {
uint weights;
};
layout(set = 0, binding = 0) buffer restrict readonly VertexIn { ModelVertex vertexIn[]; };
layout(set = 0, binding = 1) buffer restrict writeonly VertexOut { ModelVertex vertexOut[]; };
layout(set = 0, binding = 0) buffer readonly VertexIn { ModelVertex vertexIn[]; };
layout(set = 0, binding = 1) buffer writeonly VertexOut { ModelVertex vertexOut[]; };
layout(set = 0, binding = 2) buffer restrict readonly VertexWeights { SkinVertex skin[]; };
layout(set = 0, binding = 3) uniform JointTransforms { mat4 joints[256]; };

65
etc/shaders/blender.comp Normal file
View File

@ -0,0 +1,65 @@
#version 460
#extension GL_GOOGLE_include_directive : require
#include "lovr.glsl"
layout(local_size_x = 32, local_size_x_id = 0) in;
layout(push_constant) uniform PushConstants {
uint baseVertex;
uint vertexCount;
uint blendShapeCount;
uint baseBlendVertex;
bool first;
};
struct ModelVertex {
float px, py, pz;
float nx, ny, nz;
float u, v;
uint color;
float tx, ty, tz;
};
struct BlendVertex {
float px, py, pz;
float nx, ny, nz;
float tx, ty, tz;
};
layout(set = 0, binding = 0) buffer restrict RawVertices { ModelVertex rawVertices[]; };
layout(set = 0, binding = 1) buffer restrict Vertices { ModelVertex vertices[]; };
layout(set = 0, binding = 2) buffer restrict readonly BlendVertices { BlendVertex blendVertex[]; };
layout(set = 0, binding = 3) uniform Weights { vec4 weights[16]; };
void lovrmain() {
if (GlobalThreadID.x >= vertexCount) return;
uint vertexIndex = baseVertex + GlobalThreadID.x;
uint blendVertexIndex = baseBlendVertex + GlobalThreadID.x;
ModelVertex vertex = first ? rawVertices[vertexIndex] : vertices[vertexIndex];
for (uint i = 0; i < blendShapeCount; i++, blendVertexIndex += vertexCount) {
float weight = weights[i / 4][i % 4];
if (weight == 0.) {
continue;
}
BlendVertex blendShape = blendVertex[blendVertexIndex];
vertex.px += blendShape.px * weight;
vertex.py += blendShape.py * weight;
vertex.pz += blendShape.pz * weight;
vertex.nx += blendShape.nx * weight;
vertex.ny += blendShape.ny * weight;
vertex.nz += blendShape.nz * weight;
vertex.tx += blendShape.tx * weight;
vertex.ty += blendShape.ty * weight;
vertex.tz += blendShape.tz * weight;
}
vertices[vertexIndex] = vertex;
}

View File

@ -6,8 +6,19 @@
vec4 lovrmain() {
vec3 dir = normalize(Normal);
float phi = acos(dir.y);
float theta = atan(dir.x, dir.z);
vec2 uv = vec2(.5 + theta / (2 * PI), phi / PI);
return Color * getPixel(ColorTexture, uv);
float theta1 = -atan(dir.x, dir.z) / (2. * PI);
float theta2 = fract(theta1);
float dx1 = dFdx(theta1);
float dy1 = dFdy(theta1);
float dx2 = dFdx(theta2);
float dy2 = dFdy(theta2);
float phi = acos(dir.y) / PI;
vec2 dx = vec2(abs(dx1) - 1e-5 < abs(dx2) ? dx1 : dx2, dFdx(phi));
vec2 dy = vec2(abs(dy1) - 1e-5 < abs(dy2) ? dy1 : dy2, dFdy(phi));
return Color * textureGrad(sampler2D(ColorTexture, Sampler), vec2(theta1, phi), dx, dy);
}

View File

@ -1,5 +1,6 @@
#version 460
#extension GL_EXT_multiview : require
#extension GL_EXT_samplerless_texture_functions : require
#extension GL_GOOGLE_include_directive : require
#include "lovr.glsl"
@ -7,5 +8,5 @@
layout(set = 1, binding = 1) uniform texture2DArray ArrayTexture;
vec4 lovrmain() {
return Color * getPixel(ArrayTexture, UV, ViewIndex);
return Color * getPixel(ArrayTexture, UV, min(ViewIndex, textureSize(ArrayTexture, 0).z - 1));
}

View File

@ -1,11 +0,0 @@
#version 460
#extension GL_EXT_multiview : require
#extension GL_GOOGLE_include_directive : require
#include "lovr.glsl"
layout(set = 1, binding = 1) uniform texture2DArray ArrayTexture;
vec4 lovrmain() {
return Color * getPixel(ArrayTexture, UV, 0);
}

View File

@ -1,7 +1,7 @@
#version 460
#extension GL_EXT_multiview : require
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require
#extension GL_GOOGLE_include_directive : require
#include "lovr.glsl"

View File

@ -1,6 +1,6 @@
// Flags
#ifndef GL_COMPUTE_SHADER
layout(constant_id = 1000) const float flag_pointSize = 1.f;
layout(constant_id = 1001) const uint flag_attachmentCount = 1;
layout(constant_id = 1002) const bool flag_passColor = true;
layout(constant_id = 1003) const bool flag_materialColor = true;
layout(constant_id = 1004) const bool flag_vertexColors = true;
@ -16,6 +16,7 @@ layout(constant_id = 1013) const bool flag_roughnessTexture = true;
layout(constant_id = 1014) const bool flag_ambientOcclusion = true;
layout(constant_id = 1015) const bool flag_clearcoatTexture = false;
layout(constant_id = 1016) const bool flag_tonemap = false;
#endif
// Resources
#ifndef GL_COMPUTE_SHADER
@ -27,17 +28,16 @@ struct Camera {
};
struct Draw {
mat4 transform;
mat4 normalMatrix;
mat4x3 transform;
vec4 color;
};
layout(set = 0, binding = 0) uniform Globals { vec2 Resolution; float Time; };
layout(set = 0, binding = 1) uniform CameraBuffer { Camera Cameras[6]; };
layout(set = 0, binding = 2) uniform DrawBuffer { Draw Draws[256]; };
layout(set = 0, binding = 2) uniform DrawBuffer { layout(row_major) Draw Draws[256]; };
layout(set = 0, binding = 3) uniform sampler Sampler;
layout(set = 1, binding = 0) uniform MaterialBuffer {
struct MaterialData {
vec4 color;
vec4 glow;
vec2 uvShift;
@ -50,7 +50,11 @@ layout(set = 1, binding = 0) uniform MaterialBuffer {
float occlusionStrength;
float normalScale;
float alphaCutoff;
} Material;
};
layout(set = 1, binding = 0) uniform MaterialBuffer {
MaterialData Material;
};
layout(set = 1, binding = 1) uniform texture2D ColorTexture;
layout(set = 1, binding = 2) uniform texture2D GlowTexture;
@ -72,27 +76,27 @@ layout(location = 14) in vec3 VertexTangent;
// Framebuffer
#ifdef GL_FRAGMENT_SHADER
layout(location = 0) out vec4 PixelColor[flag_attachmentCount];
layout(location = 0) out vec4 PixelColor;
#endif
// Varyings
#ifdef GL_VERTEX_SHADER
layout(location = 10) out vec3 PositionWorld;
layout(location = 11) out vec3 Normal;
layout(location = 12) out vec4 Color;
layout(location = 13) out vec2 UV;
layout(location = 12) out vec2 UV;
layout(location = 13) out vec4 Color;
layout(location = 14) out vec3 Tangent;
#endif
#ifdef GL_FRAGMENT_SHADER
layout(location = 10) in vec3 PositionWorld;
layout(location = 11) in vec3 Normal;
layout(location = 12) in vec4 Color;
layout(location = 13) in vec2 UV;
layout(location = 12) in vec2 UV;
layout(location = 13) in vec4 Color;
layout(location = 14) in vec3 Tangent;
#endif
// Macros
// Builtins
#ifdef GL_COMPUTE_SHADER
#define SubgroupCount gl_NumSubgroups
#define WorkgroupCount gl_NumWorkGroups
@ -102,49 +106,31 @@ layout(location = 14) in vec3 Tangent;
#define LocalThreadID gl_LocalInvocationID
#define LocalThreadIndex gl_LocalInvocationIndex
#else
#define BaseInstance gl_BaseInstance
#define BaseVertex gl_BaseVertex
#define ClipDistance gl_ClipDistance
#define CullDistance gl_CullDistance
#define PrimitiveID gl_PrimitiveID
#define ViewIndex gl_ViewIndex
#endif
#ifdef GL_VERTEX_SHADER
#define BaseInstance gl_BaseInstance
#define BaseVertex gl_BaseVertex
#define DrawIndex gl_DrawIndex
#define InstanceIndex (gl_InstanceIndex - gl_BaseInstance)
#define PointSize gl_PointSize
#define Position gl_Position
#define VertexIndex gl_VertexIndex
#endif
#ifdef GL_FRAGMENT_SHADER
#define FragCoord gl_FragCoord
#define FragDepth gl_FragDepth
#define FrontFacing gl_FrontFacing
#define PointCoord gl_PointCoord
#define PointSize gl_PointSize
#define Position gl_Position
#define PrimitiveID gl_PrimitiveID
#define SampleID gl_SampleID
#define SampleMaskIn gl_SampleMaskIn
#define SampleMask gl_SampleMask
#define SamplePosition gl_SamplePosition
#define VertexIndex gl_VertexIndex
#define ViewIndex gl_ViewIndex
#define DrawID gl_BaseInstance
#define Projection Cameras[ViewIndex].projection
#define View Cameras[ViewIndex].view
#define ViewProjection Cameras[ViewIndex].viewProjection
#define InverseProjection Cameras[ViewIndex].inverseProjection
#define Transform Draws[DrawID].transform
#define NormalMatrix mat3(Draws[DrawID].normalMatrix)
#define PassColor Draws[DrawID].color
#define ClipFromLocal (ViewProjection * Transform)
#define ClipFromWorld (ViewProjection)
#define ClipFromView (Projection)
#define ViewFromLocal (View * Transform)
#define ViewFromWorld (View)
#define ViewFromClip (InverseProjection)
#define WorldFromLocal (Transform)
#define WorldFromView (inverse(View))
#define WorldFromClip (inverse(ViewProjection))
#define CameraPositionWorld (-View[3].xyz * mat3(View))
#define DefaultPosition (ClipFromLocal * VertexPosition)
#define DefaultColor (flag_colorTexture ? (Color * getPixel(ColorTexture, UV)) : Color)
#endif
// Constants
@ -161,6 +147,49 @@ layout(location = 14) in vec3 Tangent;
#define var(x) layout(set = 2, binding = x)
#endif
#ifndef GL_COMPUTE_SHADER
#define Projection Cameras[ViewIndex].projection
#define View Cameras[ViewIndex].view
#define ViewProjection Cameras[ViewIndex].viewProjection
#define InverseProjection Cameras[ViewIndex].inverseProjection
#define CameraPositionWorld (-View[3].xyz * mat3(View))
#endif
#ifdef GL_VERTEX_SHADER
#define DrawID gl_BaseInstance
#define Transform mat4(Draws[DrawID].transform)
#define NormalMatrix (cofactor3(Draws[DrawID].transform))
#define PassColor Draws[DrawID].color
#define ClipFromLocal (ViewProjection * Transform)
#define ClipFromWorld (ViewProjection)
#define ClipFromView (Projection)
#define ViewFromLocal (View * Transform)
#define ViewFromWorld (View)
#define ViewFromClip (InverseProjection)
#define WorldFromLocal (Transform)
#define WorldFromView (inverse(View))
#define WorldFromClip (inverse(ViewProjection))
#define DefaultPosition (ClipFromLocal * VertexPosition)
mat3 cofactor3(mat4x3 m) {
return mat3(vec3(
(m[1][1] * m[2][2] - m[2][1] * m[1][2]),
-(m[1][0] * m[2][2] - m[2][0] * m[1][2]),
(m[1][0] * m[2][1] - m[2][0] * m[1][1])), vec3(
-(m[0][1] * m[2][2] - m[2][1] * m[0][2]),
(m[0][0] * m[2][2] - m[2][0] * m[0][2]),
-(m[0][0] * m[2][1] - m[2][0] * m[0][1])), vec3(
(m[0][1] * m[1][2] - m[1][1] * m[0][2]),
-(m[0][0] * m[1][2] - m[1][0] * m[0][2]),
(m[0][0] * m[1][1] - m[1][0] * m[0][1])
));
}
#endif
#ifdef GL_FRAGMENT_SHADER
#define DefaultColor (flag_colorTexture ? (Color * getPixel(ColorTexture, UV)) : Color)
#endif
// Helper for sampling textures using the default sampler set using Pass:setSampler
#ifndef GL_COMPUTE_SHADER
vec4 getPixel(texture2D t, vec2 uv) { return texture(sampler2D(t, Sampler), uv); }
@ -184,13 +213,13 @@ struct Surface {
vec3 f0;
vec3 diffuse;
vec3 emissive;
vec4 baseColor;
float metalness;
float roughness;
float roughness2;
float occlusion;
float clearcoat;
float clearcoatRoughness;
float alpha;
};
#define TangentMatrix getTangentMatrix()
@ -217,52 +246,99 @@ mat3 getTangentMatrix() {
}
}
void initSurface(out Surface surface) {
Surface newSurface() {
Surface surface;
surface.position = PositionWorld;
vec3 normal = normalize(Normal);
if (!FrontFacing) {
normal = -normal;
}
if (flag_normalMap) {
vec3 normalScale = vec3(Material.normalScale, Material.normalScale, 1.);
surface.normal = TangentMatrix * (normalize(getPixel(NormalTexture, UV).rgb * 2. - 1.) * normalScale);
} else {
surface.normal = normal;
}
surface.geometricNormal = normal;
surface.geometricNormal = normalize(Normal);
surface.normal = surface.geometricNormal;
surface.view = normalize(CameraPositionWorld - PositionWorld);
surface.reflection = reflect(-surface.view, normal);
surface.emissive = vec3(0.);
surface.baseColor = Color;
surface.metalness = 1.;
surface.roughness = 1.;
surface.occlusion = 1.;
surface.clearcoat = 0.;
surface.clearcoatRoughness = 0.;
return surface;
}
vec4 getMaterialBaseColor() {
vec4 color = Color;
if (flag_colorTexture) color *= getPixel(ColorTexture, UV);
return color;
}
surface.metalness = Material.metalness;
if (flag_metalnessTexture) surface.metalness *= getPixel(MetalnessTexture, UV).b;
vec3 getMaterialEmissive() {
vec3 emissive = Material.glow.rgb * Material.glow.a;
if (flag_glow && flag_glowTexture) emissive *= getPixel(GlowTexture, UV).rgb;
return emissive;
}
surface.f0 = mix(vec3(.04), color.rgb, surface.metalness);
surface.diffuse = mix(color.rgb, vec3(0.), surface.metalness);
float getMaterialMetalness() {
float metalness = Material.metalness;
if (flag_metalnessTexture) metalness *= getPixel(MetalnessTexture, UV).b;
return metalness;
}
surface.emissive = Material.glow.rgb * Material.glow.a;
if (flag_glow && flag_glowTexture) surface.emissive *= getPixel(GlowTexture, UV).rgb;
float getMaterialRoughness() {
float roughness = Material.roughness;
if (flag_roughnessTexture) roughness *= getPixel(RoughnessTexture, UV).g;
return roughness;
}
surface.roughness = Material.roughness;
if (flag_roughnessTexture) surface.roughness *= getPixel(RoughnessTexture, UV).g;
float getMaterialOcclusion() {
float occlusion = 1.;
if (flag_ambientOcclusion) occlusion *= getPixel(OcclusionTexture, UV).r * Material.occlusionStrength;
return occlusion;
}
float getMaterialClearcoat() {
float clearcoat = Material.clearcoat;
if (flag_clearcoatTexture) clearcoat *= getPixel(ClearcoatTexture, UV).r;
return clearcoat;
}
float getMaterialClearcoatRoughness() {
return Material.clearcoatRoughness;
}
Surface applyMaterial(inout Surface surface) {
surface.baseColor = getMaterialBaseColor();
surface.emissive = getMaterialEmissive();
surface.metalness = getMaterialMetalness();
surface.roughness = getMaterialRoughness();
surface.occlusion = getMaterialOcclusion();
surface.clearcoat = getMaterialClearcoat();
surface.clearcoatRoughness = getMaterialClearcoatRoughness();
if (flag_normalMap) {
vec3 normalScale = vec3(Material.normalScale, Material.normalScale, 1.);
surface.normal = TangentMatrix * normalize((getPixel(NormalTexture, UV).rgb * 2. - 1.) * normalScale);
}
return surface;
}
void finalizeSurface(inout Surface surface) {
if (!FrontFacing) {
surface.normal = -surface.normal;
surface.geometricNormal = -surface.geometricNormal;
}
surface.reflection = reflect(-surface.view, surface.normal);
surface.f0 = mix(vec3(.04), surface.baseColor.rgb, surface.metalness);
surface.diffuse = mix(surface.baseColor.rgb, vec3(0.), surface.metalness);
surface.roughness = max(surface.roughness, .05);
surface.roughness2 = surface.roughness * surface.roughness;
}
surface.occlusion = 1.;
if (flag_ambientOcclusion) surface.occlusion *= getPixel(OcclusionTexture, UV).r * Material.occlusionStrength;
Surface getDefaultSurface() {
Surface surface = newSurface();
applyMaterial(surface);
finalizeSurface(surface);
return surface;
}
surface.clearcoat = Material.clearcoat;
if (flag_clearcoatTexture) surface.clearcoat *= getPixel(ClearcoatTexture, UV).r;
surface.clearcoatRoughness = Material.clearcoatRoughness;
surface.alpha = color.a;
// Deprecated
void initSurface(out Surface surface) {
surface = getDefaultSurface();
}
float D_GGX(const Surface surface, float NoH) {
@ -373,7 +449,7 @@ void main() {
if (flag_materialColor) Color *= Material.color;
if (flag_vertexColors) Color *= VertexColor;
if (flag_normalMap && flag_vertexTangents) {
if (flag_vertexTangents) {
Tangent = NormalMatrix * VertexTangent;
}
@ -390,21 +466,21 @@ void main() {
#ifdef GL_FRAGMENT_SHADER
vec4 lovrmain();
void main() {
PixelColor[0] = lovrmain();
PixelColor = lovrmain();
if (flag_glow) {
if (flag_glowTexture) {
PixelColor[0].rgb += getPixel(GlowTexture, UV).rgb * Material.glow.rgb * Material.glow.a;
PixelColor.rgb += getPixel(GlowTexture, UV).rgb * Material.glow.rgb * Material.glow.a;
} else {
PixelColor[0].rgb += Material.glow.rgb * Material.glow.a;
PixelColor.rgb += Material.glow.rgb * Material.glow.a;
}
}
if (flag_tonemap) {
PixelColor[0].rgb = tonemap(PixelColor[0].rgb);
PixelColor.rgb = tonemap(PixelColor.rgb);
}
if (flag_alphaCutoff && PixelColor[0].a <= Material.alphaCutoff) {
if (flag_alphaCutoff && PixelColor.a <= Material.alphaCutoff) {
discard;
}
}

View File

@ -3,25 +3,23 @@
layout(local_size_x = 32) in;
layout(push_constant) uniform PushConstants {
uint first;
uint count;
uint views;
float period;
};
layout(set = 0, binding = 0) buffer readonly restrict Src { uint src[]; };
layout(set = 0, binding = 1) buffer writeonly restrict Dst { uint dst[]; };
void main() {
uint id = gl_GlobalInvocationID.x;
if (id >= count) return;
uint base = (first + id) * views * 2;
uint index = gl_GlobalInvocationID.x;
if (index >= count) return;
uint base = index * views;
uint total = 0;
for (uint i = 0; i < views; i++) {
total += src[base + views + i] - src[base + i];
total += src[base + i];
}
dst[id] = uint(total * period);
dst[index] = total;
}

View File

@ -48,7 +48,7 @@ var webxr = {
},
webxr_init__deps: ['$buttons', '$axes'],
webxr_init: function(supersample, offset, msaa, overlay) {
webxr_init: function(config) {
if (!navigator.xr) {
return false;
}
@ -174,12 +174,21 @@ var webxr = {
}
},
webxr_getDriverName: function(name, size) {
return false;
}
webxr_getName: function(name, size) {
return false;
},
webxr_getOriginType: function() {
return 1; /* ORIGIN_FLOOR */
webxr_isSeated: function() {
// TODO
},
webxr_getDisplayDimensions: function(width, height) {
HEAPU32[width >> 2] = state.layer.framebufferWidth;
HEAPU32[height >> 2] = state.layer.framebufferHeight;
},
webxr_getDisplayTime: function() {
@ -190,11 +199,6 @@ var webxr = {
return (state.displayTime - state.lastDisplayTime) / 1000.0;
},
webxr_getDisplayDimensions: function(width, height) {
HEAPU32[width >> 2] = state.layer.framebufferWidth;
HEAPU32[height >> 2] = state.layer.framebufferHeight;
},
webxr_getDisplayFrequency: function() {
return 0.0;
},
@ -375,11 +379,15 @@ var webxr = {
return !!actuator;
},
webxr_stopVibration: function(device) {
return;
},
webxr_newModelData: function(device, animated) {
return 0; /* NULL */
},
webxr_animate: function(device, model) {
webxr_animate: function(model) {
return false;
},
@ -411,6 +419,14 @@ var webxr = {
return true;
},
webxr_isPassthroughEnabled: function() {
return false;
},
webxr_setPassthroughEnabled: function() {
return false;
},
webxr_update: function() {
return (state.displayTime - state.lastDisplayTime) / 1000.0;
}

17
plugins/README.md Normal file
View File

@ -0,0 +1,17 @@
Plugins
===
Plugins are optional native libraries that are built with LÖVR. CMake will scan `plugins` for any
folder with a `CMakeLists.txt` and include it in the build. Any CMake targets the plugin declares
will automatically include and link with LÖVR's copy of Lua, and any shared libraries they produce
will be copied next to the `lovr` executable, allowing them to be used from Lua via `require`.
Prebuilt plugin libraries can also be copied next to the lovr executable. However, this won't work
easily for Android builds, since the APK requires re-signing if it is modified.
See the [Plugins documentation](https://lovr.org/docs/Plugins) for more info and a list of known
plugins.
LÖVR includes some "core plugins" here which are included in builds by default. They are entirely
optional -- their folders here can be removed or their libraries can be deleted after building
without disturbing the LÖVR build.

1
plugins/lua-enet Submodule

@ -0,0 +1 @@
Subproject commit daaee4ef0056958c33b204a83a97c770ff0a2cbc

View File

@ -1,5 +1,6 @@
#include "api.h"
#include "util.h"
#include "lib/lua/lutf8lib.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
@ -40,6 +41,14 @@ static int luax_meta__tostring(lua_State* L) {
return 1;
}
// Currently the same as tostring
static int luax_type(lua_State* L) {
lua_getfield(L, 1, "__info");
TypeInfo* info = lua_touserdata(L, -1);
lua_pushstring(L, info->name);
return 1;
}
static int luax_meta__gc(lua_State* L) {
Proxy* p = lua_touserdata(L, 1);
if (p) {
@ -110,6 +119,9 @@ void luax_preload(lua_State* L) {
#endif
#ifndef LOVR_DISABLE_TIMER
{ "lovr.timer", luaopen_lovr_timer },
#endif
#ifndef LOVR_DISABLE_UTF8
{ "utf8", luaopen_utf8 },
#endif
{ NULL, NULL }
};
@ -144,15 +156,19 @@ void _luax_registertype(lua_State* L, const char* name, const luaL_Reg* function
lua_pushcfunction(L, luax_meta__tostring);
lua_setfield(L, -2, "__tostring");
// Register class functions
// Register class methods
if (functions) {
luax_register(L, functions);
}
// :release function
// :release method
lua_pushcfunction(L, luax_meta__gc);
lua_setfield(L, -2, "release");
// :type method
lua_pushcfunction(L, luax_type);
lua_setfield(L, -2, "type");
// Pop metatable
lua_pop(L, 1);
}
@ -292,6 +308,19 @@ int luax_resume(lua_State* T, int n) {
#endif
}
int luax_loadbufferx(lua_State* L, const char* buffer, size_t size, const char* name, const char* mode) {
#if LUA_VERSION_NUM >= 502
return luaL_loadbufferx(L, buffer, size, name, mode);
#else
bool binary = buffer[0] == LUA_SIGNATURE[0];
if (mode && !strchr(mode, binary ? 'b' : 't')) {
lua_pushliteral(L, "attempt to load chunk with wrong mode");
return LUA_ERRSYNTAX;
}
return luaL_loadbuffer(L, buffer, size, name);
#endif
}
void luax_vthrow(void* context, const char* format, va_list args) {
lua_State* L = (lua_State*) context;
lua_pushvfstring(L, format, args);
@ -535,7 +564,15 @@ int luax_readmesh(lua_State* L, int index, float** vertices, uint32_t* vertexCou
*shouldFree = false;
return index + 1;
}
Mesh* mesh = luax_totype(L, index, Mesh);
if (mesh) {
lovrMeshGetTriangles(mesh, vertices, indices, vertexCount, indexCount);
*shouldFree = true;
return index + 1;
}
#endif
return luaL_argerror(L, index, "table or Model");
return luaL_argerror(L, index, "table, Mesh, or Model");
}

View File

@ -28,6 +28,7 @@ extern StringEntry lovrBufferLayout[];
extern StringEntry lovrChannelLayout[];
extern StringEntry lovrCompareMode[];
extern StringEntry lovrCullMode[];
extern StringEntry lovrDataType[];
extern StringEntry lovrDefaultAttribute[];
extern StringEntry lovrDefaultShader[];
extern StringEntry lovrDevice[];
@ -37,14 +38,13 @@ extern StringEntry lovrDrawMode[];
extern StringEntry lovrDrawStyle[];
extern StringEntry lovrEffect[];
extern StringEntry lovrEventType[];
extern StringEntry lovrFieldType[];
extern StringEntry lovrFilterMode[];
extern StringEntry lovrHeadsetDriver[];
extern StringEntry lovrHeadsetOrigin[];
extern StringEntry lovrHorizontalAlign[];
extern StringEntry lovrJointType[];
extern StringEntry lovrKeyboardKey[];
extern StringEntry lovrMeshMode[];
extern StringEntry lovrMeshStorage[];
extern StringEntry lovrModelDrawMode[];
extern StringEntry lovrOriginType[];
extern StringEntry lovrPassType[];
extern StringEntry lovrPermission[];
@ -55,7 +55,6 @@ extern StringEntry lovrShapeType[];
extern StringEntry lovrSmoothMode[];
extern StringEntry lovrStackType[];
extern StringEntry lovrStencilAction[];
extern StringEntry lovrTallyType[];
extern StringEntry lovrTextureFeature[];
extern StringEntry lovrTextureFormat[];
extern StringEntry lovrTextureType[];
@ -115,6 +114,7 @@ void _luax_pushtype(lua_State* L, const char* name, uint64_t hash, void* object)
int _luax_checkenum(lua_State* L, int index, const StringEntry* map, const char* fallback, const char* label);
void luax_registerloader(lua_State* L, int (*loader)(lua_State* L), int index);
int luax_resume(lua_State* T, int n);
int luax_loadbufferx(lua_State* L, const char* buffer, size_t size, const char* name, const char* mode);
void luax_vthrow(void* L, const char* format, va_list args);
void luax_vlog(void* context, int level, const char* tag, const char* format, va_list args);
void luax_traceback(lua_State* L, lua_State* T, const char* message, int level);
@ -134,9 +134,13 @@ int luax_readmesh(lua_State* L, int index, float** vertices, uint32_t* vertexCou
#ifndef LOVR_DISABLE_DATA
struct Blob;
struct Image;
struct ModelData;
struct Blob* luax_readblob(lua_State* L, int index, const char* debug);
struct Image* luax_checkimage(lua_State* L, int index);
uint32_t luax_checkcodepoint(lua_State* L, int index);
uint32_t luax_checkanimationindex(lua_State* L, int index, struct ModelData* model);
uint32_t luax_checkmaterialindex(lua_State* L, int index, struct ModelData* model);
uint32_t luax_checknodeindex(lua_State* L, int index, struct ModelData* model);
#endif
#ifndef LOVR_DISABLE_EVENT
@ -152,22 +156,25 @@ bool luax_writefile(const char* filename, const void* data, size_t size);
#ifndef LOVR_DISABLE_GRAPHICS
struct Buffer;
struct DataField;
struct ColoredString;
struct Model;
struct Buffer* luax_checkbuffer(lua_State* L, int index);
void luax_readbufferfield(lua_State* L, int index, int type, void* data);
void luax_readbufferdata(lua_State* L, int index, struct Buffer* buffer, char* data);
void luax_checkdataformat(lua_State* L, int index, struct DataField* format, uint32_t* count, uint32_t max);
void luax_checkbufferdata(lua_State* L, int index, const struct DataField* format, char* data);
int luax_pushbufferdata(lua_State* L, const struct DataField* format, char* data);
uint32_t luax_checkcomparemode(lua_State* L, int index);
struct ColoredString* luax_checkcoloredstrings(lua_State* L, int index, uint32_t* count, struct ColoredString* stack);
uint32_t luax_checknodeindex(lua_State* L, int index, struct Model* model);
#endif
#ifndef LOVR_DISABLE_MATH
#include "math/pool.h" // TODO
#include "math/math.h" // TODO
float* luax_tovector(lua_State* L, int index, VectorType* type);
float* luax_checkvector(lua_State* L, int index, VectorType type, const char* expected);
float* luax_newtempvector(lua_State* L, VectorType type);
int luax_readvec2(lua_State* L, int index, float* v, const char* expected);
int luax_readvec3(lua_State* L, int index, float* v, const char* expected);
int luax_readvec4(lua_State* L, int index, float* v, const char* expected);
int luax_readscale(lua_State* L, int index, float* v, int components, const char* expected);
int luax_readquat(lua_State* L, int index, float* q, const char* expected);
int luax_readmat4(lua_State* L, int index, float* m, int scaleComponents);

View File

@ -55,15 +55,15 @@ StringEntry lovrVolumeUnit[] = {
{ 0 }
};
static void onDevice(const void* id, size_t size, const char* name, bool isDefault, void* userdata) {
static void onDevice(AudioDevice* device, void* userdata) {
lua_State* L = userdata;
lua_createtable(L, 0, 3);
void* p = lua_newuserdata(L, size);
memcpy(p, id, size);
void* p = lua_newuserdata(L, device->idSize);
memcpy(p, device->id, device->idSize);
lua_setfield(L, -2, "id");
lua_pushstring(L, name);
lua_pushstring(L, device->name);
lua_setfield(L, -2, "name");
lua_pushboolean(L, isDefault);
lua_pushboolean(L, device->isDefault);
lua_setfield(L, -2, "default");
lua_rawseti(L, -2, luax_len(L, -2) + 1);
}
@ -75,6 +75,18 @@ static int l_lovrAudioGetDevices(lua_State *L) {
return 1;
}
static int l_lovrAudioGetDevice(lua_State* L) {
AudioType type = luax_checkenum(L, 1, AudioType, "playback");
AudioDevice device;
if (lovrAudioGetDevice(type, &device)) {
lua_pushstring(L, device.name);
void* p = lua_newuserdata(L, device.idSize);
memcpy(p, device.id, device.idSize);
return 2;
}
return 0;
}
static int l_lovrAudioSetDevice(lua_State *L) {
AudioType type = luax_checkenum(L, 1, AudioType, "playback");
void* id = lua_touserdata(L, 2);
@ -121,7 +133,7 @@ static int l_lovrAudioSetVolume(lua_State* L) {
}
static int l_lovrAudioGetPosition(lua_State* L) {
float position[4], orientation[4];
float position[3], orientation[4];
lovrAudioGetPose(position, orientation);
lua_pushnumber(L, position[0]);
lua_pushnumber(L, position[1]);
@ -130,7 +142,7 @@ static int l_lovrAudioGetPosition(lua_State* L) {
}
static int l_lovrAudioSetPosition(lua_State* L) {
float position[4], orientation[4];
float position[3], orientation[4];
lovrAudioGetPose(position, orientation);
luax_readvec3(L, 1, position, NULL);
lovrAudioSetPose(position, orientation);
@ -138,7 +150,7 @@ static int l_lovrAudioSetPosition(lua_State* L) {
}
static int l_lovrAudioGetOrientation(lua_State* L) {
float position[4], orientation[4], angle, ax, ay, az;
float position[3], orientation[4], angle, ax, ay, az;
lovrAudioGetPose(position, orientation);
quat_getAngleAxis(orientation, &angle, &ax, &ay, &az);
lua_pushnumber(L, angle);
@ -149,7 +161,7 @@ static int l_lovrAudioGetOrientation(lua_State* L) {
}
static int l_lovrAudioSetOrientation(lua_State* L) {
float position[4], orientation[4];
float position[3], orientation[4];
lovrAudioGetPose(position, orientation);
luax_readquat(L, 1, orientation, NULL);
lovrAudioSetPose(position, orientation);
@ -157,7 +169,7 @@ static int l_lovrAudioSetOrientation(lua_State* L) {
}
static int l_lovrAudioGetPose(lua_State *L) {
float position[4], orientation[4], angle, ax, ay, az;
float position[3], orientation[4], angle, ax, ay, az;
lovrAudioGetPose(position, orientation);
quat_getAngleAxis(orientation, &angle, &ax, &ay, &az);
lua_pushnumber(L, position[0]);
@ -172,7 +184,7 @@ static int l_lovrAudioGetPose(lua_State *L) {
static int l_lovrAudioSetPose(lua_State *L) {
int index = 1;
float position[4], orientation[4];
float position[3], orientation[4];
index = luax_readvec3(L, index, position, NULL);
index = luax_readquat(L, index, orientation, NULL);
lovrAudioSetPose(position, orientation);
@ -227,7 +239,7 @@ static int l_lovrAudioNewSource(lua_State* L) {
Sound* sound = luax_totype(L, 1, Sound);
bool decode = false;
bool pitchable = false;
bool pitchable = true;
bool spatial = true;
uint32_t effects = ~0u;
if (lua_gettop(L) >= 2) {
@ -238,7 +250,7 @@ static int l_lovrAudioNewSource(lua_State* L) {
lua_pop(L, 1);
lua_getfield(L, 2, "pitchable");
pitchable = lua_toboolean(L, -1);
if (!lua_isnil(L, -1)) pitchable = lua_toboolean(L, -1);
lua_pop(L, 1);
lua_getfield(L, 2, "effects");
@ -261,7 +273,7 @@ static int l_lovrAudioNewSource(lua_State* L) {
lua_pop(L, 1);
lua_getfield(L, 2, "spatial");
spatial = lua_isnil(L, -1) ? true : lua_toboolean(L, -1);
if (!lua_isnil(L, -1)) spatial = lua_toboolean(L, -1);
lua_pop(L, 1);
}
@ -282,6 +294,7 @@ static int l_lovrAudioNewSource(lua_State* L) {
static const luaL_Reg lovrAudio[] = {
{ "getDevices", l_lovrAudioGetDevices },
{ "getDevice", l_lovrAudioGetDevice },
{ "setDevice", l_lovrAudioSetDevice },
{ "start", l_lovrAudioStart },
{ "stop", l_lovrAudioStop },

View File

@ -108,7 +108,7 @@ static int l_lovrSourceGetDuration(lua_State* L) {
static int l_lovrSourceGetPosition(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);
float position[4], orientation[4];
float position[3], orientation[4];
lovrSourceGetPose(source, position, orientation);
lua_pushnumber(L, position[0]);
lua_pushnumber(L, position[1]);
@ -118,16 +118,15 @@ static int l_lovrSourceGetPosition(lua_State* L) {
static int l_lovrSourceSetPosition(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);
float position[4], orientation[4];
lovrSourceGetPose(source, position, orientation);
float position[3];
luax_readvec3(L, 2, position, NULL);
lovrSourceSetPose(source, position, orientation);
lovrSourceSetPose(source, position, NULL);
return 0;
}
static int l_lovrSourceGetOrientation(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);
float position[4], orientation[4], angle, ax, ay, az;
float position[3], orientation[4], angle, ax, ay, az;
lovrSourceGetPose(source, position, orientation);
quat_getAngleAxis(orientation, &angle, &ax, &ay, &az);
lua_pushnumber(L, angle);
@ -139,16 +138,15 @@ static int l_lovrSourceGetOrientation(lua_State* L) {
static int l_lovrSourceSetOrientation(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);
float position[4], orientation[4];
lovrSourceGetPose(source, position, orientation);
float orientation[4];
luax_readquat(L, 2, orientation, NULL);
lovrSourceSetPose(source, position, orientation);
lovrSourceSetPose(source, NULL, orientation);
return 0;
}
static int l_lovrSourceGetPose(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);
float position[4], orientation[4], angle, ax, ay, az;
float position[3], orientation[4], angle, ax, ay, az;
lovrSourceGetPose(source, position, orientation);
quat_getAngleAxis(orientation, &angle, &ax, &ay, &az);
lua_pushnumber(L, position[0]);
@ -163,7 +161,7 @@ static int l_lovrSourceGetPose(lua_State* L) {
static int l_lovrSourceSetPose(lua_State *L) {
Source* source = luax_checktype(L, 1, Source);
float position[4], orientation[4];
float position[3], orientation[4];
int index = 2;
index = luax_readvec3(L, index, position, NULL);
index = luax_readquat(L, index, orientation, NULL);

View File

@ -12,6 +12,7 @@ StringEntry lovrAnimationProperty[] = {
[PROP_TRANSLATION] = ENTRY("translation"),
[PROP_ROTATION] = ENTRY("rotation"),
[PROP_SCALE] = ENTRY("scale"),
[PROP_WEIGHTS] = ENTRY("weights"),
{ 0 }
};
@ -37,13 +38,13 @@ StringEntry lovrDefaultAttribute[] = {
{ 0 }
};
StringEntry lovrDrawMode[] = {
[DRAW_POINTS] = ENTRY("points"),
[DRAW_LINES] = ENTRY("lines"),
StringEntry lovrModelDrawMode[] = {
[DRAW_POINT_LIST] = ENTRY("points"),
[DRAW_LINE_LIST] = ENTRY("lines"),
[DRAW_LINE_STRIP] = ENTRY("linestrip"),
[DRAW_LINE_LOOP] = ENTRY("lineloop"),
[DRAW_TRIANGLE_LIST] = ENTRY("triangles"),
[DRAW_TRIANGLE_STRIP] = ENTRY("strip"),
[DRAW_TRIANGLES] = ENTRY("triangles"),
[DRAW_TRIANGLE_FAN] = ENTRY("fan"),
{ 0 }
};

View File

@ -3,67 +3,67 @@
#include "core/maf.h"
#include "util.h"
static ModelNode* luax_checknode(lua_State* L, int index, ModelData* model) {
switch (lua_type(L, index)) {
case LUA_TNUMBER: {
uint32_t node = luax_checku32(L, index) - 1;
lovrCheck(node < model->nodeCount, "Invalid node index '%d'", node + 1);
return &model->nodes[node];
}
case LUA_TSTRING: {
size_t length;
const char* name = lua_tolstring(L, index, &length);
uint64_t hash = hash64(name, length);
uint64_t entry = map_get(&model->nodeMap, hash);
lovrCheck(entry != MAP_NIL, "ModelData has no node named '%s'", name);
return &model->nodes[(uint32_t) entry];
}
default: return luax_typeerror(L, index, "number or string"), NULL;
}
}
static ModelMaterial* luax_checkmaterial(lua_State* L, int index, ModelData* model) {
switch (lua_type(L, index)) {
case LUA_TNUMBER: {
uint32_t material = luax_checku32(L, index) - 1;
lovrCheck(material < model->materialCount, "Invalid material index '%d'", material + 1);
return &model->materials[material];
}
case LUA_TSTRING: {
size_t length;
const char* name = lua_tolstring(L, index, &length);
uint64_t hash = hash64(name, length);
uint64_t entry = map_get(&model->materialMap, hash);
lovrCheck(entry != MAP_NIL, "ModelData has no material named '%s'", name);
return &model->materials[(uint32_t) entry];
}
default: return luax_typeerror(L, index, "number or string"), NULL;
}
}
static ModelAnimation* luax_checkanimation(lua_State* L, int index, ModelData* model) {
uint32_t luax_checkanimationindex(lua_State* L, int index, ModelData* model) {
switch (lua_type(L, index)) {
case LUA_TNUMBER: {
uint32_t animation = luax_checku32(L, index) - 1;
lovrCheck(animation < model->animationCount, "Invalid animation index '%d'", animation + 1);
return &model->animations[animation];
return animation;
}
case LUA_TSTRING: {
size_t length;
const char* name = lua_tolstring(L, index, &length);
uint64_t hash = hash64(name, length);
uint64_t entry = map_get(&model->animationMap, hash);
lovrCheck(entry != MAP_NIL, "ModelData has no animation named '%s'", name);
return &model->animations[(uint32_t) entry];
lovrCheck(entry != MAP_NIL, "Model has no animation named '%s'", name);
return (uint32_t) entry;
}
default: return luax_typeerror(L, index, "number or string"), NULL;
default: return luax_typeerror(L, index, "number or string"), ~0u;
}
}
uint32_t luax_checkmaterialindex(lua_State* L, int index, ModelData* model) {
switch (lua_type(L, index)) {
case LUA_TNUMBER: {
uint32_t material = luax_checku32(L, index) - 1;
lovrCheck(material < model->materialCount, "Invalid material index '%d'", material + 1);
return material;
}
case LUA_TSTRING: {
size_t length;
const char* name = lua_tolstring(L, index, &length);
uint64_t hash = hash64(name, length);
uint64_t entry = map_get(&model->materialMap, hash);
lovrCheck(entry != MAP_NIL, "Model has no material named '%s'", name);
return (uint32_t) entry;
}
default: return luax_typeerror(L, index, "number or string"), ~0u;
}
}
uint32_t luax_checknodeindex(lua_State* L, int index, ModelData* model) {
switch (lua_type(L, index)) {
case LUA_TNUMBER: {
uint32_t node = luax_checku32(L, index) - 1;
lovrCheck(node < model->nodeCount, "Invalid node index '%d'", node + 1);
return node;
}
case LUA_TSTRING: {
size_t length;
const char* name = lua_tolstring(L, index, &length);
uint64_t hash = hash64(name, length);
uint64_t entry = map_get(&model->nodeMap, hash);
lovrCheck(entry != MAP_NIL, "Model has no node named '%s'", name);
return (uint32_t) entry;
}
default: return luax_typeerror(L, index, "number or string"), ~0u;
}
}
static int l_lovrModelDataGetMetadata(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
if (!model->metadata || model->metadataSize == 0) {
if (!model->metadata || model->metadataSize == 0 || model->metadataType != META_GLTF_JSON) {
lua_pushnil(L);
} else {
lua_pushlstring(L, model->metadata, model->metadataSize);
@ -122,7 +122,7 @@ static int l_lovrModelDataGetNodeName(lua_State* L) {
static int l_lovrModelDataGetNodeParent(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelNode* node = luax_checknode(L, 2, model);
ModelNode* node = &model->nodes[luax_checknodeindex(L, 2, model)];
if (node->parent == ~0u) {
lua_pushnil(L);
} else {
@ -133,7 +133,7 @@ static int l_lovrModelDataGetNodeParent(lua_State* L) {
static int l_lovrModelDataGetNodeChildren(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelNode* node = luax_checknode(L, 2, model);
ModelNode* node = &model->nodes[luax_checknodeindex(L, 2, model)];
lua_createtable(L, node->childCount, 0);
for (uint32_t i = 0; i < node->childCount; i++) {
lua_pushinteger(L, node->children[i] + 1);
@ -144,9 +144,9 @@ static int l_lovrModelDataGetNodeChildren(lua_State* L) {
static int l_lovrModelDataGetNodePosition(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelNode* node = luax_checknode(L, 2, model);
ModelNode* node = &model->nodes[luax_checknodeindex(L, 2, model)];
if (node->hasMatrix) {
float position[4];
float position[3];
mat4_getPosition(node->transform.matrix, position);
lua_pushnumber(L, position[0]);
lua_pushnumber(L, position[1]);
@ -162,7 +162,7 @@ static int l_lovrModelDataGetNodePosition(lua_State* L) {
static int l_lovrModelDataGetNodeOrientation(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelNode* node = luax_checknode(L, 2, model);
ModelNode* node = &model->nodes[luax_checknodeindex(L, 2, model)];
float angle, ax, ay, az;
if (node->hasMatrix) {
mat4_getAngleAxis(node->transform.matrix, &angle, &ax, &ay, &az);
@ -178,7 +178,7 @@ static int l_lovrModelDataGetNodeOrientation(lua_State* L) {
static int l_lovrModelDataGetNodeScale(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelNode* node = luax_checknode(L, 2, model);
ModelNode* node = &model->nodes[luax_checknodeindex(L, 2, model)];
if (node->hasMatrix) {
float scale[3];
mat4_getScale(node->transform.matrix, scale);
@ -195,7 +195,7 @@ static int l_lovrModelDataGetNodeScale(lua_State* L) {
static int l_lovrModelDataGetNodePose(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelNode* node = luax_checknode(L, 2, model);
ModelNode* node = &model->nodes[luax_checknodeindex(L, 2, model)];
if (node->hasMatrix) {
float position[3], angle, ax, ay, az;
mat4_getPosition(node->transform.matrix, position);
@ -223,9 +223,9 @@ static int l_lovrModelDataGetNodePose(lua_State* L) {
static int l_lovrModelDataGetNodeTransform(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelNode* node = luax_checknode(L, 2, model);
ModelNode* node = &model->nodes[luax_checknodeindex(L, 2, model)];
if (node->hasMatrix) {
float position[4], scale[4], angle, ax, ay, az;
float position[3], scale[4], angle, ax, ay, az;
mat4_getPosition(node->transform.matrix, position);
mat4_getScale(node->transform.matrix, scale);
mat4_getAngleAxis(node->transform.matrix, &angle, &ax, &ay, &az);
@ -258,7 +258,7 @@ static int l_lovrModelDataGetNodeTransform(lua_State* L) {
static int l_lovrModelDataGetNodeMeshes(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelNode* node = luax_checknode(L, 2, model);
ModelNode* node = &model->nodes[luax_checknodeindex(L, 2, model)];
lua_createtable(L, node->primitiveCount, 0);
for (uint32_t i = 0; i < node->primitiveCount; i++) {
lua_pushinteger(L, node->primitiveIndex + i + 1);
@ -269,7 +269,7 @@ static int l_lovrModelDataGetNodeMeshes(lua_State* L) {
static int l_lovrModelDataGetNodeSkin(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelNode* node = luax_checknode(L, 2, model);
ModelNode* node = &model->nodes[luax_checknodeindex(L, 2, model)];
if (node->skin == ~0u) {
lua_pushnil(L);
} else {
@ -289,7 +289,7 @@ static int l_lovrModelDataGetMeshDrawMode(lua_State* L) {
uint32_t index = luax_checku32(L, 2) - 1;
lovrCheck(index < model->primitiveCount, "Invalid mesh index '%d'", index + 1);
ModelPrimitive* mesh = &model->primitives[index];
luax_pushenum(L, DrawMode, mesh->mode);
luax_pushenum(L, ModelDrawMode, mesh->mode);
return 1;
}
@ -560,7 +560,7 @@ static int l_lovrModelDataGetMaterialName(lua_State* L) {
static int l_lovrModelDataGetMaterial(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelMaterial* material = luax_checkmaterial(L, 2, model);
ModelMaterial* material = &model->materials[luax_checkmaterialindex(L, 2, model)];
lua_newtable(L);
@ -636,21 +636,21 @@ static int l_lovrModelDataGetAnimationName(lua_State* L) {
static int l_lovrModelDataGetAnimationDuration(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelAnimation* animation = luax_checkanimation(L, 2, model);
ModelAnimation* animation = &model->animations[luax_checkanimationindex(L, 2, model)];
lua_pushnumber(L, animation->duration);
return 1;
}
static int l_lovrModelDataGetAnimationChannelCount(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelAnimation* animation = luax_checkanimation(L, 2, model);
ModelAnimation* animation = &model->animations[luax_checkanimationindex(L, 2, model)];
lua_pushinteger(L, animation->channelCount);
return 1;
}
static int l_lovrModelDataGetAnimationNode(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelAnimation* animation = luax_checkanimation(L, 2, model);
ModelAnimation* animation = &model->animations[luax_checkanimationindex(L, 2, model)];
uint32_t index = luax_checku32(L, 3) - 1;
lovrCheck(index < animation->channelCount, "Invalid channel index '%d'", index + 1);
ModelAnimationChannel* channel = &animation->channels[index];
@ -660,7 +660,7 @@ static int l_lovrModelDataGetAnimationNode(lua_State* L) {
static int l_lovrModelDataGetAnimationProperty(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelAnimation* animation = luax_checkanimation(L, 2, model);
ModelAnimation* animation = &model->animations[luax_checkanimationindex(L, 2, model)];
uint32_t index = luax_checku32(L, 3) - 1;
lovrCheck(index < animation->channelCount, "Invalid channel index '%d'", index + 1);
ModelAnimationChannel* channel = &animation->channels[index];
@ -670,7 +670,7 @@ static int l_lovrModelDataGetAnimationProperty(lua_State* L) {
static int l_lovrModelDataGetAnimationSmoothMode(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelAnimation* animation = luax_checkanimation(L, 2, model);
ModelAnimation* animation = &model->animations[luax_checkanimationindex(L, 2, model)];
uint32_t index = luax_checku32(L, 3) - 1;
lovrCheck(index < animation->channelCount, "Invalid channel index '%d'", index + 1);
ModelAnimationChannel* channel = &animation->channels[index];
@ -680,7 +680,7 @@ static int l_lovrModelDataGetAnimationSmoothMode(lua_State* L) {
static int l_lovrModelDataGetAnimationKeyframeCount(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelAnimation* animation = luax_checkanimation(L, 2, model);
ModelAnimation* animation = &model->animations[luax_checkanimationindex(L, 2, model)];
uint32_t index = luax_checku32(L, 3) - 1;
lovrCheck(index < animation->channelCount, "Invalid channel index '%d'", index + 1);
ModelAnimationChannel* channel = &animation->channels[index];
@ -690,15 +690,20 @@ static int l_lovrModelDataGetAnimationKeyframeCount(lua_State* L) {
static int l_lovrModelDataGetAnimationKeyframe(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelAnimation* animation = luax_checkanimation(L, 2, model);
ModelAnimation* animation = &model->animations[luax_checkanimationindex(L, 2, model)];
uint32_t index = luax_checku32(L, 3) - 1;
lovrCheck(index < animation->channelCount, "Invalid channel index '%d'", index + 1);
ModelAnimationChannel* channel = &animation->channels[index];
uint32_t keyframe = luax_checku32(L, 4) - 1;
lovrCheck(keyframe < channel->keyframeCount, "Invalid keyframe index '%d'", keyframe + 1);
lua_pushnumber(L, channel->times[keyframe]);
int counts[] = { [PROP_TRANSLATION] = 3, [PROP_ROTATION] = 4, [PROP_SCALE] = 3 };
int count = counts[channel->property];
int count;
switch (channel->property) {
case PROP_TRANSLATION: count = 3; break;
case PROP_ROTATION: count = 4; break;
case PROP_SCALE: count = 3; break;
case PROP_WEIGHTS: count = model->nodes[channel->nodeIndex].blendShapeCount; break;
}
for (int i = 0; i < count; i++) {
lua_pushnumber(L, channel->data[keyframe * count + i]);
}
@ -738,6 +743,20 @@ static int l_lovrModelDataGetSkinInverseBindMatrix(lua_State* L) {
return 16;
}
static int l_lovrModelDataGetBlendShapeCount(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
lua_pushinteger(L, model->blendShapeCount);
return 1;
}
static int l_lovrModelDataGetBlendShapeName(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
uint32_t index = luax_checku32(L, 2) - 1;
lovrCheck(index < model->blendShapeCount, "Invalid blend shape index '%d'", index + 1);
lua_pushstring(L, model->blendShapes[index].name);
return 1;
}
const luaL_Reg lovrModelData[] = {
{ "getMetadata", l_lovrModelDataGetMetadata },
{ "getBlobCount", l_lovrModelDataGetBlobCount },
@ -790,5 +809,7 @@ const luaL_Reg lovrModelData[] = {
{ "getSkinCount", l_lovrModelDataGetSkinCount },
{ "getSkinJoints", l_lovrModelDataGetSkinJoints },
{ "getSkinInverseBindMatrix", l_lovrModelDataGetSkinInverseBindMatrix },
{ "getBlendShapeCount", l_lovrModelDataGetBlendShapeCount },
{ "getBlendShapeName", l_lovrModelDataGetBlendShapeName },
{ NULL, NULL }
};

View File

@ -8,11 +8,17 @@
StringEntry lovrEventType[] = {
[EVENT_QUIT] = ENTRY("quit"),
[EVENT_RESTART] = ENTRY("restart"),
[EVENT_VISIBLE] = ENTRY("visible"),
[EVENT_FOCUS] = ENTRY("focus"),
[EVENT_RECENTER] = ENTRY("recenter"),
[EVENT_RESIZE] = ENTRY("resize"),
[EVENT_KEYPRESSED] = ENTRY("keypressed"),
[EVENT_KEYRELEASED] = ENTRY("keyreleased"),
[EVENT_TEXTINPUT] = ENTRY("textinput"),
[EVENT_MOUSEPRESSED] = ENTRY("mousepressed"),
[EVENT_MOUSERELEASED] = ENTRY("mousereleased"),
[EVENT_MOUSEMOVED] = ENTRY("mousemoved"),
[EVENT_MOUSEWHEELMOVED] = ENTRY("wheelmoved"),
#ifndef LOVR_DISABLE_THREAD
[EVENT_THREAD_ERROR] = ENTRY("threaderror"),
#endif
@ -65,15 +71,44 @@ void luax_checkvariant(lua_State* L, int index, Variant* variant) {
lua_pushliteral(L, "__info");
lua_rawget(L, -2);
TypeInfo* info = lua_touserdata(L, -1);
variant->value.object.type = info->name;
variant->value.object.destructor = info->destructor;
lua_pop(L, 1);
if (!lua_isnil(L, -1)) {
TypeInfo* info = lua_touserdata(L, -1);
variant->value.object.type = info->name;
variant->value.object.destructor = info->destructor;
lua_pop(L, 1);
variant->value.object.pointer = proxy->object;
lovrRetain(proxy->object);
lua_pop(L, 1);
break;
variant->value.object.pointer = proxy->object;
lovrRetain(proxy->object);
lua_pop(L, 1);
break;
} else {
lua_pop(L, 2);
}
/* fallthrough */
case LUA_TLIGHTUSERDATA: {
VectorType type;
float* v = luax_tovector(L, index, &type);
if (v) {
if (type == V_MAT4) {
variant->type = TYPE_MATRIX;
variant->value.matrix.data = malloc(16 * sizeof(float));
lovrAssert(variant->value.matrix.data, "Out of memory");
memcpy(variant->value.matrix.data, v, 16 * sizeof(float));
break;
} else {
variant->type = TYPE_VECTOR;
variant->value.vector.type = type;
memcpy(variant->value.vector.data, v, (type == V_VEC2 ? 2 : 4) * sizeof(float));
break;
}
} else if (lua_type(L, index) == LUA_TLIGHTUSERDATA) {
variant->type = TYPE_POINTER;
variant->value.pointer = lua_touserdata(L, index);
break;
}
lovrThrow("Bad userdata variant for argument %d (expected object, vector, or lightuserdata)", index);
}
default:
lovrThrow("Bad variant type for argument %d: %s", index, lua_typename(L, type));
@ -88,7 +123,10 @@ int luax_pushvariant(lua_State* L, Variant* variant) {
case TYPE_NUMBER: lua_pushnumber(L, variant->value.number); return 1;
case TYPE_STRING: lua_pushlstring(L, variant->value.string.pointer, variant->value.string.length); return 1;
case TYPE_MINISTRING: lua_pushlstring(L, variant->value.ministring.data, variant->value.ministring.length); return 1;
case TYPE_POINTER: lua_pushlightuserdata(L, variant->value.pointer); return 1;
case TYPE_OBJECT: _luax_pushtype(L, variant->value.object.type, hash64(variant->value.object.type, strlen(variant->value.object.type)), variant->value.object.pointer); return 1;
case TYPE_VECTOR: memcpy(luax_newtempvector(L, variant->value.vector.type), variant->value.vector.data, (variant->value.vector.type == V_VEC2 ? 2 : 4) * sizeof(float)); return 1;
case TYPE_MATRIX: memcpy(luax_newtempvector(L, V_MAT4), variant->value.vector.data, 16 * sizeof(float)); return 1;
default: return 0;
}
}
@ -111,10 +149,17 @@ static int nextEvent(lua_State* L) {
lua_pushnumber(L, event.data.quit.exitCode);
return 2;
case EVENT_VISIBLE:
lua_pushboolean(L, event.data.boolean.value);
return 2;
case EVENT_FOCUS:
lua_pushboolean(L, event.data.boolean.value);
return 2;
case EVENT_RECENTER:
return 1;
case EVENT_RESIZE:
lua_pushinteger(L, event.data.resize.width);
lua_pushinteger(L, event.data.resize.height);
@ -136,6 +181,25 @@ static int nextEvent(lua_State* L) {
lua_pushinteger(L, event.data.text.codepoint);
return 3;
case EVENT_MOUSEPRESSED:
case EVENT_MOUSERELEASED:
lua_pushnumber(L, event.data.mouse.x);
lua_pushnumber(L, event.data.mouse.y);
lua_pushinteger(L, event.data.mouse.button + 1);
return 4;
case EVENT_MOUSEMOVED:
lua_pushnumber(L, event.data.mouse.x);
lua_pushnumber(L, event.data.mouse.y);
lua_pushnumber(L, event.data.mouse.dx);
lua_pushnumber(L, event.data.mouse.dy);
return 5;
case EVENT_MOUSEWHEELMOVED:
lua_pushnumber(L, event.data.wheel.x);
lua_pushnumber(L, event.data.wheel.y);
return 3;
#ifndef LOVR_DISABLE_THREAD
case EVENT_THREAD_ERROR:
luax_pushtype(L, Thread, event.data.thread.thread);
@ -173,11 +237,6 @@ static int l_lovrEventPoll(lua_State* L) {
return 1;
}
static int l_lovrEventPump(lua_State* L) {
lovrEventPump();
return 0;
}
static int l_lovrEventPush(lua_State* L) {
CustomEvent eventData;
const char* name = luaL_checkstring(L, 1);
@ -207,7 +266,6 @@ static int l_lovrEventRestart(lua_State* L) {
static const luaL_Reg lovrEvent[] = {
{ "clear", l_lovrEventClear },
{ "poll", l_lovrEventPoll },
{ "pump", l_lovrEventPump },
{ "push", l_lovrEventPush },
{ "quit", l_lovrEventQuit },
{ "restart", l_lovrEventRestart },

View File

@ -58,7 +58,7 @@ static void pushDirectoryItem(void* context, const char* path) {
lua_pop(L, 1);
}
static int luax_loadfile(lua_State* L, const char* path, const char* debug) {
static int luax_loadfile(lua_State* L, const char* path, const char* debug, const char* mode) {
size_t size;
void* buffer = luax_readfile(path, &size);
if (!buffer) {
@ -66,7 +66,7 @@ static int luax_loadfile(lua_State* L, const char* path, const char* debug) {
lua_pushfstring(L, "Could not load file '%s'", path);
return 2;
}
int status = luaL_loadbuffer(L, buffer, size, debug);
int status = luax_loadbufferx(L, buffer, size, debug, mode);
free(buffer);
switch (status) {
case LUA_ERRMEM: return luaL_error(L, "Memory allocation error: %s", lua_tostring(L, -1));
@ -259,9 +259,10 @@ static int l_lovrFilesystemIsFused(lua_State* L) {
static int l_lovrFilesystemLoad(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
const char* mode = luaL_optstring(L, 2, "bt");
lua_pushfstring(L, "@%s", path);
const char* debug = lua_tostring(L, -1);
return luax_loadfile(L, path, debug);
return luax_loadfile(L, path, debug, mode);
}
static int l_lovrFilesystemMount(lua_State* L) {
@ -393,7 +394,7 @@ static int luaLoader(lua_State* L) {
*f = '\0';
if (lovrFilesystemIsFile(filename)) {
return luax_loadfile(L, filename, debug);
return luax_loadfile(L, filename, debug, NULL);
}
if (*p == '\0') {

View File

@ -56,54 +56,60 @@ StringEntry lovrDefaultShader[] = {
[SHADER_FONT] = ENTRY("font"),
[SHADER_CUBEMAP] = ENTRY("cubemap"),
[SHADER_EQUIRECT] = ENTRY("equirect"),
[SHADER_FILL] = ENTRY("fill"),
[SHADER_FILL_2D] = ENTRY("fill"),
[SHADER_FILL_ARRAY] = ENTRY("fillarray"),
[SHADER_FILL_LAYER] = ENTRY("filllayer"),
[SHADER_LOGO] = ENTRY("logo"),
{ 0 }
};
StringEntry lovrDrawMode[] = {
[DRAW_POINTS] = ENTRY("points"),
[DRAW_LINES] = ENTRY("lines"),
[DRAW_TRIANGLES] = ENTRY("triangles"),
{ 0 }
};
StringEntry lovrDrawStyle[] = {
[STYLE_FILL] = ENTRY("fill"),
[STYLE_LINE] = ENTRY("line"),
{ 0 }
};
StringEntry lovrFieldType[] = {
[FIELD_I8x4] = ENTRY("i8x4"),
[FIELD_U8x4] = ENTRY("u8x4"),
[FIELD_SN8x4] = ENTRY("sn8x4"),
[FIELD_UN8x4] = ENTRY("un8x4"),
[FIELD_UN10x3] = ENTRY("un10x3"),
[FIELD_I16] = ENTRY("i16"),
[FIELD_I16x2] = ENTRY("i16x2"),
[FIELD_I16x4] = ENTRY("i16x4"),
[FIELD_U16] = ENTRY("u16"),
[FIELD_U16x2] = ENTRY("u16x2"),
[FIELD_U16x4] = ENTRY("u16x4"),
[FIELD_SN16x2] = ENTRY("sn16x2"),
[FIELD_SN16x4] = ENTRY("sn16x4"),
[FIELD_UN16x2] = ENTRY("un16x2"),
[FIELD_UN16x4] = ENTRY("un16x4"),
[FIELD_I32] = ENTRY("i32"),
[FIELD_I32x2] = ENTRY("i32x2"),
[FIELD_I32x3] = ENTRY("i32x3"),
[FIELD_I32x4] = ENTRY("i32x4"),
[FIELD_U32] = ENTRY("u32"),
[FIELD_U32x2] = ENTRY("u32x2"),
[FIELD_U32x3] = ENTRY("u32x3"),
[FIELD_U32x4] = ENTRY("u32x4"),
[FIELD_F16x2] = ENTRY("f16x2"),
[FIELD_F16x4] = ENTRY("f16x4"),
[FIELD_F32] = ENTRY("f32"),
[FIELD_F32x2] = ENTRY("f32x2"),
[FIELD_F32x3] = ENTRY("f32x3"),
[FIELD_F32x4] = ENTRY("f32x4"),
[FIELD_MAT2] = ENTRY("mat2"),
[FIELD_MAT3] = ENTRY("mat3"),
[FIELD_MAT4] = ENTRY("mat4"),
[FIELD_INDEX16] = ENTRY("index16"),
[FIELD_INDEX32] = ENTRY("index32"),
StringEntry lovrDataType[] = {
[TYPE_I8x4] = ENTRY("i8x4"),
[TYPE_U8x4] = ENTRY("u8x4"),
[TYPE_SN8x4] = ENTRY("sn8x4"),
[TYPE_UN8x4] = ENTRY("un8x4"),
[TYPE_UN10x3] = ENTRY("un10x3"),
[TYPE_I16] = ENTRY("i16"),
[TYPE_I16x2] = ENTRY("i16x2"),
[TYPE_I16x4] = ENTRY("i16x4"),
[TYPE_U16] = ENTRY("u16"),
[TYPE_U16x2] = ENTRY("u16x2"),
[TYPE_U16x4] = ENTRY("u16x4"),
[TYPE_SN16x2] = ENTRY("sn16x2"),
[TYPE_SN16x4] = ENTRY("sn16x4"),
[TYPE_UN16x2] = ENTRY("un16x2"),
[TYPE_UN16x4] = ENTRY("un16x4"),
[TYPE_I32] = ENTRY("i32"),
[TYPE_I32x2] = ENTRY("i32x2"),
[TYPE_I32x3] = ENTRY("i32x3"),
[TYPE_I32x4] = ENTRY("i32x4"),
[TYPE_U32] = ENTRY("u32"),
[TYPE_U32x2] = ENTRY("u32x2"),
[TYPE_U32x3] = ENTRY("u32x3"),
[TYPE_U32x4] = ENTRY("u32x4"),
[TYPE_F16x2] = ENTRY("f16x2"),
[TYPE_F16x4] = ENTRY("f16x4"),
[TYPE_F32] = ENTRY("f32"),
[TYPE_F32x2] = ENTRY("f32x2"),
[TYPE_F32x3] = ENTRY("f32x3"),
[TYPE_F32x4] = ENTRY("f32x4"),
[TYPE_MAT2] = ENTRY("mat2"),
[TYPE_MAT3] = ENTRY("mat3"),
[TYPE_MAT4] = ENTRY("mat4"),
[TYPE_INDEX16] = ENTRY("index16"),
[TYPE_INDEX32] = ENTRY("index32"),
{ 0 }
};
@ -120,10 +126,9 @@ StringEntry lovrHorizontalAlign[] = {
{ 0 }
};
StringEntry lovrMeshMode[] = {
[MESH_POINTS] = ENTRY("points"),
[MESH_LINES] = ENTRY("lines"),
[MESH_TRIANGLES] = ENTRY("triangles"),
StringEntry lovrMeshStorage[] = {
[MESH_CPU] = ENTRY("cpu"),
[MESH_GPU] = ENTRY("gpu"),
{ 0 }
};
@ -133,13 +138,6 @@ StringEntry lovrOriginType[] = {
{ 0 }
};
StringEntry lovrPassType[] = {
[PASS_RENDER] = ENTRY("render"),
[PASS_COMPUTE] = ENTRY("compute"),
[PASS_TRANSFER] = ENTRY("transfer"),
{ 0 }
};
StringEntry lovrShaderStage[] = {
[STAGE_VERTEX] = ENTRY("vertex"),
[STAGE_FRAGMENT] = ENTRY("fragment"),
@ -171,13 +169,6 @@ StringEntry lovrStencilAction[] = {
{ 0 }
};
StringEntry lovrTallyType[] = {
[TALLY_TIME] = ENTRY("time"),
[TALLY_SHADER] = ENTRY("shader"),
[TALLY_PIXEL] = ENTRY("pixel"),
{ 0 }
};
StringEntry lovrTextureFeature[] = {
[0] = ENTRY("sample"),
[1] = ENTRY("filter"),
@ -225,58 +216,12 @@ StringEntry lovrWrapMode[] = {
{ 0 }
};
static struct { uint32_t size, scalarAlign, baseAlign, components; } fieldInfo[] = {
[FIELD_I8x4] = { 4, 1, 4, 4 },
[FIELD_U8x4] = { 4, 1, 4, 4 },
[FIELD_SN8x4] = { 4, 1, 4, 4 },
[FIELD_UN8x4] = { 4, 1, 4, 4 },
[FIELD_UN10x3] = { 4, 4, 4, 3 },
[FIELD_I16] = { 2, 2, 2, 1 },
[FIELD_I16x2] = { 4, 2, 4, 2 },
[FIELD_I16x4] = { 8, 2, 8, 4 },
[FIELD_U16] = { 2, 2, 2, 1 },
[FIELD_U16x2] = { 4, 2, 4, 2 },
[FIELD_U16x4] = { 8, 2, 8, 4 },
[FIELD_SN16x2] = { 4, 2, 4, 2 },
[FIELD_SN16x4] = { 8, 2, 8, 4 },
[FIELD_UN16x2] = { 4, 2, 4, 2 },
[FIELD_UN16x4] = { 8, 2, 8, 4 },
[FIELD_I32] = { 4, 4, 4, 1 },
[FIELD_I32x2] = { 8, 4, 8, 2 },
[FIELD_I32x3] = { 12, 4, 16, 3 },
[FIELD_I32x4] = { 16, 4, 16, 4 },
[FIELD_U32] = { 4, 4, 4, 1 },
[FIELD_U32x2] = { 8, 4, 8, 2 },
[FIELD_U32x3] = { 12, 4, 16, 3 },
[FIELD_U32x4] = { 16, 4, 16, 4 },
[FIELD_F16x2] = { 4, 2, 4, 2 },
[FIELD_F16x4] = { 8, 2, 8, 4 },
[FIELD_F32] = { 4, 4, 4, 1 },
[FIELD_F32x2] = { 8, 4, 8, 2 },
[FIELD_F32x3] = { 12, 4, 16, 3 },
[FIELD_F32x4] = { 16, 4, 16, 4 },
[FIELD_MAT2] = { 16, 4, 8, 4 },
[FIELD_MAT3] = { 64, 4, 16, 9 },
[FIELD_MAT4] = { 64, 4, 16, 16 },
[FIELD_INDEX16] = { 2, 2, 2, 1 },
[FIELD_INDEX32] = { 4, 4, 4, 1 }
};
static uint32_t luax_checkfieldtype(lua_State* L, int index, uint32_t* nameHash) {
static uint32_t luax_checkdatatype(lua_State* L, int index) {
size_t length;
const char* string = luaL_checklstring(L, index, &length);
char* colon = strchr(string, ':');
if (colon) {
*nameHash = (uint32_t) hash64(colon + 1, length - (colon + 1 - string));
length = colon - string;
} else {
*nameHash = 0;
}
if (length < 3) {
return luaL_error(L, "invalid FieldType '%s'", string), 0;
return luaL_error(L, "invalid DataType '%s'", string), 0;
}
// Plurals are allowed and ignored
@ -291,284 +236,115 @@ static uint32_t luax_checkfieldtype(lua_State* L, int index, uint32_t* nameHash)
// vec[234]
if (length == 4 && string[0] == 'v' && string[1] == 'e' && string[2] == 'c' && string[3] >= '2' && string[3] <= '4') {
return FIELD_F32x2 + string[3] - '2';
return TYPE_F32x2 + string[3] - '2';
}
if (length == 3 && !memcmp(string, "int", length)) {
return FIELD_I32;
return TYPE_I32;
}
if (length == 4 && !memcmp(string, "uint", length)) {
return FIELD_U32;
return TYPE_U32;
}
if (length == 5 && !memcmp(string, "float", length)) {
return FIELD_F32;
return TYPE_F32;
}
if (length == 5 && !memcmp(string, "color", length)) {
return FIELD_UN8x4;
return TYPE_UN8x4;
}
if (length == 5 && !memcmp(string, "index", length)) {
return FIELD_INDEX32;
return TYPE_INDEX32;
}
for (int i = 0; lovrFieldType[i].length; i++) {
if (length == lovrFieldType[i].length && !memcmp(string, lovrFieldType[i].string, length)) {
for (int i = 0; lovrDataType[i].length; i++) {
if (length == lovrDataType[i].length && !memcmp(string, lovrDataType[i].string, length)) {
return i;
}
}
return luaL_error(L, "invalid FieldType '%s'", string), 0;
return luaL_error(L, "invalid DataType '%s'", string), 0;
}
static void luax_checkbufferformat(lua_State* L, int index, BufferInfo* info) {
switch (lua_type(L, index)) {
case LUA_TNONE:
case LUA_TNIL:
info->stride = 1;
break;
case LUA_TSTRING:
info->fieldCount = 1;
info->fields[0].type = luax_checkfieldtype(L, index, &info->fields[0].hash);
info->fields[0].location = 10;
info->stride = fieldInfo[info->fields[0].type].size;
break;
case LUA_TTABLE:
lua_getfield(L, index, "layout");
BufferLayout layout = luax_checkenum(L, -1, BufferLayout, "packed");
lua_pop(L, 1);
int length = info->fieldCount = luax_len(L, index);
lovrAssert(info->fieldCount <= COUNTOF(info->fields), "Too many buffer fields (max is %d)", COUNTOF(info->fields));
uint32_t offset = 0;
uint32_t extent = 0;
uint32_t location = 10;
uint32_t maxAlign = 1;
for (int i = 0; i < length; i++) {
BufferField* field = &info->fields[i];
lua_rawgeti(L, index, i + 1);
if (lua_istable(L, -1)) {
lua_getfield(L, -1, "type");
lovrAssert(lua_type(L, -1) == LUA_TSTRING, "When given as a table, Buffer fields must have a 'type' key.");
field->type = luax_checkfieldtype(L, -1, &field->hash);
lua_pop(L, 1);
uint32_t align = layout == LAYOUT_PACKED ? fieldInfo[field->type].scalarAlign : fieldInfo[field->type].baseAlign;
maxAlign = MAX(maxAlign, align);
lua_getfield(L, -1, "offset");
if (lua_isnil(L, -1)) {
field->offset = ALIGN(offset, align);
offset = field->offset + fieldInfo[field->type].size;
} else {
field->offset = luaL_checkinteger(L, -1);
}
lua_pop(L, 1);
lua_getfield(L, -1, "location");
if (lua_type(L, -1) == LUA_TSTRING) {
size_t nameLength;
const char* name = lua_tolstring(L, -1, &nameLength);
field->hash = (uint32_t) hash64(name, nameLength);
} else {
field->location = lua_isnil(L, -1) ? location++ : luaL_checkinteger(L, -1);
}
lua_pop(L, 1);
} else if (lua_isstring(L, -1)) {
FieldType type = luax_checkfieldtype(L, -1, &field->hash);
uint32_t align = layout == LAYOUT_PACKED ? fieldInfo[type].scalarAlign : fieldInfo[type].baseAlign;
field->offset = ALIGN(offset, align);
field->location = location++;
field->type = type;
offset = field->offset + fieldInfo[type].size;
maxAlign = MAX(maxAlign, align);
}
lua_pop(L, 1);
extent = MAX(extent, field->offset + fieldInfo[field->type].size);
}
lua_getfield(L, index, "stride");
if (lua_isnil(L, -1)) {
switch (layout) {
case LAYOUT_PACKED: info->stride = offset; break;
case LAYOUT_STD140: info->stride = ALIGN(offset, 16); break;
case LAYOUT_STD430: info->stride = ALIGN(offset, maxAlign); break;
default: break;
}
} else {
info->stride = luax_checku32(L, -1);
lovrCheck(info->stride > 0, "Buffer stride can not be zero");
lovrCheck(info->stride <= extent, "Buffer stride can not be smaller than the size of a single item");
}
lua_pop(L, 1);
break;
default: luaL_argerror(L, index, "Expected nil, string, or table");
void luax_checkdataformat(lua_State* L, int index, DataField* fields, uint32_t* count, uint32_t max) {
if (lua_type(L, index) == LUA_TSTRING) {
lovrCheck(*count < max, "Too many buffer fields");
fields[*count] = (DataField) { .type = luax_checkdatatype(L, index), .location = ~0u };
++*count;
return;
}
}
uint32_t luax_getbufferlength(lua_State* L, int index, BufferInfo* info) {
switch (lua_type(L, 1)) {
case LUA_TNUMBER: return lua_tointeger(L, 1); break;
case LUA_TTABLE: {
uint32_t length = luax_len(L, 1);
lovrCheck(lua_istable(L, index), "Expected string or table for Buffer field type");
int length = luax_len(L, index);
if (length < info->fieldCount) {
return 1;
}
lua_rawgeti(L, index, 1);
bool nested = lua_istable(L, -1);
lua_pop(L, 1);
lua_rawgeti(L, 1, 1);
if (lua_istable(L, -1)) {
lua_pop(L, 1);
return length;
}
lua_pop(L, 1);
lovrCheck(length > 0, "At least one Buffer field must be provided");
lovrCheck(*count + length + 1 < max, "Too many buffer fields");
DataField* parent = &fields[*count];
memset(parent, 0, sizeof(*parent));
parent->children = parent + 1;
parent->childCount = length;
++*count;
uint32_t tableStride = 0;
for (uint32_t i = 0, j = 1; i < info->fieldCount; i++) {
lua_rawgeti(L, 1, (int) j);
if (lua_isuserdata(L, -1)) {
tableStride++;
j++;
} else {
uint32_t components = fieldInfo[info->fields[i].type].components;
tableStride += components;
j += components;
}
lua_pop(L, 1);
}
return length / tableStride;
}
default: {
Blob* blob = luax_totype(L, 1, Blob);
if (blob) {
return (uint32_t) blob->size / info->stride;
} else {
return luax_typeerror(L, 1, "number, table, or Blob");
}
}
}
}
static Canvas luax_checkcanvas(lua_State* L, int index) {
Canvas canvas = {
.loads = { LOAD_CLEAR, LOAD_CLEAR, LOAD_CLEAR, LOAD_CLEAR },
.depth.format = FORMAT_D32F,
.depth.load = LOAD_CLEAR,
.depth.clear = 0.f,
.samples = 4
};
if (lua_type(L, index) == LUA_TSTRING && !strcmp(lua_tostring(L, index), "window")) {
canvas.count = 1;
canvas.textures[0] = lovrGraphicsGetWindowTexture();
lovrGraphicsGetBackgroundColor(canvas.clears[0]);
} else if (lua_isuserdata(L, index)) {
canvas.count = 1;
canvas.textures[0] = luax_checktype(L, index, Texture);
lovrGraphicsGetBackgroundColor(canvas.clears[0]);
} else if (!lua_istable(L, index)) {
luax_typeerror(L, index, "Texture or table");
} else {
for (uint32_t i = 0; i < 4; i++, canvas.count++) {
if (nested) {
DataField* child = parent->children;
for (int i = 0; i < length; i++, child++) {
lua_rawgeti(L, index, i + 1);
lua_getfield(L, -1, "type");
if (lua_isnil(L, -1)) {
break;
} else if (lua_type(L, -1) == LUA_TSTRING && !strcmp(lua_tostring(L, -1), "window")) {
canvas.textures[i] = lovrGraphicsGetWindowTexture();
lua_pop(L, 1);
lua_rawgeti(L, -1, 2);
}
lovrCheck(lua_type(L, -1) == LUA_TSTRING || lua_type(L, -1) == LUA_TTABLE, "Buffer fields must have a 'type' key");
luax_checkdataformat(L, -1, fields, count, max);
lua_pop(L, 1);
lua_getfield(L, -1, "name");
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
lua_rawgeti(L, -1, 1);
// Deprecated (named locations)
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
lua_getfield(L, -1, "location");
}
}
if (lua_type(L, -1) == LUA_TSTRING) {
child->name = lua_tostring(L, -1);
} else {
canvas.textures[i] = luax_checktype(L, -1, Texture);
child->name = NULL;
}
lua_pop(L, 1);
lua_getfield(L, -1, "offset");
child->offset = lua_isnil(L, -1) ? 0 : luax_checku32(L, -1);
lua_pop(L, 1);
lua_getfield(L, -1, "location");
child->location = lua_type(L, -1) == LUA_TNUMBER ? luax_checku32(L, -1) : ~0u;
lua_pop(L, 1);
lua_getfield(L, -1, "length");
child->length = lua_isnil(L, -1) ? 0 : luax_checku32(L, -1);
lua_pop(L, 1);
lua_pop(L, 1);
}
lua_getfield(L, index, "depth");
switch (lua_type(L, -1)) {
case LUA_TBOOLEAN: canvas.depth.format = lua_toboolean(L, -1) ? canvas.depth.format : 0; break;
case LUA_TSTRING: canvas.depth.format = luax_checkenum(L, -1, TextureFormat, NULL); break;
case LUA_TUSERDATA: canvas.depth.texture = luax_checktype(L, -1, Texture); break;
case LUA_TTABLE:
lua_getfield(L, -1, "format");
canvas.depth.format = lua_isnil(L, -1) ? canvas.depth.format : (uint32_t) luax_checkenum(L, -1, TextureFormat, NULL);
lua_pop(L, 1);
lua_getfield(L, -1, "texture");
canvas.depth.texture = lua_isnil(L, -1) ? NULL : luax_checktype(L, -1, Texture);
lua_pop(L, 1);
lua_getfield(L, -1, "clear");
switch (lua_type(L, -1)) {
case LUA_TNIL: break;
case LUA_TBOOLEAN: canvas.depth.load = lua_toboolean(L, -1) ? LOAD_DISCARD : LOAD_KEEP; break;
case LUA_TNUMBER: canvas.depth.clear = lua_tonumber(L, -1); break;
default: lovrThrow("Expected boolean or number for depth clear");
}
lua_pop(L, 1);
} else {
for (int i = 0; i < length; i++) {
lua_rawgeti(L, index, i + 1);
parent->children[i] = (DataField) { .type = luax_checkdatatype(L, -1), .location = ~0u };
lua_pop(L, 1);
++*count;
}
lua_pop(L, 1);
lua_getfield(L, index, "clear");
if (lua_istable(L, -1)) {
lua_rawgeti(L, -1, 1);
if (lua_type(L, -1) == LUA_TNUMBER) {
lua_rawgeti(L, -2, 2);
lua_rawgeti(L, -3, 3);
lua_rawgeti(L, -4, 4);
canvas.clears[0][0] = luax_checkfloat(L, -4);
canvas.clears[0][1] = luax_checkfloat(L, -3);
canvas.clears[0][2] = luax_checkfloat(L, -2);
canvas.clears[0][3] = luax_optfloat(L, -1, 1.f);
lua_pop(L, 4);
for (uint32_t i = 1; i < canvas.count; i++) {
memcpy(canvas.clears[i], canvas.clears[0], 4 * sizeof(float));
}
} else {
lua_pop(L, 1);
for (uint32_t i = 0; i < canvas.count; i++) {
lua_rawgeti(L, -1, i + 1);
if (lua_istable(L, -1)) {
lua_rawgeti(L, -1, 1);
lua_rawgeti(L, -2, 2);
lua_rawgeti(L, -3, 3);
lua_rawgeti(L, -4, 4);
canvas.clears[i][0] = luax_checkfloat(L, -4);
canvas.clears[i][1] = luax_checkfloat(L, -3);
canvas.clears[i][2] = luax_checkfloat(L, -2);
canvas.clears[i][3] = luax_optfloat(L, -1, 1.f);
lua_pop(L, 4);
} else {
canvas.loads[i] = lua_toboolean(L, -1) ? LOAD_DISCARD : LOAD_KEEP;
}
lua_pop(L, 1);
}
}
} else if (lua_isnumber(L, -1) || lua_isuserdata(L, -1)) {
luax_optcolor(L, -1, canvas.clears[0]);
} else if (!lua_isnil(L, -1)) {
LoadAction load = lua_toboolean(L, -1) ? LOAD_DISCARD : LOAD_KEEP;
canvas.loads[0] = canvas.loads[1] = canvas.loads[2] = canvas.loads[3] = load;
} else {
for (uint32_t i = 0; i < canvas.count; i++) {
lovrGraphicsGetBackgroundColor(canvas.clears[i]);
}
}
lua_pop(L, 1);
lua_getfield(L, index, "samples");
canvas.samples = lua_isnil(L, -1) ? canvas.samples : lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "mipmap");
canvas.mipmap = lua_toboolean(L, -1);
lua_pop(L, 1);
}
return canvas;
}
uint32_t luax_checkcomparemode(lua_State* L, int index) {
@ -686,6 +462,18 @@ static int l_lovrGraphicsIsInitialized(lua_State* L) {
return 1;
}
static int l_lovrGraphicsIsTimingEnabled(lua_State* L) {
bool enabled = lovrGraphicsIsTimingEnabled();
lua_pushboolean(L, enabled);
return 1;
}
static int l_lovrGraphicsSetTimingEnabled(lua_State* L) {
bool enable = lua_toboolean(L, 1);
lovrGraphicsSetTimingEnabled(enable);
return 0;
}
static int l_lovrGraphicsSubmit(lua_State* L) {
bool table = lua_istable(L, 1);
int length = table ? luax_len(L, 1) : lua_gettop(L);
@ -748,8 +536,8 @@ static int l_lovrGraphicsGetFeatures(lua_State* L) {
lua_pushboolean(L, features.textureASTC), lua_setfield(L, -2, "textureASTC");
lua_pushboolean(L, features.wireframe), lua_setfield(L, -2, "wireframe");
lua_pushboolean(L, features.depthClamp), lua_setfield(L, -2, "depthClamp");
lua_pushboolean(L, features.depthResolve), lua_setfield(L, -2, "depthResolve");
lua_pushboolean(L, features.indirectDrawFirstInstance), lua_setfield(L, -2, "indirectDrawFirstInstance");
lua_pushboolean(L, features.shaderTally), lua_setfield(L, -2, "shaderTally");
lua_pushboolean(L, features.float64), lua_setfield(L, -2, "float64");
lua_pushboolean(L, features.int64), lua_setfield(L, -2, "int64");
lua_pushboolean(L, features.int16), lua_setfield(L, -2, "int16");
@ -821,9 +609,10 @@ static int l_lovrGraphicsIsFormatSupported(lua_State* L) {
for (int i = 2; i <= top; i++) {
features |= 1 << luax_checkenum(L, i, TextureFeature, NULL);
}
bool supported = lovrGraphicsIsFormatSupported(format, features);
lua_pushboolean(L, supported);
return 1;
uint32_t support = lovrGraphicsGetFormatSupport(format, features);
lua_pushboolean(L, support & (1 << 0)); // linear
lua_pushboolean(L, support & (1 << 1)); // srgb
return 2;
}
static int l_lovrGraphicsGetBackgroundColor(lua_State* L) {
@ -855,19 +644,142 @@ static int l_lovrGraphicsGetDefaultFont(lua_State* L) {
return 1;
}
static int luax_newbuffer(lua_State* L, BufferInfo* info, DataField* format, uint32_t fieldCount) {
Shader* shader;
Blob* blob;
// Handle deprecated variants (type/format given second)
if (lua_type(L, 2) == LUA_TSTRING) {
lua_settop(L, 2);
lua_insert(L, 1);
} else if (lua_type(L, 2) == LUA_TTABLE) {
lua_rawgeti(L, 2, 1);
if (lua_type(L, -1) == LUA_TSTRING) {
lua_settop(L, 2);
lua_insert(L, 1);
} else if (lua_type(L, -1) == LUA_TTABLE) {
lua_getfield(L, -1, "type");
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
lua_rawgeti(L, -1, 2);
if (lua_type(L, -1) == LUA_TSTRING) {
lua_settop(L, 2);
lua_insert(L, 1);
} else {
lua_pop(L, 2);
}
} else {
lua_settop(L, 2);
lua_insert(L, 1);
}
} else {
lua_pop(L, 1);
}
}
int type = lua_type(L, 1);
if (type == LUA_TNUMBER) {
info->size = luax_checku32(L, 1);
return 0;
} else if (type == LUA_TUSERDATA && (blob = luax_totype(L, 1, Blob)) != NULL) {
lovrCheck(blob->size < UINT32_MAX, "Blob is too big to create a Buffer (max size is 1GB)");
info->size = (uint32_t) blob->size;
return 1;
} else if (type == LUA_TUSERDATA && (shader = luax_totype(L, 1, Shader)) != NULL) {
const char* name = NULL;
size_t length = 0;
uint32_t slot = ~0u;
switch (lua_type(L, 2)) {
case LUA_TSTRING: name = lua_tolstring(L, 2, &length); break;
case LUA_TNUMBER: slot = lua_tointeger(L, 2) - 1; break;
default: return luax_typeerror(L, 2, "string or number");
}
info->format = lovrShaderGetBufferFormat(shader, name, length, slot, &info->size, &info->fieldCount);
if (!info->format) {
if (name) {
lovrThrow("Shader has no Buffer named '%s'", name);
} else {
lovrThrow("Shader has no Buffer at slot %d", slot + 1);
}
}
return 3;
} else if (type == LUA_TTABLE || type == LUA_TSTRING) {
info->format = format;
luax_checkdataformat(L, 1, format, &info->fieldCount, fieldCount);
bool hasLength = false;
if (lua_istable(L, 1)) {
lua_getfield(L, 1, "layout");
info->layout = luax_checkenum(L, -1, BufferLayout, "packed");
lua_pop(L, 1);
lua_getfield(L, 1, "stride");
format->stride = lua_isnil(L, -1) ? 0 : luax_checku32(L, -1);
lua_pop(L, 1);
// You can give an explicit length, since length detection from table/Blob is unreliable
lua_getfield(L, 1, "length");
format->length = lua_isnil(L, -1) ? 0 : (hasLength = true, luax_checku32(L, -1));
lua_pop(L, 1);
}
// Unwrap structs that only have a single field
if (format->childCount == 1) {
*format = format->children[0];
info->fieldCount = 1;
}
// Note that we can set the length or the size and the other one will be inferred
switch (lua_type(L, 2)) {
case LUA_TNIL:
case LUA_TNONE:
format->length = format->childCount > 0 ? 0 : 1;
return 0;
case LUA_TNUMBER:
format->length = lua_tointeger(L, 2);
return 0;
case LUA_TTABLE:
if (!hasLength) {
format->length = luax_len(L, 2);
}
return 2;
default:
if ((blob = luax_totype(L, 2, Blob)) != NULL) {
lovrCheck(blob->size < UINT32_MAX, "Blob is too big to create a Buffer (max size is 1GB)");
info->size = (uint32_t) blob->size;
return 2;
}
return luax_typeerror(L, 2, "nil, number, table, or Blob");
}
} else {
return luax_typeerror(L, 1, "number, Blob, Shader, table, or string");
}
}
// Deprecated
static int l_lovrGraphicsGetBuffer(lua_State* L) {
BufferInfo info = { 0 };
DataField fields[128];
luax_checkbufferformat(L, 2, &info);
info.length = luax_getbufferlength(L, 1, &info);
int index = luax_newbuffer(L, &info, fields, COUNTOF(fields));
void* pointer;
bool hasData = !lua_isnumber(L, 1);
Buffer* buffer = lovrGraphicsGetBuffer(&info, hasData ? &pointer : NULL);
Buffer* buffer = lovrGraphicsGetBuffer(&info, index > 0 ? &pointer : NULL);
if (hasData) {
lua_settop(L, 1);
luax_readbufferdata(L, 1, buffer, pointer);
if (index) {
if (lua_istable(L, index)) {
const DataField* format = lovrBufferGetInfo(buffer)->format;
luax_checkbufferdata(L, index, format, pointer);
} else {
Blob* blob = luax_checktype(L, index, Blob);
memcpy(pointer, blob->data, info.size);
}
}
luax_pushtype(L, Buffer, buffer);
@ -877,17 +789,21 @@ static int l_lovrGraphicsGetBuffer(lua_State* L) {
static int l_lovrGraphicsNewBuffer(lua_State* L) {
BufferInfo info = { 0 };
DataField fields[128];
luax_checkbufferformat(L, 2, &info);
info.length = luax_getbufferlength(L, 1, &info);
int index = luax_newbuffer(L, &info, fields, COUNTOF(fields));
void* pointer;
bool hasData = !lua_isnumber(L, 1);
Buffer* buffer = lovrBufferCreate(&info, hasData ? &pointer : NULL);
Buffer* buffer = lovrBufferCreate(&info, index ? &pointer : NULL);
if (hasData) {
lua_settop(L, 1);
luax_readbufferdata(L, 1, buffer, pointer);
if (index) {
if (lua_istable(L, index)) {
const DataField* format = lovrBufferGetInfo(buffer)->format;
luax_checkbufferdata(L, index, format, pointer);
} else {
Blob* blob = luax_checktype(L, index, Blob);
memcpy(pointer, blob->data, info.size);
}
}
luax_pushtype(L, Buffer, buffer);
@ -918,6 +834,7 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
info.type = TEXTURE_ARRAY;
}
info.usage |= TEXTURE_RENDER;
info.mipmaps = 1;
} else if (lua_istable(L, 1)) {
info.imageCount = luax_len(L, index++);
images = info.imageCount > COUNTOF(stack) ? malloc(info.imageCount * sizeof(Image*)) : stack;
@ -1438,9 +1355,93 @@ static int l_lovrGraphicsNewFont(lua_State* L) {
return 1;
}
static int l_lovrGraphicsNewMesh(lua_State* L) {
MeshInfo info = { 0 };
bool hasFormat = false;
if (lua_istable(L, 1)) {
lua_rawgeti(L, 1, 1);
if (lua_istable(L, -1)) {
lua_getfield(L, -1, "type");
lua_rawgeti(L, -2, 1);
if (lua_type(L, -2) == LUA_TSTRING || lua_type(L, -1) == LUA_TSTRING) {
luax_checkdataformat(L, 1, info.format, &info.fieldCount, COUNTOF(info.format));
hasFormat = true;
}
lua_pop(L, 2);
}
lua_pop(L, 1);
}
if (!hasFormat) {
DataField format[] = {
{ .stride = 32 },
{ .name = "VertexPosition", .type = TYPE_F32x3, .offset = 0 },
{ .name = "VertexNormal", .type = TYPE_F32x3, .offset = 12 },
{ .name = "VertexUV", .type = TYPE_F32x2, .offset = 24 }
};
memcpy(info.format, format, sizeof(format));
info.format->childCount = COUNTOF(format) - 1;
info.format->children = info.format + 1;
info.fieldCount = COUNTOF(format);
}
Blob* blob = NULL;
bool hasData = false;
int index = 1 + hasFormat;
switch (lua_type(L, index)) {
case LUA_TNUMBER: info.format->length = luax_checku32(L, index++); break;
case LUA_TTABLE: info.format->length = luax_len(L, index++); hasData = true; break;
case LUA_TUSERDATA:
if ((info.vertexBuffer = luax_totype(L, index++, Buffer)) != NULL) break;
if ((blob = luax_totype(L, index++, Blob)) != NULL) {
lovrCheck(blob->size % info.format->stride == 0, "Blob size must be a multiple of vertex size");
info.format->length = blob->size / info.format->stride;
hasData = true;
break;
}
default: return luax_typeerror(L, index, "number, table, Blob, or Buffer");
}
if (info.vertexBuffer || luax_totype(L, index, Buffer)) {
info.storage = MESH_GPU;
} else {
info.storage = luax_checkenum(L, index + (luax_totype(L, index, Blob) ? 2 : 1), MeshStorage, "cpu");
}
void* vertices = NULL;
Mesh* mesh = lovrMeshCreate(&info, hasData ? &vertices : NULL);
if (blob) {
memcpy(vertices, blob->data, blob->size);
} else if (hasData) {
luax_checkbufferdata(L, index - 1, lovrMeshGetVertexFormat(mesh), vertices);
}
if (!lua_isnoneornil(L, index)) {
luax_pushtype(L, Mesh, mesh);
lua_getfield(L, -1, "setIndices");
lua_pushvalue(L, -2);
lua_pushvalue(L, index);
if (luax_totype(L, -1, Blob)) {
lua_pushvalue(L, index + 1);
lua_call(L, 3, 0);
} else {
lua_call(L, 2, 0);
}
} else {
luax_pushtype(L, Mesh, mesh);
}
lovrRelease(mesh, lovrMeshDestroy);
return 1;
}
static int l_lovrGraphicsNewModel(lua_State* L) {
ModelInfo info = { 0 };
info.data = luax_totype(L, 1, ModelData);
info.materials = true;
info.mipmaps = true;
if (!info.data) {
@ -1455,6 +1456,10 @@ static int l_lovrGraphicsNewModel(lua_State* L) {
lua_getfield(L, 2, "mipmaps");
info.mipmaps = lua_isnil(L, -1) || lua_toboolean(L, -1);
lua_pop(L, 1);
lua_getfield(L, 2, "materials");
info.materials = lua_isnil(L, -1) || lua_toboolean(L, -1);
lua_pop(L, 1);
}
Model* model = lovrModelCreate(&info);
@ -1464,43 +1469,34 @@ static int l_lovrGraphicsNewModel(lua_State* L) {
return 1;
}
static int l_lovrGraphicsNewTally(lua_State* L) {
TallyInfo info;
info.type = luax_checkenum(L, 1, TallyType, NULL);
info.count = luax_checku32(L, 2);
info.views = luax_optu32(L, 3, 2);
Tally* tally = lovrTallyCreate(&info);
luax_pushtype(L, Tally, tally);
lovrRelease(tally, lovrTallyDestroy);
int l_lovrPassSetCanvas(lua_State* L);
static int l_lovrGraphicsNewPass(lua_State* L) {
Pass* pass = lovrPassCreate();
luax_pushtype(L, Pass, pass);
lua_insert(L, 1);
l_lovrPassSetCanvas(L);
lua_settop(L, 1);
lovrRelease(pass, lovrPassDestroy);
return 1;
}
// Deprecated
static int l_lovrGraphicsGetPass(lua_State* L) {
PassInfo info = { 0 };
info.type = luax_checkenum(L, 1, PassType, NULL);
if (info.type == PASS_RENDER) {
info.canvas = luax_checkcanvas(L, 2);
}
if (lua_istable(L, 2)) {
lua_getfield(L, 2, "label");
info.label = lua_tostring(L, -1);
lua_pop(L, 1);
} else {
info.label = NULL;
}
Pass* pass = lovrGraphicsGetPass(&info);
Pass* pass = lovrGraphicsGetPass();
luax_pushtype(L, Pass, pass);
lovrRelease(pass, lovrPassDestroy);
lua_replace(L, 1);
l_lovrPassSetCanvas(L);
lua_settop(L, 1);
// No release, I live forever
return 1;
}
static const luaL_Reg lovrGraphics[] = {
{ "initialize", l_lovrGraphicsInitialize },
{ "isInitialized", l_lovrGraphicsIsInitialized },
{ "isTimingEnabled", l_lovrGraphicsIsTimingEnabled },
{ "setTimingEnabled", l_lovrGraphicsSetTimingEnabled },
{ "submit", l_lovrGraphicsSubmit },
{ "present", l_lovrGraphicsPresent },
{ "wait", l_lovrGraphicsWait },
@ -1520,8 +1516,9 @@ static const luaL_Reg lovrGraphics[] = {
{ "newShader", l_lovrGraphicsNewShader },
{ "newMaterial", l_lovrGraphicsNewMaterial },
{ "newFont", l_lovrGraphicsNewFont },
{ "newMesh", l_lovrGraphicsNewMesh },
{ "newModel", l_lovrGraphicsNewModel },
{ "newTally", l_lovrGraphicsNewTally },
{ "newPass", l_lovrGraphicsNewPass },
{ "getPass", l_lovrGraphicsGetPass },
{ NULL, NULL }
};
@ -1532,9 +1529,9 @@ extern const luaL_Reg lovrSampler[];
extern const luaL_Reg lovrShader[];
extern const luaL_Reg lovrMaterial[];
extern const luaL_Reg lovrFont[];
extern const luaL_Reg lovrMesh[];
extern const luaL_Reg lovrModel[];
extern const luaL_Reg lovrReadback[];
extern const luaL_Reg lovrTally[];
extern const luaL_Reg lovrPass[];
int luaopen_lovr_graphics(lua_State* L) {
@ -1546,9 +1543,9 @@ int luaopen_lovr_graphics(lua_State* L) {
luax_registertype(L, Shader);
luax_registertype(L, Material);
luax_registertype(L, Font);
luax_registertype(L, Mesh);
luax_registertype(L, Model);
luax_registertype(L, Readback);
luax_registertype(L, Tally);
luax_registertype(L, Pass);
return 1;
}

View File

@ -13,41 +13,47 @@ static const uint32_t vectorComponents[MAX_VECTOR_TYPES] = {
[V_MAT4] = 16
};
Buffer* luax_checkbuffer(lua_State* L, int index) {
Buffer* buffer = luax_checktype(L, index, Buffer);
lovrCheck(lovrBufferIsValid(buffer), "Buffers created with getBuffer can only be used for a single frame (unable to use this Buffer again because lovr.graphics.submit has been called since it was created)");
return buffer;
}
static const uint32_t fieldComponents[] = {
[FIELD_I8x4] = 4,
[FIELD_U8x4] = 4,
[FIELD_SN8x4] = 4,
[FIELD_UN8x4] = 4,
[FIELD_UN10x3] = 3,
[FIELD_I16] = 1,
[FIELD_I16x2] = 2,
[FIELD_I16x4] = 4,
[FIELD_U16] = 1,
[FIELD_U16x2] = 2,
[FIELD_U16x4] = 4,
[FIELD_SN16x2] = 2,
[FIELD_SN16x4] = 4,
[FIELD_UN16x2] = 2,
[FIELD_UN16x4] = 4,
[FIELD_I32] = 1,
[FIELD_I32x2] = 2,
[FIELD_I32x3] = 3,
[FIELD_I32x4] = 4,
[FIELD_U32] = 1,
[FIELD_U32x2] = 2,
[FIELD_U32x3] = 3,
[FIELD_U32x4] = 4,
[FIELD_F16x2] = 2,
[FIELD_F16x4] = 4,
[FIELD_F32] = 1,
[FIELD_F32x2] = 2,
[FIELD_F32x3] = 3,
[FIELD_F32x4] = 4,
[FIELD_MAT2] = 4,
[FIELD_MAT3] = 9,
[FIELD_MAT4] = 16,
[FIELD_INDEX16] = 1,
[FIELD_INDEX32] = 1
[TYPE_I8x4] = 4,
[TYPE_U8x4] = 4,
[TYPE_SN8x4] = 4,
[TYPE_UN8x4] = 4,
[TYPE_UN10x3] = 3,
[TYPE_I16] = 1,
[TYPE_I16x2] = 2,
[TYPE_I16x4] = 4,
[TYPE_U16] = 1,
[TYPE_U16x2] = 2,
[TYPE_U16x4] = 4,
[TYPE_SN16x2] = 2,
[TYPE_SN16x4] = 4,
[TYPE_UN16x2] = 2,
[TYPE_UN16x4] = 4,
[TYPE_I32] = 1,
[TYPE_I32x2] = 2,
[TYPE_I32x3] = 3,
[TYPE_I32x4] = 4,
[TYPE_U32] = 1,
[TYPE_U32x2] = 2,
[TYPE_U32x3] = 3,
[TYPE_U32x4] = 4,
[TYPE_F16x2] = 2,
[TYPE_F16x4] = 4,
[TYPE_F32] = 1,
[TYPE_F32x2] = 2,
[TYPE_F32x3] = 3,
[TYPE_F32x4] = 4,
[TYPE_MAT2] = 4,
[TYPE_MAT3] = 9,
[TYPE_MAT4] = 16,
[TYPE_INDEX16] = 1,
[TYPE_INDEX32] = 1
};
typedef union {
@ -61,213 +67,358 @@ typedef union {
float* f32;
} FieldPointer;
Buffer* luax_checkbuffer(lua_State* L, int index) {
Buffer* buffer = luax_checktype(L, index, Buffer);
lovrCheck(lovrBufferIsValid(buffer), "Buffers created with getBuffer can only be used for a single frame (unable to use this Buffer again because lovr.graphics.submit has been called since it was created)");
return buffer;
}
void luax_readbufferfield(lua_State* L, int index, int type, void* data) {
static void luax_tofield(lua_State* L, int index, DataType type, void* data) {
FieldPointer p = { .raw = data };
if (lua_isuserdata(L, index)) {
VectorType vectorType;
float* v = luax_tovector(L, index, &vectorType);
lovrCheck(vectorComponents[vectorType] == fieldComponents[type], "Vector type is incompatible with field type");
lovrCheck(vectorComponents[vectorType] == fieldComponents[type], "Vector type is incompatible with field type (expected %d components, got %d)", fieldComponents[type], vectorComponents[vectorType]);
switch (type) {
case FIELD_I8x4: for (int i = 0; i < 4; i++) p.i8[i] = (int8_t) v[i]; break;
case FIELD_U8x4: for (int i = 0; i < 4; i++) p.u8[i] = (uint8_t) v[i]; break;
case FIELD_SN8x4: for (int i = 0; i < 4; i++) p.i8[i] = (int8_t) CLAMP(v[i], -1.f, 1.f) * INT8_MAX; break;
case FIELD_UN8x4: for (int i = 0; i < 4; i++) p.u8[i] = (uint8_t) CLAMP(v[i], 0.f, 1.f) * UINT8_MAX; break;
case FIELD_UN10x3: for (int i = 0; i < 3; i++) p.u32[0] |= (uint32_t) (CLAMP(v[i], 0.f, 1.f) * 1023.f) << (10 * (2 - i)); break;
case FIELD_I16x2: for (int i = 0; i < 2; i++) p.i16[i] = (int16_t) v[i]; break;
case FIELD_I16x4: for (int i = 0; i < 4; i++) p.i16[i] = (int16_t) v[i]; break;
case FIELD_U16x2: for (int i = 0; i < 2; i++) p.u16[i] = (uint16_t) v[i]; break;
case FIELD_U16x4: for (int i = 0; i < 4; i++) p.u16[i] = (uint16_t) v[i]; break;
case FIELD_SN16x2: for (int i = 0; i < 2; i++) p.i16[i] = (int16_t) CLAMP(v[i], -1.f, 1.f) * INT16_MAX; break;
case FIELD_SN16x4: for (int i = 0; i < 4; i++) p.i16[i] = (int16_t) CLAMP(v[i], -1.f, 1.f) * INT16_MAX; break;
case FIELD_UN16x2: for (int i = 0; i < 2; i++) p.u16[i] = (uint16_t) CLAMP(v[i], 0.f, 1.f) * UINT16_MAX; break;
case FIELD_UN16x4: for (int i = 0; i < 4; i++) p.u16[i] = (uint16_t) CLAMP(v[i], 0.f, 1.f) * UINT16_MAX; break;
case FIELD_I32x2: for (int i = 0; i < 2; i++) p.i32[i] = (int32_t) v[i]; break;
case FIELD_I32x3: for (int i = 0; i < 3; i++) p.i32[i] = (int32_t) v[i]; break;
case FIELD_I32x4: for (int i = 0; i < 4; i++) p.i32[i] = (int32_t) v[i]; break;
case FIELD_U32x2: for (int i = 0; i < 2; i++) p.u32[i] = (uint32_t) v[i]; break;
case FIELD_U32x3: for (int i = 0; i < 3; i++) p.u32[i] = (uint32_t) v[i]; break;
case FIELD_U32x4: for (int i = 0; i < 4; i++) p.u32[i] = (uint32_t) v[i]; break;
case FIELD_F16x2: for (int i = 0; i < 2; i++) p.u16[i] = float32to16(v[i]); break;
case FIELD_F16x4: for (int i = 0; i < 4; i++) p.u16[i] = float32to16(v[i]); break;
case FIELD_F32x2: memcpy(data, v, 2 * sizeof(float)); break;
case FIELD_F32x3: memcpy(data, v, 3 * sizeof(float)); break;
case FIELD_F32x4: memcpy(data, v, 4 * sizeof(float)); break;
case FIELD_MAT4: memcpy(data, v, 16 * sizeof(float)); break;
case TYPE_I8x4: for (int i = 0; i < 4; i++) p.i8[i] = (int8_t) v[i]; break;
case TYPE_U8x4: for (int i = 0; i < 4; i++) p.u8[i] = (uint8_t) v[i]; break;
case TYPE_SN8x4: for (int i = 0; i < 4; i++) p.i8[i] = (int8_t) CLAMP(v[i], -1.f, 1.f) * INT8_MAX; break;
case TYPE_UN8x4: for (int i = 0; i < 4; i++) p.u8[i] = (uint8_t) CLAMP(v[i], 0.f, 1.f) * UINT8_MAX; break;
case TYPE_UN10x3: for (int i = 0; i < 3; i++) p.u32[0] |= (uint32_t) (CLAMP(v[i], 0.f, 1.f) * 1023.f) << (10 * (2 - i)); break;
case TYPE_I16x2: for (int i = 0; i < 2; i++) p.i16[i] = (int16_t) v[i]; break;
case TYPE_I16x4: for (int i = 0; i < 4; i++) p.i16[i] = (int16_t) v[i]; break;
case TYPE_U16x2: for (int i = 0; i < 2; i++) p.u16[i] = (uint16_t) v[i]; break;
case TYPE_U16x4: for (int i = 0; i < 4; i++) p.u16[i] = (uint16_t) v[i]; break;
case TYPE_SN16x2: for (int i = 0; i < 2; i++) p.i16[i] = (int16_t) CLAMP(v[i], -1.f, 1.f) * INT16_MAX; break;
case TYPE_SN16x4: for (int i = 0; i < 4; i++) p.i16[i] = (int16_t) CLAMP(v[i], -1.f, 1.f) * INT16_MAX; break;
case TYPE_UN16x2: for (int i = 0; i < 2; i++) p.u16[i] = (uint16_t) CLAMP(v[i], 0.f, 1.f) * UINT16_MAX; break;
case TYPE_UN16x4: for (int i = 0; i < 4; i++) p.u16[i] = (uint16_t) CLAMP(v[i], 0.f, 1.f) * UINT16_MAX; break;
case TYPE_I32x2: for (int i = 0; i < 2; i++) p.i32[i] = (int32_t) v[i]; break;
case TYPE_I32x3: for (int i = 0; i < 3; i++) p.i32[i] = (int32_t) v[i]; break;
case TYPE_I32x4: for (int i = 0; i < 4; i++) p.i32[i] = (int32_t) v[i]; break;
case TYPE_U32x2: for (int i = 0; i < 2; i++) p.u32[i] = (uint32_t) v[i]; break;
case TYPE_U32x3: for (int i = 0; i < 3; i++) p.u32[i] = (uint32_t) v[i]; break;
case TYPE_U32x4: for (int i = 0; i < 4; i++) p.u32[i] = (uint32_t) v[i]; break;
case TYPE_F16x2: for (int i = 0; i < 2; i++) p.u16[i] = float32to16(v[i]); break;
case TYPE_F16x4: for (int i = 0; i < 4; i++) p.u16[i] = float32to16(v[i]); break;
case TYPE_F32x2: memcpy(data, v, 2 * sizeof(float)); break;
case TYPE_F32x3: memcpy(data, v, 3 * sizeof(float)); break;
case TYPE_F32x4: memcpy(data, v, 4 * sizeof(float)); break;
case TYPE_MAT4: memcpy(data, v, 16 * sizeof(float)); break;
default: lovrUnreachable();
}
} else {
for (uint32_t i = 0; i < fieldComponents[type]; i++) {
double x = lua_tonumber(L, index + i);
switch (type) {
case FIELD_I8x4: p.i8[i] = (int8_t) x; break;
case FIELD_U8x4: p.u8[i] = (uint8_t) x; break;
case FIELD_SN8x4: p.i8[i] = (int8_t) CLAMP(x, -1.f, 1.f) * INT8_MAX; break;
case FIELD_UN8x4: p.u8[i] = (uint8_t) CLAMP(x, 0.f, 1.f) * UINT8_MAX; break;
case FIELD_UN10x3: p.u32[0] |= (uint32_t) (CLAMP(x, 0.f, 1.f) * 1023.f) << (10 * (2 - i)); break;
case FIELD_I16: p.i16[i] = (int16_t) x; break;
case FIELD_I16x2: p.i16[i] = (int16_t) x; break;
case FIELD_I16x4: p.i16[i] = (int16_t) x; break;
case FIELD_U16: p.u16[i] = (uint16_t) x; break;
case FIELD_U16x2: p.u16[i] = (uint16_t) x; break;
case FIELD_U16x4: p.u16[i] = (uint16_t) x; break;
case FIELD_SN16x2: p.i16[i] = (int16_t) CLAMP(x, -1.f, 1.f) * INT16_MAX; break;
case FIELD_SN16x4: p.i16[i] = (int16_t) CLAMP(x, -1.f, 1.f) * INT16_MAX; break;
case FIELD_UN16x2: p.u16[i] = (uint16_t) CLAMP(x, 0.f, 1.f) * UINT16_MAX; break;
case FIELD_UN16x4: p.u16[i] = (uint16_t) CLAMP(x, 0.f, 1.f) * UINT16_MAX; break;
case FIELD_I32: p.i32[i] = (int32_t) x; break;
case FIELD_I32x2: p.i32[i] = (int32_t) x; break;
case FIELD_I32x3: p.i32[i] = (int32_t) x; break;
case FIELD_I32x4: p.i32[i] = (int32_t) x; break;
case FIELD_U32: p.u32[i] = (uint32_t) x; break;
case FIELD_U32x2: p.u32[i] = (uint32_t) x; break;
case FIELD_U32x3: p.u32[i] = (uint32_t) x; break;
case FIELD_U32x4: p.i32[i] = (uint32_t) x; break;
case FIELD_F16x2: p.u16[i] = float32to16(x); break;
case FIELD_F16x4: p.u16[i] = float32to16(x); break;
case FIELD_F32: p.f32[i] = (float) x; break;
case FIELD_F32x2: p.f32[i] = (float) x; break;
case FIELD_F32x3: p.f32[i] = (float) x; break;
case FIELD_F32x4: p.f32[i] = (float) x; break;
case FIELD_MAT2: p.f32[i] = (float) x; break;
case FIELD_MAT3: p.f32[i] = (float) x; break;
case FIELD_MAT4: p.f32[i] = (float) x; break;
case FIELD_INDEX16: p.u16[i] = (uint16_t) x - 1; break;
case FIELD_INDEX32: p.u32[i] = (uint32_t) x - 1; break;
case TYPE_I8x4: p.i8[i] = (int8_t) x; break;
case TYPE_U8x4: p.u8[i] = (uint8_t) x; break;
case TYPE_SN8x4: p.i8[i] = (int8_t) CLAMP(x, -1.f, 1.f) * INT8_MAX; break;
case TYPE_UN8x4: p.u8[i] = (uint8_t) CLAMP(x, 0.f, 1.f) * UINT8_MAX; break;
case TYPE_UN10x3: p.u32[0] |= (uint32_t) (CLAMP(x, 0.f, 1.f) * 1023.f) << (10 * (2 - i)); break;
case TYPE_I16: p.i16[i] = (int16_t) x; break;
case TYPE_I16x2: p.i16[i] = (int16_t) x; break;
case TYPE_I16x4: p.i16[i] = (int16_t) x; break;
case TYPE_U16: p.u16[i] = (uint16_t) x; break;
case TYPE_U16x2: p.u16[i] = (uint16_t) x; break;
case TYPE_U16x4: p.u16[i] = (uint16_t) x; break;
case TYPE_SN16x2: p.i16[i] = (int16_t) CLAMP(x, -1.f, 1.f) * INT16_MAX; break;
case TYPE_SN16x4: p.i16[i] = (int16_t) CLAMP(x, -1.f, 1.f) * INT16_MAX; break;
case TYPE_UN16x2: p.u16[i] = (uint16_t) CLAMP(x, 0.f, 1.f) * UINT16_MAX; break;
case TYPE_UN16x4: p.u16[i] = (uint16_t) CLAMP(x, 0.f, 1.f) * UINT16_MAX; break;
case TYPE_I32: p.i32[i] = (int32_t) x; break;
case TYPE_I32x2: p.i32[i] = (int32_t) x; break;
case TYPE_I32x3: p.i32[i] = (int32_t) x; break;
case TYPE_I32x4: p.i32[i] = (int32_t) x; break;
case TYPE_U32: p.u32[i] = (uint32_t) x; break;
case TYPE_U32x2: p.u32[i] = (uint32_t) x; break;
case TYPE_U32x3: p.u32[i] = (uint32_t) x; break;
case TYPE_U32x4: p.i32[i] = (uint32_t) x; break;
case TYPE_F16x2: p.u16[i] = float32to16(x); break;
case TYPE_F16x4: p.u16[i] = float32to16(x); break;
case TYPE_F32: p.f32[i] = (float) x; break;
case TYPE_F32x2: p.f32[i] = (float) x; break;
case TYPE_F32x3: p.f32[i] = (float) x; break;
case TYPE_F32x4: p.f32[i] = (float) x; break;
case TYPE_MAT2: p.f32[i] = (float) x; break;
case TYPE_MAT3: p.f32[i] = (float) x; break;
case TYPE_MAT4: p.f32[i] = (float) x; break;
case TYPE_INDEX16: p.u16[i] = (uint16_t) x - 1; break;
case TYPE_INDEX32: p.u32[i] = (uint32_t) x - 1; break;
default: lovrUnreachable();
}
}
}
}
void luax_readbufferdata(lua_State* L, int index, Buffer* buffer, char* data) {
const BufferInfo* info = lovrBufferGetInfo(buffer);
uint32_t stride = info->stride;
static void luax_checkstruct(lua_State* L, int index, const DataField* field, char* data) {
index = index > 0 ? index : lua_gettop(L) + 1 + index;
Blob* blob = luax_totype(L, index, Blob);
uint32_t srcIndex = luax_optu32(L, index + 1, 1) - 1;
uint32_t dstIndex = luax_optu32(L, index + 2, 1) - 1;
if (blob) {
uint32_t limit = MIN(blob->size / stride - srcIndex, info->length - dstIndex);
uint32_t count = luax_optu32(L, index + 3, limit);
lovrCheck(srcIndex + count <= blob->size / stride, "Tried to read too many elements from the Blob");
lovrCheck(dstIndex + count <= info->length, "Tried to write Buffer elements [%d,%d] but Buffer can only hold %d things", dstIndex + 1, dstIndex + count - 1, info->length);
data = data ? data : lovrBufferMap(buffer, dstIndex * stride, count * stride);
char* src = (char*) blob->data + srcIndex * stride;
memcpy(data, src, count * stride);
return;
if (!lua_istable(L, index)) {
if (field->childCount == 1) {
luax_checkbufferdata(L, index, field->children, data + field->children->offset);
return;
} else {
lovrThrow("Expected table for struct data");
}
}
luaL_checktype(L, index, LUA_TTABLE);
lua_rawgeti(L, index, 1);
bool nested = lua_istable(L, -1);
lua_pop(L, 1);
if (!field->children[0].name || luax_len(L, index) > 0) {
for (uint32_t i = 0, j = 1; i < field->childCount; i++) {
const DataField* child = &field->children[i];
int n = 1;
uint32_t length = luax_len(L, index);
uint32_t limit = nested ? MIN(length - srcIndex, info->length - dstIndex) : info->length - dstIndex;
uint32_t count = luax_optu32(L, index + 3, limit);
lovrCheck(dstIndex + count <= info->length, "Tried to write Buffer elements [%d,%d] but Buffer can only hold %d things", dstIndex + 1, dstIndex + count - 1, info->length);
data = data ? data : lovrBufferMap(buffer, dstIndex * stride, count * stride);
if (nested) {
for (uint32_t i = 0; i < count; i++) {
lua_rawgeti(L, index, i + srcIndex + 1);
lovrCheck(lua_type(L, -1) == LUA_TTABLE, "Expected table of tables");
int j = 1;
for (uint32_t f = 0; f < info->fieldCount; f++) {
int n = 1;
lua_rawgeti(L, -1, j);
const BufferField* field = &info->fields[f];
if (!lua_isuserdata(L, -1)) {
n = fieldComponents[field->type];
for (int c = 1; c < n; c++) {
lua_rawgeti(L, -c - 1, j + c);
}
lua_rawgeti(L, index, j);
if (child->length == 0 && child->childCount == 0 && lua_type(L, -1) == LUA_TNUMBER) {
for (uint32_t c = fieldComponents[child->type]; c > 1; c--, n++) {
lua_rawgeti(L, index, j + n);
}
luax_readbufferfield(L, -n, field->type, data + field->offset);
lua_pop(L, n);
j += n;
}
data += info->stride;
luax_checkbufferdata(L, -n, child, data + child->offset);
lua_pop(L, n);
j += n;
}
} else {
for (uint32_t i = 0; i < field->childCount; i++) {
const DataField* child = &field->children[i];
lua_pushstring(L, child->name);
lua_rawget(L, index);
luax_checkbufferdata(L, -1, child, data + child->offset);
lua_pop(L, 1);
}
}
}
static void luax_checkarray(lua_State* L, int index, uint32_t offset, uint32_t count, const DataField* field, char* data) {
lovrCheck(lua_istable(L, index), "Expected table for array data");
if (field->childCount > 0) {
for (uint32_t i = 0; i < count; i++, data += field->stride) {
lua_rawgeti(L, index, i + offset + 1);
luax_checkstruct(L, -1, field, data);
lua_pop(L, 1);
}
} else {
for (uint32_t i = 0, j = srcIndex + 1; i < count && j <= length; i++) {
for (uint32_t f = 0; f < info->fieldCount; f++) {
int n = 1;
lua_rawgeti(L, index, j);
const BufferField* field = &info->fields[f];
if (!lua_isuserdata(L, -1)) {
n = fieldComponents[field->type];
for (int c = 1; c < n; c++) {
lua_rawgeti(L, index, (int) j + c);
}
int n = fieldComponents[field->type];
lua_rawgeti(L, index, 1);
int type = lua_type(L, -1);
lua_pop(L, 1);
if (type == LUA_TUSERDATA || type == LUA_TLIGHTUSERDATA) {
for (uint32_t i = 0; i < count; i++, data += field->stride) {
lua_rawgeti(L, index, i + offset + 1);
int type = lua_type(L, -1);
if (type == LUA_TUSERDATA || type == LUA_TLIGHTUSERDATA) {
luax_tofield(L, -1, field->type, data);
} else if (type == LUA_TNIL) {
break;
} else {
lovrThrow("Expected vector object for array value (arrays must use the same type for all elements)");
}
luax_readbufferfield(L, -n, field->type, data + field->offset);
lua_pop(L, n);
j += n;
lua_pop(L, 1);
}
data += info->stride;
} else if (type == LUA_TNUMBER) {
index = index > 0 ? index : lua_gettop(L) + 1 + index;
for (uint32_t i = 0; i < count; i++, data += field->stride) {
for (int c = 1; c <= n; c++) {
lua_rawgeti(L, index, i * n + offset + c);
}
luax_tofield(L, -n, field->type, data);
lua_pop(L, n);
}
} else if (type == LUA_TTABLE) {
for (uint32_t i = 0; i < count; i++, data += field->stride) {
lua_rawgeti(L, index, i + offset + 1);
lovrCheck(lua_istable(L, -1), "Expected nested table for array value (arrays must use the same type for all elements)");
for (int c = 1, j = -1; c <= n; c++, j--) {
lua_rawgeti(L, j, c);
}
luax_tofield(L, -n, field->type, data);
lua_pop(L, n + 1);
}
} else if (type == LUA_TNIL) {
return;
} else {
lovrThrow("Expected number, table, or vector for array contents");
}
}
}
void luax_checkbufferdata(lua_State* L, int index, const DataField* field, char* data) {
if (field->length > 0) {
luax_checkarray(L, index, 0, field->length, field, data);
} else if (field->childCount > 0) {
luax_checkstruct(L, index, field, data);
} else if (lua_type(L, index) == LUA_TTABLE) {
int n = fieldComponents[field->type];
for (int c = 0; c < n; c++) {
lua_rawgeti(L, index < 0 ? index - c : index, c + 1);
}
luax_tofield(L, -n, field->type, data);
lua_pop(L, n);
} else {
luax_tofield(L, index, field->type, data);
}
}
static int luax_pushcomponents(lua_State* L, const DataField* field, char* data) {
FieldPointer p = { .raw = data };
int n = (int) fieldComponents[field->type];
switch (field->type) {
case TYPE_I8x4: for (int i = 0; i < n; i++) lua_pushinteger(L, p.i8[i]); return n;
case TYPE_U8x4: for (int i = 0; i < n; i++) lua_pushinteger(L, p.u8[i]); return n;
case TYPE_SN8x4: for (int i = 0; i < n; i++) lua_pushnumber(L, MAX((float) p.i8[i] / 127, -1.f)); return n;
case TYPE_UN8x4: for (int i = 0; i < n; i++) lua_pushnumber(L, (float) p.u8[i] / 255); return n;
case TYPE_UN10x3: for (int i = 0; i < n; i++) lua_pushnumber(L, (float) ((p.u32[0] >> (10 * (2 - i))) & 0x3ff) / 1023.f); return n;
case TYPE_I16x2: for (int i = 0; i < n; i++) lua_pushinteger(L, p.i16[i]); return n;
case TYPE_I16x4: for (int i = 0; i < n; i++) lua_pushinteger(L, p.i16[i]); return n;
case TYPE_U16x2: for (int i = 0; i < n; i++) lua_pushinteger(L, p.u16[i]); return n;
case TYPE_U16x4: for (int i = 0; i < n; i++) lua_pushinteger(L, p.u16[i]); return n;
case TYPE_SN16x2: for (int i = 0; i < n; i++) lua_pushnumber(L, MAX((float) p.i16[i] / 32767, -1.f)); return n;
case TYPE_SN16x4: for (int i = 0; i < n; i++) lua_pushnumber(L, MAX((float) p.i16[i] / 32767, -1.f)); return n;
case TYPE_UN16x2: for (int i = 0; i < n; i++) lua_pushnumber(L, (float) p.u16[i] / 65535); return n;
case TYPE_UN16x4: for (int i = 0; i < n; i++) lua_pushnumber(L, (float) p.u16[i] / 65535); return n;
case TYPE_I32: lua_pushinteger(L, p.i32[0]); return n;
case TYPE_I32x2: for (int i = 0; i < n; i++) lua_pushinteger(L, p.i32[i]); return n;
case TYPE_I32x3: for (int i = 0; i < n; i++) lua_pushinteger(L, p.i32[i]); return n;
case TYPE_I32x4: for (int i = 0; i < n; i++) lua_pushinteger(L, p.i32[i]); return n;
case TYPE_U32: lua_pushinteger(L, p.u32[0]); return n;
case TYPE_U32x2: for (int i = 0; i < n; i++) lua_pushinteger(L, p.u32[i]); return n;
case TYPE_U32x3: for (int i = 0; i < n; i++) lua_pushinteger(L, p.u32[i]); return n;
case TYPE_U32x4: for (int i = 0; i < n; i++) lua_pushinteger(L, p.u32[i]); return n;
case TYPE_F16x2: for (int i = 0; i < n; i++) lua_pushnumber(L, float16to32(p.u16[i])); return n;
case TYPE_F16x4: for (int i = 0; i < n; i++) lua_pushnumber(L, float16to32(p.u16[i])); return n;
case TYPE_F32: lua_pushnumber(L, p.f32[0]); return n;
case TYPE_F32x2: for (int i = 0; i < n; i++) lua_pushnumber(L, p.f32[i]); return n;
case TYPE_F32x3: for (int i = 0; i < n; i++) lua_pushnumber(L, p.f32[i]); return n;
case TYPE_F32x4: for (int i = 0; i < n; i++) lua_pushnumber(L, p.f32[i]); return n;
case TYPE_MAT2: for (int i = 0; i < n; i++) lua_pushnumber(L, p.f32[i]); return n;
case TYPE_MAT3: for (int i = 0; i < n; i++) lua_pushnumber(L, p.f32[i]); return n;
case TYPE_MAT4: for (int i = 0; i < n; i++) lua_pushnumber(L, p.f32[i]); return n;
case TYPE_INDEX16: lua_pushinteger(L, p.u16[0] + 1); return n;
case TYPE_INDEX32: lua_pushinteger(L, p.u32[0] + 1); return n;
default: lovrUnreachable(); return 0;
}
}
static int luax_pushstruct(lua_State* L, const DataField* field, char* data) {
lua_createtable(L, 0, field->childCount);
for (uint32_t i = 0; i < field->childCount; i++) {
const DataField* child = &field->children[i];
if (child->childCount > 0 || child->length > 0 || fieldComponents[child->type] == 1) {
luax_pushbufferdata(L, &field->children[i], data + field->children[i].offset);
} else {
int n = fieldComponents[field->type];
lua_createtable(L, n, 0);
luax_pushbufferdata(L, &field->children[i], data + field->children[i].offset);
for (int j = n + 1, k = n; k >= 1; k++, j--) {
lua_rawseti(L, -j, k);
}
}
lua_setfield(L, -2, field->children[i].name);
}
return 1;
}
int luax_pushbufferdata(lua_State* L, const DataField* field, char* data) {
if (field->length > 0) {
lua_createtable(L, field->length, 0);
if (field->childCount > 0) {
for (uint32_t i = 0; i < field->length; i++) {
luax_pushstruct(L, field, data);
lua_rawseti(L, -2, i + 1);
data += field->stride;
}
} else {
for (uint32_t i = 0; i < field->length; i++) {
int n = (int) fieldComponents[field->type];
if (n > 1) {
lua_createtable(L, n, 0);
luax_pushcomponents(L, field, data);
for (int j = n + 1, k = n; k >= 1; k--, j--) {
lua_rawseti(L, -j, k);
}
} else {
luax_pushcomponents(L, field, data);
}
lua_rawseti(L, -2, i + 1);
data += field->stride;
}
}
return 1;
} else if (field->childCount > 0) {
return luax_pushstruct(L, field, data);
} else {
return luax_pushcomponents(L, field, data);
}
}
static int l_lovrBufferGetSize(lua_State* L) {
Buffer* buffer = luax_checkbuffer(L, 1);
const BufferInfo* info = lovrBufferGetInfo(buffer);
uint32_t size = info->length * MAX(info->stride, 1);
lua_pushinteger(L, size);
lua_pushinteger(L, info->size);
return 1;
}
static int l_lovrBufferGetLength(lua_State* L) {
Buffer* buffer = luax_checkbuffer(L, 1);
const BufferInfo* info = lovrBufferGetInfo(buffer);
lua_pushinteger(L, info->length);
uint32_t length = info->format ? info->format->length : 0;
lua_pushinteger(L, length);
return 1;
}
static int l_lovrBufferGetStride(lua_State* L) {
Buffer* buffer = luax_checkbuffer(L, 1);
const BufferInfo* info = lovrBufferGetInfo(buffer);
lua_pushinteger(L, info->stride);
uint32_t stride = info->format && info->format->length > 0 ? info->format->stride : 0;
lua_pushinteger(L, stride);
return 1;
}
static void luax_pushbufferformat(lua_State* L, const DataField* format, uint32_t count, bool root) {
lua_createtable(L, count, 0);
for (uint32_t i = 0; i < count; i++) {
const DataField* field = &format[i];
lua_newtable(L);
if (field->name) {
lua_pushstring(L, field->name);
lua_setfield(L, -2, "name");
}
if (field->location != ~0u) {
lua_pushinteger(L, field->location);
lua_setfield(L, -2, "location");
}
if (field->childCount > 0) {
luax_pushbufferformat(L, field->children, field->childCount, false);
} else {
luax_pushenum(L, DataType, field->type);
}
lua_setfield(L, -2, "type");
lua_pushinteger(L, field->offset);
lua_setfield(L, -2, "offset");
if (field->length > 0 && !root) {
lua_pushinteger(L, field->length);
lua_setfield(L, -2, "length");
lua_pushinteger(L, field->stride);
lua_setfield(L, -2, "stride");
}
lua_rawseti(L, -2, i + 1);
}
}
static int l_lovrBufferGetFormat(lua_State* L) {
Buffer* buffer = luax_checkbuffer(L, 1);
const BufferInfo* info = lovrBufferGetInfo(buffer);
lua_createtable(L, info->fieldCount, 0);
for (uint32_t i = 0; i < info->fieldCount; i++) {
const BufferField* field = &info->fields[i];
lua_createtable(L, 0, 3);
luax_pushenum(L, FieldType, field->type);
lua_setfield(L, -2, "type");
lua_pushinteger(L, field->offset);
lua_setfield(L, -2, "offset");
if (!field->hash) {
lua_pushinteger(L, field->location);
lua_setfield(L, -2, "location");
}
lua_rawseti(L, -2, i + 1);
if (info->fieldCount == 0) {
lua_pushnil(L);
} else if (info->format->childCount > 0) {
luax_pushbufferformat(L, info->format->children, info->format->childCount, true);
} else {
luax_pushbufferformat(L, info->format, 1, true);
}
return 1;
}
static int l_lovrBufferGetPointer(lua_State* L) {
Buffer* buffer = luax_checkbuffer(L, 1);
if (!lovrBufferIsTemporary(buffer)) {
lua_pushnil(L);
return 1;
}
void* pointer = lovrBufferMap(buffer, 0, ~0u);
void* pointer = lovrBufferSetData(buffer, 0, ~0u);
lua_pushlightuserdata(L, pointer);
return 1;
}
@ -279,18 +430,91 @@ static int l_lovrBufferIsTemporary(lua_State* L) {
return 1;
}
static int l_lovrBufferNewReadback(lua_State* L) {
Buffer* buffer = luax_checkbuffer(L, 1);
uint32_t offset = luax_optu32(L, 2, 0);
uint32_t extent = luax_optu32(L, 3, ~0u);
Readback* readback = lovrReadbackCreateBuffer(buffer, offset, extent);
luax_pushtype(L, Readback, readback);
lovrRelease(readback, lovrReadbackDestroy);
return 1;
}
static int l_lovrBufferGetData(lua_State* L) {
Buffer* buffer = luax_checkbuffer(L, 1);
const BufferInfo* info = lovrBufferGetInfo(buffer);
void* data = lovrBufferGetData(buffer, 0, info->size);
return luax_pushbufferdata(L, info->format, data);
}
static int l_lovrBufferSetData(lua_State* L) {
Buffer* buffer = luax_checkbuffer(L, 1);
luax_readbufferdata(L, 2, buffer, NULL);
return 0;
const BufferInfo* info = lovrBufferGetInfo(buffer);
if (lua_istable(L, 2)) {
lovrCheck(info->format, "Buffer must be created with format information to copy a table to it");
if (info->format->length == 0) {
void* data = lovrBufferSetData(buffer, 0, info->size);
luax_checkbufferdata(L, 2, info->format, data);
} else {
lua_rawgeti(L, 2, 1);
bool nested = lua_istable(L, -1);
lua_pop(L, 1);
const DataField* array = info->format;
uint32_t tableLength = luax_len(L, 2);
uint32_t dstIndex = luax_optu32(L, 3, 1) - 1;
uint32_t srcIndex = luax_optu32(L, 4, 1) - 1;
uint32_t limit = nested ? MIN(array->length - dstIndex, tableLength - srcIndex) : array->length - dstIndex;
uint32_t count = luax_optu32(L, 5, limit);
lovrCheck(dstIndex + count <= array->length, "Buffer copy range exceeds the length of the target Buffer");
void* data = lovrBufferSetData(buffer, dstIndex * array->stride, count * array->stride);
luax_checkarray(L, 2, srcIndex, count, array, data);
}
return 0;
}
Blob* blob = luax_totype(L, 2, Blob);
if (blob) {
uint32_t dstOffset = luax_optu32(L, 3, 0);
uint32_t srcOffset = luax_optu32(L, 4, 0);
lovrCheck(dstOffset < info->size, "Buffer offset is bigger than the size of the Buffer");
lovrCheck(srcOffset < blob->size, "Blob offset is bigger than the size of the Blob");
uint32_t limit = (uint32_t) MIN(info->size - dstOffset, blob->size - srcOffset);
uint32_t extent = luax_optu32(L, 5, limit);
lovrCheck(extent <= info->size - dstOffset, "Buffer copy range exceeds the size of the target Buffer");
lovrCheck(extent <= blob->size - srcOffset, "Buffer copy range exceeds the size of the source Blob");
void* data = lovrBufferSetData(buffer, dstOffset, extent);
memcpy(data, (char*) blob->data + srcOffset, extent);
return 0;
}
Buffer* src = luax_totype(L, 2, Buffer);
if (src) {
Buffer* dst = buffer;
uint32_t dstOffset = luax_optu32(L, 3, 0);
uint32_t srcOffset = luax_optu32(L, 4, 0);
const BufferInfo* dstInfo = lovrBufferGetInfo(dst);
const BufferInfo* srcInfo = lovrBufferGetInfo(src);
uint32_t limit = MIN(dstInfo->size - dstOffset, srcInfo->size - srcOffset);
uint32_t extent = luax_optu32(L, 5, limit);
lovrBufferCopy(src, dst, srcOffset, dstOffset, extent);
return 0;
}
return luax_typeerror(L, 2, "table, Blob, or Buffer");
}
static int l_lovrBufferClear(lua_State* L) {
Buffer* buffer = luax_checkbuffer(L, 1);
const BufferInfo* info = lovrBufferGetInfo(buffer);
uint32_t index = luax_optu32(L, 2, 1);
uint32_t count = luax_optu32(L, 3, info->length - index + 1);
lovrBufferClear(buffer, (index - 1) * info->stride, count * info->stride);
uint32_t offset = luax_optu32(L, 2, 0);
uint32_t extent = luax_optu32(L, 3, ~0u);
lovrBufferClear(buffer, offset, extent);
return 0;
}
@ -301,6 +525,8 @@ const luaL_Reg lovrBuffer[] = {
{ "getFormat", l_lovrBufferGetFormat },
{ "getPointer", l_lovrBufferGetPointer },
{ "isTemporary", l_lovrBufferIsTemporary },
{ "newReadback", l_lovrBufferNewReadback },
{ "getData", l_lovrBufferGetData },
{ "setData", l_lovrBufferSetData },
{ "clear", l_lovrBufferClear },
{ NULL, NULL }

238
src/api/l_graphics_mesh.c Normal file
View File

@ -0,0 +1,238 @@
#include "api.h"
#include "graphics/graphics.h"
#include "data/blob.h"
#include "util.h"
#include <string.h>
static int l_lovrMeshGetVertexCount(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
const DataField* format = lovrMeshGetVertexFormat(mesh);
lua_pushinteger(L, format->length);
return 1;
}
static int l_lovrMeshGetVertexStride(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
const DataField* format = lovrMeshGetVertexFormat(mesh);
lua_pushinteger(L, format->stride);
return 1;
}
static int l_lovrMeshGetVertexFormat(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
const DataField* format = lovrMeshGetVertexFormat(mesh);
const DataField* attributes = format->childCount > 0 ? format->children : format;
uint32_t attributeCount = MAX(format->childCount, 1);
lua_createtable(L, (int) attributeCount, 0);
for (uint32_t i = 0; i < attributeCount; i++) {
const DataField* attribute = &attributes[i];
lua_createtable(L, 3, 0);
lua_pushstring(L, attribute->name);
lua_rawseti(L, -2, 1);
luax_pushenum(L, DataType, attribute->type);
lua_rawseti(L, -2, 2);
lua_rawseti(L, -2, (int) i + 1);
}
return 1;
}
static int l_lovrMeshGetVertexBuffer(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
Buffer* buffer = lovrMeshGetVertexBuffer(mesh);
luax_pushtype(L, Buffer, buffer);
return 1;
}
static int l_lovrMeshGetIndexBuffer(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
Buffer* buffer = lovrMeshGetIndexBuffer(mesh);
luax_pushtype(L, Buffer, buffer);
return 1;
}
static int l_lovrMeshSetIndexBuffer(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
Buffer* buffer = luax_checktype(L, 2, Buffer);
lovrMeshSetIndexBuffer(mesh, buffer);
return 0;
}
static int l_lovrMeshGetVertices(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
uint32_t index = luax_optu32(L, 2, 1) - 1;
uint32_t count = luax_optu32(L, 3, ~0u);
DataField format = *lovrMeshGetVertexFormat(mesh);
void* data = lovrMeshGetVertices(mesh, index, count);
format.length = count == ~0u ? format.length - index : count;
return luax_pushbufferdata(L, &format, data);
}
static int l_lovrMeshSetVertices(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
uint32_t index = luax_optu32(L, 3, 1) - 1;
uint32_t count = luax_optu32(L, 4, ~0u);
void* data = lovrMeshSetVertices(mesh, index, count);
luax_checkbufferdata(L, 2, lovrMeshGetVertexFormat(mesh), data);
return 0;
}
static int l_lovrMeshGetIndices(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
DataField format;
void* data = lovrMeshGetIndices(mesh, &format);
return luax_pushbufferdata(L, &format, data);
}
static int l_lovrMeshSetIndices(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
switch (lua_type(L, 2)) {
case LUA_TNONE:
case LUA_TNIL:
lovrMeshSetIndices(mesh, 0, TYPE_U16);
return 0;
case LUA_TTABLE: {
uint32_t count = luax_len(L, 2);
DataType type = lovrMeshGetVertexFormat(mesh)->length > 0xffff ? TYPE_INDEX32 : TYPE_INDEX16;
void* data = lovrMeshSetIndices(mesh, count, type);
DataField format = { .type = type, .length = count, .stride = type == TYPE_INDEX32 ? 4 : 2 };
luax_checkbufferdata(L, 2, &format, data);
break;
}
case LUA_TUSERDATA: {
Blob* blob = luax_checktype(L, 2, Blob);
DataType type = luax_checkenum(L, 3, DataType, NULL);
lovrCheck(type == TYPE_U16 || type == TYPE_U32, "Blob type must be u16 or u32");
size_t stride = type == TYPE_U16 ? 2 : 4;
uint32_t count = blob->size / stride;
void* data = lovrMeshSetIndices(mesh, count, type);
memcpy(data, blob->data, count * stride);
}
default: return luax_typeerror(L, 2, "nil, table, or Blob");
}
return 0;
}
static int l_lovrMeshGetBoundingBox(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
float box[6];
if (lovrMeshGetBoundingBox(mesh, box)) {
lua_pushnumber(L, box[0]);
lua_pushnumber(L, box[1]);
lua_pushnumber(L, box[2]);
lua_pushnumber(L, box[3]);
lua_pushnumber(L, box[4]);
lua_pushnumber(L, box[5]);
return 6;
} else {
lua_pushnil(L);
return 1;
}
}
static int l_lovrMeshSetBoundingBox(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
if (lua_isnoneornil(L, 2)) {
lovrMeshSetBoundingBox(mesh, NULL);
} else {
float box[6];
box[0] = luax_checkfloat(L, 2);
box[1] = luax_checkfloat(L, 3);
box[2] = luax_checkfloat(L, 4);
box[3] = luax_checkfloat(L, 5);
box[4] = luax_checkfloat(L, 6);
box[5] = luax_checkfloat(L, 7);
lovrMeshSetBoundingBox(mesh, box);
}
return 0;
}
static int l_lovrMeshComputeBoundingBox(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
bool updated = lovrMeshComputeBoundingBox(mesh);
lua_pushboolean(L, updated);
return 1;
}
static int l_lovrMeshGetDrawMode(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
DrawMode mode = lovrMeshGetDrawMode(mesh);
luax_pushenum(L, DrawMode, mode);
return 1;
}
static int l_lovrMeshSetDrawMode(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
DrawMode mode = luax_checkenum(L, 2, DrawMode, NULL);
lovrMeshSetDrawMode(mesh, mode);
return 0;
}
static int l_lovrMeshGetDrawRange(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
uint32_t start, count, offset;
lovrMeshGetDrawRange(mesh, &start, &count, &offset);
if (count == 0) {
return 0;
}
lua_pushinteger(L, start + 1);
lua_pushinteger(L, count);
lua_pushinteger(L, offset);
return 2;
}
static int l_lovrMeshSetDrawRange(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
if (lua_isnoneornil(L, 2)) {
lovrMeshSetDrawRange(mesh, 0, 0, 0);
} else {
uint32_t start = luax_checku32(L, 2) - 1;
uint32_t count = luax_checku32(L, 3);
uint32_t offset = luax_optu32(L, 3, 0);
lovrMeshSetDrawRange(mesh, start, count, offset);
}
return 0;
}
static int l_lovrMeshGetMaterial(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
Material* material = lovrMeshGetMaterial(mesh);
luax_pushtype(L, Material, material);
return 1;
}
static int l_lovrMeshSetMaterial(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
Material* material = luax_checktype(L, 2, Material);
lovrMeshSetMaterial(mesh, material);
return 0;
}
const luaL_Reg lovrMesh[] = {
{ "getVertexCount", l_lovrMeshGetVertexCount },
{ "getVertexStride", l_lovrMeshGetVertexStride },
{ "getVertexFormat", l_lovrMeshGetVertexFormat },
{ "getVertexBuffer", l_lovrMeshGetVertexBuffer },
{ "getIndexBuffer", l_lovrMeshGetIndexBuffer },
{ "setIndexBuffer", l_lovrMeshSetIndexBuffer },
{ "getVertices", l_lovrMeshGetVertices },
{ "setVertices", l_lovrMeshSetVertices },
{ "getIndices", l_lovrMeshGetIndices },
{ "setIndices", l_lovrMeshSetIndices },
{ "getBoundingBox", l_lovrMeshGetBoundingBox },
{ "setBoundingBox", l_lovrMeshSetBoundingBox },
{ "computeBoundingBox", l_lovrMeshComputeBoundingBox },
{ "getDrawMode", l_lovrMeshGetDrawMode },
{ "setDrawMode", l_lovrMeshSetDrawMode },
{ "getDrawRange", l_lovrMeshGetDrawRange },
{ "setDrawRange", l_lovrMeshSetDrawRange },
{ "getMaterial", l_lovrMeshGetMaterial },
{ "setMaterial", l_lovrMeshSetMaterial },
{ NULL, NULL }
};

View File

@ -18,44 +18,32 @@ static int luax_callmodeldata(lua_State* L, const char* method, int nrets) {
return nrets;
}
static uint32_t luax_checkanimation(lua_State* L, int index, Model* model) {
uint32_t luax_checkblendshape(lua_State* L, int index, Model* model) {
switch (lua_type(L, index)) {
case LUA_TSTRING: {
size_t length;
const char* name = lua_tolstring(L, index, &length);
ModelData* data = lovrModelGetInfo(model)->data;
uint64_t animationIndex = map_get(&data->animationMap, hash64(name, length));
lovrCheck(animationIndex != MAP_NIL, "ModelData has no animation named '%s'", name);
return (uint32_t) animationIndex;
uint64_t blendShapeIndex = map_get(&data->blendShapeMap, hash64(name, length));
lovrCheck(blendShapeIndex != MAP_NIL, "ModelData has no blend shape named '%s'", name);
return (uint32_t) blendShapeIndex;
}
case LUA_TNUMBER: {
uint32_t animation = luax_checku32(L, index) - 1;
uint32_t blendShape = luax_checku32(L, index) - 1;
ModelData* data = lovrModelGetInfo(model)->data;
lovrCheck(animation < data->animationCount, "Invalid animation index '%d'", animation + 1);
return animation;
lovrCheck(blendShape < data->blendShapeCount, "Invalid blend shape index '%d'", blendShape + 1);
return blendShape;
}
default: return luax_typeerror(L, index, "number or string"), ~0u;
}
}
uint32_t luax_checknodeindex(lua_State* L, int index, Model* model) {
switch (lua_type(L, index)) {
case LUA_TSTRING: {
size_t length;
const char* name = lua_tolstring(L, index, &length);
ModelData* data = lovrModelGetInfo(model)->data;
uint64_t nodeIndex = map_get(&data->nodeMap, hash64(name, length));
lovrCheck(nodeIndex != MAP_NIL, "ModelData has no node named '%s'", name);
return (uint32_t) nodeIndex;
}
case LUA_TNUMBER: {
uint32_t node = luax_checku32(L, index) - 1;
ModelData* data = lovrModelGetInfo(model)->data;
lovrCheck(node < data->nodeCount, "Invalid node index '%d'", node + 1);
return node;
}
default: return luax_typeerror(L, index, "number or string"), ~0u;
}
static int l_lovrModelClone(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
Model* clone = lovrModelClone(model);
luax_pushtype(L, Model, clone);
lovrRelease(clone, lovrModelDestroy);
return 1;
}
static int l_lovrModelGetData(lua_State* L) {
@ -89,37 +77,11 @@ static int l_lovrModelGetNodeChildren(lua_State* L) {
return luax_callmodeldata(L, "getNodeChildren", 1);
}
static int l_lovrModelGetNodeDrawCount(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t node = luax_checknodeindex(L, 2, model);
uint32_t count = lovrModelGetNodeDrawCount(model, node);
lua_pushinteger(L, count);
return 1;
}
static int l_lovrModelGetNodeDraw(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t node = luax_checknodeindex(L, 2, model);
uint32_t index = luax_optu32(L, 3, 1) - 1;
ModelDraw draw;
lovrModelGetNodeDraw(model, node, index, &draw);
luax_pushenum(L, MeshMode, draw.mode);
luax_pushtype(L, Material, draw.material);
lua_pushinteger(L, draw.start + 1);
lua_pushinteger(L, draw.count);
if (draw.indexed) {
lua_pushinteger(L, draw.base);
return 5;
} else {
return 4;
}
}
static int l_lovrModelGetNodePosition(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t node = luax_checknodeindex(L, 2, model);
uint32_t node = luax_checknodeindex(L, 2, lovrModelGetInfo(model)->data);
OriginType origin = luax_checkenum(L, 3, OriginType, "root");
float position[4], scale[4], rotation[4];
float position[3], scale[3], rotation[4];
lovrModelGetNodeTransform(model, node, position, scale, rotation, origin);
lua_pushnumber(L, position[0]);
lua_pushnumber(L, position[1]);
@ -129,8 +91,8 @@ static int l_lovrModelGetNodePosition(lua_State* L) {
static int l_lovrModelSetNodePosition(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t node = luax_checknodeindex(L, 2, model);
float position[4];
uint32_t node = luax_checknodeindex(L, 2, lovrModelGetInfo(model)->data);
float position[3];
int index = luax_readvec3(L, 3, position, NULL);
float alpha = luax_optfloat(L, index, 1.f);
lovrModelSetNodeTransform(model, node, position, NULL, NULL, alpha);
@ -139,9 +101,9 @@ static int l_lovrModelSetNodePosition(lua_State* L) {
static int l_lovrModelGetNodeScale(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t node = luax_checknodeindex(L, 2, model);
uint32_t node = luax_checknodeindex(L, 2, lovrModelGetInfo(model)->data);
OriginType origin = luax_checkenum(L, 3, OriginType, "root");
float position[4], scale[4], rotation[4];
float position[3], scale[3], rotation[4];
lovrModelGetNodeTransform(model, node, position, scale, rotation, origin);
lua_pushnumber(L, scale[0]);
lua_pushnumber(L, scale[1]);
@ -151,8 +113,8 @@ static int l_lovrModelGetNodeScale(lua_State* L) {
static int l_lovrModelSetNodeScale(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t node = luax_checknodeindex(L, 2, model);
float scale[4];
uint32_t node = luax_checknodeindex(L, 2, lovrModelGetInfo(model)->data);
float scale[3];
int index = luax_readscale(L, 3, scale, 3, NULL);
float alpha = luax_optfloat(L, index, 1.f);
lovrModelSetNodeTransform(model, node, NULL, scale, NULL, alpha);
@ -161,9 +123,9 @@ static int l_lovrModelSetNodeScale(lua_State* L) {
static int l_lovrModelGetNodeOrientation(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t node = luax_checknodeindex(L, 2, model);
uint32_t node = luax_checknodeindex(L, 2, lovrModelGetInfo(model)->data);
OriginType origin = luax_checkenum(L, 3, OriginType, "root");
float position[4], scale[4], rotation[4], angle, ax, ay, az;
float position[3], scale[3], rotation[4], angle, ax, ay, az;
lovrModelGetNodeTransform(model, node, position, scale, rotation, origin);
quat_getAngleAxis(rotation, &angle, &ax, &ay, &az);
lua_pushnumber(L, angle);
@ -175,7 +137,7 @@ static int l_lovrModelGetNodeOrientation(lua_State* L) {
static int l_lovrModelSetNodeOrientation(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t node = luax_checknodeindex(L, 2, model);
uint32_t node = luax_checknodeindex(L, 2, lovrModelGetInfo(model)->data);
float rotation[4];
int index = luax_readquat(L, 3, rotation, NULL);
float alpha = luax_optfloat(L, index, 1.f);
@ -185,9 +147,9 @@ static int l_lovrModelSetNodeOrientation(lua_State* L) {
static int l_lovrModelGetNodePose(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t node = luax_checknodeindex(L, 2, model);
uint32_t node = luax_checknodeindex(L, 2, lovrModelGetInfo(model)->data);
OriginType origin = luax_checkenum(L, 3, OriginType, "root");
float position[4], scale[4], rotation[4], angle, ax, ay, az;
float position[3], scale[3], rotation[4], angle, ax, ay, az;
lovrModelGetNodeTransform(model, node, position, scale, rotation, origin);
quat_getAngleAxis(rotation, &angle, &ax, &ay, &az);
lua_pushnumber(L, position[0]);
@ -202,9 +164,9 @@ static int l_lovrModelGetNodePose(lua_State* L) {
static int l_lovrModelSetNodePose(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t node = luax_checknodeindex(L, 2, model);
uint32_t node = luax_checknodeindex(L, 2, lovrModelGetInfo(model)->data);
int index = 3;
float position[4], rotation[4];
float position[3], rotation[4];
index = luax_readvec3(L, index, position, NULL);
index = luax_readquat(L, index, rotation, NULL);
float alpha = luax_optfloat(L, index, 1.f);
@ -214,9 +176,9 @@ static int l_lovrModelSetNodePose(lua_State* L) {
static int l_lovrModelGetNodeTransform(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t node = luax_checknodeindex(L, 2, model);
uint32_t node = luax_checknodeindex(L, 2, lovrModelGetInfo(model)->data);
OriginType origin = luax_checkenum(L, 3, OriginType, "root");
float position[4], scale[4], rotation[4], angle, ax, ay, az;
float position[3], scale[3], rotation[4], angle, ax, ay, az;
lovrModelGetNodeTransform(model, node, position, scale, rotation, origin);
quat_getAngleAxis(rotation, &angle, &ax, &ay, &az);
lua_pushnumber(L, position[0]);
@ -234,10 +196,10 @@ static int l_lovrModelGetNodeTransform(lua_State* L) {
static int l_lovrModelSetNodeTransform(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t node = luax_checknodeindex(L, 2, model);
uint32_t node = luax_checknodeindex(L, 2, lovrModelGetInfo(model)->data);
int index = 3;
VectorType type;
float position[4], scale[4], rotation[4];
float position[3], scale[3], rotation[4];
float* m = luax_tovector(L, index, &type);
if (m && type == V_MAT4) {
mat4_getPosition(m, position);
@ -281,13 +243,37 @@ static int l_lovrModelHasJoints(lua_State* L) {
static int l_lovrModelAnimate(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t animation = luax_checkanimation(L, 2, model);
uint32_t animation = luax_checkanimationindex(L, 2, lovrModelGetInfo(model)->data);
float time = luax_checkfloat(L, 3);
float alpha = luax_optfloat(L, 4, 1.f);
lovrModelAnimate(model, animation, time, alpha);
return 0;
}
static int l_lovrModelGetBlendShapeCount(lua_State* L) {
return luax_callmodeldata(L, "getBlendShapeCount", 1);
}
static int l_lovrModelGetBlendShapeName(lua_State* L) {
return luax_callmodeldata(L, "getBlendShapeName", 1);
}
static int l_lovrModelGetBlendShapeWeight(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t blendShape = luax_checkblendshape(L, 2, model);
float weight = lovrModelGetBlendShapeWeight(model, blendShape);
lua_pushnumber(L, weight);
return 1;
}
static int l_lovrModelSetBlendShapeWeight(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t blendShape = luax_checkblendshape(L, 2, model);
float weight = luax_checkfloat(L, 3);
lovrModelSetBlendShapeWeight(model, blendShape, weight);
return 0;
}
static int l_lovrModelGetTriangles(lua_State* L) {
return luax_callmodeldata(L, "getTriangles", 2);
}
@ -342,6 +328,30 @@ static int l_lovrModelGetIndexBuffer(lua_State* L) {
return 1;
}
static int l_lovrModelGetMeshCount(lua_State* L) {
return luax_callmodeldata(L, "getMeshCount", 1);
}
static int l_lovrModelGetMesh(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t index = luax_checku32(L, 3) - 1;
Mesh* mesh = lovrModelGetMesh(model, index);
luax_pushtype(L, Mesh, mesh);
return 1;
}
static int l_lovrModelGetTextureCount(lua_State* L) {
return luax_callmodeldata(L, "getImageCount", 1);
}
static int l_lovrModelGetTexture(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t index = luax_checku32(L, 2) - 1;
Texture* texture = lovrModelGetTexture(model, index);
luax_pushtype(L, Texture, texture);
return 1;
}
static int l_lovrModelGetMaterialCount(lua_State* L) {
return luax_callmodeldata(L, "getMaterialCount", 1);
}
@ -350,27 +360,16 @@ static int l_lovrModelGetMaterialName(lua_State* L) {
return luax_callmodeldata(L, "getMaterialName", 1);
}
static int l_lovrModelGetTextureCount(lua_State* L) {
return luax_callmodeldata(L, "getImageCount", 1);
}
static int l_lovrModelGetMaterial(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t index = luaL_checkinteger(L, 2);
uint32_t index = luax_checkmaterialindex(L, 2, lovrModelGetInfo(model)->data);
Material* material = lovrModelGetMaterial(model, index);
luax_pushtype(L, Material, material);
return 1;
}
static int l_lovrModelGetTexture(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t index = luaL_checkinteger(L, 2);
Texture* texture = lovrModelGetTexture(model, index);
luax_pushtype(L, Texture, texture);
return 1;
}
const luaL_Reg lovrModel[] = {
{ "clone", l_lovrModelClone },
{ "getData", l_lovrModelGetData },
{ "getMetadata", l_lovrModelGetMetadata },
{ "getRootNode", l_lovrModelGetRootNode },
@ -378,8 +377,6 @@ const luaL_Reg lovrModel[] = {
{ "getNodeName", l_lovrModelGetNodeName },
{ "getNodeParent", l_lovrModelGetNodeParent },
{ "getNodeChildren", l_lovrModelGetNodeChildren },
{ "getNodeDrawCount", l_lovrModelGetNodeDrawCount },
{ "getNodeDraw", l_lovrModelGetNodeDraw },
{ "getNodePosition", l_lovrModelGetNodePosition },
{ "setNodePosition", l_lovrModelSetNodePosition },
{ "getNodeOrientation", l_lovrModelGetNodeOrientation },
@ -396,6 +393,10 @@ const luaL_Reg lovrModel[] = {
{ "getAnimationDuration", l_lovrModelGetAnimationDuration },
{ "hasJoints", l_lovrModelHasJoints },
{ "animate", l_lovrModelAnimate },
{ "getBlendShapeCount", l_lovrModelGetBlendShapeCount },
{ "getBlendShapeName", l_lovrModelGetBlendShapeName },
{ "getBlendShapeWeight", l_lovrModelGetBlendShapeWeight },
{ "setBlendShapeWeight", l_lovrModelSetBlendShapeWeight },
{ "getTriangles", l_lovrModelGetTriangles },
{ "getTriangleCount", l_lovrModelGetTriangleCount },
{ "getVertexCount", l_lovrModelGetVertexCount },
@ -408,10 +409,12 @@ const luaL_Reg lovrModel[] = {
{ "getBoundingSphere", l_lovrModelGetBoundingSphere },
{ "getVertexBuffer", l_lovrModelGetVertexBuffer },
{ "getIndexBuffer", l_lovrModelGetIndexBuffer },
{ "getMeshCount", l_lovrModelGetMeshCount },
{ "getMesh", l_lovrModelGetMesh },
{ "getTextureCount", l_lovrModelGetTextureCount },
{ "getTexture", l_lovrModelGetTexture },
{ "getMaterialCount", l_lovrModelGetMaterialCount },
{ "getMaterialName", l_lovrModelGetMaterialName },
{ "getTextureCount", l_lovrModelGetTextureCount },
{ "getMaterial", l_lovrModelGetMaterial },
{ "getTexture", l_lovrModelGetTexture },
{ NULL, NULL }
};

File diff suppressed because it is too large Load Diff

View File

@ -20,32 +20,14 @@ static int l_lovrReadbackWait(lua_State* L) {
static int l_lovrReadbackGetData(lua_State* L) {
Readback* readback = luax_checktype(L, 1, Readback);
const ReadbackInfo* info = lovrReadbackGetInfo(readback);
void* data = lovrReadbackGetData(readback);
uint32_t* u32 = data;
switch (info->type) {
case READBACK_BUFFER:
// TODO
return 0;
case READBACK_TEXTURE:
lua_pushnil(L);
return 1;
case READBACK_TALLY: {
int count = (int) info->tally.count;
if (lovrTallyGetInfo(info->tally.object)->type == TALLY_SHADER) {
count *= 4; // The number of pipeline statistics that are tracked
}
lua_createtable(L, count, 0);
for (int i = 0; i < count; i++) {
lua_pushinteger(L, u32[i]);
lua_rawseti(L, -2, i + 1);
}
return 1;
}
DataField format;
void* data = lovrReadbackGetData(readback, &format);
if (data) {
return luax_pushbufferdata(L, &format, data);
} else {
lua_pushnil(L);
return 1;
}
return 0;
}
static int l_lovrReadbackGetBlob(lua_State* L) {

View File

@ -1,31 +0,0 @@
#include "api.h"
#include "graphics/graphics.h"
#include "util.h"
static int l_lovrTallyGetType(lua_State* L) {
Tally* tally = luax_checktype(L, 1, Tally);
const TallyInfo* info = lovrTallyGetInfo(tally);
luax_pushenum(L, TallyType, info->type);
return 1;
}
static int l_lovrTallyGetCount(lua_State* L) {
Tally* tally = luax_checktype(L, 1, Tally);
const TallyInfo* info = lovrTallyGetInfo(tally);
lua_pushinteger(L, info->count);
return 1;
}
static int l_lovrTallyGetViewCount(lua_State* L) {
Tally* tally = luax_checktype(L, 1, Tally);
const TallyInfo* info = lovrTallyGetInfo(tally);
lua_pushinteger(L, info->views);
return 1;
}
const luaL_Reg lovrTally[] = {
{ "getType", l_lovrTallyGetType },
{ "getCount", l_lovrTallyGetCount },
{ "getViewCount", l_lovrTallyGetViewCount },
{ NULL, NULL }
};

View File

@ -1,15 +1,24 @@
#include "api.h"
#include "util.h"
#include "graphics/graphics.h"
#include "data/image.h"
static int l_lovrTextureNewView(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
TextureViewInfo info = { .parent = texture };
info.type = luax_checkenum(L, 2, TextureType, NULL);
info.layerIndex = luaL_optinteger(L, 3, 1) - 1;
info.layerCount = luaL_optinteger(L, 4, 1);
info.levelIndex = luaL_optinteger(L, 5, 1) - 1;
info.levelCount = luaL_optinteger(L, 6, 0);
if (lua_type(L, 2) == LUA_TNUMBER) {
info.type = TEXTURE_2D;
info.layerIndex = luax_checku32(L, 2) - 1;
info.layerCount = 1;
info.levelIndex = luax_optu32(L, 3, 1) - 1;
info.levelCount = 1;
} else if (lua_isstring(L, 2)) {
info.type = luax_checkenum(L, 2, TextureType, NULL);
info.layerIndex = luaL_optinteger(L, 3, 1) - 1;
info.layerCount = luaL_optinteger(L, 4, 1);
info.levelIndex = luaL_optinteger(L, 5, 1) - 1;
info.levelCount = luaL_optinteger(L, 6, 0);
}
Texture* view = lovrTextureCreateView(&info);
luax_pushtype(L, Texture, view);
lovrRelease(view, lovrTextureDestroy);
@ -102,6 +111,115 @@ static int l_lovrTextureHasUsage(lua_State* L) {
return 1;
}
static int l_lovrTextureNewReadback(lua_State* L) {
Texture* texture = luax_totype(L, 1, Texture);
uint32_t offset[4], extent[2];
offset[0] = luax_optu32(L, 2, 0);
offset[1] = luax_optu32(L, 3, 0);
offset[2] = luax_optu32(L, 4, 1) - 1;
offset[3] = luax_optu32(L, 5, 1) - 1;
extent[0] = luax_optu32(L, 6, ~0u);
extent[1] = luax_optu32(L, 7, ~0u);
Readback* readback = lovrReadbackCreateTexture(texture, offset, extent);
luax_pushtype(L, Readback, readback);
lovrRelease(readback, lovrReadbackDestroy);
return 1;
}
static int l_lovrTextureGetPixels(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
uint32_t offset[4], extent[3];
offset[0] = luax_optu32(L, 2, 0);
offset[1] = luax_optu32(L, 3, 0);
offset[2] = luax_optu32(L, 4, 1) - 1;
offset[3] = luax_optu32(L, 5, 1) - 1;
extent[0] = luax_optu32(L, 6, ~0u);
extent[1] = luax_optu32(L, 7, ~0u);
extent[2] = 1;
Image* image = lovrTextureGetPixels(texture, offset, extent);
luax_pushtype(L, Image, image);
lovrRelease(image, lovrImageDestroy);
return 1;
}
static int l_lovrTextureSetPixels(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
Image* image = luax_totype(L, 2, Image);
if (image) {
uint32_t dstOffset[4];
uint32_t srcOffset[4];
uint32_t extent[3];
dstOffset[0] = luax_optu32(L, 3, 0);
dstOffset[1] = luax_optu32(L, 4, 0);
dstOffset[2] = luax_optu32(L, 5, 1) - 1;
dstOffset[3] = luax_optu32(L, 6, 1) - 1;
srcOffset[0] = luax_optu32(L, 7, 0);
srcOffset[1] = luax_optu32(L, 8, 0);
srcOffset[2] = luax_optu32(L, 9, 1) - 1;
srcOffset[3] = luax_optu32(L, 10, 1) - 1;
extent[0] = luax_optu32(L, 11, ~0u);
extent[1] = luax_optu32(L, 12, ~0u);
extent[2] = luax_optu32(L, 13, ~0u);
lovrTextureSetPixels(texture, image, dstOffset, srcOffset, extent);
return 0;
}
Texture* src = luax_totype(L, 2, Texture);
if (texture) {
Texture* dst = texture;
uint32_t dstOffset[4];
uint32_t srcOffset[4];
uint32_t dstExtent[3];
uint32_t srcExtent[3];
dstOffset[0] = luax_optu32(L, 3, 0);
dstOffset[1] = luax_optu32(L, 4, 0);
dstOffset[2] = luax_optu32(L, 5, 1) - 1;
dstOffset[3] = luax_optu32(L, 6, 1) - 1;
srcOffset[0] = luax_optu32(L, 7, 0);
srcOffset[1] = luax_optu32(L, 8, 0);
srcOffset[2] = luax_optu32(L, 9, 1) - 1;
srcOffset[3] = luax_optu32(L, 10, 1) - 1;
dstExtent[0] = luax_optu32(L, 11, ~0u);
dstExtent[1] = luax_optu32(L, 12, ~0u);
dstExtent[2] = luax_optu32(L, 13, ~0u);
srcExtent[0] = luax_optu32(L, 14, dstExtent[0]);
srcExtent[1] = luax_optu32(L, 15, dstExtent[1]);
srcExtent[2] = luax_optu32(L, 16, dstExtent[2]);
FilterMode filter = luax_checkenum(L, 17, FilterMode, "linear");
if (srcExtent[0] == dstExtent[0] && srcExtent[1] == dstExtent[1] && srcExtent[2] == dstExtent[2]) {
lovrTextureCopy(src, dst, srcOffset, dstOffset, dstExtent);
} else {
lovrTextureBlit(src, dst, srcOffset, dstOffset, srcExtent, dstExtent, filter);
}
return 0;
}
return luax_typeerror(L, 2, "Image or Texture");
}
static int l_lovrTextureClear(lua_State* L) {
Texture* texture = luax_totype(L, 1, Texture);
float value[4];
luax_optcolor(L, 2, value);
uint32_t layer = luax_optu32(L, 3, 1) - 1;
uint32_t layerCount = luax_optu32(L, 4, ~0u);
uint32_t level = luax_optu32(L, 5, 1) - 1;
uint32_t levelCount = luax_optu32(L, 6, ~0u);
lovrTextureClear(texture, value, layer, layerCount, level, levelCount);
return 0;
}
static int l_lovrTextureGenerateMipmaps(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
uint32_t base = luax_optu32(L, 2, 1) - 1;
uint32_t count = luax_optu32(L, 3, ~0u);
lovrTextureGenerateMipmaps(texture, base, count);
return 0;
}
const luaL_Reg lovrTexture[] = {
{ "newView", l_lovrTextureNewView },
{ "isView", l_lovrTextureIsView },
@ -115,5 +233,10 @@ const luaL_Reg lovrTexture[] = {
{ "getMipmapCount", l_lovrTextureGetMipmapCount },
{ "getSampleCount", l_lovrTextureGetSampleCount },
{ "hasUsage", l_lovrTextureHasUsage },
{ "newReadback", l_lovrTextureNewReadback },
{ "getPixels", l_lovrTextureGetPixels },
{ "setPixels", l_lovrTextureSetPixels },
{ "clear", l_lovrTextureClear },
{ "generateMipmaps", l_lovrTextureGenerateMipmaps },
{ NULL, NULL }
};

View File

@ -6,24 +6,32 @@
#include <stdlib.h>
StringEntry lovrHeadsetDriver[] = {
[DRIVER_DESKTOP] = ENTRY("desktop"),
[DRIVER_SIMULATOR] = ENTRY("desktop"),
[DRIVER_OPENXR] = ENTRY("openxr"),
[DRIVER_WEBXR] = ENTRY("webxr"),
{ 0 }
};
StringEntry lovrHeadsetOrigin[] = {
[ORIGIN_HEAD] = ENTRY("head"),
[ORIGIN_FLOOR] = ENTRY("floor"),
StringEntry lovrPassthroughMode[] = {
[PASSTHROUGH_OPAQUE] = ENTRY("opaque"),
[PASSTHROUGH_BLEND] = ENTRY("blend"),
[PASSTHROUGH_ADD] = ENTRY("add"),
{ 0 }
};
StringEntry lovrDevice[] = {
[DEVICE_HEAD] = ENTRY("head"),
[DEVICE_FLOOR] = ENTRY("floor"),
[DEVICE_HAND_LEFT] = ENTRY("hand/left"),
[DEVICE_HAND_RIGHT] = ENTRY("hand/right"),
[DEVICE_HAND_LEFT_GRIP] = ENTRY("hand/left/grip"),
[DEVICE_HAND_RIGHT_GRIP] = ENTRY("hand/right/grip"),
[DEVICE_HAND_LEFT_POINT] = ENTRY("hand/left/point"),
[DEVICE_HAND_RIGHT_POINT] = ENTRY("hand/right/point"),
[DEVICE_HAND_LEFT_PINCH] = ENTRY("hand/left/pinch"),
[DEVICE_HAND_RIGHT_PINCH] = ENTRY("hand/right/pinch"),
[DEVICE_HAND_LEFT_POKE] = ENTRY("hand/left/poke"),
[DEVICE_HAND_RIGHT_POKE] = ENTRY("hand/right/poke"),
[DEVICE_ELBOW_LEFT] = ENTRY("elbow/left"),
[DEVICE_ELBOW_RIGHT] = ENTRY("elbow/right"),
[DEVICE_SHOULDER_LEFT] = ENTRY("shoulder/left"),
@ -82,7 +90,13 @@ static int l_lovrHeadsetStart(lua_State* L) {
static int l_lovrHeadsetGetDriver(lua_State* L) {
luax_pushenum(L, HeadsetDriver, lovrHeadsetInterface->driverType);
return 1;
char name[256];
if (lovrHeadsetInterface->getDriverName(name, sizeof(name))) {
lua_pushstring(L, name);
} else {
lua_pushnil(L);
}
return 2;
}
static int l_lovrHeadsetGetName(lua_State* L) {
@ -95,8 +109,8 @@ static int l_lovrHeadsetGetName(lua_State* L) {
return 1;
}
static int l_lovrHeadsetGetOriginType(lua_State* L) {
luax_pushenum(L, HeadsetOrigin, lovrHeadsetInterface->getOriginType());
static int l_lovrHeadsetIsSeated(lua_State* L) {
lua_pushboolean(L, lovrHeadsetInterface->isSeated());
return 1;
}
@ -122,48 +136,70 @@ static int l_lovrHeadsetGetDisplayDimensions(lua_State* L) {
return 2;
}
static int l_lovrHeadsetGetDisplayFrequency(lua_State* L) {
float frequency = lovrHeadsetInterface->getDisplayFrequency ? lovrHeadsetInterface->getDisplayFrequency() : 0.f;
if (frequency == 0.f) {
static int l_lovrHeadsetGetRefreshRate(lua_State* L) {
float refreshRate = lovrHeadsetInterface->getRefreshRate ? lovrHeadsetInterface->getRefreshRate() : 0.f;
if (refreshRate == 0.f) {
lua_pushnil(L);
} else {
lua_pushnumber(L, frequency);
lua_pushnumber(L, refreshRate);
}
return 1;
}
static int l_lovrHeadsetGetDisplayFrequencies(lua_State* L) {
if (!lovrHeadsetInterface->getDisplayFrequencies) {
static int l_lovrHeadsetSetRefreshRate(lua_State* L) {
float refreshRate = luax_checkfloat(L, 1);
bool success = lovrHeadsetInterface->setRefreshRate ? lovrHeadsetInterface->setRefreshRate(refreshRate) : false;
lua_pushboolean(L, success);
return 1;
}
static int l_lovrHeadsetGetRefreshRates(lua_State* L) {
uint32_t count;
const float* refreshRates = lovrHeadsetInterface->getRefreshRates(&count);
if (!refreshRates) {
lua_pushnil(L);
return 1;
} else {
lua_settop(L, 0);
lua_createtable(L, count, 0);
for (uint32_t i = 0; i < count; ++i) {
lua_pushnumber(L, refreshRates[i]);
lua_rawseti(L, 1, i + 1);
}
}
uint32_t count = 0;
float* frequencies = lovrHeadsetInterface->getDisplayFrequencies(&count);
if (!frequencies) {
lua_pushnil(L);
return 1;
}
lua_settop(L, 0);
lua_createtable(L, count, 0);
for (uint32_t i = 0; i < count; ++i) {
lua_pushnumber(L, frequencies[i]);
lua_rawseti(L, 1, i + 1);
}
free(frequencies);
return 1;
}
static int l_lovrHeadsetSetDisplayFrequency(lua_State* L) {
float frequency = luax_checkfloat(L, 1);
bool res = false;
if (lovrHeadsetInterface->setDisplayFrequency) {
res = lovrHeadsetInterface->setDisplayFrequency(frequency);
static int l_lovrHeadsetGetPassthrough(lua_State* L) {
PassthroughMode mode = lovrHeadsetInterface->getPassthrough();
luax_pushenum(L, PassthroughMode, mode);
return 1;
}
static int l_lovrHeadsetSetPassthrough(lua_State* L) {
PassthroughMode mode;
if (lua_isnoneornil(L, 1)) {
mode = PASSTHROUGH_DEFAULT;
} else if (lua_isboolean(L, 1)) {
mode = lua_toboolean(L, 1) ? PASSTHROUGH_TRANSPARENT : PASSTHROUGH_OPAQUE;
} else {
mode = luax_checkenum(L, 1, PassthroughMode, NULL);
}
bool success = lovrHeadsetInterface->setPassthrough(mode);
lua_pushboolean(L, success);
return 1;
}
static int l_lovrHeadsetGetPassthroughModes(lua_State* L) {
lua_createtable(L, 0, 3);
for (int i = 0; lovrPassthroughMode[i].length > 0; i++) {
lua_pushlstring(L, lovrPassthroughMode[i].string, lovrPassthroughMode[i].length);
lua_pushboolean(L, lovrHeadsetInterface->isPassthroughSupported(i));
lua_settable(L, -3);
}
lua_pushboolean(L, res);
return 1;
}
@ -173,7 +209,7 @@ static int l_lovrHeadsetGetViewCount(lua_State* L) {
}
static int l_lovrHeadsetGetViewPose(lua_State* L) {
float position[4], orientation[4];
float position[3], orientation[4];
uint32_t view = luax_checku32(L, 1) - 1;
if (!lovrHeadsetInterface->getViewPose(view, position, orientation)) {
lua_pushnil(L);
@ -273,14 +309,14 @@ static int l_lovrHeadsetGetBoundsGeometry(lua_State* L) {
static int l_lovrHeadsetIsTracked(lua_State* L) {
Device device = luax_optdevice(L, 1);
float position[4], orientation[4];
float position[3], orientation[4];
lua_pushboolean(L, lovrHeadsetInterface->getPose(device, position, orientation));
return 1;
}
static int l_lovrHeadsetGetPose(lua_State* L) {
Device device = luax_optdevice(L, 1);
float position[4], orientation[4];
float position[3], orientation[4];
if (lovrHeadsetInterface->getPose(device, position, orientation)) {
float angle, ax, ay, az;
quat_getAngleAxis(orientation, &angle, &ax, &ay, &az);
@ -301,7 +337,7 @@ static int l_lovrHeadsetGetPose(lua_State* L) {
static int l_lovrHeadsetGetPosition(lua_State* L) {
Device device = luax_optdevice(L, 1);
float position[4], orientation[4];
float position[3], orientation[4];
if (lovrHeadsetInterface->getPose(device, position, orientation)) {
lua_pushnumber(L, position[0]);
lua_pushnumber(L, position[1]);
@ -316,7 +352,7 @@ static int l_lovrHeadsetGetPosition(lua_State* L) {
static int l_lovrHeadsetGetOrientation(lua_State* L) {
Device device = luax_optdevice(L, 1);
float position[4], orientation[4];
float position[3], orientation[4];
if (lovrHeadsetInterface->getPose(device, position, orientation)) {
float angle, ax, ay, az;
quat_getAngleAxis(orientation, &angle, &ax, &ay, &az);
@ -332,9 +368,26 @@ static int l_lovrHeadsetGetOrientation(lua_State* L) {
return 4;
}
static int l_lovrHeadsetGetDirection(lua_State* L) {
Device device = luax_optdevice(L, 1);
float position[3], orientation[4];
if (lovrHeadsetInterface->getPose(device, position, orientation)) {
float direction[3];
quat_getDirection(orientation, direction);
lua_pushnumber(L, direction[0]);
lua_pushnumber(L, direction[1]);
lua_pushnumber(L, direction[2]);
return 3;
}
for (int i = 0; i < 3; i++) {
lua_pushnumber(L, 0.);
}
return 3;
}
static int l_lovrHeadsetGetVelocity(lua_State* L) {
Device device = luax_optdevice(L, 1);
float velocity[4], angularVelocity[4];
float velocity[3], angularVelocity[3];
if (lovrHeadsetInterface->getVelocity(device, velocity, angularVelocity)) {
lua_pushnumber(L, velocity[0]);
lua_pushnumber(L, velocity[1]);
@ -349,7 +402,7 @@ static int l_lovrHeadsetGetVelocity(lua_State* L) {
static int l_lovrHeadsetGetAngularVelocity(lua_State* L) {
Device device = luax_optdevice(L, 1);
float velocity[4], angularVelocity[4];
float velocity[3], angularVelocity[3];
if (lovrHeadsetInterface->getVelocity(device, velocity, angularVelocity)) {
lua_pushnumber(L, angularVelocity[0]);
lua_pushnumber(L, angularVelocity[1]);
@ -370,7 +423,8 @@ static int l_lovrHeadsetIsDown(lua_State* L) {
lua_pushboolean(L, down);
return 1;
}
return 0;
lua_pushnil(L);
return 1;
}
static int l_lovrHeadsetWasPressed(lua_State* L) {
@ -405,7 +459,8 @@ static int l_lovrHeadsetIsTouched(lua_State* L) {
lua_pushboolean(L, touched);
return 1;
}
return 0;
lua_pushnil(L);
return 1;
}
static const int axisCounts[MAX_AXES] = {
@ -465,6 +520,9 @@ static int l_lovrHeadsetGetSkeleton(lua_State* L) {
lua_rawseti(L, -3, 2);
lua_rawseti(L, -2, 1);
lua_pushnumber(L, pose[3]);
lua_setfield(L, -2, "radius");
lua_rawseti(L, -2, i + 1);
}
@ -487,6 +545,12 @@ static int l_lovrHeadsetVibrate(lua_State* L) {
return 1;
}
static int l_lovrHeadsetStopVibration(lua_State* L) {
Device device = luax_optdevice(L, 1);
lovrHeadsetInterface->stopVibration(device);
return 0;
}
static int l_lovrHeadsetNewModel(lua_State* L) {
Device device = luax_optdevice(L, 1);
bool animated = false;
@ -512,13 +576,8 @@ static int l_lovrHeadsetNewModel(lua_State* L) {
}
static int l_lovrHeadsetAnimate(lua_State* L) {
Device device = luax_optdevice(L, 1);
Model* model = luax_checktype(L, 2, Model);
if (lovrHeadsetInterface->animate(device, model)) {
lua_pushboolean(L, true);
return 1;
}
lua_pushboolean(L, false);
Model* model = luax_checktype(L, 1, Model);
lua_pushboolean(L, lovrHeadsetInterface->animate(model));
return 1;
}
@ -539,6 +598,11 @@ static int l_lovrHeadsetSubmit(lua_State* L) {
return 0;
}
static int l_lovrHeadsetIsVisible(lua_State* L) {
lua_pushboolean(L, lovrHeadsetInterface->isVisible());
return 1;
}
static int l_lovrHeadsetIsFocused(lua_State* L) {
lua_pushboolean(L, lovrHeadsetInterface->isFocused());
return 1;
@ -573,7 +637,7 @@ static int l_lovrHeadsetGetHands(lua_State* L) {
}
int count = 0;
float position[4], orientation[4];
float position[3], orientation[4];
Device hands[] = { DEVICE_HAND_LEFT, DEVICE_HAND_RIGHT };
for (size_t i = 0; i < COUNTOF(hands); i++) {
if (lovrHeadsetInterface->getPose(hands[i], position, orientation)) {
@ -586,22 +650,35 @@ static int l_lovrHeadsetGetHands(lua_State* L) {
return 1;
}
// Deprecated
static int l_lovrHeadsetGetOriginType(lua_State* L) {
if (lovrHeadsetInterface->isSeated()) {
lua_pushliteral(L, "head");
} else {
lua_pushliteral(L, "floor");
}
return 1;
}
static const luaL_Reg lovrHeadset[] = {
{ "start", l_lovrHeadsetStart },
{ "getDriver", l_lovrHeadsetGetDriver },
{ "getName", l_lovrHeadsetGetName },
{ "getOriginType", l_lovrHeadsetGetOriginType },
{ "getDisplayWidth", l_lovrHeadsetGetDisplayWidth },
{ "getDisplayHeight", l_lovrHeadsetGetDisplayHeight },
{ "getDisplayDimensions", l_lovrHeadsetGetDisplayDimensions },
{ "getDisplayFrequency", l_lovrHeadsetGetDisplayFrequency },
{ "getDisplayFrequencies", l_lovrHeadsetGetDisplayFrequencies },
{ "setDisplayFrequency", l_lovrHeadsetSetDisplayFrequency },
{ "getRefreshRate", l_lovrHeadsetGetRefreshRate },
{ "setRefreshRate", l_lovrHeadsetSetRefreshRate },
{ "getRefreshRates", l_lovrHeadsetGetRefreshRates },
{ "getPassthrough", l_lovrHeadsetGetPassthrough },
{ "setPassthrough", l_lovrHeadsetSetPassthrough },
{ "getPassthroughModes", l_lovrHeadsetGetPassthroughModes },
{ "getViewCount", l_lovrHeadsetGetViewCount },
{ "getViewPose", l_lovrHeadsetGetViewPose },
{ "getViewAngles", l_lovrHeadsetGetViewAngles },
{ "getClipDistance", l_lovrHeadsetGetClipDistance },
{ "setClipDistance", l_lovrHeadsetSetClipDistance },
{ "isSeated", l_lovrHeadsetIsSeated },
{ "getBoundsWidth", l_lovrHeadsetGetBoundsWidth },
{ "getBoundsDepth", l_lovrHeadsetGetBoundsDepth },
{ "getBoundsDimensions", l_lovrHeadsetGetBoundsDimensions },
@ -610,6 +687,7 @@ static const luaL_Reg lovrHeadset[] = {
{ "getPose", l_lovrHeadsetGetPose },
{ "getPosition", l_lovrHeadsetGetPosition },
{ "getOrientation", l_lovrHeadsetGetOrientation },
{ "getDirection", l_lovrHeadsetGetDirection },
{ "getVelocity", l_lovrHeadsetGetVelocity },
{ "getAngularVelocity", l_lovrHeadsetGetAngularVelocity },
{ "isDown", l_lovrHeadsetIsDown },
@ -619,16 +697,25 @@ static const luaL_Reg lovrHeadset[] = {
{ "getAxis", l_lovrHeadsetGetAxis },
{ "getSkeleton", l_lovrHeadsetGetSkeleton },
{ "vibrate", l_lovrHeadsetVibrate },
{ "stopVibration", l_lovrHeadsetStopVibration },
{ "newModel", l_lovrHeadsetNewModel },
{ "animate", l_lovrHeadsetAnimate },
{ "getTexture", l_lovrHeadsetGetTexture },
{ "getPass", l_lovrHeadsetGetPass },
{ "submit", l_lovrHeadsetSubmit },
{ "isVisible", l_lovrHeadsetIsVisible },
{ "isFocused", l_lovrHeadsetIsFocused },
{ "update", l_lovrHeadsetUpdate },
{ "getTime", l_lovrHeadsetGetTime },
{ "getDeltaTime", l_lovrHeadsetGetDeltaTime },
{ "getHands", l_lovrHeadsetGetHands },
// Deprecated
{ "getOriginType", l_lovrHeadsetGetOriginType },
{ "getDisplayFrequency", l_lovrHeadsetGetRefreshRate },
{ "getDisplayFrequencies", l_lovrHeadsetGetRefreshRates },
{ "setDisplayFrequency", l_lovrHeadsetSetRefreshRate },
{ NULL, NULL }
};
@ -642,7 +729,7 @@ int luaopen_lovr_headset(lua_State* L) {
.drivers = drivers,
.driverCount = 0,
.supersample = 1.f,
.offset = 1.7f,
.seated = false,
.stencil = false,
.antialias = true,
.submitDepth = true,
@ -671,8 +758,8 @@ int luaopen_lovr_headset(lua_State* L) {
}
lua_pop(L, 1);
lua_getfield(L, -1, "offset");
config.offset = luax_optfloat(L, -1, 1.7f);
lua_getfield(L, -1, "seated");
config.seated = lua_toboolean(L, -1);
lua_pop(L, 1);
lua_getfield(L, -1, "stencil");

View File

@ -1,10 +1,8 @@
#include "api.h"
#include "math/math.h"
#include "math/curve.h"
#include "math/pool.h"
#include "math/randomGenerator.h"
#include "util.h"
#include <stdlib.h>
#include <string.h>
int l_lovrRandomGeneratorRandom(lua_State* L);
int l_lovrRandomGeneratorRandomNormal(lua_State* L);
@ -15,9 +13,16 @@ int l_lovrVec3Set(lua_State* L);
int l_lovrVec4Set(lua_State* L);
int l_lovrQuatSet(lua_State* L);
int l_lovrMat4Set(lua_State* L);
static LOVR_THREAD_LOCAL Pool* pool;
int l_lovrVec2__metaindex(lua_State* L);
int l_lovrVec3__metaindex(lua_State* L);
int l_lovrVec4__metaindex(lua_State* L);
int l_lovrQuat__metaindex(lua_State* L);
int l_lovrMat4__metaindex(lua_State* L);
static int l_lovrMathVec2(lua_State* L);
static int l_lovrMathVec3(lua_State* L);
static int l_lovrMathVec4(lua_State* L);
static int l_lovrMathQuat(lua_State* L);
static int l_lovrMathMat4(lua_State* L);
extern const luaL_Reg lovrCurve[];
extern const luaL_Reg lovrRandomGenerator[];
extern const luaL_Reg lovrVec2[];
@ -26,28 +31,14 @@ extern const luaL_Reg lovrVec4[];
extern const luaL_Reg lovrQuat[];
extern const luaL_Reg lovrMat4[];
static const luaL_Reg* lovrVectorMetatables[] = {
[V_VEC2] = lovrVec2,
[V_VEC3] = lovrVec3,
[V_VEC4] = lovrVec4,
[V_QUAT] = lovrQuat,
[V_MAT4] = lovrMat4
};
static LOVR_THREAD_LOCAL Pool* pool;
static LOVR_THREAD_LOCAL int lovrVectorMetatableRefs[] = {
[V_VEC2] = LUA_REFNIL,
[V_VEC3] = LUA_REFNIL,
[V_VEC4] = LUA_REFNIL,
[V_QUAT] = LUA_REFNIL,
[V_MAT4] = LUA_REFNIL
};
static const char* lovrVectorTypeNames[] = {
[V_VEC2] = "vec2",
[V_VEC3] = "vec3",
[V_VEC4] = "vec4",
[V_QUAT] = "quat",
[V_MAT4] = "mat4"
static struct { const char* name; lua_CFunction constructor, indexer; const luaL_Reg* api; int metaref; } lovrVectorInfo[] = {
[V_VEC2] = { "vec2", l_lovrMathVec2, l_lovrVec2__metaindex, lovrVec2, LUA_REFNIL },
[V_VEC3] = { "vec3", l_lovrMathVec3, l_lovrVec3__metaindex, lovrVec3, LUA_REFNIL },
[V_VEC4] = { "vec4", l_lovrMathVec4, l_lovrVec4__metaindex, lovrVec4, LUA_REFNIL },
[V_QUAT] = { "quat", l_lovrMathQuat, l_lovrQuat__metaindex, lovrQuat, LUA_REFNIL },
[V_MAT4] = { "mat4", l_lovrMathMat4, l_lovrMat4__metaindex, lovrMat4, LUA_REFNIL }
};
static void luax_destroypool(void) {
@ -80,14 +71,14 @@ float* luax_tovector(lua_State* L, int index, VectorType* type) {
float* luax_checkvector(lua_State* L, int index, VectorType type, const char* expected) {
VectorType t;
float* p = luax_tovector(L, index, &t);
if (!p || t != type) luax_typeerror(L, index, expected ? expected : lovrVectorTypeNames[type]);
if (!p || t != type) luax_typeerror(L, index, expected ? expected : lovrVectorInfo[type].name);
return p;
}
static float* luax_newvector(lua_State* L, VectorType type, size_t components) {
VectorType* p = lua_newuserdata(L, sizeof(VectorType) + components * sizeof(float));
*p = type;
lua_rawgeti(L, LUA_REGISTRYINDEX, lovrVectorMetatableRefs[type]);
lua_rawgeti(L, LUA_REGISTRYINDEX, lovrVectorInfo[type].metaref);
lua_setmetatable(L, -2);
return (float*) (p + 1);
}
@ -117,7 +108,7 @@ static int l_lovrMathNewCurve(lua_State* L) {
lua_pop(L, 3);
}
} else if (top == 1 && lua_type(L, 1) == LUA_TNUMBER) {
float point[4] = { 0 };
float point[4] = { 0.f };
int count = lua_tonumber(L, 1);
lovrAssert(count > 0, "Number of curve points must be positive");
for (int i = 0; i < count; i++) {
@ -253,31 +244,31 @@ static int l_lovrMathNewMat4(lua_State* L) {
static int l_lovrMathVec2(lua_State* L) {
luax_newtempvector(L, V_VEC2);
lua_insert(L, 1);
lua_replace(L, 1);
return l_lovrVec2Set(L);
}
static int l_lovrMathVec3(lua_State* L) {
luax_newtempvector(L, V_VEC3);
lua_insert(L, 1);
lua_replace(L, 1);
return l_lovrVec3Set(L);
}
static int l_lovrMathVec4(lua_State* L) {
luax_newtempvector(L, V_VEC4);
lua_insert(L, 1);
lua_replace(L, 1);
return l_lovrVec4Set(L);
}
static int l_lovrMathQuat(lua_State* L) {
luax_newtempvector(L, V_QUAT);
lua_insert(L, 1);
lua_replace(L, 1);
return l_lovrQuatSet(L);
}
static int l_lovrMathMat4(lua_State* L) {
luax_newtempvector(L, V_MAT4);
lua_insert(L, 1);
lua_replace(L, 1);
return l_lovrMat4Set(L);
}
@ -301,11 +292,6 @@ static const luaL_Reg lovrMath[] = {
{ "newVec4", l_lovrMathNewVec4 },
{ "newQuat", l_lovrMathNewQuat },
{ "newMat4", l_lovrMathNewMat4 },
{ "vec2", l_lovrMathVec2 },
{ "vec3", l_lovrMathVec3 },
{ "vec4", l_lovrMathVec4 },
{ "quat", l_lovrMathQuat },
{ "mat4", l_lovrMathMat4 },
{ "drain", l_lovrMathDrain },
{ NULL, NULL }
};
@ -316,7 +302,7 @@ static int l_lovrLightUserdata__index(lua_State* L) {
return 0;
}
lua_rawgeti(L, LUA_REGISTRYINDEX, lovrVectorMetatableRefs[type]);
lua_rawgeti(L, LUA_REGISTRYINDEX, lovrVectorInfo[type].metaref);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
if (lua_isnil(L, -1)) {
@ -337,7 +323,7 @@ static int l_lovrLightUserdata__index(lua_State* L) {
static int l_lovrLightUserdataOp(lua_State* L) {
VectorType type;
if (!luax_tovector(L, 1, &type)) {
if (!luax_tovector(L, lua_islightuserdata(L, 1) ? 1 : 2, &type)) {
lua_pushliteral(L, "__tostring");
if (lua_rawequal(L, -1, lua_upvalueindex(1))) {
lua_pop(L, 1);
@ -348,7 +334,7 @@ static int l_lovrLightUserdataOp(lua_State* L) {
return luaL_error(L, "Unsupported lightuserdata operator %q", lua_tostring(L, lua_upvalueindex(1)));
}
lua_rawgeti(L, LUA_REGISTRYINDEX, lovrVectorMetatableRefs[type]);
lua_rawgeti(L, LUA_REGISTRYINDEX, lovrVectorInfo[type].metaref);
lua_pushvalue(L, lua_upvalueindex(1));
lua_gettable(L, -2);
lua_pushvalue(L, 1);
@ -366,10 +352,24 @@ int luaopen_lovr_math(lua_State* L) {
for (size_t i = V_NONE + 1; i < MAX_VECTOR_TYPES; i++) {
lua_newtable(L);
lua_pushstring(L, lovrVectorTypeNames[i]);
lua_newtable(L);
lua_pushcfunction(L, lovrVectorInfo[i].constructor);
lua_setfield(L, -2, "__call");
lua_pushcfunction(L, lovrVectorInfo[i].indexer);
lua_setfield(L, -2, "__index");
lua_setmetatable(L, -2);
lua_pushstring(L, lovrVectorInfo[i].name);
lua_setfield(L, -2, "__name");
luax_register(L, lovrVectorMetatables[i]);
lovrVectorMetatableRefs[i] = luaL_ref(L, LUA_REGISTRYINDEX);
// lovr.math[__name] = metatable
lua_pushstring(L, lovrVectorInfo[i].name);
lua_pushvalue(L, -2);
lua_settable(L, -4);
luax_register(L, lovrVectorInfo[i].api);
lovrVectorInfo[i].metaref = luaL_ref(L, LUA_REGISTRYINDEX);
}
// Global lightuserdata metatable
@ -403,8 +403,17 @@ int luaopen_lovr_math(lua_State* L) {
lua_getfield(L, -1, "globals");
if (lua_toboolean(L, -1)) {
for (size_t i = V_NONE + 1; i < MAX_VECTOR_TYPES; i++) {
lua_getfield(L, -4, lovrVectorTypeNames[i]);
lua_setglobal(L, lovrVectorTypeNames[i]);
lua_getfield(L, -4, lovrVectorInfo[i].name);
lua_setglobal(L, lovrVectorInfo[i].name);
// Capitalized global is permanent vector constructor
char constructor[8];
memcpy(constructor, "new", 3);
memcpy(constructor + 3, lovrVectorInfo[i].name, 4);
constructor[3] -= 32;
constructor[7] = '\0';
lua_getfield(L, -4, constructor);
lua_setglobal(L, constructor + 3);
}
}
lua_pop(L, 1);

View File

@ -1,5 +1,4 @@
#include "api.h"
#include "math/curve.h"
#include "util.h"
static int l_lovrCurveEvaluate(lua_State* L) {

View File

@ -1,5 +1,4 @@
#include "api.h"
#include "math/randomGenerator.h"
#include "util.h"
#include <math.h>

File diff suppressed because it is too large Load Diff

View File

@ -50,9 +50,9 @@ static int l_lovrPhysicsNewWorld(lua_State* L) {
static int l_lovrPhysicsNewBallJoint(lua_State* L) {
Collider* a = luax_checktype(L, 1, Collider);
Collider* b = luax_checktype(L, 2, Collider);
float anchor[4];
float anchor[3];
luax_readvec3(L, 3, anchor, NULL);
BallJoint* joint = lovrBallJointCreate(a, b, anchor[0], anchor[1], anchor[2]);
BallJoint* joint = lovrBallJointCreate(a, b, anchor);
luax_pushtype(L, BallJoint, joint);
lovrRelease(joint, lovrJointDestroy);
return 1;
@ -82,11 +82,10 @@ static int l_lovrPhysicsNewCylinderShape(lua_State* L) {
static int l_lovrPhysicsNewDistanceJoint(lua_State* L) {
Collider* a = luax_checktype(L, 1, Collider);
Collider* b = luax_checktype(L, 2, Collider);
float anchor1[4], anchor2[4];
float anchor1[3], anchor2[3];
int index = luax_readvec3(L, 3, anchor1, NULL);
luax_readvec3(L, index, anchor2, NULL);
DistanceJoint* joint = lovrDistanceJointCreate(a, b, anchor1[0], anchor1[1], anchor1[2],
anchor2[0], anchor2[1], anchor2[2]);
DistanceJoint* joint = lovrDistanceJointCreate(a, b, anchor1, anchor2);
luax_pushtype(L, DistanceJoint, joint);
lovrRelease(joint, lovrJointDestroy);
return 1;
@ -95,10 +94,10 @@ static int l_lovrPhysicsNewDistanceJoint(lua_State* L) {
static int l_lovrPhysicsNewHingeJoint(lua_State* L) {
Collider* a = luax_checktype(L, 1, Collider);
Collider* b = luax_checktype(L, 2, Collider);
float anchor[4], axis[4];
float anchor[3], axis[3];
int index = luax_readvec3(L, 3, anchor, NULL);
luax_readvec3(L, index, axis, NULL);
HingeJoint* joint = lovrHingeJointCreate(a, b, anchor[0], anchor[1], anchor[2], axis[0], axis[1], axis[2]);
HingeJoint* joint = lovrHingeJointCreate(a, b, anchor, axis);
luax_pushtype(L, HingeJoint, joint);
lovrRelease(joint, lovrJointDestroy);
return 1;
@ -114,9 +113,9 @@ static int l_lovrPhysicsNewMeshShape(lua_State* L) {
static int l_lovrPhysicsNewSliderJoint(lua_State* L) {
Collider* a = luax_checktype(L, 1, Collider);
Collider* b = luax_checktype(L, 2, Collider);
float axis[4];
float axis[3];
luax_readvec3(L, 3, axis, NULL);
SliderJoint* joint = lovrSliderJointCreate(a, b, axis[0], axis[1], axis[2]);
SliderJoint* joint = lovrSliderJointCreate(a, b, axis);
luax_pushtype(L, SliderJoint, joint);
lovrRelease(joint, lovrJointDestroy);
return 1;

View File

@ -10,6 +10,13 @@ static int l_lovrColliderDestroy(lua_State* L) {
return 0;
}
static int l_lovrColliderIsDestroyed(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
bool destroyed = lovrColliderIsDestroyed(collider);
lua_pushboolean(L, destroyed);
return 1;
}
static int l_lovrColliderGetWorld(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
World* world = lovrColliderGetWorld(collider);
@ -203,7 +210,7 @@ static int l_lovrColliderGetPosition(lua_State* L) {
static int l_lovrColliderSetPosition(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float position[4];
float position[3];
luax_readvec3(L, 2, position, NULL);
lovrColliderSetPosition(collider, position[0], position[1], position[2]);
return 0;
@ -247,7 +254,7 @@ static int l_lovrColliderGetPose(lua_State* L) {
static int l_lovrColliderSetPose(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float position[4], orientation[4];
float position[3], orientation[4];
int index = luax_readvec3(L, 2, position, NULL);
luax_readquat(L, index, orientation, NULL);
lovrColliderSetPosition(collider, position[0], position[1], position[2]);
@ -267,7 +274,7 @@ static int l_lovrColliderGetLinearVelocity(lua_State* L) {
static int l_lovrColliderSetLinearVelocity(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float velocity[4];
float velocity[3];
luax_readvec3(L, 2, velocity, NULL);
lovrColliderSetLinearVelocity(collider, velocity[0], velocity[1], velocity[2]);
return 0;
@ -285,7 +292,7 @@ static int l_lovrColliderGetAngularVelocity(lua_State* L) {
static int l_lovrColliderSetAngularVelocity(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float velocity[4];
float velocity[3];
luax_readvec3(L, 2, velocity, NULL);
lovrColliderSetAngularVelocity(collider, velocity[0], velocity[1], velocity[2]);
return 0;
@ -327,11 +334,11 @@ static int l_lovrColliderSetAngularDamping(lua_State* L) {
static int l_lovrColliderApplyForce(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float force[4];
float force[3];
int index = luax_readvec3(L, 2, force, NULL);
if (lua_gettop(L) >= index) {
float position[4];
float position[3];
luax_readvec3(L, index, position, NULL);
lovrColliderApplyForceAtPosition(collider, force[0], force[1], force[2],
position[0], position[1], position[2]);
@ -344,7 +351,7 @@ static int l_lovrColliderApplyForce(lua_State* L) {
static int l_lovrColliderApplyTorque(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float force[4];
float force[3];
luax_readvec3(L, 2, force, NULL);
lovrColliderApplyTorque(collider, force[0], force[1], force[2]);
return 0;
@ -362,7 +369,7 @@ static int l_lovrColliderGetLocalCenter(lua_State* L) {
static int l_lovrColliderGetLocalPoint(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float world[4];
float world[3];
luax_readvec3(L, 2, world, NULL);
float x, y, z;
lovrColliderGetLocalPoint(collider, world[0], world[1], world[2], &x, &y, &z);
@ -374,7 +381,7 @@ static int l_lovrColliderGetLocalPoint(lua_State* L) {
static int l_lovrColliderGetWorldPoint(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float local[4];
float local[3];
luax_readvec3(L, 2, local, NULL);
float wx, wy, wz;
lovrColliderGetWorldPoint(collider, local[0], local[1], local[2], &wx, &wy, &wz);
@ -386,7 +393,7 @@ static int l_lovrColliderGetWorldPoint(lua_State* L) {
static int l_lovrColliderGetLocalVector(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float world[4];
float world[3];
luax_readvec3(L, 2, world, NULL);
float x, y, z;
lovrColliderGetLocalVector(collider, world[0], world[1], world[2], &x, &y, &z);
@ -398,7 +405,7 @@ static int l_lovrColliderGetLocalVector(lua_State* L) {
static int l_lovrColliderGetWorldVector(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float local[4];
float local[3];
luax_readvec3(L, 2, local, NULL);
float wx, wy, wz;
lovrColliderGetWorldVector(collider, local[0], local[1], local[2], &wx, &wy, &wz);
@ -410,7 +417,7 @@ static int l_lovrColliderGetWorldVector(lua_State* L) {
static int l_lovrColliderGetLinearVelocityFromLocalPoint(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float local[4];
float local[3];
luax_readvec3(L, 2, local, NULL);
float vx, vy, vz;
lovrColliderGetLinearVelocityFromLocalPoint(collider, local[0], local[1], local[2], &vx, &vy, &vz);
@ -422,7 +429,7 @@ static int l_lovrColliderGetLinearVelocityFromLocalPoint(lua_State* L) {
static int l_lovrColliderGetLinearVelocityFromWorldPoint(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
float world[4];
float world[3];
luax_readvec3(L, 2, world, NULL);
float vx, vy, vz;
lovrColliderGetLinearVelocityFromWorldPoint(collider, world[0], world[1], world[2], &vx, &vy, &vz);
@ -491,6 +498,7 @@ static int l_lovrColliderSetTag(lua_State* L) {
const luaL_Reg lovrCollider[] = {
{ "destroy", l_lovrColliderDestroy },
{ "isDestroyed", l_lovrColliderIsDestroyed },
{ "getWorld", l_lovrColliderGetWorld },
{ "addShape", l_lovrColliderAddShape },
{ "removeShape", l_lovrColliderRemoveShape },

View File

@ -105,22 +105,22 @@ static int l_lovrJointSetEnabled(lua_State* L) {
static int l_lovrBallJointGetAnchors(lua_State* L) {
BallJoint* joint = luax_checktype(L, 1, BallJoint);
float x1, y1, z1, x2, y2, z2;
lovrBallJointGetAnchors(joint, &x1, &y1, &z1, &x2, &y2, &z2);
lua_pushnumber(L, x1);
lua_pushnumber(L, y1);
lua_pushnumber(L, z1);
lua_pushnumber(L, x2);
lua_pushnumber(L, y2);
lua_pushnumber(L, z2);
float anchor1[3], anchor2[3];
lovrBallJointGetAnchors(joint, anchor1, anchor2);
lua_pushnumber(L, anchor1[0]);
lua_pushnumber(L, anchor1[1]);
lua_pushnumber(L, anchor1[2]);
lua_pushnumber(L, anchor2[0]);
lua_pushnumber(L, anchor2[1]);
lua_pushnumber(L, anchor2[2]);
return 6;
}
static int l_lovrBallJointSetAnchor(lua_State* L) {
BallJoint* joint = luax_checktype(L, 1, BallJoint);
float anchor[4];
float anchor[3];
luax_readvec3(L, 2, anchor, NULL);
lovrBallJointSetAnchor(joint, anchor[0], anchor[1], anchor[2]);
lovrBallJointSetAnchor(joint, anchor);
return 0;
}
@ -167,23 +167,23 @@ const luaL_Reg lovrBallJoint[] = {
static int l_lovrDistanceJointGetAnchors(lua_State* L) {
DistanceJoint* joint = luax_checktype(L, 1, DistanceJoint);
float x1, y1, z1, x2, y2, z2;
lovrDistanceJointGetAnchors(joint, &x1, &y1, &z1, &x2, &y2, &z2);
lua_pushnumber(L, x1);
lua_pushnumber(L, y1);
lua_pushnumber(L, z1);
lua_pushnumber(L, x2);
lua_pushnumber(L, y2);
lua_pushnumber(L, z2);
float anchor1[3], anchor2[3];
lovrDistanceJointGetAnchors(joint, anchor1, anchor2);
lua_pushnumber(L, anchor1[0]);
lua_pushnumber(L, anchor1[1]);
lua_pushnumber(L, anchor1[2]);
lua_pushnumber(L, anchor1[0]);
lua_pushnumber(L, anchor1[1]);
lua_pushnumber(L, anchor1[2]);
return 6;
}
static int l_lovrDistanceJointSetAnchors(lua_State* L) {
DistanceJoint* joint = luax_checktype(L, 1, DistanceJoint);
float anchor1[4], anchor2[4];
float anchor1[3], anchor2[3];
int index = luax_readvec3(L, 2, anchor1, NULL);
luax_readvec3(L, index, anchor2, NULL);
lovrDistanceJointSetAnchors(joint, anchor1[0], anchor1[1], anchor1[2], anchor2[0], anchor2[1], anchor2[2]);
lovrDistanceJointSetAnchors(joint, anchor1, anchor2);
return 0;
}
@ -245,40 +245,40 @@ const luaL_Reg lovrDistanceJoint[] = {
static int l_lovrHingeJointGetAnchors(lua_State* L) {
HingeJoint* joint = luax_checktype(L, 1, HingeJoint);
float x1, y1, z1, x2, y2, z2;
lovrHingeJointGetAnchors(joint, &x1, &y1, &z1, &x2, &y2, &z2);
lua_pushnumber(L, x1);
lua_pushnumber(L, y1);
lua_pushnumber(L, z1);
lua_pushnumber(L, x2);
lua_pushnumber(L, y2);
lua_pushnumber(L, z2);
float anchor1[3], anchor2[3];
lovrHingeJointGetAnchors(joint, anchor1, anchor2);
lua_pushnumber(L, anchor1[0]);
lua_pushnumber(L, anchor1[1]);
lua_pushnumber(L, anchor1[2]);
lua_pushnumber(L, anchor2[0]);
lua_pushnumber(L, anchor2[1]);
lua_pushnumber(L, anchor2[2]);
return 6;
}
static int l_lovrHingeJointSetAnchor(lua_State* L) {
HingeJoint* joint = luax_checktype(L, 1, HingeJoint);
float anchor[4];
float anchor[3];
luax_readvec3(L, 2, anchor, NULL);
lovrHingeJointSetAnchor(joint, anchor[0], anchor[1], anchor[2]);
lovrHingeJointSetAnchor(joint, anchor);
return 0;
}
static int l_lovrHingeJointGetAxis(lua_State* L) {
HingeJoint* joint = luax_checktype(L, 1, HingeJoint);
float x, y, z;
lovrHingeJointGetAxis(joint, &x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
float axis[3];
lovrHingeJointGetAxis(joint, axis);
lua_pushnumber(L, axis[0]);
lua_pushnumber(L, axis[1]);
lua_pushnumber(L, axis[2]);
return 3;
}
static int l_lovrHingeJointSetAxis(lua_State* L) {
HingeJoint* joint = luax_checktype(L, 1, HingeJoint);
float axis[4];
float axis[3];
luax_readvec3(L, 2, axis, NULL);
lovrHingeJointSetAxis(joint, axis[0], axis[1], axis[2]);
lovrHingeJointSetAxis(joint, axis);
return 0;
}
@ -348,19 +348,19 @@ const luaL_Reg lovrHingeJoint[] = {
static int l_lovrSliderJointGetAxis(lua_State* L) {
SliderJoint* joint = luax_checktype(L, 1, SliderJoint);
float x, y, z;
lovrSliderJointGetAxis(joint, &x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
float axis[3];
lovrSliderJointGetAxis(joint, axis);
lua_pushnumber(L, axis[0]);
lua_pushnumber(L, axis[1]);
lua_pushnumber(L, axis[2]);
return 3;
}
static int l_lovrSliderJointSetAxis(lua_State* L) {
SliderJoint* joint = luax_checktype(L, 1, SliderJoint);
float axis[4];
float axis[3];
luax_readvec3(L, 2, axis, NULL);
lovrSliderJointSetAxis(joint, axis[0], axis[1], axis[2]);
lovrSliderJointSetAxis(joint, axis);
return 0;
}

View File

@ -48,7 +48,7 @@ Shape* luax_newsphereshape(lua_State* L, int index) {
}
Shape* luax_newboxshape(lua_State* L, int index) {
float size[4];
float size[3];
luax_readscale(L, index, size, 3, NULL);
return lovrBoxShapeCreate(size[0], size[1], size[2]);
}
@ -219,7 +219,7 @@ static int l_lovrShapeGetPosition(lua_State* L) {
static int l_lovrShapeSetPosition(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
lovrAssert(lovrShapeGetCollider(shape) != NULL, "Shape must be attached to collider");
float position[4];
float position[3];
luax_readvec3(L, 2, position, NULL);
lovrShapeSetPosition(shape, position[0], position[1], position[2]);
return 0;
@ -246,6 +246,34 @@ static int l_lovrShapeSetOrientation(lua_State* L) {
return 0;
}
static int l_lovrShapeGetPose(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
float x, y, z;
lovrShapeGetPosition(shape, &x, &y, &z);
float angle, ax, ay, az, orientation[4];
lovrShapeGetOrientation(shape, orientation);
quat_getAngleAxis(orientation, &angle, &ax, &ay, &az);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
lua_pushnumber(L, angle);
lua_pushnumber(L, ax);
lua_pushnumber(L, ay);
lua_pushnumber(L, az);
return 7;
}
static int l_lovrShapeSetPose(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
lovrAssert(lovrShapeGetCollider(shape) != NULL, "Shape must be attached to collider");
float position[3], orientation[4];
int index = luax_readvec3(L, 2, position, NULL);
luax_readquat(L, index, orientation, NULL);
lovrShapeSetPosition(shape, position[0], position[1], position[2]);
lovrShapeSetOrientation(shape, orientation);
return 0;
}
static int l_lovrShapeGetMass(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
float density = luax_checkfloat(L, 2);
@ -288,6 +316,8 @@ static int l_lovrShapeGetAABB(lua_State* L) {
{ "setPosition", l_lovrShapeSetPosition }, \
{ "getOrientation", l_lovrShapeGetOrientation }, \
{ "setOrientation", l_lovrShapeSetOrientation }, \
{ "getPose", l_lovrShapeGetPose }, \
{ "setPose", l_lovrShapeSetPose }, \
{ "getMass", l_lovrShapeGetMass }, \
{ "getAABB", l_lovrShapeGetAABB }
@ -313,17 +343,17 @@ const luaL_Reg lovrSphereShape[] = {
static int l_lovrBoxShapeGetDimensions(lua_State* L) {
BoxShape* box = luax_checktype(L, 1, BoxShape);
float x, y, z;
lovrBoxShapeGetDimensions(box, &x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
float w, h, d;
lovrBoxShapeGetDimensions(box, &w, &h, &d);
lua_pushnumber(L, w);
lua_pushnumber(L, h);
lua_pushnumber(L, d);
return 3;
}
static int l_lovrBoxShapeSetDimensions(lua_State* L) {
BoxShape* box = luax_checktype(L, 1, BoxShape);
float size[4];
float size[3];
luax_readscale(L, 2, size, 3, NULL);
lovrBoxShapeSetDimensions(box, size[0], size[1], size[2]);
return 0;

View File

@ -25,9 +25,8 @@ static int nextOverlap(lua_State* L) {
}
}
static void raycastCallback(Shape* shape, float x, float y, float z, float nx, float ny, float nz, void* userdata) {
static bool raycastCallback(Shape* shape, float x, float y, float z, float nx, float ny, float nz, void* userdata) {
lua_State* L = userdata;
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushvalue(L, -1);
luax_pushshape(L, shape);
lua_pushnumber(L, x);
@ -36,12 +35,25 @@ static void raycastCallback(Shape* shape, float x, float y, float z, float nx, f
lua_pushnumber(L, nx);
lua_pushnumber(L, ny);
lua_pushnumber(L, nz);
lua_call(L, 7, 0);
lua_call(L, 7, 1);
bool shouldStop = lua_type(L, -1) == LUA_TBOOLEAN && !lua_toboolean(L, -1);
lua_pop(L, 1);
return shouldStop;
}
static bool queryCallback(Shape* shape, void* userdata) {
lua_State* L = userdata;
lua_pushvalue(L, -1);
luax_pushshape(L, shape);
lua_call(L, 1, 1);
bool shouldStop = lua_type(L, -1) == LUA_TBOOLEAN && !lua_toboolean(L, -1);
lua_pop(L, 1);
return shouldStop;
}
static int l_lovrWorldNewCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float position[4];
float position[3];
luax_readvec3(L, 2, position, NULL);
Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]);
luax_pushtype(L, Collider, collider);
@ -51,7 +63,7 @@ static int l_lovrWorldNewCollider(lua_State* L) {
static int l_lovrWorldNewBoxCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float position[4];
float position[3];
int index = luax_readvec3(L, 2, position, NULL);
Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]);
BoxShape* shape = luax_newboxshape(L, index);
@ -65,7 +77,7 @@ static int l_lovrWorldNewBoxCollider(lua_State* L) {
static int l_lovrWorldNewCapsuleCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float position[4];
float position[3];
int index = luax_readvec3(L, 2, position, NULL);
Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]);
CapsuleShape* shape = luax_newcapsuleshape(L, index);
@ -79,7 +91,7 @@ static int l_lovrWorldNewCapsuleCollider(lua_State* L) {
static int l_lovrWorldNewCylinderCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float position[4];
float position[3];
int index = luax_readvec3(L, 2, position, NULL);
Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]);
CylinderShape* shape = luax_newcylindershape(L, index);
@ -93,7 +105,7 @@ static int l_lovrWorldNewCylinderCollider(lua_State* L) {
static int l_lovrWorldNewSphereCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float position[4];
float position[3];
int index = luax_readvec3(L, 2, position, NULL);
Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]);
SphereShape* shape = luax_newsphereshape(L, index);
@ -232,9 +244,9 @@ static int l_lovrWorldGetContacts(lua_State* L) {
static int l_lovrWorldRaycast(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float start[4], end[4];
int index;
index = luax_readvec3(L, 2, start, NULL);
float start[3], end[3];
int index = 2;
index = luax_readvec3(L, index, start, NULL);
index = luax_readvec3(L, index, end, NULL);
luaL_checktype(L, index, LUA_TFUNCTION);
lua_settop(L, index);
@ -242,6 +254,31 @@ static int l_lovrWorldRaycast(lua_State* L) {
return 0;
}
static int l_lovrWorldQueryBox(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float position[3], size[3];
int index = 2;
index = luax_readvec3(L, index, position, NULL);
index = luax_readvec3(L, index, size, NULL);
bool function = lua_type(L, index) == LUA_TFUNCTION;
lua_settop(L, index);
bool any = lovrWorldQueryBox(world, position, size, function ? queryCallback : NULL, L);
lua_pushboolean(L, any);
return 1;
}
static int l_lovrWorldQuerySphere(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float position[3];
int index = luax_readvec3(L, 2, position, NULL);
float radius = luax_checkfloat(L, index++);
bool function = lua_type(L, index) == LUA_TFUNCTION;
lua_settop(L, index);
bool any = lovrWorldQuerySphere(world, position, radius, function ? queryCallback : NULL, L);
lua_pushboolean(L, any);
return 1;
}
static int l_lovrWorldGetGravity(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float x, y, z;
@ -254,7 +291,7 @@ static int l_lovrWorldGetGravity(lua_State* L) {
static int l_lovrWorldSetGravity(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float gravity[4];
float gravity[3];
luax_readvec3(L, 2, gravity, NULL);
lovrWorldSetGravity(world, gravity[0], gravity[1], gravity[2]);
return 0;
@ -355,8 +392,8 @@ static int l_lovrWorldEnableCollisionBetween(lua_State* L) {
static int l_lovrWorldIsCollisionEnabledBetween(lua_State* L) {
World* world = luax_checktype(L, 1, World);
const char* tag1 = luaL_checkstring(L, 2);
const char* tag2 = luaL_checkstring(L, 3);
const char* tag1 = lua_tostring(L, 2);
const char* tag2 = lua_tostring(L, 3);
lua_pushboolean(L, lovrWorldIsCollisionEnabledBetween(world, tag1, tag2));
return 1;
}
@ -392,6 +429,8 @@ const luaL_Reg lovrWorld[] = {
{ "collide", l_lovrWorldCollide },
{ "getContacts", l_lovrWorldGetContacts },
{ "raycast", l_lovrWorldRaycast },
{ "queryBox", l_lovrWorldQueryBox },
{ "querySphere", l_lovrWorldQuerySphere },
{ "getGravity", l_lovrWorldGetGravity },
{ "setGravity", l_lovrWorldSetGravity },
{ "getTightness", l_lovrWorldGetTightness },

View File

@ -189,9 +189,87 @@ static int l_lovrSystemGetWindowDensity(lua_State* L) {
return 1;
}
static int l_lovrSystemPollEvents(lua_State* L) {
lovrSystemPollEvents();
return 0;
}
static int l_lovrSystemIsKeyDown(lua_State* L) {
os_key key = luax_checkenum(L, 1, KeyboardKey, NULL);
lua_pushboolean(L, lovrSystemIsKeyDown(key));
int count = lua_gettop(L);
for (int i = 0; i < count; i++) {
os_key key = luax_checkenum(L, i + 1, KeyboardKey, NULL);
if (lovrSystemIsKeyDown(key)) {
lua_pushboolean(L, true);
return 1;
}
}
lua_pushboolean(L, false);
return 1;
}
static int l_lovrSystemWasKeyPressed(lua_State* L) {
int count = lua_gettop(L);
for (int i = 0; i < count; i++) {
os_key key = luax_checkenum(L, i + 1, KeyboardKey, NULL);
if (lovrSystemWasKeyPressed(key)) {
lua_pushboolean(L, true);
return 1;
}
}
lua_pushboolean(L, false);
return 1;
}
static int l_lovrSystemWasKeyReleased(lua_State* L) {
int count = lua_gettop(L);
for (int i = 0; i < count; i++) {
os_key key = luax_checkenum(L, i + 1, KeyboardKey, NULL);
if (lovrSystemWasKeyReleased(key)) {
lua_pushboolean(L, true);
return 1;
}
}
lua_pushboolean(L, false);
return 1;
}
static int l_lovrSystemHasKeyRepeat(lua_State* L) {
lua_pushboolean(L, lovrSystemHasKeyRepeat());
return 1;
}
static int l_lovrSystemSetKeyRepeat(lua_State* L) {
bool repeat = lua_toboolean(L, 1);
lovrSystemSetKeyRepeat(repeat);
return 0;
}
static int l_lovrSystemGetMouseX(lua_State* L) {
double x, y;
lovrSystemGetMousePosition(&x, &y);
lua_pushnumber(L, x);
return 1;
}
static int l_lovrSystemGetMouseY(lua_State* L) {
double x, y;
lovrSystemGetMousePosition(&x, &y);
lua_pushnumber(L, y);
return 1;
}
static int l_lovrSystemGetMousePosition(lua_State* L) {
double x, y;
lovrSystemGetMousePosition(&x, &y);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
return 2;
}
static int l_lovrSystemIsMouseDown(lua_State* L) {
int button = luaL_checkint(L, 1) - 1;
bool down = lovrSystemIsMouseDown(button);
lua_pushboolean(L, down);
return 1;
}
@ -205,7 +283,16 @@ static const luaL_Reg lovrSystem[] = {
{ "getWindowHeight", l_lovrSystemGetWindowHeight },
{ "getWindowDimensions", l_lovrSystemGetWindowDimensions },
{ "getWindowDensity", l_lovrSystemGetWindowDensity },
{ "pollEvents", l_lovrSystemPollEvents },
{ "isKeyDown", l_lovrSystemIsKeyDown },
{ "wasKeyPressed", l_lovrSystemWasKeyPressed },
{ "wasKeyReleased", l_lovrSystemWasKeyReleased },
{ "hasKeyRepeat", l_lovrSystemHasKeyRepeat },
{ "setKeyRepeat", l_lovrSystemSetKeyRepeat },
{ "getMouseX", l_lovrSystemGetMouseX },
{ "getMouseY", l_lovrSystemGetMouseY },
{ "getMousePosition", l_lovrSystemGetMousePosition },
{ "isMouseDown", l_lovrSystemIsMouseDown },
{ NULL, NULL }
};

View File

@ -27,7 +27,15 @@ size_t gpu_sizeof_tally(void);
// Buffer
typedef enum {
GPU_BUFFER_STATIC,
GPU_BUFFER_STREAM,
GPU_BUFFER_UPLOAD,
GPU_BUFFER_DOWNLOAD
} gpu_buffer_type;
typedef struct {
gpu_buffer_type type;
uint32_t size;
void** pointer;
uintptr_t handle;
@ -37,14 +45,6 @@ typedef struct {
bool gpu_buffer_init(gpu_buffer* buffer, gpu_buffer_info* info);
void gpu_buffer_destroy(gpu_buffer* buffer);
typedef enum {
GPU_MAP_STREAM,
GPU_MAP_STAGING,
GPU_MAP_READBACK
} gpu_map_mode;
void* gpu_map(gpu_buffer* buffer, uint32_t size, uint32_t align, gpu_map_mode mode);
// Texture
enum {
@ -197,7 +197,6 @@ typedef enum {
} gpu_slot_type;
enum {
GPU_STAGE_ALL = 0,
GPU_STAGE_VERTEX = (1 << 0),
GPU_STAGE_FRAGMENT = (1 << 1),
GPU_STAGE_COMPUTE = (1 << 2),
@ -272,6 +271,11 @@ void gpu_bundle_write(gpu_bundle** bundles, gpu_bundle_info* info, uint32_t coun
// Pipeline
typedef enum {
GPU_PIPELINE_GRAPHICS,
GPU_PIPELINE_COMPUTE
} gpu_pipeline_type;
typedef enum {
GPU_FLAG_B32,
GPU_FLAG_I32,
@ -461,7 +465,6 @@ void gpu_pipeline_get_cache(void* data, size_t* size);
typedef enum {
GPU_TALLY_TIME,
GPU_TALLY_SHADER,
GPU_TALLY_PIXEL
} gpu_tally_type;
@ -472,6 +475,7 @@ typedef struct {
bool gpu_tally_init(gpu_tally* tally, gpu_tally_info* info);
void gpu_tally_destroy(gpu_tally* tally);
void gpu_tally_get_data(gpu_tally* tally, uint32_t index, uint32_t count, uint32_t* data);
// Stream
@ -496,6 +500,7 @@ typedef struct {
typedef struct {
gpu_texture* texture;
gpu_texture* resolve;
gpu_load_op load, stencilLoad;
gpu_save_op save, stencilSave;
struct { float depth; uint8_t stencil; } clear;
@ -560,7 +565,7 @@ void gpu_compute_end(gpu_stream* stream);
void gpu_set_viewport(gpu_stream* stream, float viewport[4], float depthRange[2]);
void gpu_set_scissor(gpu_stream* stream, uint32_t scissor[4]);
void gpu_push_constants(gpu_stream* stream, gpu_shader* shader, void* data, uint32_t size);
void gpu_bind_pipeline(gpu_stream* stream, gpu_pipeline* pipeline, bool compute);
void gpu_bind_pipeline(gpu_stream* stream, gpu_pipeline* pipeline, gpu_pipeline_type type);
void gpu_bind_bundles(gpu_stream* stream, gpu_shader* shader, gpu_bundle** bundles, uint32_t first, uint32_t count, uint32_t* dynamicOffsets, uint32_t dynamicOffsetCount);
void gpu_bind_vertex_buffers(gpu_stream* stream, gpu_buffer** buffers, uint32_t* offsets, uint32_t first, uint32_t count);
void gpu_bind_index_buffer(gpu_stream* stream, gpu_buffer* buffer, uint32_t offset, gpu_index_type type);
@ -570,18 +575,18 @@ void gpu_draw_indirect(gpu_stream* stream, gpu_buffer* buffer, uint32_t offset,
void gpu_draw_indirect_indexed(gpu_stream* stream, gpu_buffer* buffer, uint32_t offset, uint32_t drawCount, uint32_t stride);
void gpu_compute(gpu_stream* stream, uint32_t x, uint32_t y, uint32_t z);
void gpu_compute_indirect(gpu_stream* stream, gpu_buffer* buffer, uint32_t offset);
void gpu_copy_buffers(gpu_stream* stream, gpu_buffer* src, gpu_buffer* dst, uint32_t srcOffset, uint32_t dstOffset, uint32_t size);
void gpu_copy_textures(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t size[3]);
void gpu_copy_buffers(gpu_stream* stream, gpu_buffer* src, gpu_buffer* dst, uint32_t srcOffset, uint32_t dstOffset, uint32_t extent);
void gpu_copy_textures(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t extent[3]);
void gpu_copy_buffer_texture(gpu_stream* stream, gpu_buffer* src, gpu_texture* dst, uint32_t srcOffset, uint32_t dstOffset[4], uint32_t extent[3]);
void gpu_copy_texture_buffer(gpu_stream* stream, gpu_texture* src, gpu_buffer* dst, uint32_t srcOffset[4], uint32_t dstOffset, uint32_t extent[3]);
void gpu_copy_tally_buffer(gpu_stream* stream, gpu_tally* src, gpu_buffer* dst, uint32_t srcIndex, uint32_t dstOffset, uint32_t count, uint32_t stride);
void gpu_copy_tally_buffer(gpu_stream* stream, gpu_tally* src, gpu_buffer* dst, uint32_t srcIndex, uint32_t dstOffset, uint32_t count);
void gpu_clear_buffer(gpu_stream* stream, gpu_buffer* buffer, uint32_t offset, uint32_t size);
void gpu_clear_texture(gpu_stream* stream, gpu_texture* texture, float value[4], uint32_t layer, uint32_t layerCount, uint32_t level, uint32_t levelCount);
void gpu_clear_tally(gpu_stream* stream, gpu_tally* tally, uint32_t index, uint32_t count);
void gpu_blit(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t srcExtent[3], uint32_t dstExtent[3], gpu_filter filter);
void gpu_sync(gpu_stream* stream, gpu_barrier* barriers, uint32_t count);
void gpu_tally_begin(gpu_stream* stream, gpu_tally* tally, uint32_t index);
void gpu_tally_end(gpu_stream* stream, gpu_tally* tally, uint32_t index);
void gpu_tally_finish(gpu_stream* stream, gpu_tally* tally, uint32_t index);
void gpu_tally_mark(gpu_stream* stream, gpu_tally* tally, uint32_t index);
// Entry
@ -607,13 +612,14 @@ enum {
};
typedef struct {
uint8_t formats[GPU_FORMAT_COUNT];
uint8_t formats[GPU_FORMAT_COUNT][2];
bool textureBC;
bool textureASTC;
bool wireframe;
bool depthClamp;
bool depthResolve;
bool indirectDrawFirstInstance;
bool shaderTally;
bool shaderDebug;
bool float64;
bool int64;
bool int16;

File diff suppressed because it is too large Load Diff

725
src/core/gpu_web.c Normal file
View File

@ -0,0 +1,725 @@
#include "gpu.h"
#include <webgpu/webgpu.h>
#include <emscripten/html5_webgpu.h>
#include <string.h>
struct gpu_buffer {
WGPUBuffer handle;
};
struct gpu_texture {
WGPUTexture handle;
WGPUTextureView view;
};
struct gpu_sampler {
WGPUSampler handle;
};
struct gpu_layout {
WGPUBindGroupLayout handle;
};
struct gpu_shader {
WGPUShaderModule handles[2];
WGPUPipelineLayout pipelineLayout;
};
struct gpu_pipeline {
WGPURenderPipeline render;
WGPUComputePipeline compute;
};
struct gpu_stream {
WGPUCommandEncoder commands;
union {
WGPURenderPassEncoder render;
WGPUComputePassEncoder compute;
};
};
size_t gpu_sizeof_buffer(void) { return sizeof(gpu_buffer); }
size_t gpu_sizeof_texture(void) { return sizeof(gpu_texture); }
size_t gpu_sizeof_sampler(void) { return sizeof(gpu_sampler); }
size_t gpu_sizeof_layout(void) { return sizeof(gpu_layout); }
size_t gpu_sizeof_shader(void) { return sizeof(gpu_shader); }
size_t gpu_sizeof_pipeline(void) { return sizeof(gpu_pipeline); }
// State
static struct {
WGPUDevice device;
} state;
// Helpers
#define COUNTOF(x) (sizeof(x) / sizeof(x[0]))
static WGPUTextureFormat convertFormat(gpu_texture_format format, bool srgb);
// Buffer
bool gpu_buffer_init(gpu_buffer* buffer, gpu_buffer_info* info) {
buffer->handle = wgpuDeviceCreateBuffer(state.device, &(WGPUBufferDescriptor) {
.label = info->label,
.usage =
WGPUBufferUsage_Vertex |
WGPUBufferUsage_Index |
WGPUBufferUsage_Uniform |
WGPUBufferUsage_Storage |
WGPUBufferUsage_Indirect |
WGPUBufferUsage_CopySrc |
WGPUBufferUsage_CopyDst |
WGPUBufferUsage_QueryResolve,
.size = info->size,
.mappedAtCreation = !!info->pointer
});
return !!buffer->handle;
}
void gpu_buffer_destroy(gpu_buffer* buffer) {
wgpuBufferDestroy(buffer->handle);
}
// Texture
bool gpu_texture_init(gpu_texture* texture, gpu_texture_info* info) {
static const WGPUTextureDimension dimensions[] = {
[GPU_TEXTURE_2D] = WGPUTextureDimension_2D,
[GPU_TEXTURE_3D] = WGPUTextureDimension_3D,
[GPU_TEXTURE_CUBE] = WGPUTextureDimension_2D,
[GPU_TEXTURE_ARRAY] = WGPUTextureDimension_2D
};
static const WGPUTextureViewDimension types[] = {
[GPU_TEXTURE_2D] = WGPUTextureViewDimension_2D,
[GPU_TEXTURE_3D] = WGPUTextureViewDimension_3D,
[GPU_TEXTURE_CUBE] = WGPUTextureViewDimension_Cube,
[GPU_TEXTURE_ARRAY] = WGPUTextureViewDimension_2DArray
};
texture->handle = wgpuDeviceCreateTexture(state.device, &(WGPUTextureDescriptor) {
.label = info->label,
.usage =
((info->usage & GPU_TEXTURE_RENDER) ? WGPUTextureUsage_RenderAttachment : 0) |
((info->usage & GPU_TEXTURE_SAMPLE) ? WGPUTextureUsage_TextureBinding : 0) |
((info->usage & GPU_TEXTURE_STORAGE) ? WGPUTextureUsage_StorageBinding : 0) |
((info->usage & GPU_TEXTURE_COPY_SRC) ? WGPUTextureUsage_CopySrc : 0) |
((info->usage & GPU_TEXTURE_COPY_DST) ? WGPUTextureUsage_CopyDst : 0),
.dimension = dimensions[info->type],
.size = { info->size[0], info->size[1], info->size[2] },
.format = convertFormat(info->format, info->srgb),
.mipLevelCount = info->mipmaps,
.sampleCount = info->samples
});
texture->view = wgpuTextureCreateView(texture->handle, &(WGPUTextureViewDescriptor) {
.format = wgpuTextureGetFormat(texture->handle),
.dimension = types[info->type],
.mipLevelCount = info->mipmaps,
.arrayLayerCount = info->type == GPU_TEXTURE_ARRAY ? info->size[2] : 1
});
// TODO upload, mipgen
return true;
}
bool gpu_texture_init_view(gpu_texture* texture, gpu_texture_view_info* info) {
static const WGPUTextureViewDimension types[] = {
[GPU_TEXTURE_2D] = WGPUTextureViewDimension_2D,
[GPU_TEXTURE_3D] = WGPUTextureViewDimension_3D,
[GPU_TEXTURE_CUBE] = WGPUTextureViewDimension_Cube,
[GPU_TEXTURE_ARRAY] = WGPUTextureViewDimension_2DArray
};
texture->handle = NULL;
return texture->view = wgpuTextureCreateView(info->source->handle, &(WGPUTextureViewDescriptor) {
.format = wgpuTextureGetFormat(info->source->handle),
.dimension = types[info->type],
.baseMipLevel = info->levelIndex,
.mipLevelCount = info->levelCount,
.baseArrayLayer = info->layerIndex,
.arrayLayerCount = info->layerCount
});
}
void gpu_texture_destroy(gpu_texture* texture) {
wgpuTextureViewRelease(texture->view);
wgpuTextureDestroy(texture->handle);
}
// Sampler
bool gpu_sampler_init(gpu_sampler* sampler, gpu_sampler_info* info) {
static const WGPUFilterMode filters[] = {
[GPU_FILTER_NEAREST] = WGPUFilterMode_Nearest,
[GPU_FILTER_LINEAR] = WGPUFilterMode_Linear
};
static const WGPUAddressMode wraps[] = {
[GPU_WRAP_CLAMP] = WGPUAddressMode_ClampToEdge,
[GPU_WRAP_REPEAT] = WGPUAddressMode_Repeat,
[GPU_WRAP_MIRROR] = WGPUAddressMode_MirrorRepeat
};
static const WGPUCompareFunction compares[] = {
[GPU_COMPARE_NONE] = WGPUCompareFunction_Always,
[GPU_COMPARE_EQUAL] = WGPUCompareFunction_Equal,
[GPU_COMPARE_NEQUAL] = WGPUCompareFunction_NotEqual,
[GPU_COMPARE_LESS] = WGPUCompareFunction_Less,
[GPU_COMPARE_LEQUAL] = WGPUCompareFunction_LessEqual,
[GPU_COMPARE_GREATER] = WGPUCompareFunction_Greater,
[GPU_COMPARE_GEQUAL] = WGPUCompareFunction_GreaterEqual
};
return sampler->handle = wgpuDeviceCreateSampler(state.device, &(WGPUSamplerDescriptor) {
.addressModeU = wraps[info->wrap[0]],
.addressModeV = wraps[info->wrap[1]],
.addressModeW = wraps[info->wrap[2]],
.magFilter = filters[info->mag],
.minFilter = filters[info->min],
.mipmapFilter = filters[info->mip],
.lodMinClamp = info->lodClamp[0],
.lodMaxClamp = info->lodClamp[1],
.compare = compares[info->compare],
.maxAnisotropy = info->anisotropy
});
}
void gpu_sampler_destroy(gpu_sampler* sampler) {
wgpuSamplerRelease(sampler->handle);
}
// Layout
bool gpu_layout_init(gpu_layout* layout, gpu_layout_info* info) {
static const WGPUBufferBindingType bufferTypes[] = {
[GPU_SLOT_UNIFORM_BUFFER] = WGPUBufferBindingType_Uniform,
[GPU_SLOT_STORAGE_BUFFER] = WGPUBufferBindingType_Storage,
[GPU_SLOT_UNIFORM_BUFFER_DYNAMIC] = WGPUBufferBindingType_Uniform,
[GPU_SLOT_STORAGE_BUFFER_DYNAMIC] = WGPUBufferBindingType_Storage
};
gpu_slot* slot = info->slots;
WGPUBindGroupLayoutEntry entries[32];
for (uint32_t i = 0; i < info->count; i++, slot++) {
entries[i] = (WGPUBindGroupLayoutEntry) {
.binding = slot->number,
.visibility =
(((slot->stages & GPU_STAGE_VERTEX) ? WGPUShaderStage_Vertex : 0) |
((slot->stages & GPU_STAGE_FRAGMENT) ? WGPUShaderStage_Fragment : 0) |
((slot->stages & GPU_STAGE_COMPUTE) ? WGPUShaderStage_Compute : 0))
};
switch (info->slots[i].type) {
case GPU_SLOT_UNIFORM_BUFFER_DYNAMIC:
case GPU_SLOT_STORAGE_BUFFER_DYNAMIC:
entries[i].buffer.hasDynamicOffset = true;
/* fallthrough */
case GPU_SLOT_UNIFORM_BUFFER:
case GPU_SLOT_STORAGE_BUFFER:
entries[i].buffer.type = bufferTypes[slot->type];
break;
// FIXME need more metadata
case GPU_SLOT_SAMPLED_TEXTURE:
entries[i].texture.sampleType = WGPUTextureSampleType_Float;
entries[i].texture.viewDimension = WGPUTextureViewDimension_2D;
entries[i].texture.multisampled = false;
break;
// FIXME need more metadata
case GPU_SLOT_STORAGE_TEXTURE:
entries[i].storageTexture.access = WGPUStorageTextureAccess_WriteOnly;
entries[i].storageTexture.format = WGPUTextureFormat_Undefined;
entries[i].storageTexture.viewDimension = WGPUTextureViewDimension_2D;
break;
// FIXME need more metadata?
case GPU_SLOT_SAMPLER:
entries[i].sampler.type = WGPUSamplerBindingType_Filtering;
break;
}
}
return layout->handle = wgpuDeviceCreateBindGroupLayout(state.device, &(WGPUBindGroupLayoutDescriptor) {
.entryCount = info->count,
.entries = entries
});
}
void gpu_layout_destroy(gpu_layout* layout) {
wgpuBindGroupLayoutRelease(layout->handle);
}
// Shader
bool gpu_shader_init(gpu_shader* shader, gpu_shader_info* info) {
for (uint32_t i = 0; i < COUNTOF(info->stages) && info->stages[i].code; i++) {
WGPUShaderModuleSPIRVDescriptor spirv = {
.chain.sType = WGPUSType_ShaderModuleSPIRVDescriptor,
.codeSize = info->stages[i].length,
.code = info->stages[i].code
};
shader->handles[i] = wgpuDeviceCreateShaderModule(state.device, &(WGPUShaderModuleDescriptor) {
.nextInChain = &spirv.chain,
.label = info->label
});
}
uint32_t layoutCount = 0;
WGPUBindGroupLayout layouts[4];
for (uint32_t i = 0; i < COUNTOF(info->layouts) && info->layouts[i]; i++) {
layouts[layoutCount++] = info->layouts[i]->handle;
}
return shader->pipelineLayout = wgpuDeviceCreatePipelineLayout(state.device, &(WGPUPipelineLayoutDescriptor) {
.bindGroupLayoutCount = layoutCount,
.bindGroupLayouts = layouts
});
}
void gpu_shader_destroy(gpu_shader* shader) {
wgpuShaderModuleRelease(shader->handles[0]);
wgpuShaderModuleRelease(shader->handles[1]);
wgpuPipelineLayoutRelease(shader->pipelineLayout);
}
// Bundle
// Pipeline
bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info) {
static const WGPUPrimitiveTopology topologies[] = {
[GPU_DRAW_POINTS] = WGPUPrimitiveTopology_PointList,
[GPU_DRAW_LINES] = WGPUPrimitiveTopology_LineList,
[GPU_DRAW_TRIANGLES] = WGPUPrimitiveTopology_TriangleList
};
static const WGPUVertexFormat attributeTypes[] = {
[GPU_TYPE_I8x4] = WGPUVertexFormat_Sint8x4,
[GPU_TYPE_U8x4] = WGPUVertexFormat_Uint8x4,
[GPU_TYPE_SN8x4] = WGPUVertexFormat_Snorm8x4,
[GPU_TYPE_UN8x4] = WGPUVertexFormat_Unorm8x4,
[GPU_TYPE_UN10x3] = WGPUVertexFormat_Undefined,
[GPU_TYPE_I16] = WGPUVertexFormat_Undefined,
[GPU_TYPE_I16x2] = WGPUVertexFormat_Sint16x2,
[GPU_TYPE_I16x4] = WGPUVertexFormat_Sint16x4,
[GPU_TYPE_U16] = WGPUVertexFormat_Undefined,
[GPU_TYPE_U16x2] = WGPUVertexFormat_Uint16x2,
[GPU_TYPE_U16x4] = WGPUVertexFormat_Uint16x4,
[GPU_TYPE_SN16x2] = WGPUVertexFormat_Snorm16x2,
[GPU_TYPE_SN16x4] = WGPUVertexFormat_Snorm16x4,
[GPU_TYPE_UN16x2] = WGPUVertexFormat_Unorm16x2,
[GPU_TYPE_UN16x4] = WGPUVertexFormat_Unorm16x4,
[GPU_TYPE_I32] = WGPUVertexFormat_Sint32,
[GPU_TYPE_I32x2] = WGPUVertexFormat_Sint32x2,
[GPU_TYPE_I32x3] = WGPUVertexFormat_Sint32x3,
[GPU_TYPE_I32x4] = WGPUVertexFormat_Sint32x4,
[GPU_TYPE_U32] = WGPUVertexFormat_Uint32,
[GPU_TYPE_U32x2] = WGPUVertexFormat_Uint32x2,
[GPU_TYPE_U32x3] = WGPUVertexFormat_Uint32x3,
[GPU_TYPE_U32x4] = WGPUVertexFormat_Uint32x4,
[GPU_TYPE_F16x2] = WGPUVertexFormat_Float16x2,
[GPU_TYPE_F16x4] = WGPUVertexFormat_Float16x4,
[GPU_TYPE_F32] = WGPUVertexFormat_Float32,
[GPU_TYPE_F32x2] = WGPUVertexFormat_Float32x2,
[GPU_TYPE_F32x3] = WGPUVertexFormat_Float32x3,
[GPU_TYPE_F32x4] = WGPUVertexFormat_Float32x4
};
static const WGPUFrontFace frontFaces[] = {
[GPU_WINDING_CCW] = WGPUFrontFace_CCW,
[GPU_WINDING_CW] = WGPUFrontFace_CW
};
static const WGPUCullMode cullModes[] = {
[GPU_CULL_NONE] = WGPUCullMode_None,
[GPU_CULL_FRONT] = WGPUCullMode_Front,
[GPU_CULL_BACK] = WGPUCullMode_Back
};
static const WGPUCompareFunction compares[] = {
[GPU_COMPARE_NONE] = WGPUCompareFunction_Always,
[GPU_COMPARE_EQUAL] = WGPUCompareFunction_Equal,
[GPU_COMPARE_NEQUAL] = WGPUCompareFunction_NotEqual,
[GPU_COMPARE_LESS] = WGPUCompareFunction_Less,
[GPU_COMPARE_LEQUAL] = WGPUCompareFunction_LessEqual,
[GPU_COMPARE_GREATER] = WGPUCompareFunction_Greater,
[GPU_COMPARE_GEQUAL] = WGPUCompareFunction_GreaterEqual
};
static const WGPUStencilOperation stencilOps[] = {
[GPU_STENCIL_KEEP] = WGPUStencilOperation_Keep,
[GPU_STENCIL_ZERO] = WGPUStencilOperation_Zero,
[GPU_STENCIL_REPLACE] = WGPUStencilOperation_Replace,
[GPU_STENCIL_INCREMENT] = WGPUStencilOperation_IncrementClamp,
[GPU_STENCIL_DECREMENT] = WGPUStencilOperation_DecrementClamp,
[GPU_STENCIL_INCREMENT_WRAP] = WGPUStencilOperation_IncrementWrap,
[GPU_STENCIL_DECREMENT_WRAP] = WGPUStencilOperation_DecrementWrap,
[GPU_STENCIL_INVERT] = WGPUStencilOperation_Invert
};
static const WGPUBlendFactor blendFactors[] = {
[GPU_BLEND_ZERO] = WGPUBlendFactor_Zero,
[GPU_BLEND_ONE] = WGPUBlendFactor_One,
[GPU_BLEND_SRC_COLOR] = WGPUBlendFactor_Src,
[GPU_BLEND_ONE_MINUS_SRC_COLOR] = WGPUBlendFactor_OneMinusSrc,
[GPU_BLEND_SRC_ALPHA] = WGPUBlendFactor_SrcAlpha,
[GPU_BLEND_ONE_MINUS_SRC_ALPHA] = WGPUBlendFactor_OneMinusSrcAlpha,
[GPU_BLEND_DST_COLOR] = WGPUBlendFactor_Dst,
[GPU_BLEND_ONE_MINUS_DST_COLOR] = WGPUBlendFactor_OneMinusDst,
[GPU_BLEND_DST_ALPHA] = WGPUBlendFactor_DstAlpha,
[GPU_BLEND_ONE_MINUS_DST_ALPHA] = WGPUBlendFactor_OneMinusDstAlpha
};
static const WGPUBlendOperation blendOps[] = {
[GPU_BLEND_ADD] = WGPUBlendOperation_Add,
[GPU_BLEND_SUB] = WGPUBlendOperation_Subtract,
[GPU_BLEND_RSUB] = WGPUBlendOperation_ReverseSubtract,
[GPU_BLEND_MIN] = WGPUBlendOperation_Min,
[GPU_BLEND_MAX] = WGPUBlendOperation_Max
};
uint32_t totalAttributeCount = 0;
WGPUVertexAttribute attributes[16];
WGPUVertexBufferLayout vertexBuffers[16];
for (uint32_t i = 0; i < info->vertex.bufferCount; i++) {
vertexBuffers[i] = (WGPUVertexBufferLayout) {
.arrayStride = info->vertex.bufferStrides[i],
.stepMode = (info->vertex.instancedBuffers & (1 << i)) ? WGPUVertexStepMode_Instance : WGPUVertexStepMode_Vertex,
.attributeCount = 0,
.attributes = &attributes[totalAttributeCount]
};
for (uint32_t j = 0; j < info->vertex.attributeCount; j++) {
if (info->vertex.attributes[j].buffer == i) {
attributes[totalAttributeCount++] = (WGPUVertexAttribute) {
.format = attributeTypes[info->vertex.attributes[j].type],
.offset = info->vertex.attributes[j].offset,
.shaderLocation = info->vertex.attributes[j].location
};
}
}
totalAttributeCount += vertexBuffers[i].attributeCount;
}
WGPUVertexState vertex = {
.module = info->shader->handles[0],
.entryPoint = "main",
.bufferCount = info->vertex.bufferCount,
.buffers = vertexBuffers
};
WGPUPrimitiveState primitive = {
.topology = topologies[info->drawMode],
.frontFace = frontFaces[info->rasterizer.winding],
.cullMode = cullModes[info->rasterizer.cullMode],
};
WGPUStencilFaceState stencil = {
.compare = compares[info->stencil.test],
.failOp = stencilOps[info->stencil.failOp],
.depthFailOp = stencilOps[info->stencil.depthFailOp],
.passOp = stencilOps[info->stencil.passOp]
};
WGPUDepthStencilState depth = {
.format = convertFormat(info->depth.format, false),
.depthWriteEnabled = info->depth.write,
.depthCompare = compares[info->depth.test],
.stencilFront = stencil,
.stencilBack = stencil,
.stencilReadMask = info->stencil.testMask,
.stencilWriteMask = info->stencil.writeMask,
.depthBias = info->rasterizer.depthOffset,
.depthBiasSlopeScale = info->rasterizer.depthOffsetSloped,
.depthBiasClamp = info->rasterizer.depthOffsetClamp
};
WGPUMultisampleState multisample = {
.count = info->multisample.count,
.alphaToCoverageEnabled = info->multisample.alphaToCoverage
};
WGPUBlendState blends[4];
WGPUColorTargetState targets[4];
for (uint32_t i = 0; i < info->attachmentCount; i++) {
targets[i] = (WGPUColorTargetState) {
.format = convertFormat(info->color[i].format, info->color[i].srgb),
.blend = info->color[i].blend.enabled ? &blends[i] : NULL,
.writeMask = info->color[i].mask
};
if (info->color[i].blend.enabled) {
blends[i] = (WGPUBlendState) {
.color.operation = blendOps[info->color[i].blend.color.op],
.color.srcFactor = blendFactors[info->color[i].blend.color.src],
.color.dstFactor = blendFactors[info->color[i].blend.color.dst],
.alpha.operation = blendOps[info->color[i].blend.alpha.op],
.alpha.srcFactor = blendFactors[info->color[i].blend.alpha.src],
.alpha.dstFactor = blendFactors[info->color[i].blend.alpha.dst]
};
}
}
WGPUFragmentState fragment = {
.module = info->shader->handles[1],
.entryPoint = "main",
.targetCount = info->attachmentCount,
.targets = targets
};
WGPURenderPipelineDescriptor pipelineInfo = {
.label = info->label,
.layout = info->shader->pipelineLayout,
.vertex = vertex,
.primitive = primitive,
.depthStencil = info->depth.format ? &depth : NULL,
.multisample = multisample,
.fragment = &fragment
};
return pipeline->render = wgpuDeviceCreateRenderPipeline(state.device, &pipelineInfo);
}
bool gpu_pipeline_init_compute(gpu_pipeline* pipeline, gpu_compute_pipeline_info* info) {
return false; // TODO
}
void gpu_pipeline_destroy(gpu_pipeline* pipeline) {
if (pipeline->render) wgpuRenderPipelineRelease(pipeline->render);
if (pipeline->compute) wgpuComputePipelineRelease(pipeline->compute);
}
// Tally
// Stream
void gpu_compute_begin(gpu_stream* stream) {
stream->compute = wgpuCommandEncoderBeginComputePass(stream->commands, NULL);
}
void gpu_compute_end(gpu_stream* stream) {
wgpuComputePassEncoderEnd(stream->compute);
}
void gpu_set_viewport(gpu_stream* stream, float view[4], float depth[2]) {
wgpuRenderPassEncoderSetViewport(stream->render, view[0], view[1], view[2], view[3], depth[0], depth[1]);
}
void gpu_set_scissor(gpu_stream* stream, uint32_t scissor[4]) {
wgpuRenderPassEncoderSetScissorRect(stream->render, scissor[0], scissor[1], scissor[2], scissor[3]);
}
void gpu_push_constants(gpu_stream* stream, gpu_shader* shader, void* data, uint32_t size) {
// Unsupported
}
void gpu_bind_pipeline(gpu_stream* stream, gpu_pipeline* pipeline, gpu_pipeline_type type) {
if (type == GPU_PIPELINE_COMPUTE) {
wgpuComputePassEncoderSetPipeline(stream->compute, pipeline->compute);
} else {
wgpuRenderPassEncoderSetPipeline(stream->render, pipeline->render);
}
}
void gpu_bind_vertex_buffers(gpu_stream* stream, gpu_buffer** buffers, uint32_t* offsets, uint32_t first, uint32_t count) {
for (uint32_t i = 0; i < count; i++) {
uint64_t size = wgpuBufferGetSize(buffers[i]->handle) - offsets[i];
wgpuRenderPassEncoderSetVertexBuffer(stream->render, first + i, buffers[i]->handle, offsets[i], size);
}
}
void gpu_bind_index_buffer(gpu_stream* stream, gpu_buffer* buffer, uint32_t offset, gpu_index_type type) {
WGPUIndexFormat indexTypes[] = {
[GPU_INDEX_U16] = WGPUIndexFormat_Uint16,
[GPU_INDEX_U32] = WGPUIndexFormat_Uint32
};
uint64_t size = wgpuBufferGetSize(buffer->handle) - offset;
wgpuRenderPassEncoderSetIndexBuffer(stream->render, buffer->handle, indexTypes[type], offset, size);
}
void gpu_draw(gpu_stream* stream, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t baseInstance) {
wgpuRenderPassEncoderDraw(stream->render, vertexCount, instanceCount, firstVertex, baseInstance);
}
void gpu_draw_indexed(gpu_stream* stream, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, uint32_t baseVertex, uint32_t baseInstance) {
wgpuRenderPassEncoderDrawIndexed(stream->render, indexCount, instanceCount, firstIndex, baseVertex, baseInstance);
}
void gpu_draw_indirect(gpu_stream* stream, gpu_buffer* buffer, uint32_t offset, uint32_t drawCount, uint32_t stride) {
stride = stride ? stride : 16;
for (uint32_t i = 0; i < drawCount; i++) {
wgpuRenderPassEncoderDrawIndirect(stream->render, buffer->handle, offset + stride * i);
}
}
void gpu_draw_indirect_indexed(gpu_stream* stream, gpu_buffer* buffer, uint32_t offset, uint32_t drawCount, uint32_t stride) {
stride = stride ? stride : 20;
for (uint32_t i = 0; i < drawCount; i++) {
wgpuRenderPassEncoderDrawIndexedIndirect(stream->render, buffer->handle, offset + stride * i);
}
}
void gpu_compute(gpu_stream* stream, uint32_t x, uint32_t y, uint32_t z) {
wgpuComputePassEncoderDispatchWorkgroups(stream->compute, x, y, z);
}
void gpu_compute_indirect(gpu_stream* stream, gpu_buffer* buffer, uint32_t offset) {
wgpuComputePassEncoderDispatchWorkgroupsIndirect(stream->compute, buffer->handle, offset);
}
void gpu_copy_buffers(gpu_stream* stream, gpu_buffer* src, gpu_buffer* dst, uint32_t srcOffset, uint32_t dstOffset, uint32_t extent) {
wgpuCommandEncoderCopyBufferToBuffer(stream->commands, src->handle, srcOffset, dst->handle, dstOffset, extent);
}
void gpu_copy_textures(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t extent[3]) {
WGPUImageCopyTexture srcRegion = {
.texture = src->handle,
.mipLevel = srcOffset[3],
.origin = { srcOffset[0], srcOffset[1], srcOffset[2] }
};
WGPUImageCopyTexture dstRegion = {
.texture = dst->handle,
.mipLevel = dstOffset[3],
.origin = { dstOffset[0], dstOffset[1], dstOffset[2] }
};
WGPUExtent3D size = { extent[0], extent[1], extent[2] };
wgpuCommandEncoderCopyTextureToTexture(stream->commands, &srcRegion, &dstRegion, &size);
}
void gpu_copy_buffer_texture(gpu_stream* stream, gpu_buffer* src, gpu_texture* dst, uint32_t srcOffset, uint32_t dstOffset[4], uint32_t extent[3]) {
WGPUImageCopyBuffer srcRegion = {
.layout.offset = srcOffset,
.layout.bytesPerRow = 0, // FIXME
.layout.rowsPerImage = 0, // FIXME
.buffer = src->handle
};
WGPUImageCopyTexture dstRegion = {
.texture = dst->handle,
.mipLevel = dstOffset[3],
.origin = { dstOffset[0], dstOffset[1], dstOffset[2] }
};
WGPUExtent3D size = { extent[0], extent[1], extent[2] };
wgpuCommandEncoderCopyBufferToTexture(stream->commands, &srcRegion, &dstRegion, &size);
}
void gpu_copy_texture_buffer(gpu_stream* stream, gpu_texture* src, gpu_buffer* dst, uint32_t srcOffset[4], uint32_t dstOffset, uint32_t extent[3]) {
WGPUImageCopyTexture srcRegion = {
.texture = src->handle,
.mipLevel = srcOffset[3],
.origin = { srcOffset[0], srcOffset[1], srcOffset[2] }
};
WGPUImageCopyBuffer dstRegion = {
.layout.offset = dstOffset,
.layout.bytesPerRow = 0, // FIXME
.layout.rowsPerImage = 0, // FIXME
.buffer = dst->handle
};
WGPUExtent3D size = { extent[0], extent[1], extent[2] };
wgpuCommandEncoderCopyTextureToBuffer(stream->commands, &srcRegion, &dstRegion, &size);
}
void gpu_copy_tally_buffer(gpu_stream* stream, gpu_tally* src, gpu_buffer* dst, uint32_t srcIndex, uint32_t dstOffset, uint32_t count) {
// TODO
}
void gpu_clear_buffer(gpu_stream* stream, gpu_buffer* buffer, uint32_t offset, uint32_t size) {
wgpuCommandEncoderClearBuffer(stream->commands, buffer->handle, offset, size);
}
void gpu_clear_texture(gpu_stream* stream, gpu_texture* texture, float value[4], uint32_t layer, uint32_t layerCount, uint32_t level, uint32_t levelCount) {
// TODO Unsupported
}
void gpu_clear_tally(gpu_stream* stream, gpu_tally* tally, uint32_t index, uint32_t count) {
// TODO
}
void gpu_blit(gpu_stream* stream, gpu_texture* src, gpu_texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t srcExtent[3], uint32_t dstExtent[3], gpu_filter filter) {
// TODO Unsupported
}
void gpu_sync(gpu_stream* stream, gpu_barrier* barriers, uint32_t count) {
//
}
// Entry
bool gpu_init(gpu_config* config) {
state.device = emscripten_webgpu_get_device();
return !!state.device;
}
void gpu_destroy(void) {
if (state.device) wgpuDeviceDestroy(state.device);
memset(&state, 0, sizeof(state));
}
// Helpers
static WGPUTextureFormat convertFormat(gpu_texture_format format, bool srgb) {
static const WGPUTextureFormat formats[][2] = {
[GPU_FORMAT_R8] = { WGPUTextureFormat_R8Unorm, WGPUTextureFormat_R8Unorm },
[GPU_FORMAT_RG8] = { WGPUTextureFormat_RG8Unorm, WGPUTextureFormat_RG8Unorm },
[GPU_FORMAT_RGBA8] = { WGPUTextureFormat_RGBA8Unorm, WGPUTextureFormat_RGBA8UnormSrgb },
[GPU_FORMAT_R16] = { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined },
[GPU_FORMAT_RG16] = { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined },
[GPU_FORMAT_RGBA16] = { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined },
[GPU_FORMAT_R16F] = { WGPUTextureFormat_R16Float, WGPUTextureFormat_R16Float },
[GPU_FORMAT_RG16F] = { WGPUTextureFormat_RG16Float, WGPUTextureFormat_RG16Float },
[GPU_FORMAT_RGBA16F] = { WGPUTextureFormat_RGBA16Float, WGPUTextureFormat_RGBA16Float },
[GPU_FORMAT_R32F] = { WGPUTextureFormat_R32Float, WGPUTextureFormat_R32Float },
[GPU_FORMAT_RG32F] = { WGPUTextureFormat_RG32Float, WGPUTextureFormat_RG32Float },
[GPU_FORMAT_RGBA32F] = { WGPUTextureFormat_RGBA32Float, WGPUTextureFormat_RGBA32Float },
[GPU_FORMAT_RGB565] = { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined },
[GPU_FORMAT_RGB5A1] = { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined },
[GPU_FORMAT_RGB10A2] = {WGPUTextureFormat_RGB10A2Unorm, WGPUTextureFormat_RGB10A2Unorm },
[GPU_FORMAT_RG11B10F] = { WGPUTextureFormat_RG11B10Ufloat, WGPUTextureFormat_RG11B10Ufloat },
[GPU_FORMAT_D16] = { WGPUTextureFormat_Depth16Unorm, WGPUTextureFormat_Depth16Unorm },
[GPU_FORMAT_D32F] = { WGPUTextureFormat_Depth32Float, WGPUTextureFormat_Depth32Float },
[GPU_FORMAT_D24S8] = { WGPUTextureFormat_Depth24PlusStencil8, WGPUTextureFormat_Depth24PlusStencil8 },
[GPU_FORMAT_D32FS8] = { WGPUTextureFormat_Depth32FloatStencil8, WGPUTextureFormat_Depth32FloatStencil8 },
[GPU_FORMAT_BC1] = { WGPUTextureFormat_BC1RGBAUnorm, WGPUTextureFormat_BC1RGBAUnormSrgb },
[GPU_FORMAT_BC2] = { WGPUTextureFormat_BC2RGBAUnorm, WGPUTextureFormat_BC2RGBAUnormSrgb },
[GPU_FORMAT_BC3] = { WGPUTextureFormat_BC3RGBAUnorm, WGPUTextureFormat_BC3RGBAUnormSrgb },
[GPU_FORMAT_BC4U] = { WGPUTextureFormat_BC4RUnorm, WGPUTextureFormat_BC4RUnorm },
[GPU_FORMAT_BC4S] = { WGPUTextureFormat_BC4RSnorm, WGPUTextureFormat_BC4RSnorm },
[GPU_FORMAT_BC5U] = { WGPUTextureFormat_BC5RGUnorm, WGPUTextureFormat_BC5RGUnorm },
[GPU_FORMAT_BC5S] = { WGPUTextureFormat_BC5RGSnorm, WGPUTextureFormat_BC5RGSnorm },
[GPU_FORMAT_BC6UF] = { WGPUTextureFormat_BC6HRGBUfloat, WGPUTextureFormat_BC6HRGBUfloat },
[GPU_FORMAT_BC6SF] = { WGPUTextureFormat_BC6HRGBFloat, WGPUTextureFormat_BC6HRGBFloat },
[GPU_FORMAT_BC7] = { WGPUTextureFormat_BC7RGBAUnorm, WGPUTextureFormat_BC7RGBAUnormSrgb },
[GPU_FORMAT_ASTC_4x4] = { WGPUTextureFormat_ASTC4x4Unorm, WGPUTextureFormat_ASTC4x4UnormSrgb },
[GPU_FORMAT_ASTC_5x4] = { WGPUTextureFormat_ASTC5x4Unorm, WGPUTextureFormat_ASTC5x4UnormSrgb },
[GPU_FORMAT_ASTC_5x5] = { WGPUTextureFormat_ASTC5x5Unorm, WGPUTextureFormat_ASTC5x5UnormSrgb },
[GPU_FORMAT_ASTC_6x5] = { WGPUTextureFormat_ASTC6x5Unorm, WGPUTextureFormat_ASTC6x5UnormSrgb },
[GPU_FORMAT_ASTC_6x6] = { WGPUTextureFormat_ASTC6x6Unorm, WGPUTextureFormat_ASTC6x6UnormSrgb },
[GPU_FORMAT_ASTC_8x5] = { WGPUTextureFormat_ASTC8x5Unorm, WGPUTextureFormat_ASTC8x5UnormSrgb },
[GPU_FORMAT_ASTC_8x6] = { WGPUTextureFormat_ASTC8x6Unorm, WGPUTextureFormat_ASTC8x6UnormSrgb },
[GPU_FORMAT_ASTC_8x8] = { WGPUTextureFormat_ASTC8x8Unorm, WGPUTextureFormat_ASTC8x8UnormSrgb },
[GPU_FORMAT_ASTC_10x5] = { WGPUTextureFormat_ASTC10x5Unorm, WGPUTextureFormat_ASTC10x5UnormSrgb },
[GPU_FORMAT_ASTC_10x6] = { WGPUTextureFormat_ASTC10x6Unorm, WGPUTextureFormat_ASTC10x6UnormSrgb },
[GPU_FORMAT_ASTC_10x8] = { WGPUTextureFormat_ASTC10x8Unorm, WGPUTextureFormat_ASTC10x8UnormSrgb },
[GPU_FORMAT_ASTC_10x10] = { WGPUTextureFormat_ASTC10x10Unorm, WGPUTextureFormat_ASTC10x10UnormSrgb },
[GPU_FORMAT_ASTC_12x10] = { WGPUTextureFormat_ASTC12x10Unorm, WGPUTextureFormat_ASTC12x10UnormSrgb },
[GPU_FORMAT_ASTC_12x12] = { WGPUTextureFormat_ASTC12x12Unorm, WGPUTextureFormat_ASTC12x12UnormSrgb }
};
return formats[format][srgb];
}

View File

@ -1,10 +0,0 @@
#include "gpu.h"
#include <webgpu/webgpu.h>
bool gpu_init(gpu_config* config) {
return false;
}
void gpu_destroy(void) {
//
}

View File

@ -10,10 +10,95 @@
#define M_PI 3.14159265358979
#endif
typedef float* vec2;
typedef float* vec3;
typedef float* vec4;
typedef float* quat;
typedef float* mat4;
// vec2
MAF vec2 vec2_set(vec2 v, float x, float y) {
v[0] = x;
v[1] = y;
return v;
}
MAF vec2 vec2_init(vec2 v, const vec2 u) {
return memcpy(v, u, 2 * sizeof(float));
}
MAF vec2 vec2_add(vec2 v, const vec2 u) {
v[0] += u[0];
v[1] += u[1];
return v;
}
MAF vec2 vec2_sub(vec2 v, const vec2 u) {
v[0] -= u[0];
v[1] -= u[1];
return v;
}
MAF vec2 vec2_mul(vec2 v, const vec2 u) {
v[0] *= u[0];
v[1] *= u[1];
return v;
}
MAF vec2 vec2_div(vec2 v, const vec2 u) {
v[0] /= u[0];
v[1] /= u[1];
return v;
}
MAF vec2 vec2_scale(vec2 v, float s) {
v[0] *= s;
v[1] *= s;
return v;
}
MAF float vec2_length(vec2 v) {
return sqrtf(v[0] * v[0] + v[1] * v[1]);
}
MAF vec2 vec2_normalize(vec2 v) {
float length = vec2_length(v);
return length == 0.f ? v : vec2_scale(v, 1.f / length);
}
MAF float vec2_distance2(const vec2 v, const vec2 u) {
float dx = v[0] - u[0];
float dy = v[1] - u[1];
return dx * dx + dy * dy;
}
MAF float vec2_distance(const vec2 v, const vec2 u) {
return sqrtf(vec2_distance2(v, u));
}
MAF float vec2_dot(const vec2 v, const vec2 u) {
return v[0] * u[0] + v[1] * u[1];
}
MAF vec2 vec2_lerp(vec2 v, const vec2 u, float t) {
v[0] = v[0] * (1.f - t) + u[0] * t;
v[1] = v[1] * (1.f - t) + u[1] * t;
return v;
}
MAF float vec2_angle(const vec2 v, const vec2 u) {
float denom = vec2_length(v) * vec2_length(u);
if (denom == 0.f) {
return (float) M_PI / 2.f;
} else {
float cos = vec2_dot(v, u) / denom;
cos = cos < -1.f ? -1.f : cos;
cos = cos > 1.f ? 1.f : cos;
return acosf(cos);
}
}
// vec3
MAF vec3 vec3_set(vec3 v, float x, float y, float z) {
@ -24,29 +109,34 @@ MAF vec3 vec3_set(vec3 v, float x, float y, float z) {
}
MAF vec3 vec3_init(vec3 v, const vec3 u) {
float x = u[0], y = u[1], z = u[2], w = u[3];
v[0] = x;
v[1] = y;
v[2] = z;
v[3] = w;
return v;
return memcpy(v, u, 3 * sizeof(float));
}
MAF vec3 vec3_add(vec3 v, const vec3 u) {
float x = v[0] + u[0], y = v[1] + u[1], z = v[2] + u[2], w = v[3] + u[3];
v[0] = x;
v[1] = y;
v[2] = z;
v[3] = w;
v[0] += u[0];
v[1] += u[1];
v[2] += u[2];
return v;
}
MAF vec3 vec3_sub(vec3 v, const vec3 u) {
float x = v[0] - u[0], y = v[1] - u[1], z = v[2] - u[2], w = v[3] - u[3];
v[0] = x;
v[1] = y;
v[2] = z;
v[3] = w;
v[0] -= u[0];
v[1] -= u[1];
v[2] -= u[2];
return v;
}
MAF vec3 vec3_mul(vec3 v, const vec3 u) {
v[0] *= u[0];
v[1] *= u[1];
v[2] *= u[2];
return v;
}
MAF vec3 vec3_div(vec3 v, const vec3 u) {
v[0] /= u[0];
v[1] /= u[1];
v[2] /= u[2];
return v;
}
@ -54,7 +144,6 @@ MAF vec3 vec3_scale(vec3 v, float s) {
v[0] *= s;
v[1] *= s;
v[2] *= s;
v[3] *= s;
return v;
}
@ -63,15 +152,19 @@ MAF float vec3_length(const vec3 v) {
}
MAF vec3 vec3_normalize(vec3 v) {
float len = vec3_length(v);
return len == 0.f ? v : vec3_scale(v, 1.f / len);
float length = vec3_length(v);
return length == 0.f ? v : vec3_scale(v, 1.f / length);
}
MAF float vec3_distance(const vec3 v, const vec3 u) {
MAF float vec3_distance2(const vec3 v, const vec3 u) {
float dx = v[0] - u[0];
float dy = v[1] - u[1];
float dz = v[2] - u[2];
return sqrtf(dx * dx + dy * dy + dz * dz);
return dx * dx + dy * dy + dz * dz;
}
MAF float vec3_distance(const vec3 v, const vec3 u) {
return sqrtf(vec3_distance2(v, u));
}
MAF float vec3_dot(const vec3 v, const vec3 u) {
@ -79,46 +172,23 @@ MAF float vec3_dot(const vec3 v, const vec3 u) {
}
MAF vec3 vec3_cross(vec3 v, const vec3 u) {
return vec3_set(v,
v[1] * u[2] - v[2] * u[1],
v[2] * u[0] - v[0] * u[2],
v[0] * u[1] - v[1] * u[0]
);
float cx = v[1] * u[2] - v[2] * u[1];
float cy = v[2] * u[0] - v[0] * u[2];
float cz = v[0] * u[1] - v[1] * u[0];
return vec3_set(v, cx, cy, cz);
}
MAF vec3 vec3_lerp(vec3 v, const vec3 u, float t) {
float x = v[0] + (u[0] - v[0]) * t;
float y = v[1] + (u[1] - v[1]) * t;
float z = v[2] + (u[2] - v[2]) * t;
float w = v[3] + (u[3] - v[3]) * t;
v[0] = x;
v[1] = y;
v[2] = z;
v[3] = w;
v[0] = v[0] * (1.f - t) + u[0] * t;
v[1] = v[1] * (1.f - t) + u[1] * t;
v[2] = v[2] * (1.f - t) + u[2] * t;
return v;
}
MAF vec3 vec3_min(vec3 v, const vec3 u) {
float x = v[0] < u[0] ? v[0] : u[0];
float y = v[1] < u[1] ? v[1] : u[1];
float z = v[2] < u[2] ? v[2] : u[2];
float w = v[3] < u[3] ? v[3] : u[3];
v[0] = x;
v[1] = y;
v[2] = z;
v[3] = w;
return v;
}
MAF vec3 vec3_max(vec3 v, const vec3 u) {
float x = v[0] > u[0] ? v[0] : u[0];
float y = v[1] > u[1] ? v[1] : u[1];
float z = v[2] > u[2] ? v[2] : u[2];
float w = v[3] > u[3] ? v[3] : u[3];
v[0] = x;
v[1] = y;
v[2] = z;
v[3] = w;
MAF vec3 vec3_abs(vec3 v) {
v[0] = fabsf(v[0]);
v[1] = fabsf(v[1]);
v[2] = fabsf(v[2]);
return v;
}
@ -128,8 +198,115 @@ MAF float vec3_angle(const vec3 v, const vec3 u) {
return (float) M_PI / 2.f;
} else {
float cos = vec3_dot(v, u) / denom;
cos = cos < -1 ? -1 : cos;
cos = cos > 1 ? 1 : cos;
cos = cos < -1.f ? -1.f : cos;
cos = cos > 1.f ? 1.f : cos;
return acosf(cos);
}
}
// vec4
MAF vec4 vec4_set(vec4 v, float x, float y, float z, float w) {
v[0] = x;
v[1] = y;
v[2] = z;
v[3] = w;
return v;
}
MAF vec4 vec4_init(vec4 v, const vec4 u) {
return memcpy(v, u, 4 * sizeof(float));
}
MAF vec2 vec4_add(vec4 v, const vec4 u) {
v[0] += u[0];
v[1] += u[1];
v[2] += u[2];
v[3] += u[3];
return v;
}
MAF vec4 vec4_sub(vec4 v, const vec4 u) {
v[0] -= u[0];
v[1] -= u[1];
v[2] -= u[2];
v[3] -= u[3];
return v;
}
MAF vec4 vec4_mul(vec4 v, const vec4 u) {
v[0] *= u[0];
v[1] *= u[1];
v[2] *= u[2];
v[3] *= u[3];
return v;
}
MAF vec4 vec4_div(vec4 v, const vec4 u) {
v[0] /= u[0];
v[1] /= u[1];
v[2] /= u[2];
v[3] /= u[3];
return v;
}
MAF vec4 vec4_scale(vec4 v, float s) {
v[0] *= s;
v[1] *= s;
v[2] *= s;
v[3] *= s;
return v;
}
MAF float vec4_length(vec4 v) {
return sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]);
}
MAF vec4 vec4_normalize(vec4 v) {
float length = vec4_length(v);
return length == 0.f ? v : vec4_scale(v, 1.f / length);
}
MAF float vec4_distance2(const vec4 v, const vec4 u) {
float dx = v[0] - u[0];
float dy = v[1] - u[1];
float dz = v[2] - u[2];
float dw = v[3] - u[3];
return dx * dx + dy * dy + dz * dz + dw * dw;
}
MAF float vec4_distance(const vec4 v, const vec4 u) {
return sqrtf(vec4_distance2(v, u));
}
MAF float vec4_dot(const vec4 v, const vec4 u) {
return v[0] * u[0] + v[1] * u[1] + v[2] * u[2] + v[3] * u[3];
}
MAF vec4 vec4_lerp(vec4 v, const vec4 u, float t) {
v[0] = v[0] * (1.f - t) + u[0] * t;
v[1] = v[1] * (1.f - t) + u[1] * t;
v[2] = v[2] * (1.f - t) + u[2] * t;
v[3] = v[3] * (1.f - t) + u[3] * t;
return v;
}
MAF vec4 vec4_abs(vec4 v) {
v[0] = fabsf(v[0]);
v[1] = fabsf(v[1]);
v[2] = fabsf(v[2]);
v[3] = fabsf(v[3]);
return v;
}
MAF float vec4_angle(const vec4 v, const vec4 u) {
float denom = vec4_length(v) * vec4_length(u);
if (denom == 0.f) {
return (float) M_PI / 2.f;
} else {
float cos = vec4_dot(v, u) / denom;
cos = cos < -1.f ? -1.f : cos;
cos = cos > 1.f ? 1.f : cos;
return acosf(cos);
}
}
@ -158,23 +335,37 @@ MAF quat quat_fromAngleAxis(quat q, float angle, float ax, float ay, float az) {
return quat_set(q, s * ax, s * ay, s * az, c);
}
// https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf
MAF quat quat_fromMat4(quat q, mat4 m) {
float sx = vec3_length(m + 0);
float sy = vec3_length(m + 4);
float sz = vec3_length(m + 8);
float diagonal[4] = { m[0] / sx, m[5] / sy, m[10] / sz };
float a = 1.f + diagonal[0] - diagonal[1] - diagonal[2];
float b = 1.f - diagonal[0] + diagonal[1] - diagonal[2];
float c = 1.f - diagonal[0] - diagonal[1] + diagonal[2];
float d = 1.f + diagonal[0] + diagonal[1] + diagonal[2];
float x = sqrtf(a > 0.f ? a : 0.f) / 2.f;
float y = sqrtf(b > 0.f ? b : 0.f) / 2.f;
float z = sqrtf(c > 0.f ? c : 0.f) / 2.f;
float w = sqrtf(d > 0.f ? d : 0.f) / 2.f;
x = (m[9] / sz - m[6] / sy) > 0.f ? -x : x;
y = (m[2] / sx - m[8] / sz) > 0.f ? -y : y;
z = (m[4] / sy - m[1] / sx) > 0.f ? -z : z;
return quat_set(q, x, y, z, w);
float sx = 1.f / vec3_length(m + 0);
float sy = 1.f / vec3_length(m + 4);
float sz = 1.f / vec3_length(m + 8);
float m00 = m[0] * sx, m01 = m[1] * sx, m02 = m[2] * sx;
float m10 = m[4] * sy, m11 = m[5] * sy, m12 = m[6] * sy;
float m20 = m[8] * sz, m21 = m[9] * sz, m22 = m[10] * sz;
if (m22 < 0.f) {
if (m00 > m11) {
float t = 1.f + m00 - m11 - m22;
float s = .5f / sqrtf(t);
return quat_set(q, t * s, (m01 + m10) * s, (m20 + m02) * s, (m12 - m21) * s);
} else {
float t = 1.f - m00 + m11 - m22;
float s = .5f / sqrtf(t);
return quat_set(q, (m01 + m10) * s, t * s, (m12 + m21) * s, (m20 - m02) * s);
}
} else {
if (m00 < -m11) {
float t = 1.f - m00 - m11 + m22;
float s = .5f / sqrtf(t);
return quat_set(q, (m20 + m02) * s, (m12 + m21) * s, t * s, (m01 - m10) * s);
} else {
float t = 1.f + m00 + m11 + m22;
float s = .5f / sqrtf(t);
return quat_set(q, (m12 - m21) * s, (m20 - m02) * s, (m01 - m10) * s, t * s);
}
}
}
MAF quat quat_identity(quat q) {
@ -305,7 +496,7 @@ MAF quat quat_between(quat q, vec3 u, vec3 v) {
// mat4
#define MAT4_IDENTITY { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }
#define MAT4_IDENTITY { 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f }
#define mat4_init mat4_set
MAF mat4 mat4_set(mat4 m, mat4 n) {
@ -316,46 +507,6 @@ MAF mat4 mat4_set(mat4 m, mat4 n) {
return m;
}
MAF mat4 mat4_fromMat34(mat4 m, float (*n)[4]) {
m[0] = n[0][0];
m[1] = n[1][0];
m[2] = n[2][0];
m[3] = 0.f;
m[4] = n[0][1];
m[5] = n[1][1];
m[6] = n[2][1];
m[7] = 0.f;
m[8] = n[0][2];
m[9] = n[1][2];
m[10] = n[2][2];
m[11] = 0.f;
m[12] = n[0][3];
m[13] = n[1][3];
m[14] = n[2][3];
m[15] = 1.f;
return m;
}
MAF mat4 mat4_fromMat44(mat4 m, float (*n)[4]) {
m[0] = n[0][0];
m[1] = n[1][0];
m[2] = n[2][0];
m[3] = n[3][0];
m[4] = n[0][1];
m[5] = n[1][1];
m[6] = n[2][1];
m[7] = n[3][1];
m[8] = n[0][2];
m[9] = n[1][2];
m[10] = n[2][2];
m[11] = n[3][2];
m[12] = n[0][3];
m[13] = n[1][3];
m[14] = n[2][3];
m[15] = n[3][3];
return m;
}
MAF mat4 mat4_fromQuat(mat4 m, quat q) {
float x = q[0], y = q[1], z = q[2], w = q[3];
m[0] = 1.f - 2.f * y * y - 2.f * z * z;
@ -377,6 +528,12 @@ MAF mat4 mat4_fromQuat(mat4 m, quat q) {
return m;
}
MAF mat4 mat4_fromPose(mat4 m, vec3 v, quat q) {
mat4_fromQuat(m, q);
vec3_init(m + 12, v);
return m;
}
MAF mat4 mat4_identity(mat4 m) {
m[0] = 1.f;
m[1] = 0.f;
@ -398,10 +555,7 @@ MAF mat4 mat4_identity(mat4 m) {
}
MAF mat4 mat4_transpose(mat4 m) {
float a01 = m[1], a02 = m[2], a03 = m[3],
a12 = m[6], a13 = m[7],
a23 = m[11];
float a01 = m[1], a02 = m[2], a03 = m[3], a12 = m[6], a13 = m[7], a23 = m[11];
m[1] = m[4];
m[2] = m[8];
m[3] = m[12];
@ -462,30 +616,6 @@ MAF mat4 mat4_invert(mat4 m) {
return m;
}
MAF mat4 mat4_cofactor(mat4 m) {
float m00 = m[0], m04 = m[4], m08 = m[8], m12 = m[12];
float m01 = m[1], m05 = m[5], m09 = m[9], m13 = m[13];
float m02 = m[2], m06 = m[6], m10 = m[10], m14 = m[14];
float m03 = m[3], m07 = m[7], m11 = m[11], m15 = m[15];
m[0] = (m05 * (m10 * m15 - m11 * m14) - m09 * (m06 * m15 - m07 * m14) + m13 * (m06 * m11 - m07 * m10));
m[1] = -(m04 * (m10 * m15 - m11 * m14) - m08 * (m06 * m15 - m07 * m14) + m12 * (m06 * m11 - m07 * m10));
m[2] = (m04 * (m09 * m15 - m11 * m13) - m08 * (m05 * m15 - m07 * m13) + m12 * (m05 * m11 - m07 * m09));
m[3] = -(m04 * (m09 * m14 - m10 * m13) - m08 * (m05 * m14 - m06 * m13) + m12 * (m05 * m10 - m06 * m09));
m[4] = -(m01 * (m10 * m15 - m11 * m14) - m09 * (m02 * m15 - m03 * m14) + m13 * (m02 * m11 - m03 * m10));
m[5] = (m00 * (m10 * m15 - m11 * m14) - m08 * (m02 * m15 - m03 * m14) + m12 * (m02 * m11 - m03 * m10));
m[6] = -(m00 * (m09 * m15 - m11 * m13) - m08 * (m01 * m15 - m03 * m13) + m12 * (m01 * m11 - m03 * m09));
m[7] = (m00 * (m09 * m14 - m10 * m13) - m08 * (m01 * m14 - m02 * m13) + m12 * (m01 * m10 - m02 * m09));
m[8] = (m01 * (m06 * m15 - m07 * m14) - m05 * (m02 * m15 - m03 * m14) + m13 * (m02 * m07 - m03 * m06));
m[9] = -(m00 * (m06 * m15 - m07 * m14) - m04 * (m02 * m15 - m03 * m14) + m12 * (m02 * m07 - m03 * m06));
m[10] = (m00 * (m05 * m15 - m07 * m13) - m04 * (m01 * m15 - m03 * m13) + m12 * (m01 * m07 - m03 * m05));
m[11] = -(m00 * (m05 * m14 - m06 * m13) - m04 * (m01 * m14 - m02 * m13) + m12 * (m01 * m06 - m02 * m05));
m[12] = -(m01 * (m06 * m11 - m07 * m10) - m05 * (m02 * m11 - m03 * m10) + m09 * (m02 * m07 - m03 * m06));
m[13] = (m00 * (m06 * m11 - m07 * m10) - m04 * (m02 * m11 - m03 * m10) + m08 * (m02 * m07 - m03 * m06));
m[14] = -(m00 * (m05 * m11 - m07 * m09) - m04 * (m01 * m11 - m03 * m09) + m08 * (m01 * m07 - m03 * m05));
m[15] = (m00 * (m05 * m10 - m06 * m09) - m04 * (m01 * m10 - m02 * m09) + m08 * (m01 * m06 - m02 * m05));
return m;
}
// Calculate matrix equivalent to "apply n, then m"
MAF mat4 mat4_mul(mat4 m, mat4 n) {
float m00 = m[0], m01 = m[1], m02 = m[2], m03 = m[3],
@ -517,16 +647,28 @@ MAF mat4 mat4_mul(mat4 m, mat4 n) {
return m;
}
MAF float* mat4_mulVec4(mat4 m, float* v) {
MAF vec4 mat4_mulVec4(mat4 m, vec4 v) {
float x = v[0] * m[0] + v[1] * m[4] + v[2] * m[8] + v[3] * m[12];
float y = v[0] * m[1] + v[1] * m[5] + v[2] * m[9] + v[3] * m[13];
float z = v[0] * m[2] + v[1] * m[6] + v[2] * m[10] + v[3] * m[14];
float w = v[0] * m[3] + v[1] * m[7] + v[2] * m[11] + v[3] * m[15];
v[0] = x;
v[1] = y;
v[2] = z;
v[3] = w;
return v;
return vec4_set(v, x, y, z, w);
}
MAF vec4 mat4_mulPoint(mat4 m, vec3 v) {
float x = v[0] * m[0] + v[1] * m[4] + v[2] * m[8] + m[12];
float y = v[0] * m[1] + v[1] * m[5] + v[2] * m[9] + m[13];
float z = v[0] * m[2] + v[1] * m[6] + v[2] * m[10] + m[14];
float w = v[0] * m[3] + v[1] * m[7] + v[2] * m[11] + m[15];
return vec3_set(v, x / w, y / w, z / w);
}
MAF vec4 mat4_mulDirection(mat4 m, vec3 v) {
float x = v[0] * m[0] + v[1] * m[4] + v[2] * m[8];
float y = v[0] * m[1] + v[1] * m[5] + v[2] * m[9];
float z = v[0] * m[2] + v[1] * m[6] + v[2] * m[10];
return vec3_set(v, x, y, z);
}
MAF mat4 mat4_translate(mat4 m, float x, float y, float z) {
@ -676,9 +818,9 @@ MAF void mat4_getFov(mat4 m, float* left, float* right, float* up, float* down)
}
MAF mat4 mat4_lookAt(mat4 m, vec3 from, vec3 to, vec3 up) {
float x[4];
float y[4];
float z[4];
float x[3];
float y[3];
float z[3];
vec3_normalize(vec3_sub(vec3_init(z, from), to));
vec3_normalize(vec3_cross(vec3_init(x, up), z));
vec3_cross(vec3_init(y, z), x);
@ -702,9 +844,9 @@ MAF mat4 mat4_lookAt(mat4 m, vec3 from, vec3 to, vec3 up) {
}
MAF mat4 mat4_target(mat4 m, vec3 from, vec3 to, vec3 up) {
float x[4];
float y[4];
float z[4];
float x[3];
float y[3];
float z[3];
vec3_normalize(vec3_sub(vec3_init(z, from), to));
vec3_normalize(vec3_cross(vec3_init(x, up), z));
vec3_cross(vec3_init(y, z), x);
@ -747,27 +889,3 @@ MAF mat4 mat4_reflect(mat4 m, vec3 p, vec3 n) {
m[15] = 1.f;
return m;
}
// Apply matrix to a vec3
// Difference from mat4_mulVec4: w normalize is performed, w in vec3 is ignored
MAF void mat4_transform(mat4 m, vec3 v) {
float x = v[0] * m[0] + v[1] * m[4] + v[2] * m[8] + m[12];
float y = v[0] * m[1] + v[1] * m[5] + v[2] * m[9] + m[13];
float z = v[0] * m[2] + v[1] * m[6] + v[2] * m[10] + m[14];
float w = v[0] * m[3] + v[1] * m[7] + v[2] * m[11] + m[15];
v[0] = x / w;
v[1] = y / w;
v[2] = z / w;
v[3] = w / w;
}
MAF void mat4_transformDirection(mat4 m, vec3 v) {
float x = v[0] * m[0] + v[1] * m[4] + v[2] * m[8];
float y = v[0] * m[1] + v[1] * m[5] + v[2] * m[9];
float z = v[0] * m[2] + v[1] * m[6] + v[2] * m[10];
float w = v[0] * m[3] + v[1] * m[7] + v[2] * m[11];
v[0] = x;
v[1] = y;
v[2] = z;
v[3] = w;
}

View File

@ -125,12 +125,14 @@ typedef enum {
OS_PERMISSION_AUDIO_CAPTURE
} os_permission;
typedef void fn_gl_proc(void);
typedef void fn_quit(void);
typedef void fn_focus(bool focused);
typedef void fn_resize(uint32_t width, uint32_t height);
typedef void fn_key(os_button_action action, os_key key, uint32_t scancode, bool repeat);
typedef void fn_text(uint32_t codepoint);
typedef void fn_mouse_button(int button, bool pressed);
typedef void fn_mouse_move(double x, double y);
typedef void fn_mousewheel_move(double deltaX, double deltaY);
typedef void fn_permission(os_permission permission, bool granted);
bool os_init(void);
@ -153,12 +155,16 @@ void os_on_focus(fn_focus* callback);
void os_on_resize(fn_resize* callback);
void os_on_key(fn_key* callback);
void os_on_text(fn_text* callback);
void os_on_mouse_button(fn_mouse_button* callback);
void os_on_mouse_move(fn_mouse_move* callback);
void os_on_mousewheel_move(fn_mousewheel_move* callback);
void os_on_permission(fn_permission* callback);
bool os_window_open(const os_window_config* config);
bool os_window_is_open(void);
void os_window_get_size(uint32_t* width, uint32_t* height);
float os_window_get_pixel_density(void);
void os_window_message_box(const char* message);
size_t os_get_home_directory(char* buffer, size_t size);
size_t os_get_data_directory(char* buffer, size_t size);

View File

@ -165,11 +165,11 @@ void android_main(struct android_app* app) {
(*app->activity->vm)->DetachCurrentThread(app->activity->vm);
}
bool os_init() {
bool os_init(void) {
return true;
}
void os_destroy() {
void os_destroy(void) {
// There are two ways a quit can happen, which need to be handled slightly differently:
// - If the system tells us to quit, we get an event with APP_CMD_DESTROY. In response we push a
// QUIT event to lovr.event and main will eventually exit cleanly. No other teardown necessary.
@ -189,11 +189,11 @@ void os_destroy() {
memset(&state, 0, sizeof(state));
}
const char* os_get_name() {
const char* os_get_name(void) {
return "Android";
}
uint32_t os_get_core_count() {
uint32_t os_get_core_count(void) {
return sysconf(_SC_NPROCESSORS_ONLN);
}
@ -220,14 +220,14 @@ static void* log_main(void* data) {
return 0;
}
void os_open_console() {
void os_open_console(void) {
pthread_create(&log.thread, NULL, log_main, log.handles);
pthread_detach(log.thread);
}
#define NS_PER_SEC 1000000000ULL
double os_get_time() {
double os_get_time(void) {
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return (double) t.tv_sec + (t.tv_nsec / (double) NS_PER_SEC);
@ -287,7 +287,7 @@ bool os_vm_release(void* p, size_t size) {
// - Block if the app is paused or no window is present
// - If the app was active and becomes inactive after an event, break instead of waiting for
// another event. This gives the main loop a chance to respond (e.g. exit VR mode).
void os_poll_events() {
void os_poll_events(void) {
while (!state.app->destroyRequested) {
int events;
struct android_poll_source* source;
@ -326,6 +326,18 @@ void os_on_text(fn_text* callback) {
state.onTextEvent = callback;
}
void os_on_mouse_button(fn_mouse_button* callback) {
//
}
void os_on_mouse_move(fn_mouse_move* callback) {
//
}
void os_on_mousewheel_move(fn_mousewheel_move* callback) {
//
}
void os_on_permission(fn_permission* callback) {
state.onPermissionEvent = callback;
}
@ -334,7 +346,7 @@ bool os_window_open(const os_window_config* config) {
return true;
}
bool os_window_is_open() {
bool os_window_is_open(void) {
return false;
}
@ -346,6 +358,10 @@ float os_window_get_pixel_density(void) {
return 0.f;
}
void os_window_message_box(const char* message) {
//
}
size_t os_get_home_directory(char* buffer, size_t size) {
return 0;
}
@ -418,11 +434,11 @@ bool os_is_key_down(os_key key) {
// Private, must be declared manually to use
void* os_get_java_vm() {
void* os_get_java_vm(void) {
return state.app->activity->vm;
}
void* os_get_jni_context() {
void* os_get_jni_context(void) {
return state.app->activity->clazz;
}

View File

@ -1,10 +1,10 @@
#ifndef LOVR_USE_GLFW
void os_destroy() {
void os_destroy(void) {
//
}
void os_poll_events() {
void os_poll_events(void) {
//
}
@ -12,7 +12,7 @@ bool os_window_open(const os_window_config* config) {
return false;
}
bool os_window_is_open() {
bool os_window_is_open(void) {
return false;
}
@ -44,6 +44,19 @@ void os_on_text(fn_text* callback) {
//
}
void os_on_mouse_button(fn_mouse_button* callback) {
//
}
void os_on_mouse_move(fn_mouse_move* callback) {
//
}
void os_on_mousewheel_move(fn_wheel_move* callback)
{
//
}
void os_get_mouse_position(double* x, double* y) {
*x = *y = 0.;
}
@ -95,6 +108,9 @@ static struct {
fn_resize* onWindowResize;
fn_key* onKeyboardEvent;
fn_text* onTextEvent;
fn_mouse_button* onMouseButton;
fn_mouse_move* onMouseMove;
fn_mousewheel_move* onMouseWheelMove;
} glfwState;
static void onError(int code, const char* description) {
@ -228,11 +244,30 @@ static void onTextEvent(GLFWwindow* window, unsigned int codepoint) {
}
}
static void onMouseButton(GLFWwindow* window, int button, int action, int mods) {
if (glfwState.onMouseButton) {
glfwState.onMouseButton(button, action == GLFW_PRESS);
}
}
static void onMouseMove(GLFWwindow* window, double x, double y) {
if (glfwState.onMouseMove) {
glfwState.onMouseMove(x, y);
}
}
static void onMouseWheelMove(GLFWwindow* window, double deltaX, double deltaY) {
if (glfwState.onMouseWheelMove) {
deltaX = (deltaX == 0.0) ? 0.0 : -deltaX;
glfwState.onMouseWheelMove(deltaX, deltaY);
}
}
static int convertMouseButton(os_mouse_button button) {
switch (button) {
case MOUSE_LEFT: return GLFW_MOUSE_BUTTON_LEFT;
case MOUSE_RIGHT: return GLFW_MOUSE_BUTTON_RIGHT;
default: return -1;
default: return (int) button;
}
}
@ -248,17 +283,19 @@ static int convertKey(os_key key) {
case KEY_DOWN: return GLFW_KEY_DOWN;
case KEY_LEFT: return GLFW_KEY_LEFT;
case KEY_RIGHT: return GLFW_KEY_RIGHT;
case KEY_LEFT_SHIFT: return GLFW_KEY_LEFT_SHIFT;
case KEY_RIGHT_SHIFT: return GLFW_KEY_RIGHT_SHIFT;
case KEY_ESCAPE: return GLFW_KEY_ESCAPE;
case KEY_F5: return GLFW_KEY_F5;
default: return GLFW_KEY_UNKNOWN;
}
}
void os_destroy() {
void os_destroy(void) {
glfwTerminate();
}
void os_poll_events() {
void os_poll_events(void) {
if (glfwState.window) {
glfwPollEvents();
}
@ -314,10 +351,13 @@ bool os_window_open(const os_window_config* config) {
glfwSetWindowSizeCallback(glfwState.window, onWindowResize);
glfwSetKeyCallback(glfwState.window, onKeyboardEvent);
glfwSetCharCallback(glfwState.window, onTextEvent);
glfwSetMouseButtonCallback(glfwState.window, onMouseButton);
glfwSetCursorPosCallback(glfwState.window, onMouseMove);
glfwSetScrollCallback(glfwState.window, onMouseWheelMove);
return true;
}
bool os_window_is_open() {
bool os_window_is_open(void) {
return glfwState.window;
}
@ -364,6 +404,18 @@ void os_on_text(fn_text* callback) {
glfwState.onTextEvent = callback;
}
void os_on_mouse_button(fn_mouse_button* callback) {
glfwState.onMouseButton = callback;
}
void os_on_mouse_move(fn_mouse_move* callback) {
glfwState.onMouseMove = callback;
}
void os_on_mousewheel_move(fn_mousewheel_move* callback) {
glfwState.onMouseWheelMove = callback;
}
void os_get_mouse_position(double* x, double* y) {
if (glfwState.window) {
glfwGetCursorPos(glfwState.window, x, y);
@ -388,7 +440,7 @@ bool os_is_key_down(os_key key) {
}
#ifdef _WIN32
HANDLE os_get_win32_window() {
HANDLE os_get_win32_window(void) {
return (HANDLE) glfwGetWin32Window(glfwState.window);
}
#endif

View File

@ -10,23 +10,23 @@
#define NS_PER_SEC 1000000000ULL
bool os_init() {
bool os_init(void) {
return true;
}
const char* os_get_name() {
const char* os_get_name(void) {
return "Linux";
}
uint32_t os_get_core_count() {
uint32_t os_get_core_count(void) {
return sysconf(_SC_NPROCESSORS_ONLN);
}
void os_open_console() {
void os_open_console(void) {
//
}
double os_get_time() {
double os_get_time(void) {
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return (double) t.tv_sec + (t.tv_nsec / (double) NS_PER_SEC);
@ -64,6 +64,10 @@ bool os_vm_release(void* p, size_t size) {
return !madvise(p, size, MADV_DONTNEED);
}
void os_window_message_box(const char* message) {
//
}
size_t os_get_home_directory(char* buffer, size_t size) {
const char* path = getenv("HOME");

View File

@ -19,29 +19,29 @@ static struct {
fn_permission* onPermissionEvent;
} state;
bool os_init() {
bool os_init(void) {
mach_timebase_info_data_t info;
mach_timebase_info(&info);
state.frequency = (info.denom * 1e9) / info.numer;
return true;
}
const char* os_get_name() {
const char* os_get_name(void) {
return "macOS";
}
uint32_t os_get_core_count() {
uint32_t os_get_core_count(void) {
uint32_t count;
size_t size = sizeof(count);
sysctlbyname("hw.logicalcpu", &count, &size, NULL, 0);
return count;
}
void os_open_console() {
void os_open_console(void) {
//
}
double os_get_time() {
double os_get_time(void) {
return mach_absolute_time() / (double) state.frequency;
}
@ -105,6 +105,10 @@ void os_on_permission(fn_permission* callback) {
state.onPermissionEvent = callback;
}
void os_window_message_box(const char* message) {
//
}
size_t os_get_home_directory(char* buffer, size_t size) {
const char* path = getenv("HOME");

View File

@ -9,11 +9,13 @@
#define CANVAS "#canvas"
static struct {
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context;
fn_quit* onQuitRequest;
fn_focus* onWindowFocus;
fn_resize* onWindowResize;
fn_key* onKeyboardEvent;
fn_mouse_button* onMouseButton;
fn_mouse_move* onMouseMove;
fn_mousewheel_move* onMouseWheelMove;
bool keyMap[KEY_COUNT];
bool mouseMap[2];
os_mouse_mode mouseMode;
@ -62,7 +64,13 @@ static EM_BOOL onMouseButton(int type, const EmscriptenMouseEvent* data, void* u
default: return false;
}
state.mouseMap[button] = type == EMSCRIPTEN_EVENT_MOUSEDOWN;
bool pressed = type == EMSCRIPTEN_EVENT_MOUSEDOWN;
if (state.onMouseButton) {
state.onMouseButton((int) button, pressed);
}
state.mouseMap[button] = pressed;
return false;
}
@ -74,6 +82,16 @@ static EM_BOOL onMouseMove(int type, const EmscriptenMouseEvent* data, void* use
state.mouseX = data->clientX;
state.mouseY = data->clientY;
}
if (state.onMouseMove) {
state.onMouseMove(state.mouseX, state.mouseY);
}
return false;
}
static EM_BOOL onMouseWheelMove(int type, const EmscriptenWheelEvent* data, void* userdata) {
if (state.onMouseWheelMove) {
state.onMouseWheelMove(data->deltaX, -data->deltaY);
}
return false;
}
@ -178,7 +196,7 @@ static EM_BOOL onKeyEvent(int type, const EmscriptenKeyboardEvent* data, void* u
return false;
}
bool os_init() {
bool os_init(void) {
emscripten_set_beforeunload_callback(NULL, onBeforeUnload);
emscripten_set_focus_callback(CANVAS, NULL, true, onFocusChanged);
emscripten_set_blur_callback(CANVAS, NULL, true, onFocusChanged);
@ -186,12 +204,13 @@ bool os_init() {
emscripten_set_mousedown_callback(CANVAS, NULL, true, onMouseButton);
emscripten_set_mouseup_callback(CANVAS, NULL, true, onMouseButton);
emscripten_set_mousemove_callback(CANVAS, NULL, true, onMouseMove);
emscripten_set_wheel_callback(CANVAS, NULL, true, onMouseWheelMove);
emscripten_set_keydown_callback(CANVAS, NULL, true, onKeyEvent);
emscripten_set_keyup_callback(CANVAS, NULL, true, onKeyEvent);
return true;
}
void os_destroy() {
void os_destroy(void) {
emscripten_set_beforeunload_callback(NULL, NULL);
emscripten_set_focus_callback(CANVAS, NULL, true, NULL);
emscripten_set_blur_callback(CANVAS, NULL, true, NULL);
@ -203,19 +222,19 @@ void os_destroy() {
emscripten_set_keyup_callback(CANVAS, NULL, true, NULL);
}
const char* os_get_name() {
const char* os_get_name(void) {
return "Web";
}
uint32_t os_get_core_count() {
uint32_t os_get_core_count(void) {
return 1;
}
void os_open_console() {
void os_open_console(void) {
//
}
double os_get_time() {
double os_get_time(void) {
return emscripten_get_now() / 1000.;
}
@ -227,7 +246,7 @@ void os_request_permission(os_permission permission) {
//
}
void os_poll_events() {
void os_poll_events(void) {
//
}
@ -267,30 +286,10 @@ size_t os_get_bundle_path(char* buffer, size_t size, const char** root) {
}
bool os_window_open(const os_window_config* flags) {
if (state.context) {
return true;
}
EmscriptenWebGLContextAttributes attributes;
emscripten_webgl_init_context_attributes(&attributes);
attributes.alpha = false;
attributes.depth = true;
attributes.stencil = true;
attributes.preserveDrawingBuffer = false;
attributes.majorVersion = 2;
attributes.minorVersion = 0;
state.context = emscripten_webgl_create_context(CANVAS, &attributes);
if (state.context < 0) {
state.context = 0;
return false;
}
emscripten_webgl_make_context_current(state.context);
emscripten_webgl_get_drawing_buffer_size(state.context, &state.width, &state.height);
return true;
}
bool os_window_is_open() {
bool os_window_is_open(void) {
return state.context > 0;
}
@ -301,11 +300,15 @@ void os_window_get_size(uint32_t* width, uint32_t* height) {
float os_window_get_pixel_density(void) {
int w, h, fw, fh;
emscripten_get_canvas_element_size(state.context, &w, &h);
emscripten_get_canvas_element_size(CANVAS, &w, &h);
emscripten_webgl_get_drawing_buffer_size(state.context, &fw, &fh);
return (w == 0 || h == 0) ? 0.f : (float) fw / w;
}
void os_window_message_box(const char* message) {
//
}
void os_on_quit(fn_quit* callback) {
state.onQuitRequest = callback;
}
@ -326,6 +329,18 @@ void os_on_text(fn_text* callback) {
//
}
void os_on_mouse_button(fn_mouse_button* callback) {
state.onMouseButton = callback;
}
void os_on_mouse_move(fn_mouse_move* callback) {
state.onMouseMove = callback;
}
void os_on_mousewheel_move(fn_mousewheel_move* callback) {
state.onMouseWheelMove = callback;
}
void os_get_mouse_position(double* x, double* y) {
*x = state.mouseX;
*y = state.mouseY;

View File

@ -55,24 +55,24 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE prev, LPSTR args, int show) {
#endif
bool os_init() {
bool os_init(void) {
LARGE_INTEGER f;
QueryPerformanceFrequency(&f);
frequency = f.QuadPart;
return true;
}
const char* os_get_name() {
const char* os_get_name(void) {
return "Windows";
}
uint32_t os_get_core_count() {
uint32_t os_get_core_count(void) {
SYSTEM_INFO info;
GetSystemInfo(&info);
return info.dwNumberOfProcessors;
}
void os_open_console() {
void os_open_console(void) {
if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
if (GetLastError() != ERROR_ACCESS_DENIED) {
if (!AllocConsole()) {
@ -86,7 +86,7 @@ void os_open_console() {
freopen("CONOUT$", "w", stderr);
}
double os_get_time() {
double os_get_time(void) {
LARGE_INTEGER t;
QueryPerformanceCounter(&t);
return t.QuadPart / (double) frequency;
@ -120,6 +120,14 @@ void os_on_permission(fn_permission* callback) {
//
}
HANDLE os_get_win32_window(void);
void os_window_message_box(const char* message) {
HANDLE window = os_get_win32_window();
if (window) {
MessageBox(window, message, NULL, 0);
}
}
size_t os_get_home_directory(char* buffer, size_t size) {
PWSTR wpath = NULL;
if (SHGetKnownFolderPath(&FOLDERID_Profile, 0, NULL, &wpath) == S_OK) {

View File

@ -33,7 +33,10 @@ typedef union {
} constant;
struct {
uint16_t word;
uint16_t name;
union {
uint16_t name;
uint16_t arrayStride;
};
} type;
} spv_cache;
@ -43,6 +46,7 @@ typedef struct {
size_t wordCount;
uint32_t bound;
spv_cache* cache;
spv_field* fields;
} spv_context;
#define OP_CODE(op) (op[0] & 0xffff)
@ -56,7 +60,7 @@ static spv_result spv_parse_type(spv_context* spv, const uint32_t* op, spv_info*
static spv_result spv_parse_spec_constant(spv_context* spv, const uint32_t* op, spv_info* info);
static spv_result spv_parse_constant(spv_context* spv, const uint32_t* op, spv_info* info);
static spv_result spv_parse_variable(spv_context* spv, const uint32_t* op, spv_info* info);
static spv_result spv_parse_push_constants(spv_context* spv, const uint32_t* op, spv_info* info);
static spv_result spv_parse_field(spv_context* spv, const uint32_t* op, spv_field* field, spv_info* info);
static bool spv_load_type(spv_context* spv, uint32_t id, const uint32_t** op);
spv_result spv_parse(const void* source, size_t size, spv_info* info) {
@ -65,6 +69,7 @@ spv_result spv_parse(const void* source, size_t size, spv_info* info) {
spv.words = source;
spv.wordCount = size / sizeof(uint32_t);
spv.edge = spv.words + spv.wordCount - 8;
spv.fields = info->fields;
if (spv.wordCount < 16 || spv.words[0] != 0x07230203) {
return SPV_INVALID;
@ -84,10 +89,9 @@ spv_result spv_parse(const void* source, size_t size, spv_info* info) {
info->featureCount = 0;
info->specConstantCount = 0;
info->pushConstantCount = 0;
info->pushConstantSize = 0;
info->attributeCount = 0;
info->resourceCount = 0;
info->fieldCount = 0;
const uint32_t* op = spv.words + 5;
@ -162,9 +166,8 @@ const char* spv_result_to_string(spv_result result) {
case SPV_OK: return "OK";
case SPV_INVALID: return "Invalid SPIR-V";
case SPV_TOO_BIG: return "SPIR-V contains too many types/variables (max ID is 65534)";
case SPV_UNSUPPORTED_IMAGE_TYPE: return "This type of image variable is not supported";
case SPV_UNSUPPORTED_SPEC_CONSTANT_TYPE: return "This type of specialization constant is not supported";
case SPV_UNSUPPORTED_PUSH_CONSTANT_TYPE: return "Push constants must be square matrices, vectors, 32 bit numbers, or bools";
case SPV_UNSUPPORTED_DATA_TYPE: return "Struct fields must be square float matrices, float/int/uint vectors, 32 bit numbers, or bools";
default: return NULL;
}
}
@ -214,10 +217,11 @@ static spv_result spv_parse_decoration(spv_context* spv, const uint32_t* op, spv
}
switch (decoration) {
case 1: spv->cache[id].flag.number = op[3]; break; // SpecID
case 6: spv->cache[id].type.arrayStride = op[3]; break; // ArrayStride (overrides name)
case 30: spv->cache[id].attribute.location = op[3]; break; // Location
case 33: spv->cache[id].variable.binding = op[3]; break; // Binding
case 34: spv->cache[id].variable.set = op[3]; break; // Set
case 30: spv->cache[id].attribute.location = op[3]; break; // Location
case 1: spv->cache[id].flag.number = op[3]; break; // SpecID
default: break;
}
@ -322,8 +326,30 @@ static spv_result spv_parse_variable(spv_context* spv, const uint32_t* op, spv_i
}
}
// Unwrap the pointer
const uint32_t* pointer;
if (!spv_load_type(spv, pointerId, &pointer) || OP_CODE(pointer) != 32) { // OpTypePointer
return SPV_INVALID;
}
// Load the type
const uint32_t* type;
uint32_t typeId = pointer[3];
if (!spv_load_type(spv, typeId, &type)) {
return SPV_INVALID;
}
if (storageClass == 9) { // PushConstant
return spv_parse_push_constants(spv, op, info);
if (OP_CODE(type) != 30) { // OpTypeStruct
return SPV_INVALID;
}
if (info->fields) {
info->pushConstants = spv->fields++;
}
info->fieldCount++;
return spv_parse_field(spv, type, info->pushConstants, info);
}
uint32_t set = spv->cache[variableId].variable.set;
@ -336,7 +362,14 @@ static spv_result spv_parse_variable(spv_context* spv, const uint32_t* op, spv_i
if (!info->resources) {
info->resourceCount++;
return SPV_OK;
// If it's a buffer, be sure to count how many struct fields it has
if (storageClass == 2 || storageClass == 12) {
info->fieldCount++;
return spv_parse_field(spv, type, NULL, info);
} else {
return SPV_OK;
}
}
spv_resource* resource = &info->resources[info->resourceCount++];
@ -344,17 +377,6 @@ static spv_result spv_parse_variable(spv_context* spv, const uint32_t* op, spv_i
resource->set = set;
resource->binding = binding;
const uint32_t* pointer;
if (!spv_load_type(spv, pointerId, &pointer)) {
return SPV_INVALID;
}
const uint32_t* type;
uint32_t typeId = pointer[3];
if (!spv_load_type(spv, typeId, &type)) {
return SPV_INVALID;
}
// If it's an array, read the array size and unwrap the inner type
if (OP_CODE(type) == 28) { // OpTypeArray
const uint32_t* array = type;
@ -372,9 +394,9 @@ static spv_result spv_parse_variable(spv_context* spv, const uint32_t* op, spv_i
return SPV_INVALID;
}
resource->arraySize = length[3];
resource->count = length[3];
} else {
resource->arraySize = 0;
resource->count = 0;
}
// Buffers
@ -386,7 +408,17 @@ static spv_result spv_parse_variable(spv_context* spv, const uint32_t* op, spv_i
resource->name = (char*) (spv->words + spv->cache[typeId].type.name);
}
return SPV_OK;
if (OP_CODE(type) != 30) { // OpTypeStruct
return SPV_INVALID;
}
info->fieldCount++;
resource->fields = spv->fields++;
resource->fields[0].offset = 0;
resource->fields[0].name = resource->name;
return spv_parse_field(spv, type, resource->fields, info);
} else {
resource->fields = NULL;
}
// Sampler and texture variables are named directly
@ -401,161 +433,217 @@ static spv_result spv_parse_variable(spv_context* spv, const uint32_t* op, spv_i
// Combined image samplers are currently not supported (ty webgpu)
if (OP_CODE(type) == 27) { // OpTypeSampledImage
return SPV_UNSUPPORTED_IMAGE_TYPE;
resource->type = SPV_COMBINED_TEXTURE_SAMPLER;
return SPV_OK;
} else if (OP_CODE(type) != 25) { // OpTypeImage
return SPV_INVALID;
}
// Reject texel buffers (DimBuffer) and input attachments (DimSubpassData)
if (type[3] == 5 || type[3] == 6) {
return SPV_UNSUPPORTED_IMAGE_TYPE;
// Texel buffers use the DimBuffer dimensionality
if (type[3] == 5) {
switch (type[7]) {
case 1: resource->type = SPV_UNIFORM_TEXEL_BUFFER; return SPV_OK;
case 2: resource->type = SPV_STORAGE_TEXEL_BUFFER; return SPV_OK;
default: return SPV_INVALID;
}
}
// Input attachments use the DimSubpassData dimensionality, and "Sampled" must be 2
if (type[3] == 6) {
if (type[7] == 2) {
resource->type = SPV_INPUT_ATTACHMENT;
return SPV_OK;
} else {
return SPV_INVALID;
}
}
// Read the Sampled key to determine if it's a sampled image (1) or a storage image (2)
switch (type[7]) {
case 1: resource->type = SPV_SAMPLED_TEXTURE; return SPV_OK;
case 2: resource->type = SPV_STORAGE_TEXTURE; return SPV_OK;
default: return SPV_UNSUPPORTED_IMAGE_TYPE;
default: return SPV_INVALID;
}
}
static spv_result spv_parse_push_constants(spv_context* spv, const uint32_t* op, spv_info* info) {
const uint32_t* pointer;
if (!spv_load_type(spv, op[1], &pointer) || OP_CODE(pointer) != 32) {
return SPV_INVALID;
static spv_result spv_parse_field(spv_context* spv, const uint32_t* word, spv_field* field, spv_info* info) {
if (OP_CODE(word) == 28) { // OpTypeArray
uint32_t lengthId = word[3];
const uint32_t* lengthWord = spv->words + spv->cache[lengthId].constant.word;
// Length must be an OpConstant or OpSpecConstant
if (lengthId > spv->bound || lengthWord > spv->edge || (OP_CODE(lengthWord) != 43 && OP_CODE(lengthWord) != 50)) {
return SPV_INVALID;
}
if (field) {
field->arrayLength = lengthWord[3];
field->arrayStride = spv->cache[word[1]].type.arrayStride;
}
// Unwrap inner array type and fall through
if (!spv_load_type(spv, word[2], &word)) {
return SPV_INVALID;
}
} else if (OP_CODE(word) == 29) { // OpTypeRuntimeArray
if (field) {
field->arrayLength = ~0u;
field->arrayStride = spv->cache[word[1]].type.arrayStride;
}
// Unwrap inner array type and fall through
if (!spv_load_type(spv, word[2], &word)) {
return SPV_INVALID;
}
} else if (field) {
field->arrayLength = 0;
field->arrayStride = 0;
}
const uint32_t* structure;
if (!spv_load_type(spv, pointer[3], &structure) || OP_CODE(structure) != 30) {
return SPV_INVALID;
}
// If this is the initial scan, just do a recursive count of all the struct fields
if (!field) {
if (OP_CODE(word) == 30) { // OpTypeStruct
uint32_t childCount = OP_LENGTH(word) - 2;
uint32_t memberCount = OP_LENGTH(structure) - 2;
const uint32_t* type;
for (uint32_t i = 0; i < childCount; i++) {
if (!spv_load_type(spv, word[i + 2], &type)) {
return SPV_INVALID;
}
if (!info->pushConstants) {
info->pushConstantCount = memberCount;
spv_result result = spv_parse_field(spv, type, NULL, info);
if (result != SPV_OK) {
return result;
}
}
info->fieldCount += childCount;
} else {
info->fieldCount++;
}
return SPV_OK;
}
const uint32_t* type;
for (uint32_t i = 0; i < memberCount; i++) {
if (!spv_load_type(spv, structure[i + 2], &type)) {
// If it's a struct, recursively parse each member
if (OP_CODE(word) == 30) { // OpTypeStruct
uint32_t childCount = OP_LENGTH(word) - 2;
field->type = SPV_STRUCT;
field->elementSize = 0;
field->fieldCount = childCount;
field->totalFieldCount = childCount;
field->fields = spv->fields;
info->fieldCount += childCount;
spv->fields += childCount;
for (uint32_t i = 0; i < childCount; i++) {
field->fields[i].name = NULL;
field->fields[i].offset = 0;
const uint32_t* type;
if (!spv_load_type(spv, word[i + 2], &type)) {
return SPV_INVALID;
}
spv_result result = spv_parse_field(spv, type, &field->fields[i], info);
if (result != SPV_OK) {
return result;
}
field->totalFieldCount += field->fields[i].totalFieldCount;
}
// Collect member names and offsets. Requires a scan over the name/decoration words, which is
// kind of sad. Trying to be fancy resulted in questionable increase in code/branching/storage.
uint32_t structId = word[1];
int32_t namesRemaining = childCount;
int32_t offsetsRemaining = childCount;
for (word = spv->words + 5; word < spv->edge && (namesRemaining > 0 || offsetsRemaining > 0); word += OP_LENGTH(word)) {
if (OP_LENGTH(word) == 0 || word + OP_LENGTH(word) > spv->edge) {
return SPV_INVALID;
}
if (OP_CODE(word) == 6 && OP_LENGTH(word) >= 4 && word[1] == structId && word[2] < childCount) {
field->fields[word[2]].name = (char*) &word[3];
namesRemaining--;
} else if (OP_CODE(word) == 72 && OP_LENGTH(word) == 5 && word[1] == structId && word[2] < childCount && word[3] == 35) { // Offset
spv_field* child = &field->fields[word[2]];
uint32_t offset = word[4];
child->offset = offset;
offsetsRemaining--;
// Struct size is maximum extent of any of its members
uint32_t size = child->arrayLength > 0 ? child->arrayLength * child->arrayStride : child->elementSize;
if (offset + size > field->elementSize) {
field->elementSize = offset + size;
}
} else if (OP_CODE(word) == 59) { // OpVariable, can break
break;
}
}
return SPV_OK;
} else {
field->fieldCount = 0;
field->totalFieldCount = 0;
field->fields = NULL;
}
uint32_t columnCount = 1;
uint32_t componentCount = 1;
if (OP_CODE(word) == 24) { // OpTypeMatrix
columnCount = word[3];
if (!spv_load_type(spv, word[2], &word)) {
return SPV_INVALID;
}
}
spv_push_constant* constant = &info->pushConstants[info->pushConstantCount++];
uint32_t columnCount = 1;
uint32_t componentCount = 1;
if (OP_CODE(type) == 24) { // OpTypeMatrix
columnCount = type[3];
if (!spv_load_type(spv, type[2], &type)) {
return SPV_INVALID;
}
if (OP_CODE(word) == 23) { // OpTypeVector
componentCount = word[3];
if (!spv_load_type(spv, word[2], &word)) {
return SPV_INVALID;
}
}
if (OP_CODE(type) == 23) { // OpTypeVector
componentCount = type[3];
if (!spv_load_type(spv, type[2], &type)) {
return SPV_INVALID;
}
}
if (OP_CODE(type) == 22 && type[2] == 32) { // OpTypeFloat
if (columnCount >= 2 && columnCount <= 4 && componentCount == columnCount) {
constant->type = SPV_MAT2 + columnCount - 2;
} else if (columnCount == 1 && componentCount >= 2 && componentCount <= 4) {
constant->type = SPV_F32x2 + componentCount - 2;
} else if (columnCount == 1 && componentCount == 1) {
constant->type = SPV_F32;
} else {
return SPV_UNSUPPORTED_PUSH_CONSTANT_TYPE;
}
} else if (OP_CODE(type) == 21 && type[2] == 32) { // OpTypeInteger
if (type[3] > 0) { // signed
if (columnCount == 1 && componentCount >= 2 && componentCount <= 4) {
constant->type = SPV_I32x2 + componentCount - 2;
} else if (columnCount == 1 && componentCount == 1) {
constant->type = SPV_I32;
} else {
return SPV_UNSUPPORTED_PUSH_CONSTANT_TYPE;
}
} else {
if (columnCount == 1 && componentCount >= 2 && componentCount <= 4) {
constant->type = SPV_U32x2 + componentCount - 2;
} else if (columnCount == 1 && componentCount == 1) {
constant->type = SPV_U32;
} else {
return SPV_UNSUPPORTED_PUSH_CONSTANT_TYPE;
}
}
} else if (OP_CODE(type) == 20 && columnCount == 1 && componentCount == 1) { // OpTypeBool
constant->type = SPV_B32;
if (OP_CODE(word) == 22 && word[2] == 32) { // OpTypeFloat
if (columnCount >= 2 && columnCount <= 4 && componentCount >= 2 && componentCount <= 4) {
field->type = SPV_MAT2x2 + (columnCount - 2) * 3 + (componentCount - 2);
} else if (columnCount == 1 && componentCount >= 2 && componentCount <= 4) {
field->type = SPV_F32x2 + componentCount - 2;
} else if (columnCount == 1 && componentCount == 1) {
field->type = SPV_F32;
} else {
return SPV_UNSUPPORTED_PUSH_CONSTANT_TYPE;
return SPV_UNSUPPORTED_DATA_TYPE;
}
} else if (OP_CODE(word) == 21 && word[2] == 32) { // OpTypeInteger
if (word[3] > 0) { // signed
if (columnCount == 1 && componentCount >= 2 && componentCount <= 4) {
field->type = SPV_I32x2 + componentCount - 2;
} else if (columnCount == 1 && componentCount == 1) {
field->type = SPV_I32;
} else {
return SPV_UNSUPPORTED_DATA_TYPE;
}
} else {
if (columnCount == 1 && componentCount >= 2 && componentCount <= 4) {
field->type = SPV_U32x2 + componentCount - 2;
} else if (columnCount == 1 && componentCount == 1) {
field->type = SPV_U32;
} else {
return SPV_UNSUPPORTED_DATA_TYPE;
}
}
} else if (OP_CODE(word) == 20 && columnCount == 1 && componentCount == 1) { // OpTypeBool
field->type = SPV_B32;
} else {
return SPV_UNSUPPORTED_DATA_TYPE;
}
// Need to do a second pass to find the name and offset decorations, hard to cache them
op = spv->words + 5;
while (op < spv->words + spv->wordCount) {
uint16_t opcode = OP_CODE(op);
uint16_t length = OP_LENGTH(op);
if (OP_LENGTH(op) == 0 || op + OP_LENGTH(op) > spv->words + spv->wordCount) {
return SPV_INVALID;
}
switch (opcode) {
case 6: // OpMemberName
if (length < 4 || op[1] > spv->bound) {
return SPV_INVALID;
} else if (op[1] == structure[1] && op[2] < info->pushConstantCount) {
info->pushConstants[op[2]].name = (char*) &op[3];
}
break;
case 72: // OpMemberDecorate
if (op[1] > spv->bound) {
return SPV_INVALID;
} else if (length == 5 && op[1] == structure[1] && op[2] < info->pushConstantCount && op[3] == 35) { // Offset
info->pushConstants[op[2]].offset = op[4];
uint32_t size = 0;
switch (info->pushConstants[op[2]].type) {
case SPV_B32: size = 4; break;
case SPV_I32: size = 4; break;
case SPV_I32x2: size = 8; break;
case SPV_I32x3: size = 12; break;
case SPV_I32x4: size = 16; break;
case SPV_U32: size = 4; break;
case SPV_U32x2: size = 8; break;
case SPV_U32x3: size = 12; break;
case SPV_U32x4: size = 16; break;
case SPV_F32: size = 4; break;
case SPV_F32x2: size = 8; break;
case SPV_F32x3: size = 12; break;
case SPV_F32x4: size = 16; break;
case SPV_MAT2: size = 16; break;
case SPV_MAT3: size = 36; break;
case SPV_MAT4: size = 64; break;
default: break;
}
if (info->pushConstantSize < op[4] + size) {
info->pushConstantSize = op[4] + size;
}
}
break;
case 59: // OpVariable, can exit
op = spv->words + spv->wordCount;
length = 0;
break;
}
op += length;
}
field->elementSize = 4 * componentCount * columnCount;
return SPV_OK;
}

View File

@ -17,9 +17,16 @@ typedef enum {
SPV_F32x2,
SPV_F32x3,
SPV_F32x4,
SPV_MAT2,
SPV_MAT3,
SPV_MAT4
SPV_MAT2x2,
SPV_MAT2x3,
SPV_MAT2x4,
SPV_MAT3x2,
SPV_MAT3x3,
SPV_MAT3x4,
SPV_MAT4x2,
SPV_MAT4x3,
SPV_MAT4x4,
SPV_STRUCT
} spv_type;
typedef struct {
@ -28,11 +35,17 @@ typedef struct {
spv_type type;
} spv_spec_constant;
typedef struct {
typedef struct spv_field {
const char* name;
uint32_t offset;
spv_type type;
} spv_push_constant;
uint32_t offset;
uint32_t arrayLength;
uint32_t arrayStride;
uint32_t elementSize;
uint16_t fieldCount;
uint16_t totalFieldCount;
struct spv_field* fields;
} spv_field;
typedef struct {
const char* name;
@ -44,7 +57,11 @@ typedef enum {
SPV_STORAGE_BUFFER,
SPV_SAMPLED_TEXTURE,
SPV_STORAGE_TEXTURE,
SPV_SAMPLER
SPV_SAMPLER,
SPV_COMBINED_TEXTURE_SAMPLER,
SPV_UNIFORM_TEXEL_BUFFER,
SPV_STORAGE_TEXEL_BUFFER,
SPV_INPUT_ATTACHMENT
} spv_resource_type;
typedef struct {
@ -52,7 +69,8 @@ typedef struct {
uint32_t binding;
const char* name;
spv_resource_type type;
uint32_t arraySize;
uint32_t count;
spv_field* fields;
} spv_resource;
typedef struct {
@ -60,24 +78,23 @@ typedef struct {
uint32_t workgroupSize[3];
uint32_t featureCount;
uint32_t specConstantCount;
uint32_t pushConstantCount;
uint32_t pushConstantSize;
uint32_t attributeCount;
uint32_t resourceCount;
uint32_t fieldCount;
uint32_t* features;
spv_spec_constant* specConstants;
spv_push_constant* pushConstants;
spv_field* pushConstants;
spv_attribute* attributes;
spv_resource* resources;
spv_field* fields;
} spv_info;
typedef enum {
SPV_OK,
SPV_INVALID,
SPV_TOO_BIG,
SPV_UNSUPPORTED_IMAGE_TYPE,
SPV_UNSUPPORTED_SPEC_CONSTANT_TYPE,
SPV_UNSUPPORTED_PUSH_CONSTANT_TYPE
SPV_UNSUPPORTED_DATA_TYPE
} spv_result;
spv_result spv_parse(const void* source, size_t size, spv_info* info);

331
src/lib/lua/lutf8lib.c Normal file
View File

@ -0,0 +1,331 @@
/*
** $Id: lutf8lib.c $
** Standard library for UTF-8 manipulation
**
** Copyright © 19942021 Lua.org, PUC-Rio.
**
** Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
** associated documentation files (the "Software"), to deal in the Software without restriction,
** including without limitation the rights to use, copy, modify, merge, publish, distribute,
** sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in all copies or
** substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
** NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
** DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#define lutf8lib_c
#define LUA_LIB
#include <assert.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#define MAXUNICODE 0x10FFFFu
#define MAXUTF 0x7FFFFFFFu
#define UTF8BUFFSZ 8
/*
** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits.
*/
#if (UINT_MAX >> 30) >= 1
typedef unsigned int utfint;
#else
typedef unsigned long utfint;
#endif
#define iscont(p) ((*(p) & 0xC0) == 0x80)
/* from strlib */
/* translate a relative string position: negative means back from end */
static lua_Integer u_posrelat (lua_Integer pos, size_t len) {
if (pos >= 0) return pos;
else if (0u - (size_t)pos > len) return 0;
else return (lua_Integer)len + pos + 1;
}
/*
** Decode one UTF-8 sequence, returning NULL if byte sequence is
** invalid. The array 'limits' stores the minimum value for each
** sequence length, to check for overlong representations. Its first
** entry forces an error for non-ascii bytes with no continuation
** bytes (count == 0).
*/
static const char *utf8_decode (const char *s, utfint *val, int strict) {
static const utfint limits[] =
{~(utfint)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u};
unsigned int c = (unsigned char)s[0];
utfint res = 0; /* final result */
if (c < 0x80) /* ascii? */
res = c;
else {
int count = 0; /* to count number of continuation bytes */
for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */
unsigned int cc = (unsigned char)s[++count]; /* read next byte */
if ((cc & 0xC0) != 0x80) /* not a continuation byte? */
return NULL; /* invalid byte sequence */
res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
}
res |= ((utfint)(c & 0x7F) << (count * 5)); /* add first byte */
if (count > 5 || res > MAXUTF || res < limits[count])
return NULL; /* invalid byte sequence */
s += count; /* skip continuation bytes read */
}
if (strict) {
/* check for invalid code points; too large or surrogates */
if (res > MAXUNICODE || (0xD800u <= res && res <= 0xDFFFu))
return NULL;
}
if (val) *val = res;
return s + 1; /* +1 to include first byte */
}
/*
** utf8len(s [, i [, j [, lax]]]) --> number of characters that
** start in the range [i,j], or nil + current position if 's' is not
** well formed in that interval
*/
static int utflen (lua_State *L) {
lua_Integer n = 0; /* counter for the number of characters */
size_t len; /* string length in bytes */
const char *s = luaL_checklstring(L, 1, &len);
lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len);
lua_Integer posj = u_posrelat(luaL_optinteger(L, 3, -1), len);
int lax = lua_toboolean(L, 4);
luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 2,
"initial position out of bounds");
luaL_argcheck(L, --posj < (lua_Integer)len, 3,
"final position out of bounds");
while (posi <= posj) {
const char *s1 = utf8_decode(s + posi, NULL, !lax);
if (s1 == NULL) { /* conversion error? */
lua_pushnil(L); /* return nil ... */
lua_pushinteger(L, posi + 1); /* ... and current position */
return 2;
}
posi = s1 - s;
n++;
}
lua_pushinteger(L, n);
return 1;
}
/*
** codepoint(s, [i, [j [, lax]]]) -> returns codepoints for all
** characters that start in the range [i,j]
*/
static int codepoint (lua_State *L) {
size_t len;
const char *s = luaL_checklstring(L, 1, &len);
lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len);
lua_Integer pose = u_posrelat(luaL_optinteger(L, 3, posi), len);
int lax = lua_toboolean(L, 4);
int n;
const char *se;
luaL_argcheck(L, posi >= 1, 2, "out of bounds");
luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of bounds");
if (posi > pose) return 0; /* empty interval; return no values */
if (pose - posi >= INT_MAX) /* (lua_Integer -> int) overflow? */
return luaL_error(L, "string slice too long");
n = (int)(pose - posi) + 1; /* upper bound for number of returns */
luaL_checkstack(L, n, "string slice too long");
n = 0; /* count the number of returns */
se = s + pose; /* string end */
for (s += posi - 1; s < se;) {
utfint code;
s = utf8_decode(s, &code, !lax);
if (s == NULL)
return luaL_error(L, "invalid UTF-8 code");
lua_pushinteger(L, code);
n++;
}
return n;
}
static int utf8esc (char *buff, unsigned long x) {
int n = 1; /* number of bytes put in buffer (backwards) */
lua_assert(x <= 0x7FFFFFFFu);
if (x < 0x80) /* ascii? */
buff[UTF8BUFFSZ - 1] = (char) x;
else { /* need continuation bytes */
unsigned int mfb = 0x3f; /* maximum that fits in first byte */
do { /* add continuation bytes */
buff[UTF8BUFFSZ - (n++)] = (char) (0x80 | (x & 0x3f));
x >>= 6; /* remove added bits */
mfb >>= 1; /* now there is one less bit available in first byte */
} while (x > mfb); /* still needs continuation byte? */
buff[UTF8BUFFSZ - n] = (char) ((~mfb << 1) | x); /* add first byte */
}
return n;
}
static void pushutfchar (lua_State *L, int arg) {
utfint code = (utfint)luaL_checkinteger(L, arg);
luaL_argcheck(L, code <= MAXUTF, arg, "value out of range");
char buff[UTF8BUFFSZ];
int l = utf8esc(buff, (long) code);
lua_pushlstring(L, buff + UTF8BUFFSZ - l, l);
}
/*
** utfchar(n1, n2, ...) -> char(n1)..char(n2)...
*/
static int utfchar (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
if (n == 1) /* optimize common case of single char */
pushutfchar(L, 1);
else {
int i;
luaL_Buffer b;
luaL_buffinit(L, &b);
for (i = 1; i <= n; i++) {
pushutfchar(L, i);
luaL_addvalue(&b);
}
luaL_pushresult(&b);
}
return 1;
}
/*
** offset(s, n, [i]) -> index where n-th character counting from
** position 'i' starts; 0 means character at 'i'.
*/
static int byteoffset (lua_State *L) {
size_t len;
const char *s = luaL_checklstring(L, 1, &len);
lua_Integer n = luaL_checkinteger(L, 2);
lua_Integer posi = (n >= 0) ? 1 : len + 1;
posi = u_posrelat(luaL_optinteger(L, 3, posi), len);
luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3,
"position out of bounds");
if (n == 0) {
/* find beginning of current byte sequence */
while (posi > 0 && iscont(s + posi)) posi--;
}
else {
if (iscont(s + posi))
return luaL_error(L, "initial position is a continuation byte");
if (n < 0) {
while (n < 0 && posi > 0) { /* move back */
do { /* find beginning of previous character */
posi--;
} while (posi > 0 && iscont(s + posi));
n++;
}
}
else {
n--; /* do not move for 1st character */
while (n > 0 && posi < (lua_Integer)len) {
do { /* find beginning of next character */
posi++;
} while (iscont(s + posi)); /* (cannot pass final '\0') */
n--;
}
}
}
if (n == 0) /* did it find given character? */
lua_pushinteger(L, posi + 1);
else /* no such character */
lua_pushnil(L);
return 1;
}
static int iter_aux (lua_State *L, int strict) {
size_t len;
const char *s = luaL_checklstring(L, 1, &len);
unsigned int n = (unsigned int) lua_tointeger(L, 2);
if (n < len) {
while (iscont(s + n)) n++; /* skip continuation bytes */
}
if (n >= len) /* (also handles original 'n' being negative) */
return 0; /* no more codepoints */
else {
utfint code;
const char *next = utf8_decode(s + n, &code, strict);
if (next == NULL)
return luaL_error(L, "invalid UTF-8 code");
lua_pushinteger(L, n + 1);
lua_pushinteger(L, code);
return 2;
}
}
static int iter_auxstrict (lua_State *L) {
return iter_aux(L, 1);
}
static int iter_auxlax (lua_State *L) {
return iter_aux(L, 0);
}
static int iter_codes (lua_State *L) {
int lax = lua_toboolean(L, 2);
luaL_checkstring(L, 1);
lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict);
lua_pushvalue(L, 1);
lua_pushinteger(L, 0);
return 3;
}
/* pattern to match a single UTF-8 character */
#define UTF8PATT "[%z\x01-\x7F\xC2-\xFD][\x80-\xBF]*"
static const luaL_Reg funcs[] = {
{"offset", byteoffset},
{"codepoint", codepoint},
{"char", utfchar},
{"len", utflen},
{"codes", iter_codes},
/* placeholders */
{"charpattern", NULL},
{NULL, NULL}
};
int luaopen_utf8 (lua_State *L) {
lua_createtable(L, sizeof(funcs) / sizeof(funcs[0]), 0);
const luaL_Reg* f;
for (f = funcs; f->name; f++) {
if (f->func) {
lua_pushcfunction(L, f->func);
} else {
lua_pushboolean(L, 0);
}
lua_setfield(L, -2, f->name);
}
lua_pushlstring(L, UTF8PATT, sizeof(UTF8PATT)/sizeof(char) - 1);
lua_setfield(L, -2, "charpattern");
return 1;
}

3
src/lib/lua/lutf8lib.h Normal file
View File

@ -0,0 +1,3 @@
struct lua_State;
int luaopen_utf8(struct lua_State *L);

View File

@ -1,4 +1,4 @@
/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb
/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb
no warranty implied; use at your own risk
Do this:
@ -48,6 +48,8 @@ LICENSE
RECENT REVISION HISTORY:
2.28 (2023-01-29) many error fixes, security errors, just tons of stuff
2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
2.26 (2020-07-13) many minor fixes
2.25 (2020-02-02) fix warnings
2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically
@ -89,7 +91,7 @@ RECENT REVISION HISTORY:
Jeremy Sawicki (handle all ImageNet JPGs)
Optimizations & bugfixes Mikhail Morozov (1-bit BMP)
Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query)
Arseny Kapoulkine
Arseny Kapoulkine Simon Breuss (16-bit PNM)
John-Mark Allen
Carmelo J Fdez-Aguera
@ -102,19 +104,21 @@ RECENT REVISION HISTORY:
Thomas Ruf Ronny Chevalier github:rlyeh
Janez Zemva John Bartholomew Michal Cichon github:romigrou
Jonathan Blow Ken Hamada Tero Hanninen github:svdijk
Laurent Gomila Cort Stratton github:snagar
Eugene Golushkov Laurent Gomila Cort Stratton github:snagar
Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex
Cass Everitt Ryamond Barbiero github:grim210
Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw
Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus
Josh Tobin Matthew Gregan github:poppolopoppo
Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo
Julian Raschke Gregory Mullen Christian Floisand github:darealshinji
Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007
Brad Weinberger Matvey Cherevko [reserved]
Brad Weinberger Matvey Cherevko github:mosra
Luca Sas Alexander Veselov Zack Middleton [reserved]
Ryan C. Gordon [reserved] [reserved]
DO NOT ADD YOUR NAME HERE
Jacko Dirks
To add your name to the credits, pick a random blank space in the middle and fill it.
80% of merge conflicts on stb PRs are due to people adding their name at the end
of the credits.
@ -137,7 +141,7 @@ RECENT REVISION HISTORY:
// // ... x = width, y = height, n = # 8-bit components per pixel ...
// // ... replace '0' with '1'..'4' to force that many components per pixel
// // ... but 'n' will always be the number that it would have been if you said 0
// stbi_image_free(data)
// stbi_image_free(data);
//
// Standard parameters:
// int *x -- outputs image width in pixels
@ -176,6 +180,32 @@ RECENT REVISION HISTORY:
//
// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized.
//
// To query the width, height and component count of an image without having to
// decode the full file, you can use the stbi_info family of functions:
//
// int x,y,n,ok;
// ok = stbi_info(filename, &x, &y, &n);
// // returns ok=1 and sets x, y, n if image is a supported format,
// // 0 otherwise.
//
// Note that stb_image pervasively uses ints in its public API for sizes,
// including sizes of memory buffers. This is now part of the API and thus
// hard to change without causing breakage. As a result, the various image
// loaders all have certain limits on image size; these differ somewhat
// by format but generally boil down to either just under 2GB or just under
// 1GB. When the decoded image would be larger than this, stb_image decoding
// will fail.
//
// Additionally, stb_image will reject image files that have any of their
// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS,
// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit,
// the only way to have an image with such dimensions load correctly
// is for it to have a rather extreme aspect ratio. Either way, the
// assumption here is that such larger images are likely to be malformed
// or malicious. If you do need to load an image with individual dimensions
// larger than that, and it still fits in the overall size limit, you can
// #define STBI_MAX_DIMENSIONS on your own to be something larger.
//
// ===========================================================================
//
// UNICODE:
@ -281,11 +311,10 @@ RECENT REVISION HISTORY:
//
// iPhone PNG support:
//
// By default we convert iphone-formatted PNGs back to RGB, even though
// they are internally encoded differently. You can disable this conversion
// by calling stbi_convert_iphone_png_to_rgb(0), in which case
// you will always just get the native iphone "format" through (which
// is BGR stored in RGB).
// We optionally support converting iPhone-formatted PNGs (which store
// premultiplied BGRA) back to RGB, even though they're internally encoded
// differently. To enable this conversion, call
// stbi_convert_iphone_png_to_rgb(1).
//
// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per
// pixel to remove any premultiplied alpha *only* if the image file explicitly
@ -489,6 +518,8 @@ STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);
// as above, but only applies to images loaded on the thread that calls the function
// this function is only available if your compiler supports thread-local variables;
// calling it will fail to link if your compiler doesn't
STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply);
STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert);
STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip);
// ZLIB client - used by PNG, available for other purposes
@ -605,7 +636,7 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch
#endif
#endif
#ifdef _MSC_VER
#if defined(_MSC_VER) || defined(__SYMBIAN32__)
typedef unsigned short stbi__uint16;
typedef signed short stbi__int16;
typedef unsigned int stbi__uint32;
@ -634,7 +665,7 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
#ifdef STBI_HAS_LROTL
#define stbi_lrot(x,y) _lrotl(x,y)
#else
#define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
#define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31)))
#endif
#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED))
@ -748,9 +779,12 @@ static int stbi__sse2_available(void)
#ifdef STBI_NEON
#include <arm_neon.h>
// assume GCC or Clang on ARM targets
#ifdef _MSC_VER
#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name
#else
#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
#endif
#endif
#ifndef STBI_SIMD_ALIGN
#define STBI_SIMD_ALIGN(type, name) type name
@ -924,6 +958,7 @@ static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp);
static int stbi__pnm_test(stbi__context *s);
static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp);
static int stbi__pnm_is16(stbi__context *s);
#endif
static
@ -998,7 +1033,7 @@ static int stbi__mad3sizes_valid(int a, int b, int c, int add)
}
// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)
static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add)
{
return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&
@ -1021,7 +1056,7 @@ static void *stbi__malloc_mad3(int a, int b, int c, int add)
return stbi__malloc(a*b*c + add);
}
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)
static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)
{
if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL;
@ -1029,6 +1064,23 @@ static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)
}
#endif
// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow.
static int stbi__addints_valid(int a, int b)
{
if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow
if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0.
return a <= INT_MAX - b;
}
// returns 1 if the product of two signed shorts is valid, 0 on overflow.
static int stbi__mul2shorts_valid(short a, short b)
{
if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow
if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid
if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN
return a >= SHRT_MIN / b;
}
// stbi__err - error
// stbi__errpf - error returning pointer to float
// stbi__errpuc - error returning pointer to unsigned char
@ -1087,9 +1139,8 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re
ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order
ri->num_channels = 0;
#ifndef STBI_NO_JPEG
if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri);
#endif
// test the formats with a very explicit header first (at least a FOURCC
// or distinctive magic number first)
#ifndef STBI_NO_PNG
if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri);
#endif
@ -1107,6 +1158,13 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re
#ifndef STBI_NO_PIC
if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri);
#endif
// then the formats that can end up attempting to load with just 1 or 2
// bytes matching expectations; these are prone to false positives, so
// try them later
#ifndef STBI_NO_JPEG
if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri);
#endif
#ifndef STBI_NO_PNM
if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri);
#endif
@ -1262,12 +1320,12 @@ static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, in
#ifndef STBI_NO_STDIO
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide);
STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);
#endif
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
{
return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
@ -1277,16 +1335,16 @@ STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wch
static FILE *stbi__fopen(char const *filename, char const *mode)
{
FILE *f;
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
wchar_t wMode[64];
wchar_t wFilename[1024];
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)))
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename)))
return 0;
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)))
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode)))
return 0;
#if _MSC_VER >= 1400
#if defined(_MSC_VER) && _MSC_VER >= 1400
if (0 != _wfopen_s(&f, wFilename, wMode))
f = 0;
#else
@ -1662,7 +1720,8 @@ static int stbi__get16le(stbi__context *s)
static stbi__uint32 stbi__get32le(stbi__context *s)
{
stbi__uint32 z = stbi__get16le(s);
return z + (stbi__get16le(s) << 16);
z += (stbi__uint32)stbi__get16le(s) << 16;
return z;
}
#endif
@ -1944,9 +2003,12 @@ static int stbi__build_huffman(stbi__huffman *h, int *count)
int i,j,k=0;
unsigned int code;
// build size list for each symbol (from JPEG spec)
for (i=0; i < 16; ++i)
for (j=0; j < count[i]; ++j)
for (i=0; i < 16; ++i) {
for (j=0; j < count[i]; ++j) {
h->size[k++] = (stbi_uc) (i+1);
if(k >= 257) return stbi__err("bad size list","Corrupt JPEG");
}
}
h->size[k] = 0;
// compute actual symbols (from jpeg spec)
@ -2071,6 +2133,8 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h)
// convert the huffman code to the symbol id
c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];
if(c < 0 || c >= 256) // symbol id out of bounds!
return -1;
STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);
// convert the id to a symbol
@ -2089,14 +2153,14 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)
unsigned int k;
int sgn;
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing
sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB
sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative)
k = stbi_lrot(j->code_buffer, n);
if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0;
j->code_buffer = k & ~stbi__bmask[n];
k &= stbi__bmask[n];
j->code_bits -= n;
return k + (stbi__jbias[n] & ~sgn);
return k + (stbi__jbias[n] & (sgn - 1));
}
// get some unsigned bits
@ -2104,6 +2168,7 @@ stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n)
{
unsigned int k;
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing
k = stbi_lrot(j->code_buffer, n);
j->code_buffer = k & ~stbi__bmask[n];
k &= stbi__bmask[n];
@ -2115,6 +2180,7 @@ stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j)
{
unsigned int k;
if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);
if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing
k = j->code_buffer;
j->code_buffer <<= 1;
--j->code_bits;
@ -2146,14 +2212,16 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
t = stbi__jpeg_huff_decode(j, hdc);
if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG");
if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG");
// 0 all the ac values now so we can do it 32-bits at a time
memset(data,0,64*sizeof(data[0]));
diff = t ? stbi__extend_receive(j, t) : 0;
if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG");
dc = j->img_comp[b].dc_pred + diff;
j->img_comp[b].dc_pred = dc;
if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
data[0] = (short) (dc * dequant[0]);
// decode AC components, see JPEG spec
@ -2167,6 +2235,7 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
if (r) { // fast-AC path
k += (r >> 4) & 15; // run
s = r & 15; // combined length
if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available");
j->code_buffer <<= s;
j->code_bits -= s;
// decode into unzigzag'd location
@ -2203,12 +2272,14 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__
// first scan for DC coefficient, must be first
memset(data,0,64*sizeof(data[0])); // 0 all the ac values now
t = stbi__jpeg_huff_decode(j, hdc);
if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
diff = t ? stbi__extend_receive(j, t) : 0;
if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG");
dc = j->img_comp[b].dc_pred + diff;
j->img_comp[b].dc_pred = dc;
data[0] = (short) (dc << j->succ_low);
if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
data[0] = (short) (dc * (1 << j->succ_low));
} else {
// refinement scan for DC coefficient
if (stbi__jpeg_get_bit(j))
@ -2242,10 +2313,11 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__
if (r) { // fast-AC path
k += (r >> 4) & 15; // run
s = r & 15; // combined length
if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available");
j->code_buffer <<= s;
j->code_bits -= s;
zig = stbi__jpeg_dezigzag[k++];
data[zig] = (short) ((r >> 8) << shift);
data[zig] = (short) ((r >> 8) * (1 << shift));
} else {
int rs = stbi__jpeg_huff_decode(j, hac);
if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
@ -2263,7 +2335,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__
} else {
k += r;
zig = stbi__jpeg_dezigzag[k++];
data[zig] = (short) (stbi__extend_receive(j,s) << shift);
data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift));
}
}
} while (k <= j->spec_end);
@ -3062,6 +3134,7 @@ static int stbi__process_marker(stbi__jpeg *z, int m)
sizes[i] = stbi__get8(z->s);
n += sizes[i];
}
if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values!
L -= 17;
if (tc == 0) {
if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;
@ -3227,6 +3300,13 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan)
if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v;
}
// check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios
// and I've never seen a non-corrupted JPEG file actually use them
for (i=0; i < s->img_n; ++i) {
if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG");
if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG");
}
// compute interleaved mcu info
z->img_h_max = h_max;
z->img_v_max = v_max;
@ -3304,6 +3384,28 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)
return 1;
}
static int stbi__skip_jpeg_junk_at_end(stbi__jpeg *j)
{
// some JPEGs have junk at end, skip over it but if we find what looks
// like a valid marker, resume there
while (!stbi__at_eof(j->s)) {
int x = stbi__get8(j->s);
while (x == 255) { // might be a marker
if (stbi__at_eof(j->s)) return STBI__MARKER_none;
x = stbi__get8(j->s);
if (x != 0x00 && x != 0xff) {
// not a stuffed zero or lead-in to another marker, looks
// like an actual marker, return it
return x;
}
// stuffed zero has x=0 now which ends the loop, meaning we go
// back to regular scan loop.
// repeated 0xff keeps trying to read the next byte of the marker.
}
}
return STBI__MARKER_none;
}
// decode image to YCbCr format
static int stbi__decode_jpeg_image(stbi__jpeg *j)
{
@ -3320,25 +3422,22 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j)
if (!stbi__process_scan_header(j)) return 0;
if (!stbi__parse_entropy_coded_data(j)) return 0;
if (j->marker == STBI__MARKER_none ) {
// handle 0s at the end of image data from IP Kamera 9060
while (!stbi__at_eof(j->s)) {
int x = stbi__get8(j->s);
if (x == 255) {
j->marker = stbi__get8(j->s);
break;
}
}
j->marker = stbi__skip_jpeg_junk_at_end(j);
// if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0
}
m = stbi__get_marker(j);
if (STBI__RESTART(m))
m = stbi__get_marker(j);
} else if (stbi__DNL(m)) {
int Ld = stbi__get16be(j->s);
stbi__uint32 NL = stbi__get16be(j->s);
if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG");
if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG");
m = stbi__get_marker(j);
} else {
if (!stbi__process_marker(j, m)) return 0;
if (!stbi__process_marker(j, m)) return 1;
m = stbi__get_marker(j);
}
m = stbi__get_marker(j);
}
if (j->progressive)
stbi__jpeg_finish(j);
@ -3782,6 +3881,10 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp
else
decode_n = z->s->img_n;
// nothing to do if no components requested; check this now to avoid
// accessing uninitialized coutput[0] later
if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; }
// resample and color-convert
{
int k;
@ -3924,6 +4027,8 @@ static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int re
{
unsigned char* result;
stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));
if (!j) return stbi__errpuc("outofmem", "Out of memory");
memset(j, 0, sizeof(stbi__jpeg));
STBI_NOTUSED(ri);
j->s = s;
stbi__setup_jpeg(j);
@ -3936,6 +4041,8 @@ static int stbi__jpeg_test(stbi__context *s)
{
int r;
stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));
if (!j) return stbi__err("outofmem", "Out of memory");
memset(j, 0, sizeof(stbi__jpeg));
j->s = s;
stbi__setup_jpeg(j);
r = stbi__decode_jpeg_header(j, STBI__SCAN_type);
@ -3960,6 +4067,8 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
{
int result;
stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));
if (!j) return stbi__err("outofmem", "Out of memory");
memset(j, 0, sizeof(stbi__jpeg));
j->s = s;
result = stbi__jpeg_info_raw(j, x, y, comp);
STBI_FREE(j);
@ -3979,6 +4088,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
// fast-way is faster to check than jpeg huffman, but slow way is slower
#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables
#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1)
#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet
// zlib-style huffman encoding
// (jpegs packs from left, zlib from right, so can't share code)
@ -3988,8 +4098,8 @@ typedef struct
stbi__uint16 firstcode[16];
int maxcode[17];
stbi__uint16 firstsymbol[16];
stbi_uc size[288];
stbi__uint16 value[288];
stbi_uc size[STBI__ZNSYMS];
stbi__uint16 value[STBI__ZNSYMS];
} stbi__zhuffman;
stbi_inline static int stbi__bitreverse16(int n)
@ -4120,7 +4230,7 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z)
if (s >= 16) return -1; // invalid code!
// code size is s, so:
b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];
if (b >= (int) sizeof (z->size)) return -1; // some data was corrupt somewhere!
if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere!
if (z->size[b] != s) return -1; // was originally an assert, but report failure instead.
a->code_buffer >>= s;
a->num_bits -= s;
@ -4201,11 +4311,12 @@ static int stbi__parse_huffman_block(stbi__zbuf *a)
a->zout = zout;
return 1;
}
if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data
z -= 257;
len = stbi__zlength_base[z];
if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
z = stbi__zhuffman_decode(a, &a->z_distance);
if (z < 0) return stbi__err("bad huffman code","Corrupt PNG");
if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data
dist = stbi__zdist_base[z];
if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
@ -4317,7 +4428,7 @@ static int stbi__parse_zlib_header(stbi__zbuf *a)
return 1;
}
static const stbi_uc stbi__zdefault_length[288] =
static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] =
{
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
@ -4363,7 +4474,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
} else {
if (type == 1) {
// use fixed code lengths
if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0;
if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0;
if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0;
} else {
if (!stbi__compute_huffman_codes(a)) return 0;
@ -4759,6 +4870,7 @@ static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint3
// de-interlacing
final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0);
if (!final) return stbi__err("outofmem", "Out of memory");
for (p=0; p < 7; ++p) {
int xorig[] = { 0,4,0,2,0,1,0 };
int yorig[] = { 0,0,4,0,2,0,1 };
@ -4879,19 +4991,46 @@ static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int
return 1;
}
static int stbi__unpremultiply_on_load = 0;
static int stbi__de_iphone_flag = 0;
static int stbi__unpremultiply_on_load_global = 0;
static int stbi__de_iphone_flag_global = 0;
STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply)
{
stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply;
stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply;
}
STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
{
stbi__de_iphone_flag = flag_true_if_should_convert;
stbi__de_iphone_flag_global = flag_true_if_should_convert;
}
#ifndef STBI_THREAD_LOCAL
#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global
#define stbi__de_iphone_flag stbi__de_iphone_flag_global
#else
static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set;
static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set;
STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
{
stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply;
stbi__unpremultiply_on_load_set = 1;
}
STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert)
{
stbi__de_iphone_flag_local = flag_true_if_should_convert;
stbi__de_iphone_flag_set = 1;
}
#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \
? stbi__unpremultiply_on_load_local \
: stbi__unpremultiply_on_load_global)
#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \
? stbi__de_iphone_flag_local \
: stbi__de_iphone_flag_global)
#endif // STBI_THREAD_LOCAL
static void stbi__de_iphone(stbi__png *z)
{
stbi__context *s = z->s;
@ -4981,14 +5120,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
if (!pal_img_n) {
s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
if (scan == STBI__SCAN_header) return 1;
} else {
// if paletted, then pal_n is our final components, and
// img_n is # components to decompress/filter.
s->img_n = 1;
if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG");
// if SCAN_header, have to scan to see if we have a tRNS
}
// even with SCAN_header, have to scan to see if we have a tRNS
break;
}
@ -5020,6 +5158,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG");
if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
has_trans = 1;
// non-paletted with tRNS = constant alpha. if header-scanning, we can stop now.
if (scan == STBI__SCAN_header) { ++s->img_n; return 1; }
if (z->depth == 16) {
for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
} else {
@ -5032,7 +5172,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
case STBI__PNG_TYPE('I','D','A','T'): {
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; }
if (scan == STBI__SCAN_header) {
// header scan definitely stops at first IDAT
if (pal_img_n)
s->img_n = pal_img_n;
return 1;
}
if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes");
if ((int)(ioff + c.length) < (int)ioff) return 0;
if (ioff + c.length > idata_limit) {
stbi__uint32 idata_limit_old = idata_limit;
@ -5272,6 +5418,32 @@ typedef struct
int extra_read;
} stbi__bmp_data;
static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress)
{
// BI_BITFIELDS specifies masks explicitly, don't override
if (compress == 3)
return 1;
if (compress == 0) {
if (info->bpp == 16) {
info->mr = 31u << 10;
info->mg = 31u << 5;
info->mb = 31u << 0;
} else if (info->bpp == 32) {
info->mr = 0xffu << 16;
info->mg = 0xffu << 8;
info->mb = 0xffu << 0;
info->ma = 0xffu << 24;
info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0
} else {
// otherwise, use defaults, which is all-0
info->mr = info->mg = info->mb = info->ma = 0;
}
return 1;
}
return 0; // error
}
static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
{
int hsz;
@ -5299,6 +5471,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
if (hsz != 12) {
int compress = stbi__get32le(s);
if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE");
if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes
if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel
stbi__get32le(s); // discard sizeof
stbi__get32le(s); // discard hres
stbi__get32le(s); // discard vres
@ -5313,17 +5487,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
}
if (info->bpp == 16 || info->bpp == 32) {
if (compress == 0) {
if (info->bpp == 32) {
info->mr = 0xffu << 16;
info->mg = 0xffu << 8;
info->mb = 0xffu << 0;
info->ma = 0xffu << 24;
info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0
} else {
info->mr = 31u << 10;
info->mg = 31u << 5;
info->mb = 31u << 0;
}
stbi__bmp_set_mask_defaults(info, compress);
} else if (compress == 3) {
info->mr = stbi__get32le(s);
info->mg = stbi__get32le(s);
@ -5338,6 +5502,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
return stbi__errpuc("bad BMP", "bad BMP");
}
} else {
// V4/V5 header
int i;
if (hsz != 108 && hsz != 124)
return stbi__errpuc("bad BMP", "bad BMP");
@ -5345,6 +5510,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
info->mg = stbi__get32le(s);
info->mb = stbi__get32le(s);
info->ma = stbi__get32le(s);
if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs
stbi__bmp_set_mask_defaults(info, compress);
stbi__get32le(s); // discard color space
for (i=0; i < 12; ++i)
stbi__get32le(s); // discard color space parameters
@ -5394,9 +5561,22 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req
psize = (info.offset - info.extra_read - info.hsz) >> 2;
}
if (psize == 0) {
STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original));
if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) {
return stbi__errpuc("bad offset", "Corrupt BMP");
// accept some number of extra bytes after the header, but if the offset points either to before
// the header ends or implies a large amount of extra data, reject the file as malformed
int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original);
int header_limit = 1024; // max we actually read is below 256 bytes currently.
int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size.
if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) {
return stbi__errpuc("bad header", "Corrupt BMP");
}
// we established that bytes_read_so_far is positive and sensible.
// the first half of this test rejects offsets that are either too small positives, or
// negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn
// ensures the number computed in the second half of the test can't overflow.
if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) {
return stbi__errpuc("bad offset", "Corrupt BMP");
} else {
stbi__skip(s, info.offset - bytes_read_so_far);
}
}
@ -6342,6 +6522,7 @@ static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_c
// intermediate buffer is RGBA
result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0);
if (!result) return stbi__errpuc("outofmem", "Out of memory");
memset(result, 0xff, x*y*4);
if (!stbi__pic_load_core(s,x,y,comp, result)) {
@ -6457,6 +6638,7 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in
static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp)
{
stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif));
if (!g) return stbi__err("outofmem", "Out of memory");
if (!stbi__gif_header(s, g, comp, 1)) {
STBI_FREE(g);
stbi__rewind( s );
@ -6766,6 +6948,17 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
}
}
static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays)
{
STBI_FREE(g->out);
STBI_FREE(g->history);
STBI_FREE(g->background);
if (out) STBI_FREE(out);
if (delays && *delays) STBI_FREE(*delays);
return stbi__errpuc("outofmem", "Out of memory");
}
static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
{
if (stbi__gif_test(s)) {
@ -6777,6 +6970,10 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y,
int stride;
int out_size = 0;
int delays_size = 0;
STBI_NOTUSED(out_size);
STBI_NOTUSED(delays_size);
memset(&g, 0, sizeof(g));
if (delays) {
*delays = 0;
@ -6794,26 +6991,29 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y,
if (out) {
void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride );
if (NULL == tmp) {
STBI_FREE(g.out);
STBI_FREE(g.history);
STBI_FREE(g.background);
return stbi__errpuc("outofmem", "Out of memory");
}
if (!tmp)
return stbi__load_gif_main_outofmem(&g, out, delays);
else {
out = (stbi_uc*) tmp;
out_size = layers * stride;
}
if (delays) {
*delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers );
int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers );
if (!new_delays)
return stbi__load_gif_main_outofmem(&g, out, delays);
*delays = new_delays;
delays_size = layers * sizeof(int);
}
} else {
out = (stbi_uc*)stbi__malloc( layers * stride );
if (!out)
return stbi__load_gif_main_outofmem(&g, out, delays);
out_size = layers * stride;
if (delays) {
*delays = (int*) stbi__malloc( layers * sizeof(int) );
if (!*delays)
return stbi__load_gif_main_outofmem(&g, out, delays);
delays_size = layers * sizeof(int);
}
}
@ -7064,12 +7264,12 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re
// Run
value = stbi__get8(s);
count -= 128;
if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
for (z = 0; z < count; ++z)
scanline[i++ * 4 + k] = value;
} else {
// Dump
if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
for (z = 0; z < count; ++z)
scanline[i++ * 4 + k] = stbi__get8(s);
}
@ -7138,9 +7338,10 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp)
info.all_a = 255;
p = stbi__bmp_parse_header(s, &info);
stbi__rewind( s );
if (p == NULL)
if (p == NULL) {
stbi__rewind( s );
return 0;
}
if (x) *x = s->img_x;
if (y) *y = s->img_y;
if (comp) {
@ -7206,8 +7407,8 @@ static int stbi__psd_is16(stbi__context *s)
stbi__rewind( s );
return 0;
}
(void) stbi__get32be(s);
(void) stbi__get32be(s);
STBI_NOTUSED(stbi__get32be(s));
STBI_NOTUSED(stbi__get32be(s));
depth = stbi__get16be(s);
if (depth != 16) {
stbi__rewind( s );
@ -7286,7 +7487,6 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp)
// Known limitations:
// Does not support comments in the header section
// Does not support ASCII image data (formats P2 and P3)
// Does not support 16-bit-per-channel
#ifndef STBI_NO_PNM
@ -7307,7 +7507,8 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req
stbi_uc *out;
STBI_NOTUSED(ri);
if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n))
ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n);
if (ri->bits_per_channel == 0)
return 0;
if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)");
@ -7317,15 +7518,22 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req
*y = s->img_y;
if (comp) *comp = s->img_n;
if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0))
if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0))
return stbi__errpuc("too large", "PNM too large");
out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0);
out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0);
if (!out) return stbi__errpuc("outofmem", "Out of memory");
stbi__getn(s, out, s->img_n * s->img_x * s->img_y);
if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) {
STBI_FREE(out);
return stbi__errpuc("bad PNM", "PNM file truncated");
}
if (req_comp && req_comp != s->img_n) {
out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
if (ri->bits_per_channel == 16) {
out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y);
} else {
out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
}
if (out == NULL) return out; // stbi__convert_format frees input on failure
}
return out;
@ -7362,6 +7570,8 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c)
while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {
value = value*10 + (*c - '0');
*c = (char) stbi__get8(s);
if((value > 214748364) || (value == 214748364 && *c > '7'))
return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int");
}
return value;
@ -7392,17 +7602,29 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp)
stbi__pnm_skip_whitespace(s, &c);
*x = stbi__pnm_getinteger(s, &c); // read width
if(*x == 0)
return stbi__err("invalid width", "PPM image header had zero or overflowing width");
stbi__pnm_skip_whitespace(s, &c);
*y = stbi__pnm_getinteger(s, &c); // read height
if (*y == 0)
return stbi__err("invalid width", "PPM image header had zero or overflowing width");
stbi__pnm_skip_whitespace(s, &c);
maxv = stbi__pnm_getinteger(s, &c); // read max value
if (maxv > 255)
return stbi__err("max value > 255", "PPM image not 8-bit");
if (maxv > 65535)
return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images");
else if (maxv > 255)
return 16;
else
return 1;
return 8;
}
static int stbi__pnm_is16(stbi__context *s)
{
if (stbi__pnm_info(s, NULL, NULL, NULL) == 16)
return 1;
return 0;
}
#endif
@ -7458,6 +7680,9 @@ static int stbi__is_16_main(stbi__context *s)
if (stbi__psd_is16(s)) return 1;
#endif
#ifndef STBI_NO_PNM
if (stbi__pnm_is16(s)) return 1;
#endif
return 0;
}

View File

@ -48,7 +48,8 @@ int main(int argc, char** argv) {
"options:\n"
" -h, --help\t\tShow help and exit\n"
" -v, --version\t\tShow version and exit\n"
" --console\t\tAttach Windows console\n\n"
" --console\t\tAttach Windows console\n"
" --graphics-debug\tEnable graphics debug messages\n\n"
"<source> can be a Lua file, a folder, or a zip archive\n"
);
exit(0);
@ -89,6 +90,9 @@ int main(int argc, char** argv) {
for (int i = 1; i < argc; i++, argOffset++) {
if (!strcmp(argv[i], "--console")) {
os_open_console();
} else if (!strcmp(argv[i], "--graphics-debug")) {
lua_pushboolean(L, true);
lua_setfield(L, -2, "--graphics-debug");
} else {
break; // This is the project path
}

View File

@ -28,7 +28,7 @@ struct Source {
uint32_t offset;
float pitch;
float volume;
float position[4];
float position[3];
float orientation[4];
float radius;
float dipoleWeight;
@ -45,10 +45,11 @@ static struct {
ma_mutex lock;
ma_context context;
ma_device devices[2];
ma_device_info* deviceInfo[2];
Sound* sinks[2];
Source* sources[MAX_SOURCES];
uint64_t sourceMask;
float position[4];
float position[3];
float orientation[4];
Spatializer* spatializer;
float absorption[3];
@ -224,10 +225,11 @@ bool lovrAudioInit(const char* spatializer, uint32_t sampleRate) {
return state.initialized = true;
}
void lovrAudioDestroy() {
void lovrAudioDestroy(void) {
if (!state.initialized) return;
for (size_t i = 0; i < 2; i++) {
ma_device_uninit(&state.devices[i]);
free(state.deviceInfo[i]);
}
Source* source;
FOREACH_SOURCE(source) lovrRelease(source, lovrSourceDestroy);
@ -243,12 +245,14 @@ void lovrAudioDestroy() {
static AudioDeviceCallback* enumerateCallback;
static ma_bool32 enumPlayback(ma_context* context, ma_device_type type, const ma_device_info* info, void* userdata) {
if (type == ma_device_type_playback) enumerateCallback(&info->id, sizeof(info->id), info->name, info->isDefault, userdata);
AudioDevice device = { sizeof(info->id), &info->id, info->name, info->isDefault };
if (type == ma_device_type_playback) enumerateCallback(&device, userdata);
return MA_TRUE;
}
static ma_bool32 enumCapture(ma_context* context, ma_device_type type, const ma_device_info* info, void* userdata) {
if (type == ma_device_type_capture) enumerateCallback(&info->id, sizeof(info->id), info->name, info->isDefault, userdata);
AudioDevice device = { sizeof(info->id), &info->id, info->name, info->isDefault };
if (type == ma_device_type_capture) enumerateCallback(&device, userdata);
return MA_TRUE;
}
@ -257,6 +261,26 @@ void lovrAudioEnumerateDevices(AudioType type, AudioDeviceCallback* callback, vo
ma_context_enumerate_devices(&state.context, type == AUDIO_PLAYBACK ? enumPlayback : enumCapture, userdata);
}
bool lovrAudioGetDevice(AudioType type, AudioDevice* device) {
if (!state.devices[type].pContext) {
return false;
}
if (!state.deviceInfo[type]) {
state.deviceInfo[type] = malloc(sizeof(ma_device_info));
lovrAssert(state.deviceInfo[type], "Out of memory");
}
ma_device_info* info = state.deviceInfo[type];
ma_device_type deviceType = type == AUDIO_PLAYBACK ? ma_device_type_playback : ma_device_type_capture;
ma_result result = ma_device_get_info(&state.devices[type], deviceType, info);
device->idSize = sizeof(ma_device_id);
device->id = &info->id;
device->name = info->name;
device->isDefault = info->isDefault;
return result == MA_SUCCESS;
}
bool lovrAudioSetDevice(AudioType type, void* id, size_t size, Sound* sink, AudioShareMode shareMode) {
if (id && size != sizeof(ma_device_id)) return false;
@ -346,12 +370,12 @@ void lovrAudioSetVolume(float volume, VolumeUnit units) {
ma_device_set_master_volume(&state.devices[AUDIO_PLAYBACK], CLAMP(volume, 0.f, 1.f));
}
void lovrAudioGetPose(float position[4], float orientation[4]) {
memcpy(position, state.position, sizeof(state.position));
memcpy(orientation, state.orientation, sizeof(state.orientation));
void lovrAudioGetPose(float position[3], float orientation[4]) {
vec3_init(position, state.position);
quat_init(orientation, state.orientation);
}
void lovrAudioSetPose(float position[4], float orientation[4]) {
void lovrAudioSetPose(float position[3], float orientation[4]) {
state.spatializer->setListenerPose(position, orientation);
}
@ -362,11 +386,11 @@ bool lovrAudioSetGeometry(float* vertices, uint32_t* indices, uint32_t vertexCou
return success;
}
const char* lovrAudioGetSpatializer() {
const char* lovrAudioGetSpatializer(void) {
return state.spatializer->name;
}
uint32_t lovrAudioGetSampleRate() {
uint32_t lovrAudioGetSampleRate(void) {
return state.sampleRate;
}
@ -426,8 +450,8 @@ Source* lovrSourceClone(Source* source) {
lovrRetain(clone->sound);
clone->pitch = source->pitch;
clone->volume = source->volume;
memcpy(clone->position, source->position, 4 * sizeof(float));
memcpy(clone->orientation, source->orientation, 4 * sizeof(float));
vec3_init(clone->position, source->position);
quat_init(clone->orientation, source->orientation);
clone->radius = source->radius;
clone->dipoleWeight = source->dipoleWeight;
clone->dipolePower = source->dipolePower;
@ -555,15 +579,15 @@ bool lovrSourceIsSpatial(Source* source) {
return source->spatial;
}
void lovrSourceGetPose(Source* source, float position[4], float orientation[4]) {
memcpy(position, source->position, sizeof(source->position));
memcpy(orientation, source->orientation, sizeof(source->orientation));
void lovrSourceGetPose(Source* source, float position[3], float orientation[4]) {
vec3_init(position, source->position);
quat_init(orientation, source->orientation);
}
void lovrSourceSetPose(Source* source, float position[4], float orientation[4]) {
void lovrSourceSetPose(Source* source, float position[3], float orientation[4]) {
ma_mutex_lock(&state.lock);
memcpy(source->position, position, sizeof(source->position));
memcpy(source->orientation, orientation, sizeof(source->orientation));
if (position) vec3_init(source->position, position);
if (orientation) quat_init(source->orientation, orientation);
ma_mutex_unlock(&state.lock);
}

View File

@ -39,6 +39,13 @@ typedef enum {
AUDIO_EXCLUSIVE
} AudioShareMode;
typedef struct {
size_t idSize;
const void* id;
const char* name;
bool isDefault;
} AudioDevice;
typedef enum {
AUDIO_PLAYBACK,
AUDIO_CAPTURE
@ -54,19 +61,20 @@ typedef enum {
UNIT_DECIBELS
} VolumeUnit;
typedef void AudioDeviceCallback(const void* id, size_t size, const char* name, bool isDefault, void* userdata);
typedef void AudioDeviceCallback(AudioDevice* device, void* userdata);
bool lovrAudioInit(const char* spatializer, uint32_t sampleRate);
void lovrAudioDestroy(void);
void lovrAudioEnumerateDevices(AudioType type, AudioDeviceCallback* callback, void* userdata);
bool lovrAudioGetDevice(AudioType type, AudioDevice* device);
bool lovrAudioSetDevice(AudioType type, void* id, size_t size, struct Sound* sink, AudioShareMode shareMode);
bool lovrAudioStart(AudioType type);
bool lovrAudioStop(AudioType type);
bool lovrAudioIsStarted(AudioType type);
float lovrAudioGetVolume(VolumeUnit units);
void lovrAudioSetVolume(float volume, VolumeUnit units);
void lovrAudioGetPose(float position[4], float orientation[4]);
void lovrAudioSetPose(float position[4], float orientation[4]);
void lovrAudioGetPose(float position[3], float orientation[4]);
void lovrAudioSetPose(float position[3], float orientation[4]);
bool lovrAudioSetGeometry(float* vertices, uint32_t* indices, uint32_t vertexCount, uint32_t indexCount, AudioMaterial material);
const char* lovrAudioGetSpatializer(void);
uint32_t lovrAudioGetSampleRate(void);
@ -94,8 +102,8 @@ double lovrSourceTell(Source* source, TimeUnit units);
double lovrSourceGetDuration(Source* source, TimeUnit units);
bool lovrSourceIsPitchable(Source* source);
bool lovrSourceIsSpatial(Source* source);
void lovrSourceGetPose(Source* source, float position[4], float orientation[4]);
void lovrSourceSetPose(Source* source, float position[4], float orientation[4]);
void lovrSourceGetPose(Source* source, float position[3], float orientation[4]);
void lovrSourceSetPose(Source* source, float position[3], float orientation[4]);
float lovrSourceGetRadius(Source* source);
void lovrSourceSetRadius(Source* source, float radius);
void lovrSourceGetDirectivity(Source* source, float* weight, float* power);

View File

@ -15,7 +15,7 @@ typedef struct {
// output is stereo, frames is stereo frames, scratch is a buffer the length of output (in case that helps)
// return value is number of stereo frames written.
uint32_t (*tail)(float* scratch, float* output, uint32_t frames);
void (*setListenerPose)(float position[4], float orientation[4]);
void (*setListenerPose)(float position[3], float orientation[4]);
bool (*setGeometry)(float* vertices, uint32_t* indices, uint32_t vertexCount, uint32_t indexCount, AudioMaterial material);
void (*sourceCreate)(Source* source);
void (*sourceDestroy)(Source* source);

View File

@ -201,7 +201,7 @@ static uint32_t oculus_apply(Source* source, const float* input, float* output,
uint32_t outStatus = 0;
state.sources[idx].usedSourceThisPlayback = true;
float position[4], orientation[4];
float position[3], orientation[4];
lovrSourceGetPose(source, position, orientation);
ovrAudio_SetAudioSourcePos(state.context, idx, position[0], position[1], position[2]);
@ -254,7 +254,7 @@ static void oculusUnpackVec(ovrVector3f* ov, float* p) {
ov->x = p[0]; ov->y = p[1]; ov->z = p[2];
}
static void oculusRecreatePose(ovrPoseStatef* out, float position[4], float orientation[4]) {
static void oculusRecreatePose(ovrPoseStatef* out, float position[3], float orientation[4]) {
ovrPosef pose;
oculusUnpackVec(&pose.Position, position);
oculusUnpackQuat(&pose.Orientation, orientation);
@ -267,7 +267,7 @@ static void oculusRecreatePose(ovrPoseStatef* out, float position[4], float orie
out->TimeInSeconds = 0; //TODO-OS
}
static void oculus_setListenerPose(float position[4], float orientation[4]) {
static void oculus_setListenerPose(float position[3], float orientation[4]) {
ovrPoseStatef pose;
oculusRecreatePose(&pose, position, orientation);

View File

@ -124,14 +124,14 @@ static struct {
IPLhandle directSoundEffect[MAX_SOURCES];
IPLhandle convolutionEffect[MAX_SOURCES];
IPLRenderingSettings renderingSettings;
float listenerPosition[4];
float listenerPosition[3];
float listenerOrientation[4];
float* scratchpad;
} state;
static void phonon_destroy(void);
bool phonon_init() {
bool phonon_init(void) {
state.library = phonon_dlopen(PHONON_LIBRARY);
if (!state.library) return false;
@ -166,7 +166,7 @@ bool phonon_init() {
return true;
}
void phonon_destroy() {
void phonon_destroy(void) {
if (state.scratchpad) free(state.scratchpad);
for (size_t i = 0; i < MAX_SOURCES; i++) {
if (state.binauralEffect[i]) phonon_iplDestroyBinauralEffect(&state.binauralEffect[i]);
@ -192,7 +192,7 @@ uint32_t phonon_apply(Source* source, const float* input, float* output, uint32_
uint32_t index = lovrSourceGetIndex(source);
float x[4], y[4], z[4];
float x[3], y[3], z[3];
vec3_set(y, 0.f, 1.f, 0.f);
vec3_set(z, 0.f, 0.f, -1.f);
quat_rotate(state.listenerOrientation, y);
@ -202,7 +202,7 @@ uint32_t phonon_apply(Source* source, const float* input, float* output, uint32_
IPLVector3 up = { y[0], y[1], y[2] };
// TODO maybe this should use a matrix
float position[4], orientation[4];
float position[3], orientation[4];
lovrSourceGetPose(source, position, orientation);
vec3_set(x, 1.f, 0.f, 0.f);
vec3_set(y, 0.f, 1.f, 0.f);
@ -280,7 +280,7 @@ uint32_t phonon_tail(float* scratch, float* output, uint32_t frames) {
}
};
float y[4], z[4];
float y[3], z[3];
vec3_set(y, 0.f, 1.f, 0.f);
vec3_set(z, 0.f, 0.f, -1.f);
quat_rotate(state.listenerOrientation, y);
@ -295,7 +295,7 @@ uint32_t phonon_tail(float* scratch, float* output, uint32_t frames) {
return frames;
}
void phonon_setListenerPose(float position[4], float orientation[4]) {
void phonon_setListenerPose(float position[3], float orientation[4]) {
memcpy(state.listenerPosition, position, sizeof(state.listenerPosition));
memcpy(state.listenerOrientation, orientation, sizeof(state.listenerOrientation));
}

View File

@ -18,18 +18,18 @@ static void simple_destroy(void) {
}
static uint32_t simple_apply(Source* source, const float* input, float* output, uint32_t frames, uint32_t _frames) {
float sourcePos[4], sourceOrientation[4];
float sourcePos[3], sourceOrientation[4];
lovrSourceGetPose(source, sourcePos, sourceOrientation);
float listenerPos[4] = { 0.f };
mat4_transform(state.listener, listenerPos);
float listenerPos[3];
mat4_getPosition(state.listener, listenerPos);
float target[2] = { 1.f, 1.f };
if (lovrSourceIsEffectEnabled(source, EFFECT_SPATIALIZATION)) {
float leftEar[4] = { -0.1f, 0.0f, 0.0f, 1.0f };
float rightEar[4] = { 0.1f, 0.0f, 0.0f, 1.0f };
mat4_transform(state.listener, leftEar);
mat4_transform(state.listener, rightEar);
float leftEar[3] = { -0.1f, 0.0f, 0.0f };
float rightEar[3] = { 0.1f, 0.0f, 0.0f };
mat4_mulPoint(state.listener, leftEar);
mat4_mulPoint(state.listener, rightEar);
float ldistance = vec3_distance(sourcePos, leftEar);
float rdistance = vec3_distance(sourcePos, rightEar);
target[0] = .5f + (rdistance - ldistance) * 2.5f;
@ -39,8 +39,8 @@ static uint32_t simple_apply(Source* source, const float* input, float* output,
float weight, power;
lovrSourceGetDirectivity(source, &weight, &power);
if (weight > 0.f && power > 0.f) {
float sourceDirection[4];
float sourceToListener[4];
float sourceDirection[3];
float sourceToListener[3];
quat_getDirection(sourceOrientation, sourceDirection);
vec3_normalize(vec3_sub(vec3_init(sourceToListener, listenerPos), sourcePos));
float dot = vec3_dot(sourceToListener, sourceDirection);
@ -86,7 +86,7 @@ static uint32_t simple_tail(float* scratch, float* output, uint32_t frames) {
return 0;
}
static void simple_setListenerPose(float position[4], float orientation[4]) {
static void simple_setListenerPose(float position[3], float orientation[4]) {
mat4_identity(state.listener);
mat4_translate(state.listener, position[0], position[1], position[2]);
mat4_rotateQuat(state.listener, orientation);

View File

@ -15,11 +15,19 @@ static size_t typeSizes[] = {
[F32] = 4
};
static void* nullIO(const char* path, size_t* count) {
lovrThrow("Can't resolve external asset reference for model loaded from memory");
}
ModelData* lovrModelDataCreate(Blob* source, ModelDataIO* io) {
ModelData* model = calloc(1, sizeof(ModelData));
lovrAssert(model, "Out of memory");
model->ref = 1;
if (!io) {
io = &nullIO;
}
if (!lovrModelDataInitGltf(model, source, io)) {
if (!lovrModelDataInitObj(model, source, io)) {
if (!lovrModelDataInitStl(model, source, io)) {
@ -42,6 +50,7 @@ void lovrModelDataDestroy(void* ref) {
for (uint32_t i = 0; i < model->imageCount; i++) {
lovrRelease(model->images[i], lovrImageDestroy);
}
map_free(&model->blendShapeMap);
map_free(&model->animationMap);
map_free(&model->materialMap);
map_free(&model->nodeMap);
@ -52,24 +61,26 @@ void lovrModelDataDestroy(void* ref) {
free(model);
}
// Note: this code is a scary optimization
// Batches allocations for all the ModelData arrays
void lovrModelDataAllocate(ModelData* model) {
size_t totalSize = 0;
size_t sizes[13];
size_t sizes[15];
size_t alignment = 8;
totalSize += sizes[0] = ALIGN(model->blobCount * sizeof(Blob*), alignment);
totalSize += sizes[1] = ALIGN(model->bufferCount * sizeof(ModelBuffer), alignment);
totalSize += sizes[2] = ALIGN(model->imageCount * sizeof(Image*), alignment);
totalSize += sizes[3] = ALIGN(model->materialCount * sizeof(ModelMaterial), alignment);
totalSize += sizes[4] = ALIGN(model->attributeCount * sizeof(ModelAttribute), alignment);
totalSize += sizes[5] = ALIGN(model->primitiveCount * sizeof(ModelPrimitive), alignment);
totalSize += sizes[6] = ALIGN(model->animationCount * sizeof(ModelAnimation), alignment);
totalSize += sizes[7] = ALIGN(model->skinCount * sizeof(ModelSkin), alignment);
totalSize += sizes[8] = ALIGN(model->nodeCount * sizeof(ModelNode), alignment);
totalSize += sizes[9] = ALIGN(model->channelCount * sizeof(ModelAnimationChannel), alignment);
totalSize += sizes[10] = ALIGN(model->childCount * sizeof(uint32_t), alignment);
totalSize += sizes[11] = ALIGN(model->jointCount * sizeof(uint32_t), alignment);
totalSize += sizes[12] = model->charCount * sizeof(char);
totalSize += sizes[3] = ALIGN(model->attributeCount * sizeof(ModelAttribute), alignment);
totalSize += sizes[4] = ALIGN(model->primitiveCount * sizeof(ModelPrimitive), alignment);
totalSize += sizes[5] = ALIGN(model->materialCount * sizeof(ModelMaterial), alignment);
totalSize += sizes[6] = ALIGN(model->blendShapeCount * sizeof(ModelBlendShape), alignment);
totalSize += sizes[7] = ALIGN(model->animationCount * sizeof(ModelAnimation), alignment);
totalSize += sizes[8] = ALIGN(model->skinCount * sizeof(ModelSkin), alignment);
totalSize += sizes[9] = ALIGN(model->nodeCount * sizeof(ModelNode), alignment);
totalSize += sizes[10] = ALIGN(model->channelCount * sizeof(ModelAnimationChannel), alignment);
totalSize += sizes[11] = ALIGN(model->blendDataCount * sizeof(ModelBlendData), alignment);
totalSize += sizes[12] = ALIGN(model->childCount * sizeof(uint32_t), alignment);
totalSize += sizes[13] = ALIGN(model->jointCount * sizeof(uint32_t), alignment);
totalSize += sizes[14] = model->charCount * sizeof(char);
size_t offset = 0;
char* p = model->data = calloc(1, totalSize);
@ -77,17 +88,20 @@ void lovrModelDataAllocate(ModelData* model) {
model->blobs = (Blob**) (p + offset), offset += sizes[0];
model->buffers = (ModelBuffer*) (p + offset), offset += sizes[1];
model->images = (Image**) (p + offset), offset += sizes[2];
model->materials = (ModelMaterial*) (p + offset), offset += sizes[3];
model->attributes = (ModelAttribute*) (p + offset), offset += sizes[4];
model->primitives = (ModelPrimitive*) (p + offset), offset += sizes[5];
model->animations = (ModelAnimation*) (p + offset), offset += sizes[6];
model->skins = (ModelSkin*) (p + offset), offset += sizes[7];
model->nodes = (ModelNode*) (p + offset), offset += sizes[8];
model->channels = (ModelAnimationChannel*) (p + offset), offset += sizes[9];
model->children = (uint32_t*) (p + offset), offset += sizes[10];
model->joints = (uint32_t*) (p + offset), offset += sizes[11];
model->chars = (char*) (p + offset), offset += sizes[12];
model->attributes = (ModelAttribute*) (p + offset), offset += sizes[3];
model->primitives = (ModelPrimitive*) (p + offset), offset += sizes[4];
model->materials = (ModelMaterial*) (p + offset), offset += sizes[5];
model->blendShapes = (ModelBlendShape*) (p + offset), offset += sizes[6];
model->animations = (ModelAnimation*) (p + offset), offset += sizes[7];
model->skins = (ModelSkin*) (p + offset), offset += sizes[8];
model->nodes = (ModelNode*) (p + offset), offset += sizes[9];
model->channels = (ModelAnimationChannel*) (p + offset), offset += sizes[10];
model->blendData = (ModelBlendData*) (p + offset), offset += sizes[11];
model->children = (uint32_t*) (p + offset), offset += sizes[12];
model->joints = (uint32_t*) (p + offset), offset += sizes[13];
model->chars = (char*) (p + offset), offset += sizes[14];
map_init(&model->blendShapeMap, model->blendShapeCount);
map_init(&model->animationMap, model->animationCount);
map_init(&model->materialMap, model->materialCount);
map_init(&model->nodeMap, model->nodeCount);
@ -101,9 +115,11 @@ void lovrModelDataFinalize(ModelData* model) {
for (uint32_t i = 0; i < model->nodeCount; i++) {
ModelNode* node = &model->nodes[i];
for (uint32_t j = 0; j < model->nodeCount; j++) {
if (i == j || node->primitiveIndex != model->nodes[j].primitiveIndex) continue;
lovrCheck(node->skin == model->nodes[j].skin, "Model has a mesh used with multiple different skins, which is not supported");
if (node->primitiveCount > 0) {
for (uint32_t j = 0; j < model->nodeCount; j++) {
if (i == j || model->nodes[j].primitiveCount == 0 || node->primitiveIndex != model->nodes[j].primitiveIndex) continue;
lovrCheck(node->skin == model->nodes[j].skin, "Model has a mesh used with multiple different skins, which is not supported");
}
}
for (uint32_t j = node->primitiveIndex; j < node->primitiveIndex + node->primitiveCount; j++) {
@ -120,6 +136,8 @@ void lovrModelDataFinalize(ModelData* model) {
model->skins[primitive->skin].vertexCount += vertexCount;
model->skinnedVertexCount += vertexCount;
}
model->blendShapeVertexCount += vertexCount * primitive->blendShapeCount;
model->dynamicVertexCount += primitive->skin != ~0u || !!primitive->blendShapes ? vertexCount : 0;
model->vertexCount += vertexCount;
model->indexCount += primitive->indices ? primitive->indices->count : 0;
@ -132,13 +150,21 @@ void lovrModelDataFinalize(ModelData* model) {
}
}
for (uint32_t i = 0; i < MAX_DEFAULT_ATTRIBUTES; i++) {
ModelAttribute* attribute = primitive->attributes[i];
if (attribute) {
for (uint32_t j = 0; j < MAX_DEFAULT_ATTRIBUTES; j++) {
ModelAttribute* attribute = primitive->attributes[j];
if (!attribute) continue;
attribute->stride = model->buffers[attribute->buffer].stride;
if (attribute->stride == 0) attribute->stride = typeSizes[attribute->type] * attribute->components;
}
for (uint32_t j = 0; j < primitive->blendShapeCount; j++) {
ModelBlendData* blendData = &primitive->blendShapes[j];
ModelAttribute* attributes[] = { blendData->positions, blendData->normals, blendData->tangents };
for (uint32_t k = 0; k < COUNTOF(attributes); k++) {
if (!attributes[k]) continue;
ModelAttribute* attribute = attributes[k];
attribute->stride = model->buffers[attribute->buffer].stride;
if (attribute->stride == 0) {
attribute->stride = typeSizes[attribute->type] * attribute->components;
}
if (attribute->stride == 0) attribute->stride = typeSizes[attribute->type] * attribute->components;
}
}
}
@ -315,20 +341,20 @@ static void boundingSphereHelper(ModelData* model, uint32_t nodeIndex, uint32_t*
float* min = position->min;
float* max = position->max;
float corners[8][4] = {
{ min[0], min[1], min[2], 1.f },
{ min[0], min[1], max[2], 1.f },
{ min[0], max[1], min[2], 1.f },
{ min[0], max[1], max[2], 1.f },
{ max[0], min[1], min[2], 1.f },
{ max[0], min[1], max[2], 1.f },
{ max[0], max[1], min[2], 1.f },
{ max[0], max[1], max[2], 1.f }
float corners[8][3] = {
{ min[0], min[1], min[2] },
{ min[0], min[1], max[2] },
{ min[0], max[1], min[2] },
{ min[0], max[1], max[2] },
{ max[0], min[1], min[2] },
{ max[0], min[1], max[2] },
{ max[0], max[1], min[2] },
{ max[0], max[1], max[2] }
};
for (uint32_t j = 0; j < 8; j++) {
mat4_transform(m, corners[j]);
memcpy(points + 3 * (*pointIndex)++, corners[j], 3 * sizeof(float));
mat4_mulPoint(m, corners[j]);
vec3_init(points + 3 * (*pointIndex)++, corners[j]);
}
}
@ -357,11 +383,7 @@ void lovrModelDataGetBoundingSphere(ModelData* model, float sphere[4]) {
float max = 0.f;
float* a = NULL;
for (uint32_t i = 1; i < pointCount; i++) {
float dx = points[3 * i + 0] - points[0];
float dy = points[3 * i + 1] - points[1];
float dz = points[3 * i + 2] - points[2];
float d2 = dx * dx + dy * dy + dz * dz;
float d2 = vec3_distance2(&points[3 * i], &points[0]);
if (d2 > max) {
a = &points[3 * i];
max = d2;
@ -373,11 +395,7 @@ void lovrModelDataGetBoundingSphere(ModelData* model, float sphere[4]) {
max = 0.f;
float* b = NULL;
for (uint32_t i = 0; i < pointCount; i++) {
float dx = points[3 * i + 0] - a[0];
float dy = points[3 * i + 1] - a[1];
float dz = points[3 * i + 2] - a[2];
float d2 = dx * dx + dy * dy + dz * dz;
float d2 = vec3_distance2(&points[3 * i], a);
if (d2 > max) {
b = &points[3 * i];
max = d2;
@ -484,10 +502,10 @@ static void collectVertices(ModelData* model, uint32_t nodeIndex, float** vertic
size_t stride = positions->stride == 0 ? 3 * sizeof(float) : positions->stride;
for (uint32_t j = 0; j < positions->count; j++) {
float v[4];
float v[3];
memcpy(v, data, 3 * sizeof(float));
mat4_transform(m, v);
memcpy(*vertices, v, 3 * sizeof(float));
mat4_mulPoint(m, v);
vec3_init(*vertices, v);
*vertices += 3;
data += stride;
}

View File

@ -8,6 +8,12 @@
struct Blob;
struct Image;
typedef enum {
META_GLTF_JSON,
META_HANDTRACKING_FB,
META_CONTROLLER_MSFT
} MetadataType;
typedef struct {
uint32_t blob;
size_t offset;
@ -55,20 +61,28 @@ typedef struct {
float max[4];
} ModelAttribute;
typedef struct {
ModelAttribute* positions;
ModelAttribute* normals;
ModelAttribute* tangents;
} ModelBlendData;
typedef enum {
DRAW_POINTS,
DRAW_LINES,
DRAW_POINT_LIST,
DRAW_LINE_LIST,
DRAW_LINE_LOOP,
DRAW_LINE_STRIP,
DRAW_TRIANGLES,
DRAW_TRIANGLE_LIST,
DRAW_TRIANGLE_STRIP,
DRAW_TRIANGLE_FAN
} DrawMode;
} ModelDrawMode;
typedef struct {
ModelAttribute* attributes[MAX_DEFAULT_ATTRIBUTES];
ModelAttribute* indices;
DrawMode mode;
ModelBlendData* blendShapes;
uint32_t blendShapeCount;
ModelDrawMode mode;
uint32_t material;
uint32_t skin;
} ModelPrimitive;
@ -96,10 +110,17 @@ typedef struct {
const char* name;
} ModelMaterial;
typedef struct {
const char* name;
uint32_t node;
float weight;
} ModelBlendShape;
typedef enum {
PROP_TRANSLATION,
PROP_ROTATION,
PROP_SCALE,
PROP_WEIGHTS
} AnimationProperty;
typedef enum {
@ -136,16 +157,18 @@ typedef struct {
union {
float matrix[16];
struct {
float translation[4];
float translation[3];
float rotation[4];
float scale[4];
float scale[3];
};
} transform;
uint32_t parent;
uint32_t* children;
uint32_t childCount;
uint32_t parent;
uint32_t primitiveIndex;
uint32_t primitiveCount;
uint32_t blendShapeIndex;
uint32_t blendShapeCount;
uint32_t skin;
bool hasMatrix;
} ModelNode;
@ -154,8 +177,9 @@ typedef struct ModelData {
uint32_t ref;
void* data;
char* metadata;
void* metadata;
size_t metadataSize;
MetadataType metadataType;
struct Blob** blobs;
struct Image** images;
@ -163,6 +187,7 @@ typedef struct ModelData {
ModelAttribute* attributes;
ModelPrimitive* primitives;
ModelMaterial* materials;
ModelBlendShape* blendShapes;
ModelAnimation* animations;
ModelSkin* skins;
ModelNode* nodes;
@ -174,21 +199,28 @@ typedef struct ModelData {
uint32_t attributeCount;
uint32_t primitiveCount;
uint32_t materialCount;
uint32_t blendShapeCount;
uint32_t animationCount;
uint32_t skinCount;
uint32_t nodeCount;
ModelAnimationChannel* channels;
ModelBlendData* blendData;
uint32_t* children;
uint32_t* joints;
char* chars;
uint32_t channelCount;
uint32_t blendDataCount;
uint32_t childCount;
uint32_t jointCount;
uint32_t charCount;
// Computed properties (loaders don't need to fill these out)
uint32_t vertexCount;
uint32_t skinnedVertexCount;
uint32_t blendShapeVertexCount;
uint32_t dynamicVertexCount;
uint32_t indexCount;
AttributeType indexType;
@ -200,6 +232,9 @@ typedef struct ModelData {
uint32_t totalVertexCount;
uint32_t totalIndexCount;
// Lookups
map_t blendShapeMap;
map_t animationMap;
map_t materialMap;
map_t nodeMap;

View File

@ -43,6 +43,8 @@ typedef struct {
typedef struct {
uint32_t primitiveIndex;
uint32_t primitiveCount;
uint32_t blendShapeIndex;
uint32_t blendShapeCount;
} gltfMesh;
typedef struct {
@ -184,8 +186,12 @@ static void loadImage(ModelData* model, gltfImage* images, uint32_t index, Model
lovrAssert(data, "Could not decode base64 image");
blob = lovrBlobCreate(data, size, NULL);
} else {
lovrAssert(image->uri.length < maxLength, "Image filename is too long");
strncat(filename, image->uri.data, image->uri.length);
char* path = image->uri.data;
size_t length = image->uri.length;
lovrAssert(length < maxLength, "Image filename is too long");
lovrAssert(path[0] != '/', "Absolute paths in models are not supported");
if (path[0] && path[1] && !memcmp(path, "./", 2)) path += 2;
strncat(filename, path, length);
data = io(filename, &size);
lovrAssert(data && size > 0, "Unable to read image from '%s'", filename);
blob = lovrBlobCreate(data, size, NULL);
@ -234,6 +240,7 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
lovrAssert(model->metadata, "Out of memory");
memcpy(model->metadata, json, jsonLength);
model->metadataSize = jsonLength;
model->metadataType = META_GLTF_JSON;
// Parse JSON
jsmn_parser parser;
@ -425,16 +432,46 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
lovrAssert(meshes, "Out of memory");
gltfMesh* mesh = meshes;
model->primitiveCount = 0;
model->blendShapeCount = 0;
for (int i = (token++)->size; i > 0; i--, mesh++) {
mesh->primitiveCount = 0;
mesh->blendShapeCount = 0;
for (int k = (token++)->size; k > 0; k--) {
gltfString key = NOM_STR(json, token);
if (STR_EQ(key, "primitives")) {
mesh->primitiveIndex = model->primitiveCount;
mesh->primitiveCount += token->size;
mesh->primitiveCount = token->size;
model->primitiveCount += token->size;
// Gotta look at targets of a primitive to truly know blend shape situation :')
for (int p = (token++)->size; p > 0; p--) {
for (int k2 = (token++)->size; k2 > 0; k2--) {
gltfString key = NOM_STR(json, token);
if (STR_EQ(key, "targets")) {
if (p == 1) {
mesh->blendShapeIndex = model->blendShapeCount;
mesh->blendShapeCount = token->size;
model->blendShapeCount += token->size;
}
model->blendDataCount += token->size;
}
token += NOM_VALUE(json, token);
}
}
} else if (STR_EQ(key, "extras")) {
for (int k2 = (token++)->size; k2 > 0; k2--) {
gltfString key = NOM_STR(json, token);
if (STR_EQ(key, "targetNames")) {
for (int j = (token++)->size; j > 0; j--) {
model->charCount += token->end - token->start + 1;
token++;
}
} else {
token += NOM_VALUE(json, token);
}
}
} else {
token += NOM_VALUE(json, token);
}
token += NOM_VALUE(json, token);
}
}
@ -525,6 +562,8 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
} else {
size_t bytesRead;
lovrAssert(uri.length < maxPathLength, "Buffer filename is too long");
lovrAssert(uri.data[0] != '/', "Absolute paths in models are not supported");
if (uri.data[0] && uri.data[1] && !memcmp(uri.data, "./", 2)) uri.data += 2;
strncat(filename, uri.data, uri.length);
*blob = lovrBlobCreate(io(filename, &bytesRead), size, NULL);
lovrAssert((*blob)->data && bytesRead == size, "Unable to read %s", filename);
@ -647,6 +686,7 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
if (STR_EQ(property, "translation")) { channel->property = PROP_TRANSLATION; }
else if (STR_EQ(property, "rotation")) { channel->property = PROP_ROTATION; }
else if (STR_EQ(property, "scale")) { channel->property = PROP_SCALE; }
else if (STR_EQ(property, "weights")) { channel->property = PROP_WEIGHTS; }
else { lovrThrow("Unknown animation channel property"); }
} else {
token += NOM_VALUE(json, token);
@ -777,14 +817,16 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
// Primitives
if (model->primitiveCount > 0) {
gltfMesh* mesh = meshes;
jsmntok_t* token = info.meshes;
ModelBlendData* blendData = model->blendData;
ModelPrimitive* primitive = model->primitives;
for (int i = (token++)->size; i > 0; i--) {
for (int i = (token++)->size; i > 0; i--, mesh++) {
for (int k = (token++)->size; k > 0; k--) {
gltfString key = NOM_STR(json, token);
if (STR_EQ(key, "primitives")) {
for (uint32_t j = (token++)->size; j > 0; j--, primitive++) {
primitive->mode = DRAW_TRIANGLES;
primitive->mode = DRAW_TRIANGLE_LIST;
primitive->material = ~0u;
for (int k2 = (token++)->size; k2 > 0; k2--) {
@ -796,18 +838,17 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
lovrAssert(primitive->indices->type != U8, "Unsigned byte indices are not supported (must be unsigned shorts or unsigned ints)");
} else if (STR_EQ(key, "mode")) {
switch (NOM_INT(json, token)) {
case 0: primitive->mode = DRAW_POINTS; break;
case 1: primitive->mode = DRAW_LINES; break;
case 0: primitive->mode = DRAW_POINT_LIST; break;
case 1: primitive->mode = DRAW_LINE_LIST; break;
case 2: primitive->mode = DRAW_LINE_LOOP; break;
case 3: primitive->mode = DRAW_LINE_STRIP; break;
case 4: primitive->mode = DRAW_TRIANGLES; break;
case 4: primitive->mode = DRAW_TRIANGLE_LIST; break;
case 5: primitive->mode = DRAW_TRIANGLE_STRIP; break;
case 6: primitive->mode = DRAW_TRIANGLE_FAN; break;
default: lovrThrow("Unknown primitive mode");
}
} else if (STR_EQ(key, "attributes")) {
int attributeCount = (token++)->size;
for (int a = 0; a < attributeCount; a++) {
for (int a = (token++)->size; a > 0; a--) {
DefaultAttribute attributeType = ~0;
gltfString name = NOM_STR(json, token);
uint32_t attributeIndex = NOM_INT(json, token);
@ -822,11 +863,46 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
primitive->attributes[attributeType] = &model->attributes[attributeIndex];
}
}
} else if (STR_EQ(key, "targets")) {
primitive->blendShapes = blendData;
primitive->blendShapeCount = token->size;
for (int t = (token++)->size; t > 0; t--, blendData++) {
for (int a = (token++)->size; a > 0; a--) {
gltfString name = NOM_STR(json, token);
ModelAttribute* attribute = &model->attributes[NOM_INT(json, token)];
if (STR_EQ(name, "POSITION")) { blendData->positions = attribute; }
else if (STR_EQ(name, "NORMAL")) { blendData->normals = attribute; }
else if (STR_EQ(name, "TANGENT")) { blendData->tangents = attribute; }
}
}
} else {
token += NOM_VALUE(json, token);
}
}
}
} else if (STR_EQ(key, "weights")) {
lovrAssert((uint32_t) token->size == mesh->blendShapeCount, "Inconsistent blend shape counts");
for (int w = (token++)->size, index = mesh->blendShapeIndex; w > 0; w--, index++) {
model->blendShapes[index].weight = NOM_FLOAT(json, token);
}
} else if (STR_EQ(key, "extras")) {
for (int k2 = (token++)->size; k2 > 0; k2--) {
gltfString key = NOM_STR(json, token);
if (STR_EQ(key, "targetNames")) {
lovrAssert((uint32_t) token->size == mesh->blendShapeCount, "Inconsistent blend shape counts");
for (int k3 = (token++)->size, index = mesh->blendShapeIndex; k3 > 0; k3--, index++) {
gltfString name = NOM_STR(json, token);
uint64_t hash = hash64(name.data, name.length);
if (map_get(&model->blendShapeMap, hash) == MAP_NIL) {
map_set(&model->blendShapeMap, hash, index);
}
memcpy(model->chars, name.data, name.length);
model->chars += name.length + 1;
}
} else {
token += NOM_VALUE(json, token);
}
}
} else {
token += NOM_VALUE(json, token);
}
@ -850,12 +926,20 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
node->primitiveCount = 0;
node->skin = ~0u;
jsmntok_t* weights = NULL;
for (int k = (token++)->size; k > 0; k--) {
gltfString key = NOM_STR(json, token);
if (STR_EQ(key, "mesh")) {
gltfMesh* mesh = &meshes[NOM_INT(json, token)];
node->primitiveIndex = mesh->primitiveIndex;
node->primitiveCount = mesh->primitiveCount;
node->blendShapeIndex = mesh->blendShapeIndex;
node->blendShapeCount = mesh->blendShapeCount;
for (uint32_t i = 0, index = node->blendShapeIndex; i < node->blendShapeCount; i++, index++) {
model->blendShapes[index].node = node - model->nodes;
}
} else if (STR_EQ(key, "weights")) {
weights = token; // Deferred due to order dependency
} else if (STR_EQ(key, "skin")) {
node->skin = NOM_INT(json, token);
} else if (STR_EQ(key, "children")) {
@ -896,6 +980,13 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
token += NOM_VALUE(json, token);
}
}
if (node->blendShapeCount > 0 && weights) {
lovrAssert((uint32_t) weights->size == node->blendShapeCount, "Inconsistent blend shape counts");
for (int w = (weights++)->size, index = node->blendShapeIndex; w > 0; w--, index++) {
model->blendShapes[index].weight = NOM_FLOAT(json, token);
}
}
}
}

View File

@ -5,7 +5,6 @@
#include "util.h"
#include <stdlib.h>
#include <float.h>
#include <ctype.h>
typedef struct {
uint32_t material;
@ -21,7 +20,7 @@ typedef arr_t(objGroup) arr_group_t;
static uint32_t nomu32(char* s, char** end) {
uint32_t n = 0;
while (isdigit(*s)) { n = 10 * n + (*s++ - '0'); }
while (*s >= '0' && *s <= '9') { n = 10 * n + (*s++ - '0'); }
*end = s;
return n;
}
@ -75,8 +74,12 @@ static void parseMtl(char* path, char* base, ModelDataIO* io, arr_image_t* image
ModelMaterial* material = &materials->data[materials->length - 1];
memcpy(material->color, (float[4]) { r, g, b, 1.f }, 16);
} else if (STARTS_WITH(line, "map_Kd ")) {
const char* subpath = line + 7;
lovrAssert(subpath[0] != '/', "Absolute paths in models are not supported");
if (subpath[0] && subpath[1] && !memcmp(subpath, "./", 2)) subpath += 2;
lovrAssert(base - path + (length - 7) < 1024, "Bad OBJ: Material image filename is too long");
memcpy(base, line + 7, length - 7);
memcpy(base, subpath, length - 7);
base[length - 7] = '\0';
size_t imageSize = 0;
@ -240,6 +243,8 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source, ModelDataIO* io)
} else if (STARTS_WITH(line, "mtllib ")) {
const char* filename = line + 7;
size_t filenameLength = strlen(filename);
lovrAssert(filename[0] != '/', "Absolute paths in models are not supported");
if (filenameLength > 2 && !memcmp(filename, "./", 2)) filename += 2;
lovrAssert(baseLength + filenameLength < sizeof(path), "Bad OBJ: Material filename is too long");
memcpy(path + baseLength, filename, filenameLength);
path[baseLength + filenameLength] = '\0';
@ -357,7 +362,7 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source, ModelDataIO* io)
for (size_t i = 0; i < groups.length; i++) {
objGroup* group = &groups.data[i];
model->primitives[i] = (ModelPrimitive) {
.mode = DRAW_TRIANGLES,
.mode = DRAW_TRIANGLE_LIST,
.attributes = {
[ATTR_POSITION] = &model->attributes[0],
[ATTR_NORMAL] = &model->attributes[1],

View File

@ -33,7 +33,7 @@ static ModelData* lovrModelDataInitStlBinary(ModelData* model, Blob* source, Mod
model->attributes[0] = (ModelAttribute) { .count = vertexCount, .components = 3, .type = F32, .offset = 0 * sizeof(float) };
model->attributes[1] = (ModelAttribute) { .count = vertexCount, .components = 3, .type = F32, .offset = 3 * sizeof(float) };
model->primitives[0] = (ModelPrimitive) {
.mode = DRAW_TRIANGLES,
.mode = DRAW_TRIANGLE_LIST,
.material = ~0u,
.attributes = {
[ATTR_POSITION] = &model->attributes[0],

View File

@ -233,7 +233,7 @@ static bool loadWAV(Sound* sound, Blob* blob, bool decode) {
lovrAssert(raw, "Out of memory");
if (pcm && wav->sampleSize == 24) {
float* out = raw;
const uint8_t* in = data;
const uint8_t* in = (const uint8_t*) data;
for (size_t i = 0, j = 0; i < samples; i++, j += 3) {
int32_t x = in[j + 2] & 0x80 ? 0xff : 0;
x = (x << 8) | in[j + 2];

View File

@ -1,6 +1,5 @@
#include "event/event.h"
#include "thread/thread.h"
#include "core/os.h"
#include "util.h"
#include <stdlib.h>
#include <string.h>
@ -15,17 +14,18 @@ void lovrVariantDestroy(Variant* variant) {
switch (variant->type) {
case TYPE_STRING: free(variant->value.string.pointer); return;
case TYPE_OBJECT: lovrRelease(variant->value.object.pointer, variant->value.object.destructor); return;
case TYPE_MATRIX: free(variant->value.matrix.data); return;
default: return;
}
}
bool lovrEventInit() {
bool lovrEventInit(void) {
if (state.initialized) return false;
arr_init(&state.events, arr_alloc);
return state.initialized = true;
}
void lovrEventDestroy() {
void lovrEventDestroy(void) {
if (!state.initialized) return;
for (size_t i = state.head; i < state.events.length; i++) {
Event* event = &state.events.data[i];
@ -45,10 +45,6 @@ void lovrEventDestroy() {
memset(&state, 0, sizeof(state));
}
void lovrEventPump() {
os_poll_events();
}
void lovrEventPush(Event event) {
#ifndef LOVR_DISABLE_THREAD
if (event.type == EVENT_THREAD_ERROR) {
@ -75,7 +71,7 @@ bool lovrEventPoll(Event* event) {
return true;
}
void lovrEventClear() {
void lovrEventClear(void) {
arr_clear(&state.events);
state.head = 0;
}

View File

@ -11,11 +11,17 @@ struct Thread;
typedef enum {
EVENT_QUIT,
EVENT_RESTART,
EVENT_VISIBLE,
EVENT_FOCUS,
EVENT_RECENTER,
EVENT_RESIZE,
EVENT_KEYPRESSED,
EVENT_KEYRELEASED,
EVENT_TEXTINPUT,
EVENT_MOUSEPRESSED,
EVENT_MOUSERELEASED,
EVENT_MOUSEMOVED,
EVENT_MOUSEWHEELMOVED,
#ifndef LOVR_DISABLE_THREAD
EVENT_THREAD_ERROR,
#endif
@ -29,12 +35,16 @@ typedef enum {
TYPE_NUMBER,
TYPE_STRING,
TYPE_MINISTRING,
TYPE_OBJECT
TYPE_POINTER,
TYPE_OBJECT,
TYPE_VECTOR,
TYPE_MATRIX
} VariantType;
typedef union {
bool boolean;
double number;
void* pointer;
struct {
char* pointer;
size_t length;
@ -48,6 +58,13 @@ typedef union {
const char* type;
void (*destructor)(void*);
} object;
struct {
int type;
float data[4];
} vector;
struct {
float* data;
} matrix;
} VariantValue;
typedef struct Variant {
@ -79,6 +96,19 @@ typedef struct {
uint32_t codepoint;
} TextEvent;
typedef struct {
double x;
double y;
double dx;
double dy;
int button;
} MouseEvent;
typedef struct {
double x;
double y;
} MouseWheelEvent;
typedef struct {
struct Thread* thread;
char* error;
@ -101,6 +131,8 @@ typedef union {
ResizeEvent resize;
KeyEvent key;
TextEvent text;
MouseEvent mouse;
MouseWheelEvent wheel;
ThreadEvent thread;
CustomEvent custom;
PermissionEvent permission;
@ -115,7 +147,6 @@ void lovrVariantDestroy(Variant* variant);
bool lovrEventInit(void);
void lovrEventDestroy(void);
void lovrEventPump(void);
void lovrEventPush(Event event);
bool lovrEventPoll(Event* event);
void lovrEventClear(void);

View File

@ -174,7 +174,7 @@ bool lovrFilesystemInit(const char* archive) {
return true;
}
void lovrFilesystemDestroy() {
void lovrFilesystemDestroy(void) {
if (!state.initialized) return;
for (size_t i = 0; i < state.archives.length; i++) {
Archive* archive = &state.archives.data[i];
@ -184,11 +184,11 @@ void lovrFilesystemDestroy() {
memset(&state, 0, sizeof(state));
}
const char* lovrFilesystemGetSource() {
const char* lovrFilesystemGetSource(void) {
return state.source;
}
bool lovrFilesystemIsFused() {
bool lovrFilesystemIsFused(void) {
return state.fused;
}
@ -309,7 +309,7 @@ void lovrFilesystemGetDirectoryItems(const char* path, void (*callback)(void* co
// Writing
const char* lovrFilesystemGetIdentity() {
const char* lovrFilesystemGetIdentity(void) {
return state.identity[0] == '\0' ? NULL : state.identity;
}
@ -361,7 +361,7 @@ bool lovrFilesystemSetIdentity(const char* identity, bool precedence) {
return true;
}
const char* lovrFilesystemGetSaveDirectory() {
const char* lovrFilesystemGetSaveDirectory(void) {
return state.savePath;
}
@ -428,7 +428,7 @@ size_t lovrFilesystemGetWorkingDirectory(char* buffer, size_t size) {
return os_get_working_directory(buffer, size);
}
const char* lovrFilesystemGetRequirePath() {
const char* lovrFilesystemGetRequirePath(void) {
return state.requirePath;
}

File diff suppressed because it is too large Load Diff

View File

@ -15,9 +15,9 @@ typedef struct Sampler Sampler;
typedef struct Shader Shader;
typedef struct Material Material;
typedef struct Font Font;
typedef struct Mesh Mesh;
typedef struct Model Model;
typedef struct Readback Readback;
typedef struct Tally Tally;
typedef struct Pass Pass;
typedef struct {
@ -43,8 +43,8 @@ typedef struct {
bool textureASTC;
bool wireframe;
bool depthClamp;
bool depthResolve;
bool indirectDrawFirstInstance;
bool shaderTally;
bool float64;
bool int64;
bool int16;
@ -101,12 +101,14 @@ bool lovrGraphicsIsInitialized(void);
void lovrGraphicsGetDevice(GraphicsDevice* device);
void lovrGraphicsGetFeatures(GraphicsFeatures* features);
void lovrGraphicsGetLimits(GraphicsLimits* limits);
bool lovrGraphicsIsFormatSupported(uint32_t format, uint32_t features);
uint32_t lovrGraphicsGetFormatSupport(uint32_t format, uint32_t features);
void lovrGraphicsGetShaderCache(void* data, size_t* size);
void lovrGraphicsGetBackgroundColor(float background[4]);
void lovrGraphicsSetBackgroundColor(float background[4]);
bool lovrGraphicsIsTimingEnabled(void);
void lovrGraphicsSetTimingEnabled(bool enable);
void lovrGraphicsSubmit(Pass** passes, uint32_t count);
void lovrGraphicsPresent(void);
void lovrGraphicsWait(void);
@ -114,72 +116,81 @@ void lovrGraphicsWait(void);
// Buffer
typedef enum {
FIELD_I8x4,
FIELD_U8x4,
FIELD_SN8x4,
FIELD_UN8x4,
FIELD_UN10x3,
FIELD_I16,
FIELD_I16x2,
FIELD_I16x4,
FIELD_U16,
FIELD_U16x2,
FIELD_U16x4,
FIELD_SN16x2,
FIELD_SN16x4,
FIELD_UN16x2,
FIELD_UN16x4,
FIELD_I32,
FIELD_I32x2,
FIELD_I32x3,
FIELD_I32x4,
FIELD_U32,
FIELD_U32x2,
FIELD_U32x3,
FIELD_U32x4,
FIELD_F16x2,
FIELD_F16x4,
FIELD_F32,
FIELD_F32x2,
FIELD_F32x3,
FIELD_F32x4,
FIELD_MAT2,
FIELD_MAT3,
FIELD_MAT4,
FIELD_INDEX16,
FIELD_INDEX32
} FieldType;
typedef struct {
uint32_t hash;
uint32_t location;
uint32_t type;
uint32_t offset;
} BufferField;
TYPE_I8x4,
TYPE_U8x4,
TYPE_SN8x4,
TYPE_UN8x4,
TYPE_UN10x3,
TYPE_I16,
TYPE_I16x2,
TYPE_I16x4,
TYPE_U16,
TYPE_U16x2,
TYPE_U16x4,
TYPE_SN16x2,
TYPE_SN16x4,
TYPE_UN16x2,
TYPE_UN16x4,
TYPE_I32,
TYPE_I32x2,
TYPE_I32x3,
TYPE_I32x4,
TYPE_U32,
TYPE_U32x2,
TYPE_U32x3,
TYPE_U32x4,
TYPE_F16x2,
TYPE_F16x4,
TYPE_F32,
TYPE_F32x2,
TYPE_F32x3,
TYPE_F32x4,
TYPE_MAT2,
TYPE_MAT3,
TYPE_MAT4,
TYPE_INDEX16,
TYPE_INDEX32,
TYPE_COUNT
} DataType;
typedef enum {
LAYOUT_PACKED,
LAYOUT_STD140,
LAYOUT_STD430
} BufferLayout;
} DataLayout;
typedef struct {
typedef struct DataField {
struct DataField* children;
uint32_t childCount;
uint32_t hash;
const char* name;
uint32_t location;
uint32_t offset;
uint32_t type;
uint32_t length;
uint32_t stride;
} DataField;
typedef struct {
uint32_t size;
const DataField* format;
uint32_t fieldCount;
BufferField fields[16];
DataLayout layout;
const char* label;
uintptr_t handle;
} BufferInfo;
Buffer* lovrGraphicsGetBuffer(BufferInfo* info, void** data);
Buffer* lovrBufferCreate(const BufferInfo* info, void** data);
void lovrBufferDestroy(void* ref);
const BufferInfo* lovrBufferGetInfo(Buffer* buffer);
void* lovrBufferGetData(Buffer* buffer, uint32_t offset, uint32_t extent);
void* lovrBufferSetData(Buffer* buffer, uint32_t offset, uint32_t extent);
void lovrBufferCopy(Buffer* src, Buffer* dst, uint32_t srcOffset, uint32_t dstOffset, uint32_t extent);
void lovrBufferClear(Buffer* buffer, uint32_t offset, uint32_t extent);
// Deprecated:
Buffer* lovrGraphicsGetBuffer(const BufferInfo* info, void** data);
bool lovrBufferIsTemporary(Buffer* buffer);
bool lovrBufferIsValid(Buffer* buffer);
void* lovrBufferMap(Buffer* buffer, uint32_t offset, uint32_t size);
void lovrBufferClear(Buffer* buffer, uint32_t offset, uint32_t size);
// Texture
@ -224,19 +235,25 @@ typedef struct {
const char* label;
} TextureInfo;
typedef enum {
FILTER_NEAREST,
FILTER_LINEAR
} FilterMode;
Texture* lovrGraphicsGetWindowTexture(void);
Texture* lovrTextureCreate(const TextureInfo* info);
Texture* lovrTextureCreateView(const TextureViewInfo* view);
void lovrTextureDestroy(void* ref);
const TextureInfo* lovrTextureGetInfo(Texture* texture);
struct Image* lovrTextureGetPixels(Texture* texture, uint32_t offset[4], uint32_t extent[3]);
void lovrTextureSetPixels(Texture* texture, struct Image* image, uint32_t texOffset[4], uint32_t imgOffset[4], uint32_t extent[3]);
void lovrTextureCopy(Texture* src, Texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t extent[3]);
void lovrTextureBlit(Texture* src, Texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t srcExtent[3], uint32_t dstExtent[3], FilterMode filter);
void lovrTextureClear(Texture* texture, float value[4], uint32_t layer, uint32_t layerCount, uint32_t level, uint32_t levelCount);
void lovrTextureGenerateMipmaps(Texture* texture, uint32_t base, uint32_t count);
// Sampler
typedef enum {
FILTER_NEAREST,
FILTER_LINEAR
} FilterMode;
typedef enum {
WRAP_CLAMP,
WRAP_REPEAT,
@ -274,10 +291,12 @@ typedef enum {
SHADER_FONT,
SHADER_CUBEMAP,
SHADER_EQUIRECT,
SHADER_FILL,
SHADER_FILL_2D,
SHADER_FILL_ARRAY,
SHADER_FILL_LAYER,
SHADER_LOGO,
SHADER_ANIMATOR,
SHADER_BLENDER,
SHADER_TALLY_MERGE,
DEFAULT_SHADER_COUNT
} DefaultShader;
@ -321,6 +340,7 @@ const ShaderInfo* lovrShaderGetInfo(Shader* shader);
bool lovrShaderHasStage(Shader* shader, ShaderStage stage);
bool lovrShaderHasAttribute(Shader* shader, const char* name, uint32_t location);
void lovrShaderGetWorkgroupSize(Shader* shader, uint32_t size[3]);
DataField* lovrShaderGetBufferFormat(Shader* shader, const char* name, size_t length, uint32_t slot, uint32_t* size, uint32_t* fieldCount);
// Material
@ -398,10 +418,52 @@ float lovrFontGetWidth(Font* font, ColoredString* strings, uint32_t count);
void lovrFontGetLines(Font* font, ColoredString* strings, uint32_t count, float wrap, void (*callback)(void* context, const char* string, size_t length), void* context);
void lovrFontGetVertices(Font* font, ColoredString* strings, uint32_t count, float wrap, HorizontalAlign halign, VerticalAlign valign, GlyphVertex* vertices, uint32_t* glyphCount, uint32_t* lineCount, Material** material, bool flip);
// Mesh
typedef enum {
MESH_CPU,
MESH_GPU
} MeshStorage;
typedef enum {
DRAW_POINTS,
DRAW_LINES,
DRAW_TRIANGLES
} DrawMode;
typedef struct {
uint32_t fieldCount;
DataField format[16];
Buffer* vertexBuffer;
MeshStorage storage;
} MeshInfo;
Mesh* lovrMeshCreate(const MeshInfo* info, void** data);
void lovrMeshDestroy(void* ref);
const DataField* lovrMeshGetVertexFormat(Mesh* mesh);
Buffer* lovrMeshGetVertexBuffer(Mesh* mesh);
Buffer* lovrMeshGetIndexBuffer(Mesh* mesh);
void lovrMeshSetIndexBuffer(Mesh* mesh, Buffer* buffer);
void* lovrMeshGetVertices(Mesh* mesh, uint32_t index, uint32_t count);
void* lovrMeshSetVertices(Mesh* mesh, uint32_t index, uint32_t count);
void* lovrMeshGetIndices(Mesh* mesh, DataField* format);
void* lovrMeshSetIndices(Mesh* mesh, uint32_t count, DataType type);
void lovrMeshGetTriangles(Mesh* mesh, float** vertices, uint32_t** indices, uint32_t* vertexCount, uint32_t* indexCount);
bool lovrMeshGetBoundingBox(Mesh* mesh, float box[6]);
void lovrMeshSetBoundingBox(Mesh* mesh, float box[6]);
bool lovrMeshComputeBoundingBox(Mesh* mesh);
DrawMode lovrMeshGetDrawMode(Mesh* mesh);
void lovrMeshSetDrawMode(Mesh* mesh, DrawMode mode);
void lovrMeshGetDrawRange(Mesh* mesh, uint32_t* start, uint32_t* count, uint32_t* offset);
void lovrMeshSetDrawRange(Mesh* mesh, uint32_t start, uint32_t count, uint32_t offset);
Material* lovrMeshGetMaterial(Mesh* mesh);
void lovrMeshSetMaterial(Mesh* mesh, Material* material);
// Model
typedef struct {
struct ModelData* data;
bool materials;
bool mipmaps;
} ModelInfo;
@ -410,98 +472,50 @@ typedef enum {
ORIGIN_PARENT
} OriginType;
typedef enum {
MESH_POINTS,
MESH_LINES,
MESH_TRIANGLES
} MeshMode;
typedef struct {
MeshMode mode;
Material* material;
uint32_t start;
uint32_t count;
uint32_t base;
bool indexed;
} ModelDraw;
Model* lovrModelCreate(const ModelInfo* info);
Model* lovrModelClone(Model* model);
void lovrModelDestroy(void* ref);
const ModelInfo* lovrModelGetInfo(Model* model);
uint32_t lovrModelGetNodeDrawCount(Model* model, uint32_t node);
void lovrModelGetNodeDraw(Model* model, uint32_t node, uint32_t index, ModelDraw* draw);
void lovrModelResetNodeTransforms(Model* model);
void lovrModelAnimate(Model* model, uint32_t animationIndex, float time, float alpha);
void lovrModelGetNodeTransform(Model* model, uint32_t node, float position[4], float scale[4], float rotation[4], OriginType origin);
void lovrModelSetNodeTransform(Model* model, uint32_t node, float position[4], float scale[4], float rotation[4], float alpha);
Texture* lovrModelGetTexture(Model* model, uint32_t index);
Material* lovrModelGetMaterial(Model* model, uint32_t index);
float lovrModelGetBlendShapeWeight(Model* model, uint32_t index);
void lovrModelSetBlendShapeWeight(Model* model, uint32_t index, float weight);
void lovrModelGetNodeTransform(Model* model, uint32_t node, float position[3], float scale[3], float rotation[4], OriginType origin);
void lovrModelSetNodeTransform(Model* model, uint32_t node, float position[3], float scale[3], float rotation[4], float alpha);
Buffer* lovrModelGetVertexBuffer(Model* model);
Buffer* lovrModelGetIndexBuffer(Model* model);
Mesh* lovrModelGetMesh(Model* model, uint32_t index);
Texture* lovrModelGetTexture(Model* model, uint32_t index);
Material* lovrModelGetMaterial(Model* model, uint32_t index);
// Readback
typedef enum {
READBACK_BUFFER,
READBACK_TEXTURE,
READBACK_TALLY
} ReadbackType;
typedef struct {
ReadbackType type;
union {
struct {
Buffer* object;
uint32_t offset;
uint32_t extent;
} buffer;
struct {
Texture* object;
uint32_t offset[4];
uint32_t extent[2];
} texture;
struct {
Tally* object;
uint32_t index;
uint32_t count;
} tally;
};
} ReadbackInfo;
Readback* lovrReadbackCreate(const ReadbackInfo* info);
Readback* lovrReadbackCreateBuffer(Buffer* buffer, uint32_t offset, uint32_t extent);
Readback* lovrReadbackCreateTexture(Texture* texture, uint32_t offset[4], uint32_t extent[2]);
void lovrReadbackDestroy(void* ref);
const ReadbackInfo* lovrReadbackGetInfo(Readback* readback);
bool lovrReadbackIsComplete(Readback* readback);
bool lovrReadbackWait(Readback* readback);
void* lovrReadbackGetData(Readback* readback);
void* lovrReadbackGetData(Readback* readback, DataField* format);
struct Blob* lovrReadbackGetBlob(Readback* readback);
struct Image* lovrReadbackGetImage(Readback* readback);
// Tally
typedef enum {
TALLY_TIME,
TALLY_SHADER,
TALLY_PIXEL
} TallyType;
typedef struct {
TallyType type;
uint32_t count;
uint32_t views;
} TallyInfo;
Tally* lovrTallyCreate(const TallyInfo* info);
void lovrTallyDestroy(void* ref);
const TallyInfo* lovrTallyGetInfo(Tally* tally);
// Pass
typedef struct {
uint32_t draws;
uint32_t computes;
uint32_t drawsCulled;
size_t memoryReserved;
size_t memoryUsed;
double submitTime;
double gpuTime;
} PassStats;
typedef enum {
PASS_RENDER,
PASS_COMPUTE,
PASS_TRANSFER
} PassType;
LOAD_CLEAR,
LOAD_DISCARD,
LOAD_KEEP
} LoadAction;
typedef enum {
STACK_TRANSFORM,
@ -551,50 +565,31 @@ typedef enum {
WINDING_CLOCKWISE
} Winding;
typedef enum {
LOAD_CLEAR,
LOAD_DISCARD,
LOAD_KEEP
} LoadAction;
typedef struct {
Texture* texture;
uint32_t format;
LoadAction load;
float clear;
} DepthInfo;
typedef struct {
uint32_t count;
Texture* textures[4];
LoadAction loads[4];
float clears[4][4];
DepthInfo depth;
uint32_t samples;
bool mipmap;
} Canvas;
typedef struct {
PassType type;
Canvas canvas;
const char* label;
} PassInfo;
Pass* lovrGraphicsGetWindowPass(void);
Pass* lovrGraphicsGetPass(PassInfo* info);
Pass* lovrGraphicsGetPass(void); // Deprecated
Pass* lovrPassCreate(void);
void lovrPassDestroy(void* ref);
const PassInfo* lovrPassGetInfo(Pass* pass);
void lovrPassReset(Pass* pass);
void lovrPassAppend(Pass* pass, Pass* other);
const PassStats* lovrPassGetStats(Pass* pass);
void lovrPassGetCanvas(Pass* pass, Texture* color[4], Texture** depthTexture, uint32_t* depthFormat, uint32_t* samples);
void lovrPassSetCanvas(Pass* pass, Texture* color[4], Texture* depthTexture, uint32_t depthFormat, uint32_t samples);
void lovrPassGetClear(Pass* pass, LoadAction loads[4], float clears[4][4], LoadAction* depthLoad, float* depthClear);
void lovrPassSetClear(Pass* pass, LoadAction loads[4], float clears[4][4], LoadAction depthLoad, float depthClear);
uint32_t lovrPassGetAttachmentCount(Pass* pass, bool* depth);
uint32_t lovrPassGetWidth(Pass* pass);
uint32_t lovrPassGetHeight(Pass* pass);
uint32_t lovrPassGetViewCount(Pass* pass);
uint32_t lovrPassGetSampleCount(Pass* pass);
void lovrPassGetTarget(Pass* pass, Texture* color[4], Texture** depth, uint32_t* count);
void lovrPassGetClear(Pass* pass, float color[4][4], float* depth, uint8_t* stencil, uint32_t* count);
void lovrPassGetViewMatrix(Pass* pass, uint32_t index, float viewMatrix[16]);
void lovrPassSetViewMatrix(Pass* pass, uint32_t index, float viewMatrix[16]);
void lovrPassGetProjection(Pass* pass, uint32_t index, float projection[16]);
void lovrPassSetProjection(Pass* pass, uint32_t index, float projection[16]);
void lovrPassGetViewport(Pass* pass, float viewport[6]);
void lovrPassSetViewport(Pass* pass, float viewport[6]);
void lovrPassGetScissor(Pass* pass, uint32_t scissor[4]);
void lovrPassSetScissor(Pass* pass, uint32_t scissor[4]);
void lovrPassPush(Pass* pass, StackType stack);
void lovrPassPop(Pass* pass, StackType stack);
@ -605,34 +600,34 @@ void lovrPassScale(Pass* pass, float* scale);
void lovrPassTransform(Pass* pass, float* transform);
void lovrPassSetAlphaToCoverage(Pass* pass, bool enabled);
void lovrPassSetBlendMode(Pass* pass, BlendMode mode, BlendAlphaMode alphaMode);
void lovrPassSetBlendMode(Pass* pass, uint32_t index, BlendMode mode, BlendAlphaMode alphaMode);
void lovrPassSetColor(Pass* pass, float color[4]);
void lovrPassSetColorWrite(Pass* pass, bool r, bool g, bool b, bool a);
void lovrPassSetCullMode(Pass* pass, CullMode mode);
void lovrPassSetColorWrite(Pass* pass, uint32_t index, bool r, bool g, bool b, bool a);
void lovrPassSetDepthTest(Pass* pass, CompareMode test);
void lovrPassSetDepthWrite(Pass* pass, bool write);
void lovrPassSetDepthOffset(Pass* pass, float offset, float sloped);
void lovrPassSetDepthClamp(Pass* pass, bool clamp);
void lovrPassSetFaceCull(Pass* pass, CullMode mode);
void lovrPassSetFont(Pass* pass, Font* font);
void lovrPassSetMaterial(Pass* pass, Material* material, Texture* texture);
void lovrPassSetMeshMode(Pass* pass, MeshMode mode);
void lovrPassSetMeshMode(Pass* pass, DrawMode mode);
void lovrPassSetSampler(Pass* pass, Sampler* sampler);
void lovrPassSetScissor(Pass* pass, uint32_t scissor[4]);
void lovrPassSetShader(Pass* pass, Shader* shader);
void lovrPassSetStencilTest(Pass* pass, CompareMode test, uint8_t value, uint8_t mask);
void lovrPassSetStencilWrite(Pass* pass, StencilAction actions[3], uint8_t value, uint8_t mask);
void lovrPassSetViewport(Pass* pass, float viewport[4], float depthRange[2]);
void lovrPassSetViewCull(Pass* pass, bool enable);
void lovrPassSetWinding(Pass* pass, Winding winding);
void lovrPassSetWireframe(Pass* pass, bool wireframe);
void lovrPassSendBuffer(Pass* pass, const char* name, size_t length, uint32_t slot, Buffer* buffer, uint32_t offset, uint32_t extent);
void lovrPassSendTexture(Pass* pass, const char* name, size_t length, uint32_t slot, Texture* texture);
void lovrPassSendSampler(Pass* pass, const char* name, size_t length, uint32_t slot, Sampler* sampler);
void lovrPassSendValue(Pass* pass, const char* name, size_t length, void** data, FieldType* type);
void lovrPassSendData(Pass* pass, const char* name, size_t length, uint32_t slot, void** data, DataField** field);
void lovrPassPoints(Pass* pass, uint32_t count, float** vertices);
void lovrPassLine(Pass* pass, uint32_t count, float** vertices);
void lovrPassPlane(Pass* pass, float* transform, DrawStyle style, uint32_t cols, uint32_t rows);
void lovrPassRoundrect(Pass* pass, float* transform, float radius, uint32_t segments);
void lovrPassBox(Pass* pass, float* transform, DrawStyle style);
void lovrPassCircle(Pass* pass, float* transform, DrawStyle style, float angle1, float angle2, uint32_t segments);
void lovrPassSphere(Pass* pass, float* transform, uint32_t segmentsH, uint32_t segmentsV);
@ -644,24 +639,17 @@ void lovrPassText(Pass* pass, ColoredString* strings, uint32_t count, float* tra
void lovrPassSkybox(Pass* pass, Texture* texture);
void lovrPassFill(Pass* pass, Texture* texture);
void lovrPassMonkey(Pass* pass, float* transform);
void lovrPassDrawModel(Pass* pass, Model* model, float* transform, uint32_t node, bool recurse, uint32_t instances);
void lovrPassDrawModel(Pass* pass, Model* model, float* transform, uint32_t instances);
void lovrPassDrawMesh(Pass* pass, Mesh* mesh, float* transform, uint32_t instances);
void lovrPassDrawTexture(Pass* pass, Texture* texture, float* transform);
void lovrPassMesh(Pass* pass, Buffer* vertices, Buffer* indices, float* transform, uint32_t start, uint32_t count, uint32_t instances, uint32_t base);
void lovrPassMeshIndirect(Pass* pass, Buffer* vertices, Buffer* indices, Buffer* indirect, uint32_t count, uint32_t offset, uint32_t stride);
uint32_t lovrPassBeginTally(Pass* pass);
uint32_t lovrPassFinishTally(Pass* pass);
Buffer* lovrPassGetTallyBuffer(Pass* pass, uint32_t* offset);
void lovrPassSetTallyBuffer(Pass* pass, Buffer* buffer, uint32_t offset);
const uint32_t* lovrPassGetTallyData(Pass* pass, uint32_t* count);
void lovrPassCompute(Pass* pass, uint32_t x, uint32_t y, uint32_t z, Buffer* indirect, uint32_t offset);
void lovrPassClearBuffer(Pass* pass, Buffer* buffer, uint32_t offset, uint32_t extent);
void lovrPassClearTexture(Pass* pass, Texture* texture, float value[4], uint32_t layer, uint32_t layerCount, uint32_t level, uint32_t levelCount);
void* lovrPassCopyDataToBuffer(Pass* pass, Buffer* buffer, uint32_t offset, uint32_t extent);
void lovrPassCopyBufferToBuffer(Pass* pass, Buffer* src, Buffer* dst, uint32_t srcOffset, uint32_t dstOffset, uint32_t extent);
void lovrPassCopyTallyToBuffer(Pass* pass, Tally* src, Buffer* dst, uint32_t srcIndex, uint32_t dstOffset, uint32_t count);
void lovrPassCopyImageToTexture(Pass* pass, struct Image* src, Texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t extent[3]);
void lovrPassCopyTextureToTexture(Pass* pass, Texture* src, Texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t extent[3]);
void lovrPassBlit(Pass* pass, Texture* src, Texture* dst, uint32_t srcOffset[4], uint32_t dstOffset[4], uint32_t srcExtent[3], uint32_t dstExtent[3], FilterMode filter);
void lovrPassMipmap(Pass* pass, Texture* texture, uint32_t base, uint32_t count);
Readback* lovrPassReadBuffer(Pass* pass, Buffer* buffer, uint32_t index, uint32_t count);
Readback* lovrPassReadTexture(Pass* pass, Texture* texture, uint32_t offset[4], uint32_t extent[3]);
Readback* lovrPassReadTally(Pass* pass, Tally* tally, uint32_t index, uint32_t count);
void lovrPassTick(Pass* pass, Tally* tally, uint32_t index);
void lovrPassTock(Pass* pass, Tally* tally, uint32_t index);
void lovrPassBarrier(Pass* pass);

View File

@ -12,8 +12,8 @@ bool lovrHeadsetInit(HeadsetConfig* config) {
HeadsetInterface* interface = NULL;
switch (config->drivers[i]) {
#ifdef LOVR_USE_DESKTOP
case DRIVER_DESKTOP: interface = &lovrHeadsetDesktopDriver; break;
#ifdef LOVR_USE_SIMULATOR
case DRIVER_SIMULATOR: interface = &lovrHeadsetSimulatorDriver; break;
#endif
#ifdef LOVR_USE_OPENXR
case DRIVER_OPENXR: interface = &lovrHeadsetOpenXRDriver; break;
@ -34,7 +34,7 @@ bool lovrHeadsetInit(HeadsetConfig* config) {
return true;
}
void lovrHeadsetDestroy() {
void lovrHeadsetDestroy(void) {
if (!initialized) return;
initialized = false;
if (lovrHeadsetInterface) {

View File

@ -12,7 +12,7 @@ struct Texture;
struct Pass;
typedef enum {
DRIVER_DESKTOP,
DRIVER_SIMULATOR,
DRIVER_OPENXR,
DRIVER_WEBXR
} HeadsetDriver;
@ -21,7 +21,7 @@ typedef struct {
HeadsetDriver* drivers;
size_t driverCount;
float supersample;
float offset;
bool seated;
bool stencil;
bool antialias;
bool submitDepth;
@ -29,16 +29,26 @@ typedef struct {
} HeadsetConfig;
typedef enum {
ORIGIN_HEAD,
ORIGIN_FLOOR
} HeadsetOrigin;
PASSTHROUGH_OPAQUE,
PASSTHROUGH_BLEND,
PASSTHROUGH_ADD,
PASSTHROUGH_DEFAULT = -1,
PASSTHROUGH_TRANSPARENT = -2
} PassthroughMode;
typedef enum {
DEVICE_HEAD,
DEVICE_FLOOR,
DEVICE_HAND_LEFT,
DEVICE_HAND_RIGHT,
DEVICE_HAND_LEFT_GRIP,
DEVICE_HAND_RIGHT_GRIP,
DEVICE_HAND_LEFT_POINT,
DEVICE_HAND_RIGHT_POINT,
DEVICE_HAND_LEFT_PINCH,
DEVICE_HAND_RIGHT_PINCH,
DEVICE_HAND_LEFT_POKE,
DEVICE_HAND_RIGHT_POKE,
DEVICE_ELBOW_LEFT,
DEVICE_ELBOW_RIGHT,
DEVICE_SHOULDER_LEFT,
@ -113,7 +123,7 @@ typedef enum {
// - init is called immediately, the graphics module may not exist yet
// - start is called after the graphics module is initialized, can be used to set up textures etc.
// - graphics module currently calls stop when it's destroyed, which is hacky and should be improved
// - getDisplayFrequency may return 0.f if the information is unavailable.
// - getRefreshRate may return 0.f if the information is unavailable.
// - For isDown, changed can be set to false if change information is unavailable or inconvenient.
// - getAxis may write 4 floats to the output value. The expected number is a constant (see axisCounts in l_headset).
// - In general, most input results should be kept constant between calls to update.
@ -127,12 +137,16 @@ typedef struct HeadsetInterface {
void (*start)(void);
void (*stop)(void);
void (*destroy)(void);
bool (*getDriverName)(char* name, size_t length);
bool (*getName)(char* name, size_t length);
HeadsetOrigin (*getOriginType)(void);
bool (*isSeated)(void);
void (*getDisplayDimensions)(uint32_t* width, uint32_t* height);
float (*getDisplayFrequency)(void);
float* (*getDisplayFrequencies)(uint32_t* count);
bool (*setDisplayFrequency)(float);
float (*getRefreshRate)(void);
bool (*setRefreshRate)(float refreshRate);
const float* (*getRefreshRates)(uint32_t* count);
PassthroughMode (*getPassthrough)(void);
bool (*setPassthrough)(PassthroughMode mode);
bool (*isPassthroughSupported)(PassthroughMode mode);
double (*getDisplayTime)(void);
double (*getDeltaTime)(void);
uint32_t (*getViewCount)(void);
@ -149,19 +163,21 @@ typedef struct HeadsetInterface {
bool (*getAxis)(Device device, DeviceAxis axis, float* value);
bool (*getSkeleton)(Device device, float* poses);
bool (*vibrate)(Device device, float strength, float duration, float frequency);
void (*stopVibration)(Device device);
struct ModelData* (*newModelData)(Device device, bool animated);
bool (*animate)(Device device, struct Model* model);
bool (*animate)(struct Model* model);
struct Texture* (*getTexture)(void);
struct Pass* (*getPass)(void);
void (*submit)(void);
bool (*isVisible)(void);
bool (*isFocused)(void);
double (*update)(void);
} HeadsetInterface;
// Available drivers
extern HeadsetInterface lovrHeadsetSimulatorDriver;
extern HeadsetInterface lovrHeadsetOpenXRDriver;
extern HeadsetInterface lovrHeadsetWebXRDriver;
extern HeadsetInterface lovrHeadsetDesktopDriver;
// Active driver
extern HeadsetInterface* lovrHeadsetInterface;

View File

@ -1,352 +0,0 @@
#include "headset/headset.h"
#include "data/modelData.h"
#include "event/event.h"
#include "graphics/graphics.h"
#include "core/maf.h"
#include "core/os.h"
#include "util.h"
#include <math.h>
#include <stdint.h>
#include <stdbool.h>
static struct {
bool initialized;
float position[4];
float velocity[4];
float localVelocity[4];
float angularVelocity[4];
float headTransform[16];
float leftHandTransform[16];
double epoch;
double prevDisplayTime;
double nextDisplayTime;
double prevCursorX;
double prevCursorY;
bool mouseDown;
bool prevMouseDown;
bool focused;
float offset;
float clipNear;
float clipFar;
float pitch;
float yaw;
} state;
static void onFocus(bool focused) {
state.focused = focused;
lovrEventPush((Event) { .type = EVENT_FOCUS, .data.boolean = { focused } });
}
static bool desktop_init(HeadsetConfig* config) {
state.offset = config->offset;
state.clipNear = .01f;
state.clipFar = 0.f;
state.epoch = os_get_time();
state.prevDisplayTime = state.epoch;
state.nextDisplayTime = state.epoch;
if (!state.initialized) {
mat4_identity(state.headTransform);
mat4_identity(state.leftHandTransform);
state.initialized = true;
}
state.focused = true;
os_on_focus(onFocus);
return true;
}
static void desktop_start(void) {
//
}
static void desktop_destroy(void) {
//
}
static bool desktop_getName(char* name, size_t length) {
strncpy(name, "Simulator", length - 1);
name[length - 1] = '\0';
return true;
}
static HeadsetOrigin desktop_getOriginType(void) {
return ORIGIN_HEAD;
}
static double desktop_getDisplayTime(void) {
return state.nextDisplayTime - state.epoch;
}
static double desktop_getDeltaTime(void) {
return state.nextDisplayTime - state.prevDisplayTime;
}
static void desktop_getDisplayDimensions(uint32_t* width, uint32_t* height) {
os_window_get_size(width, height);
}
static uint32_t desktop_getViewCount(void) {
return 1;
}
static bool desktop_getViewPose(uint32_t view, float* position, float* orientation) {
vec3_init(position, state.position);
quat_fromMat4(orientation, state.headTransform);
position[1] += state.offset;
return view == 0;
}
static bool desktop_getViewAngles(uint32_t view, float* left, float* right, float* up, float* down) {
float aspect, fov;
uint32_t width, height;
desktop_getDisplayDimensions(&width, &height);
aspect = (float) width / height;
fov = .7f;
*left = atanf(tanf(fov) * aspect);
*right = atanf(tanf(fov) * aspect);
*up = fov;
*down = fov;
return view == 0;
}
static void desktop_getClipDistance(float* clipNear, float* clipFar) {
*clipNear = state.clipNear;
*clipFar = state.clipFar;
}
static void desktop_setClipDistance(float clipNear, float clipFar) {
state.clipNear = clipNear;
state.clipFar = clipFar;
}
static void desktop_getBoundsDimensions(float* width, float* depth) {
*width = *depth = 0.f;
}
static const float* desktop_getBoundsGeometry(uint32_t* count) {
*count = 0;
return NULL;
}
static bool desktop_getPose(Device device, vec3 position, quat orientation) {
if (device == DEVICE_HEAD) {
vec3_set(position, 0.f, 0.f, 0.f);
mat4_transform(state.headTransform, position);
quat_fromMat4(orientation, state.headTransform);
return true;
} else if (device == DEVICE_HAND_LEFT || device == DEVICE_HAND_LEFT_POINT) {
mat4_getPosition(state.leftHandTransform, position);
quat_fromMat4(orientation, state.leftHandTransform);
return true;
}
return false;
}
static bool desktop_getVelocity(Device device, vec3 velocity, vec3 angularVelocity) {
if (device != DEVICE_HEAD) {
return false;
}
vec3_init(velocity, state.velocity);
vec3_init(angularVelocity, state.angularVelocity);
return true;
}
static bool desktop_isDown(Device device, DeviceButton button, bool* down, bool* changed) {
if (device != DEVICE_HAND_LEFT || button != BUTTON_TRIGGER) {
return false;
}
*down = state.mouseDown;
*changed = state.mouseDown != state.prevMouseDown;
return true;
}
static bool desktop_isTouched(Device device, DeviceButton button, bool* touched) {
return false;
}
static bool desktop_getAxis(Device device, DeviceAxis axis, vec3 value) {
return false;
}
static bool desktop_getSkeleton(Device device, float* poses) {
return false;
}
static bool desktop_vibrate(Device device, float strength, float duration, float frequency) {
return false;
}
static ModelData* desktop_newModelData(Device device, bool animated) {
return NULL;
}
static bool desktop_animate(Device device, struct Model* model) {
return false;
}
static Texture* desktop_getTexture(void) {
return NULL;
}
static Pass* desktop_getPass(void) {
Pass* pass = lovrGraphicsGetWindowPass();
if (!pass) {
return pass;
}
float position[4], orientation[4];
desktop_getViewPose(0, position, orientation);
float viewMatrix[16];
mat4_fromQuat(viewMatrix, orientation);
memcpy(viewMatrix + 12, position, 3 * sizeof(float));
mat4_invert(viewMatrix);
float projection[16];
float left, right, up, down;
desktop_getViewAngles(0, &left, &right, &up, &down);
mat4_fov(projection, left, right, up, down, state.clipNear, state.clipFar);
lovrPassSetViewMatrix(pass, 0, viewMatrix);
lovrPassSetProjection(pass, 0, projection);
return pass;
}
static void desktop_submit(void) {
//
}
static bool desktop_isFocused(void) {
return state.focused;
}
static double desktop_update(void) {
bool front = os_is_key_down(KEY_W) || os_is_key_down(KEY_UP);
bool back = os_is_key_down(KEY_S) || os_is_key_down(KEY_DOWN);
bool left = os_is_key_down(KEY_A) || os_is_key_down(KEY_LEFT);
bool right = os_is_key_down(KEY_D) || os_is_key_down(KEY_RIGHT);
bool up = os_is_key_down(KEY_Q);
bool down = os_is_key_down(KEY_E);
state.prevDisplayTime = state.nextDisplayTime;
state.nextDisplayTime = os_get_time();
double dt = state.nextDisplayTime - state.prevDisplayTime;
float movespeed = 3.f * (float) dt;
float turnspeed = 3.f * (float) dt;
float damping = MAX(1.f - 20.f * (float) dt, 0);
double mx, my;
uint32_t width, height;
os_get_mouse_position(&mx, &my);
os_window_get_size(&width, &height);
double aspect = (width > 0 && height > 0) ? ((double) width / height) : 1.;
// Mouse move
if (os_is_mouse_down(MOUSE_LEFT)) {
os_set_mouse_mode(MOUSE_MODE_GRABBED);
if (state.prevCursorX == -1 && state.prevCursorY == -1) {
state.prevCursorX = mx;
state.prevCursorY = my;
}
float dx = (float) (mx - state.prevCursorX) / ((float) width);
float dy = (float) (my - state.prevCursorY) / ((float) height * aspect);
state.angularVelocity[0] = dy / (float) dt;
state.angularVelocity[1] = dx / (float) dt;
state.prevCursorX = mx;
state.prevCursorY = my;
} else {
os_set_mouse_mode(MOUSE_MODE_NORMAL);
vec3_scale(state.angularVelocity, damping);
state.prevCursorX = state.prevCursorY = -1;
}
state.prevMouseDown = state.mouseDown;
state.mouseDown = os_is_mouse_down(MOUSE_RIGHT);
// Update velocity
state.localVelocity[0] = left ? -movespeed : (right ? movespeed : state.localVelocity[0]);
state.localVelocity[1] = up ? movespeed : (down ? -movespeed : state.localVelocity[1]);
state.localVelocity[2] = front ? -movespeed : (back ? movespeed : state.localVelocity[2]);
state.localVelocity[3] = 0.f;
vec3_init(state.velocity, state.localVelocity);
mat4_transformDirection(state.headTransform, state.velocity);
vec3_scale(state.localVelocity, damping);
// Update position
vec3_add(state.position, state.velocity);
// Update orientation
state.pitch = CLAMP(state.pitch - state.angularVelocity[0] * turnspeed, -(float) M_PI / 2.f, (float) M_PI / 2.f);
state.yaw -= state.angularVelocity[1] * turnspeed;
// Update head transform
mat4_identity(state.headTransform);
mat4_translate(state.headTransform, 0.f, state.offset, 0.f);
mat4_translate(state.headTransform, state.position[0], state.position[1], state.position[2]);
mat4_rotate(state.headTransform, state.yaw, 0.f, 1.f, 0.f);
mat4_rotate(state.headTransform, state.pitch, 1.f, 0.f, 0.f);
// Update hand transform to follow cursor
double px = mx, py = my;
if (width > 0 && height > 0) {
// change coordinate system to -1.0 to 1.0
px = (px / width) * 2 - 1.0;
py = (py / height) * 2 - 1.0;
px += .2; // neutral position = pointing towards center-ish
px *= .6; // fudged range to juuust cover pointing at the whole scene, but not outside it
}
mat4_set(state.leftHandTransform, state.headTransform);
double xrange = M_PI * .2;
double yrange = xrange / aspect;
mat4_translate(state.leftHandTransform, -.1f, -.1f, -0.10f);
mat4_rotate(state.leftHandTransform, -px * xrange, 0, 1, 0);
mat4_rotate(state.leftHandTransform, -py * yrange, 1, 0, 0);
mat4_translate(state.leftHandTransform, 0, 0, -.20f);
mat4_rotate(state.leftHandTransform, -px * xrange, 0, 1, 0);
mat4_rotate(state.leftHandTransform, -py * yrange, 1, 0, 0);
return dt;
}
HeadsetInterface lovrHeadsetDesktopDriver = {
.driverType = DRIVER_DESKTOP,
.init = desktop_init,
.start = desktop_start,
.destroy = desktop_destroy,
.getName = desktop_getName,
.getOriginType = desktop_getOriginType,
.getDisplayTime = desktop_getDisplayTime,
.getDeltaTime = desktop_getDeltaTime,
.getDisplayDimensions = desktop_getDisplayDimensions,
.getViewCount = desktop_getViewCount,
.getViewPose = desktop_getViewPose,
.getViewAngles = desktop_getViewAngles,
.getClipDistance = desktop_getClipDistance,
.setClipDistance = desktop_setClipDistance,
.getBoundsDimensions = desktop_getBoundsDimensions,
.getBoundsGeometry = desktop_getBoundsGeometry,
.getPose = desktop_getPose,
.getVelocity = desktop_getVelocity,
.isDown = desktop_isDown,
.isTouched = desktop_isTouched,
.getAxis = desktop_getAxis,
.getSkeleton = desktop_getSkeleton,
.vibrate = desktop_vibrate,
.newModelData = desktop_newModelData,
.animate = desktop_animate,
.getTexture = desktop_getTexture,
.getPass = desktop_getPass,
.submit = desktop_submit,
.isFocused = desktop_isFocused,
.update = desktop_update
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,432 @@
#include "headset/headset.h"
#include "data/image.h"
#include "event/event.h"
#include "graphics/graphics.h"
#include "system/system.h"
#include "core/maf.h"
#include "core/os.h"
#include "util.h"
#include <math.h>
#include <stdint.h>
#include <stdbool.h>
#define MOVESPEED 3.f
#define SPRINTSPEED 15.f
#define MOVESMOOTH 30.f
#define TURNSPEED .005f
#define TURNSMOOTH 30.f
#define OFFSET (state.config.seated ? 0.f : 1.7f)
static struct {
bool initialized;
HeadsetConfig config;
TextureFormat depthFormat;
Texture* texture;
Pass* pass;
float pitch;
float yaw;
float distance;
float velocity[3];
float headPosition[3];
float headOrientation[4];
float handPosition[3];
float handOrientation[4];
double epoch;
double time;
double dt;
double mx;
double my;
bool triggerDown;
bool triggerChanged;
bool mouseDown;
bool focused;
float clipNear;
float clipFar;
} state;
static void onFocus(bool focused) {
state.focused = focused;
lovrEventPush((Event) { .type = EVENT_FOCUS, .data.boolean = { focused } });
}
static bool simulator_init(HeadsetConfig* config) {
state.config = *config;
state.epoch = os_get_time();
state.clipNear = .01f;
state.clipFar = 0.f;
state.distance = .5f;
if (!state.initialized) {
vec3_set(state.headPosition, 0.f, 0.f, 0.f);
vec3_set(state.handPosition, 0.f, 0.f, 0.f);
quat_identity(state.headOrientation);
quat_identity(state.handOrientation);
state.initialized = true;
}
state.focused = true;
os_on_focus(onFocus);
return true;
}
static void simulator_start(void) {
#ifdef LOVR_DISABLE_GRAPHICS
bool hasGraphics = false;
#else
bool hasGraphics = lovrGraphicsIsInitialized();
#endif
if (hasGraphics) {
state.pass = lovrPassCreate();
state.depthFormat = state.config.stencil ? FORMAT_D32FS8 : FORMAT_D32F;
if (state.config.stencil && !lovrGraphicsGetFormatSupport(state.depthFormat, TEXTURE_FEATURE_RENDER)) {
state.depthFormat = FORMAT_D24S8; // Guaranteed to be supported if the other one isn't
}
}
}
static void simulator_stop(void) {
lovrRelease(state.texture, lovrTextureDestroy);
lovrRelease(state.pass, lovrPassDestroy);
state.texture = NULL;
state.pass = NULL;
}
static void simulator_destroy(void) {
simulator_stop();
}
static bool simulator_getDriverName(char* name, size_t length) {
strncpy(name, "LÖVR", length - 1);
name[length - 1] = '\0';
return true;
}
static bool simulator_getName(char* name, size_t length) {
strncpy(name, "Simulator", length - 1);
name[length - 1] = '\0';
return true;
}
static bool simulator_isSeated(void) {
return state.config.seated;
}
static void simulator_getDisplayDimensions(uint32_t* width, uint32_t* height) {
os_window_get_size(width, height);
}
static float simulator_getRefreshRate(void) {
return 0.f;
}
static bool simulator_setRefreshRate(float refreshRate) {
return false;
}
static const float* simulator_getRefreshRates(uint32_t* count) {
return *count = 0, NULL;
}
static PassthroughMode simulator_getPassthrough(void) {
return PASSTHROUGH_OPAQUE;
}
static bool simulator_setPassthrough(PassthroughMode mode) {
return mode == PASSTHROUGH_OPAQUE;
}
static bool simulator_isPassthroughSupported(PassthroughMode mode) {
return mode == PASSTHROUGH_OPAQUE;
}
static double simulator_getDisplayTime(void) {
return state.time;
}
static double simulator_getDeltaTime(void) {
return state.dt;
}
static uint32_t simulator_getViewCount(void) {
return 1;
}
static bool simulator_getViewPose(uint32_t view, float* position, float* orientation) {
vec3_init(position, state.headPosition);
quat_init(orientation, state.headOrientation);
position[1] += OFFSET;
return view == 0;
}
static bool simulator_getViewAngles(uint32_t view, float* left, float* right, float* up, float* down) {
float aspect, fov;
uint32_t width, height;
simulator_getDisplayDimensions(&width, &height);
aspect = (float) width / height;
fov = .7f;
*left = atanf(tanf(fov) * aspect);
*right = atanf(tanf(fov) * aspect);
*up = fov;
*down = fov;
return view == 0;
}
static void simulator_getClipDistance(float* clipNear, float* clipFar) {
*clipNear = state.clipNear;
*clipFar = state.clipFar;
}
static void simulator_setClipDistance(float clipNear, float clipFar) {
state.clipNear = clipNear;
state.clipFar = clipFar;
}
static void simulator_getBoundsDimensions(float* width, float* depth) {
*width = *depth = 0.f;
}
static const float* simulator_getBoundsGeometry(uint32_t* count) {
*count = 0;
return NULL;
}
static bool simulator_getPose(Device device, vec3 position, quat orientation) {
if (device == DEVICE_HEAD) {
vec3_init(position, state.headPosition);
quat_init(orientation, state.headOrientation);
position[1] += OFFSET;
return true;
} else if (device == DEVICE_HAND_LEFT || device == DEVICE_HAND_LEFT_POINT) {
vec3_init(position, state.handPosition);
quat_init(orientation, state.handOrientation);
return !state.mouseDown;
}
return false;
}
static bool simulator_getVelocity(Device device, vec3 velocity, vec3 angularVelocity) {
vec3_init(velocity, state.velocity);
vec3_set(angularVelocity, 0.f, 0.f, 0.f);
return device == DEVICE_HEAD;
}
static bool simulator_isDown(Device device, DeviceButton button, bool* down, bool* changed) {
*down = state.triggerDown;
*changed = state.triggerChanged;
return device == DEVICE_HAND_LEFT && button == BUTTON_TRIGGER;
}
static bool simulator_isTouched(Device device, DeviceButton button, bool* touched) {
return false;
}
static bool simulator_getAxis(Device device, DeviceAxis axis, vec3 value) {
return false;
}
static bool simulator_getSkeleton(Device device, float* poses) {
return false;
}
static bool simulator_vibrate(Device device, float strength, float duration, float frequency) {
return false;
}
static void simulator_stopVibration(Device device) {
//
}
static struct ModelData* simulator_newModelData(Device device, bool animated) {
return NULL;
}
static bool simulator_animate(struct Model* model) {
return false;
}
static Texture* simulator_getTexture(void) {
return state.texture;
}
static Pass* simulator_getPass(void) {
if (!state.pass) {
return NULL;
}
lovrPassReset(state.pass);
uint32_t width, height;
simulator_getDisplayDimensions(&width, &height);
if (lovrPassGetWidth(state.pass) != width || lovrPassGetHeight(state.pass) != height) {
lovrRelease(state.texture, lovrTextureDestroy);
state.texture = lovrTextureCreate(&(TextureInfo) {
.type = TEXTURE_2D,
.format = FORMAT_RGBA8,
.srgb = true,
.width = width,
.height = height,
.layers = 1,
.mipmaps = 1,
.samples = 1,
.usage = TEXTURE_RENDER | TEXTURE_SAMPLE,
});
Texture* textures[4] = { state.texture };
lovrPassSetCanvas(state.pass, textures, NULL, state.depthFormat, state.config.antialias ? 4 : 1);
}
float background[4][4];
LoadAction loads[4] = { LOAD_CLEAR };
lovrGraphicsGetBackgroundColor(background[0]);
lovrPassSetClear(state.pass, loads, background, LOAD_CLEAR, 0.f);
float viewMatrix[16];
mat4_fromPose(viewMatrix, state.headPosition, state.headOrientation);
viewMatrix[13] += OFFSET;
mat4_invert(viewMatrix);
float projection[16];
float left, right, up, down;
simulator_getViewAngles(0, &left, &right, &up, &down);
mat4_fov(projection, left, right, up, down, state.clipNear, state.clipFar);
lovrPassSetViewMatrix(state.pass, 0, viewMatrix);
lovrPassSetProjection(state.pass, 0, projection);
return state.pass;
}
static void simulator_submit(void) {
//
}
static bool simulator_isFocused(void) {
return state.focused;
}
static double simulator_update(void) {
double t = os_get_time() - state.epoch;
state.dt = t - state.time;
state.time = t;
bool trigger = os_is_mouse_down(MOUSE_RIGHT);
state.triggerChanged = trigger != state.triggerDown;
state.triggerDown = trigger;
state.mouseDown = os_is_mouse_down(MOUSE_LEFT);
os_set_mouse_mode(state.mouseDown ? MOUSE_MODE_GRABBED : MOUSE_MODE_NORMAL);
double mx, my;
os_get_mouse_position(&mx, &my);
if (state.mouseDown) {
state.pitch = CLAMP(state.pitch - (my - state.my) * TURNSPEED, -(float) M_PI / 2.f, (float) M_PI / 2.f);
state.yaw -= (mx - state.mx) * TURNSPEED;
}
state.mx = mx;
state.my = my;
float pitch[4], yaw[4], target[4];
quat_fromAngleAxis(pitch, state.pitch, 1.f, 0.f, 0.f);
quat_fromAngleAxis(yaw, state.yaw, 0.f, 1.f, 0.f);
quat_mul(target, yaw, pitch);
quat_slerp(state.headOrientation, target, 1.f - expf(-TURNSMOOTH * state.dt));
bool sprint = os_is_key_down(KEY_LEFT_SHIFT) || os_is_key_down(KEY_RIGHT_SHIFT);
bool front = os_is_key_down(KEY_W) || os_is_key_down(KEY_UP);
bool back = os_is_key_down(KEY_S) || os_is_key_down(KEY_DOWN);
bool left = os_is_key_down(KEY_A) || os_is_key_down(KEY_LEFT);
bool right = os_is_key_down(KEY_D) || os_is_key_down(KEY_RIGHT);
bool up = os_is_key_down(KEY_Q);
bool down = os_is_key_down(KEY_E);
float velocity[3];
velocity[0] = (left ? -1.f : right ? 1.f : 0.f);
velocity[1] = (down ? -1.f : up ? 1.f : 0.f);
velocity[2] = (front ? -1.f : back ? 1.f : 0.f);
vec3_scale(velocity, sprint ? SPRINTSPEED : MOVESPEED);
vec3_lerp(state.velocity, velocity, 1.f - expf(-MOVESMOOTH * state.dt));
vec3_scale(vec3_init(velocity, state.velocity), state.dt);
quat_rotate(state.headOrientation, velocity);
vec3_add(state.headPosition, velocity);
if (!state.mouseDown) {
float inverseProjection[16], angleLeft, angleRight, angleUp, angleDown;
simulator_getViewAngles(0, &angleLeft, &angleRight, &angleUp, &angleDown);
mat4_fov(inverseProjection, angleLeft, angleRight, angleUp, angleDown, state.clipNear, state.clipFar);
mat4_invert(inverseProjection);
float ray[3];
uint32_t width, height;
os_window_get_size(&width, &height);
vec3_set(ray, mx / width * 2.f - 1.f, my / height * 2.f - 1.f, 1.f);
mat4_mulPoint(inverseProjection, ray);
quat_rotate(state.headOrientation, ray);
vec3_normalize(ray);
state.distance = CLAMP(state.distance * (1.f + lovrSystemGetScrollDelta() * .05f), .05f, 10.f);
vec3_init(state.handPosition, ray);
vec3_scale(state.handPosition, state.distance);
vec3_add(state.handPosition, state.headPosition);
state.handPosition[1] += OFFSET;
float zero[3], up[3], basis[16];
vec3_set(zero, 0.f, 0.f, 0.f);
vec3_set(up, 0.f, 1.f, 0.f);
quat_rotate(state.headOrientation, up);
mat4_target(basis, zero, ray, up);
quat_fromMat4(state.handOrientation, basis);
}
return state.dt;
}
HeadsetInterface lovrHeadsetSimulatorDriver = {
.driverType = DRIVER_SIMULATOR,
.init = simulator_init,
.start = simulator_start,
.stop = simulator_stop,
.destroy = simulator_destroy,
.getDriverName = simulator_getDriverName,
.getName = simulator_getName,
.isSeated = simulator_isSeated,
.getDisplayDimensions = simulator_getDisplayDimensions,
.getRefreshRate = simulator_getRefreshRate,
.setRefreshRate = simulator_setRefreshRate,
.getRefreshRates = simulator_getRefreshRates,
.getPassthrough = simulator_getPassthrough,
.setPassthrough = simulator_setPassthrough,
.isPassthroughSupported = simulator_isPassthroughSupported,
.getDisplayTime = simulator_getDisplayTime,
.getDeltaTime = simulator_getDeltaTime,
.getViewCount = simulator_getViewCount,
.getViewPose = simulator_getViewPose,
.getViewAngles = simulator_getViewAngles,
.getClipDistance = simulator_getClipDistance,
.setClipDistance = simulator_setClipDistance,
.getBoundsDimensions = simulator_getBoundsDimensions,
.getBoundsGeometry = simulator_getBoundsGeometry,
.getPose = simulator_getPose,
.getVelocity = simulator_getVelocity,
.isDown = simulator_isDown,
.isTouched = simulator_isTouched,
.getAxis = simulator_getAxis,
.getSkeleton = simulator_getSkeleton,
.vibrate = simulator_vibrate,
.stopVibration = simulator_stopVibration,
.newModelData = simulator_newModelData,
.animate = simulator_animate,
.getTexture = simulator_getTexture,
.getPass = simulator_getPass,
.submit = simulator_submit,
.isFocused = simulator_isFocused,
.update = simulator_update
};

View File

@ -1,13 +1,14 @@
#include "headset/headset.h"
extern bool webxr_init(float supersample, float offset, uint32_t msaa, int overlay);
extern bool webxr_init(HeadsetConfig* config);
extern void webxr_start(void);
extern void webxr_destroy(void);
extern bool webxr_getDriverName(char* name, size_t length);
extern bool webxr_getName(char* name, size_t length);
extern HeadsetOrigin webxr_getOriginType(void);
extern bool webxr_isSeated(void);
extern void webxr_getDisplayDimensions(uint32_t* width, uint32_t* height);
extern double webxr_getDisplayTime(void);
extern double webxr_getDeltaTime(void);
extern void webxr_getDisplayDimensions(uint32_t* width, uint32_t* height);
extern uint32_t webxr_getViewCount(void);
extern bool webxr_getViewPose(uint32_t view, float* position, float* orientation);
extern bool webxr_getViewAngles(uint32_t view, float* left, float* right, float* up, float* down);
@ -22,16 +23,19 @@ extern bool webxr_isTouched(Device device, DeviceButton button, bool* touched);
extern bool webxr_getAxis(Device device, DeviceAxis axis, float* value);
extern bool webxr_getSkeleton(Device device, float* poses);
extern bool webxr_vibrate(Device device, float strength, float duration, float frequency);
extern void webxr_stopVibration(Device device);
extern struct ModelData* webxr_newModelData(Device device, bool animated);
extern bool webxr_animate(Device device, struct Model* model);
extern bool webxr_animate(struct Model* model);
extern void webxr_renderTo(void (*callback)(void*), void* userdata);
extern bool webxr_isFocused(void);
extern bool webxr_isPassthroughEnabled(void);
extern bool webxr_setPassthroughEnabled(bool enable);
extern double webxr_update(void);
static bool webxrAttached = false;
static HeadsetInterface* previousHeadsetDriver;
void webxr_attach() {
void webxr_attach(void) {
if (webxrAttached || lovrHeadsetInterface == &lovrHeadsetWebXRDriver) {
return;
}
@ -41,7 +45,7 @@ void webxr_attach() {
webxrAttached = true;
}
void webxr_detach() {
void webxr_detach(void) {
if (!webxrAttached) {
return;
}
@ -56,8 +60,9 @@ HeadsetInterface lovrHeadsetWebXRDriver = {
.init = webxr_init,
.start = webxr_start,
.destroy = webxr_destroy,
.getDriverName = webxr_getDriverName,
.getName = webxr_getName,
.getOriginType = webxr_getOriginType,
.isSeated = webxr_isSeated,
.getDisplayTime = webxr_getDisplayTime,
.getDisplayDimensions = webxr_getDisplayDimensions,
.getViewCount = webxr_getViewCount,
@ -74,9 +79,9 @@ HeadsetInterface lovrHeadsetWebXRDriver = {
.getAxis = webxr_getAxis,
.getSkeleton = webxr_getSkeleton,
.vibrate = webxr_vibrate,
.stopVibration = webxr_stopVibration,
.newModelData = webxr_newModelData,
.animate = webxr_animate,
.renderTo = webxr_renderTo,
.isFocused = webxr_isFocused,
.update = webxr_update
};

View File

@ -1,138 +0,0 @@
#include "math/curve.h"
#include "core/maf.h"
#include "util.h"
#include <stdlib.h>
#include <math.h>
struct Curve {
uint32_t ref;
arr_t(float) points;
};
// Explicit curve evaluation, unroll simple cases to avoid pow overhead
static void evaluate(float* restrict P, size_t n, float t, vec3 p) {
if (n == 2) {
p[0] = P[0] + (P[4] - P[0]) * t;
p[1] = P[1] + (P[5] - P[1]) * t;
p[2] = P[2] + (P[6] - P[2]) * t;
p[3] = P[3] + (P[7] - P[3]) * t;
} else if (n == 3) {
float t1 = (1.f - t);
float a = t1 * t1;
float b = 2.f * t1 * t;
float c = t * t;
p[0] = a * P[0] + b * P[4] + c * P[8];
p[1] = a * P[1] + b * P[5] + c * P[9];
p[2] = a * P[2] + b * P[6] + c * P[10];
p[3] = a * P[3] + b * P[7] + c * P[11];
} else if (n == 4) {
float t1 = (1.f - t);
float a = t1 * t1 * t1;
float b = 3.f * t1 * t1 * t;
float c = 3.f * t1 * t * t;
float d = t * t * t;
p[0] = a * P[0] + b * P[4] + c * P[8] + d * P[12];
p[1] = a * P[1] + b * P[5] + c * P[9] + d * P[13];
p[2] = a * P[2] + b * P[6] + c * P[10] + d * P[14];
p[3] = a * P[3] + b * P[7] + c * P[11] + d * P[15];
} else {
float b = 1.f;
p[0] = p[1] = p[2] = p[3] = 0.f;
for (size_t i = 0; i < n; i++, b *= (float) (n - i) / i) {
float c1 = powf(1.f - t, n - (i + 1));
float c2 = powf(t, i);
p[0] += b * c1 * c2 * P[i * 4 + 0];
p[1] += b * c1 * c2 * P[i * 4 + 1];
p[2] += b * c1 * c2 * P[i * 4 + 2];
p[3] += b * c1 * c2 * P[i * 4 + 3];
}
}
}
Curve* lovrCurveCreate(void) {
Curve* curve = calloc(1, sizeof(Curve));
lovrAssert(curve, "Out of memory");
curve->ref = 1;
arr_init(&curve->points, arr_alloc);
arr_reserve(&curve->points, 16);
return curve;
}
void lovrCurveDestroy(void* ref) {
Curve* curve = ref;
arr_free(&curve->points);
free(curve);
}
void lovrCurveEvaluate(Curve* curve, float t, vec3 p) {
lovrAssert(curve->points.length >= 8, "Need at least 2 points to evaluate a Curve");
lovrAssert(t >= 0.f && t <= 1.f, "Curve evaluation interval must be within [0, 1]");
evaluate(curve->points.data, curve->points.length / 4, t, p);
}
void lovrCurveGetTangent(Curve* curve, float t, vec3 p) {
float q[4];
size_t n = curve->points.length / 4;
evaluate(curve->points.data, n - 1, t, q);
evaluate(curve->points.data + 4, n - 1, t, p);
vec3_add(p, vec3_scale(q, -1.f));
vec3_normalize(p);
}
Curve* lovrCurveSlice(Curve* curve, float t1, float t2) {
lovrAssert(curve->points.length >= 8, "Need at least 2 points to slice a Curve");
lovrAssert(t1 >= 0.f && t2 <= 1.f, "Curve slice interval must be within [0, 1]");
Curve* new = lovrCurveCreate();
arr_reserve(&new->points, curve->points.length);
new->points.length = curve->points.length;
size_t n = curve->points.length / 4;
// Right half of split at t1
for (size_t i = 0; i < n - 1; i++) {
evaluate(curve->points.data + 4 * i, n - i, t1, new->points.data + 4 * i);
}
vec3_init(new->points.data + 4 * (n - 1), curve->points.data + 4 * (n - 1));
// Split segment at t2, taking left half
float t = (t2 - t1) / (1.f - t1);
for (size_t i = n - 1; i >= 1; i--) {
evaluate(new->points.data, i + 1, t, new->points.data + 4 * i);
}
return new;
}
size_t lovrCurveGetPointCount(Curve* curve) {
return curve->points.length / 4;
}
void lovrCurveGetPoint(Curve* curve, size_t index, vec3 point) {
vec3_init(point, curve->points.data + 4 * index);
}
void lovrCurveSetPoint(Curve* curve, size_t index, vec3 point) {
vec3_init(curve->points.data + 4 * index, point);
}
void lovrCurveAddPoint(Curve* curve, vec3 point, size_t index) {
// Reserve enough memory for 4 more floats, then record destination once memory is allocated
arr_reserve(&curve->points, curve->points.length + 4);
float* dest = curve->points.data + index * 4;
// Shift remaining points over (if any) to create empty space
if (index * 4 != curve->points.length) {
memmove(dest + 4, dest, (curve->points.length - index * 4) * sizeof(float));
}
// Fill the empty space with the new point
curve->points.length += 4;
memcpy(dest, point, 4 * sizeof(float));
}
void lovrCurveRemovePoint(Curve* curve, size_t index) {
arr_splice(&curve->points, index * 4, 4);
}

View File

@ -1,13 +0,0 @@
#include <stddef.h>
typedef struct Curve Curve;
Curve* lovrCurveCreate(void);
void lovrCurveDestroy(void* ref);
void lovrCurveEvaluate(Curve* curve, float t, float* point);
void lovrCurveGetTangent(Curve* curve, float t, float* point);
Curve* lovrCurveSlice(Curve* curve, float t1, float t2);
size_t lovrCurveGetPointCount(Curve* curve);
void lovrCurveGetPoint(Curve* curve, size_t index, float* point);
void lovrCurveSetPoint(Curve* curve, size_t index, float* point);
void lovrCurveAddPoint(Curve* curve, float* point, size_t index);
void lovrCurveRemovePoint(Curve* curve, size_t index);

View File

@ -1,17 +1,41 @@
#include "math.h"
#include "math/randomGenerator.h"
#include "core/maf.h"
#include "util.h"
#include "lib/noise/simplexnoise1234.h"
#include <math.h>
#include <string.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
struct Curve {
uint32_t ref;
arr_t(float) points;
};
struct Pool {
uint32_t ref;
float* data;
uint32_t count;
uint32_t cursor;
uint32_t generation;
};
struct RandomGenerator {
uint32_t ref;
Seed seed;
Seed state;
double lastRandomNormal;
};
static struct {
bool initialized;
RandomGenerator* generator;
} state;
bool lovrMathInit() {
bool lovrMathInit(void) {
if (state.initialized) return false;
state.generator = lovrRandomGeneratorCreate();
Seed seed = { .b64 = (uint64_t) time(0) };
@ -19,16 +43,12 @@ bool lovrMathInit() {
return state.initialized = true;
}
void lovrMathDestroy() {
void lovrMathDestroy(void) {
if (!state.initialized) return;
lovrRelease(state.generator, lovrRandomGeneratorDestroy);
memset(&state, 0, sizeof(state));
}
RandomGenerator* lovrMathGetRandomGenerator() {
return state.generator;
}
float lovrMathGammaToLinear(float x) {
if (x <= .04045f) {
return x / 12.92f;
@ -60,3 +80,288 @@ double lovrMathNoise3(double x, double y, double z) {
double lovrMathNoise4(double x, double y, double z, double w) {
return snoise4(x, y, z, w) * .5 + .5;
}
RandomGenerator* lovrMathGetRandomGenerator(void) {
return state.generator;
}
// Curve
// Explicit curve evaluation, unroll simple cases to avoid pow overhead
static void evaluate(float* restrict P, size_t n, float t, vec4 p) {
if (n == 2) {
p[0] = P[0] + (P[4] - P[0]) * t;
p[1] = P[1] + (P[5] - P[1]) * t;
p[2] = P[2] + (P[6] - P[2]) * t;
p[3] = P[3] + (P[7] - P[3]) * t;
} else if (n == 3) {
float t1 = (1.f - t);
float a = t1 * t1;
float b = 2.f * t1 * t;
float c = t * t;
p[0] = a * P[0] + b * P[4] + c * P[8];
p[1] = a * P[1] + b * P[5] + c * P[9];
p[2] = a * P[2] + b * P[6] + c * P[10];
p[3] = a * P[3] + b * P[7] + c * P[11];
} else if (n == 4) {
float t1 = (1.f - t);
float a = t1 * t1 * t1;
float b = 3.f * t1 * t1 * t;
float c = 3.f * t1 * t * t;
float d = t * t * t;
p[0] = a * P[0] + b * P[4] + c * P[8] + d * P[12];
p[1] = a * P[1] + b * P[5] + c * P[9] + d * P[13];
p[2] = a * P[2] + b * P[6] + c * P[10] + d * P[14];
p[3] = a * P[3] + b * P[7] + c * P[11] + d * P[15];
} else {
float b = 1.f;
p[0] = p[1] = p[2] = p[3] = 0.f;
for (size_t i = 0; i < n; i++, b *= (float) (n - i) / i) {
float c1 = powf(1.f - t, n - (i + 1));
float c2 = powf(t, i);
p[0] += b * c1 * c2 * P[i * 4 + 0];
p[1] += b * c1 * c2 * P[i * 4 + 1];
p[2] += b * c1 * c2 * P[i * 4 + 2];
p[3] += b * c1 * c2 * P[i * 4 + 3];
}
}
}
Curve* lovrCurveCreate(void) {
Curve* curve = calloc(1, sizeof(Curve));
lovrAssert(curve, "Out of memory");
curve->ref = 1;
arr_init(&curve->points, arr_alloc);
arr_reserve(&curve->points, 16);
return curve;
}
void lovrCurveDestroy(void* ref) {
Curve* curve = ref;
arr_free(&curve->points);
free(curve);
}
void lovrCurveEvaluate(Curve* curve, float t, vec4 p) {
lovrAssert(curve->points.length >= 8, "Need at least 2 points to evaluate a Curve");
lovrAssert(t >= 0.f && t <= 1.f, "Curve evaluation interval must be within [0, 1]");
evaluate(curve->points.data, curve->points.length / 4, t, p);
}
void lovrCurveGetTangent(Curve* curve, float t, vec4 p) {
float q[4];
size_t n = curve->points.length / 4;
evaluate(curve->points.data, n - 1, t, q);
evaluate(curve->points.data + 4, n - 1, t, p);
vec4_add(p, vec4_scale(q, -1.f));
vec3_normalize(p);
}
Curve* lovrCurveSlice(Curve* curve, float t1, float t2) {
lovrAssert(curve->points.length >= 8, "Need at least 2 points to slice a Curve");
lovrAssert(t1 >= 0.f && t2 <= 1.f, "Curve slice interval must be within [0, 1]");
Curve* new = lovrCurveCreate();
arr_reserve(&new->points, curve->points.length);
new->points.length = curve->points.length;
size_t n = curve->points.length / 4;
// Right half of split at t1
for (size_t i = 0; i < n - 1; i++) {
evaluate(curve->points.data + 4 * i, n - i, t1, new->points.data + 4 * i);
}
vec4_init(new->points.data + 4 * (n - 1), curve->points.data + 4 * (n - 1));
// Split segment at t2, taking left half
float t = (t2 - t1) / (1.f - t1);
for (size_t i = n - 1; i >= 1; i--) {
evaluate(new->points.data, i + 1, t, new->points.data + 4 * i);
}
return new;
}
size_t lovrCurveGetPointCount(Curve* curve) {
return curve->points.length / 4;
}
void lovrCurveGetPoint(Curve* curve, size_t index, vec4 point) {
vec4_init(point, curve->points.data + 4 * index);
}
void lovrCurveSetPoint(Curve* curve, size_t index, vec4 point) {
vec4_init(curve->points.data + 4 * index, point);
}
void lovrCurveAddPoint(Curve* curve, vec4 point, size_t index) {
// Reserve enough memory for 4 more floats, then record destination once memory is allocated
arr_reserve(&curve->points, curve->points.length + 4);
float* dest = curve->points.data + index * 4;
// Shift remaining points over (if any) to create empty space
if (index * 4 != curve->points.length) {
memmove(dest + 4, dest, (curve->points.length - index * 4) * sizeof(float));
}
// Fill the empty space with the new point
curve->points.length += 4;
memcpy(dest, point, 4 * sizeof(float));
}
void lovrCurveRemovePoint(Curve* curve, size_t index) {
arr_splice(&curve->points, index * 4, 4);
}
// Pool
static const size_t vectorComponents[] = {
[V_VEC2] = 2,
[V_VEC3] = 4,
[V_VEC4] = 4,
[V_QUAT] = 4,
[V_MAT4] = 16
};
Pool* lovrPoolCreate(void) {
Pool* pool = calloc(1, sizeof(Pool));
lovrAssert(pool, "Out of memory");
pool->ref = 1;
lovrPoolGrow(pool, 1 << 12);
return pool;
}
void lovrPoolDestroy(void* ref) {
Pool* pool = ref;
free(pool->data);
free(pool);
}
void lovrPoolGrow(Pool* pool, size_t count) {
lovrAssert(count <= (1 << 24), "Temporary vector space exhausted. Try using lovr.math.drain to drain the vector pool periodically.");
pool->count = (uint32_t) count; // Assert guarantees safe
pool->data = realloc(pool->data, pool->count * sizeof(float));
lovrAssert(pool->data, "Out of memory");
}
Vector lovrPoolAllocate(Pool* pool, VectorType type, float** data) {
lovrCheck(pool, "The math module must be initialized to create vectors");
size_t count = vectorComponents[type];
if (pool->cursor + count > pool->count) {
lovrPoolGrow(pool, pool->count * 2);
}
Vector v = {
.handle = {
.type = type,
.generation = pool->generation,
.index = pool->cursor
}
};
*data = pool->data + pool->cursor;
pool->cursor += (uint32_t) count; // Cast safe because vectorComponents members are known
return v;
}
float* lovrPoolResolve(Pool* pool, Vector vector) {
lovrAssert(vector.handle.generation == pool->generation, "Attempt to use a temporary vector from a previous frame");
return pool->data + vector.handle.index;
}
void lovrPoolDrain(Pool* pool) {
pool->cursor = 0;
pool->generation = (pool->generation + 1) & 0xf;
}
// RandomGenerator (compatible with LÖVE's)
// Thomas Wang's 64-bit integer hashing function:
// https://web.archive.org/web/20110807030012/http://www.cris.com/%7ETtwang/tech/inthash.htm
static uint64_t wangHash64(uint64_t key) {
key = (~key) + (key << 21); // key = (key << 21) - key - 1;
key = key ^ (key >> 24);
key = (key + (key << 3)) + (key << 8); // key * 265
key = key ^ (key >> 14);
key = (key + (key << 2)) + (key << 4); // key * 21
key = key ^ (key >> 28);
key = key + (key << 31);
return key;
}
// 64 bit Xorshift implementation taken from the end of Sec. 3 (page 4) in
// George Marsaglia, "Xorshift RNGs", Journal of Statistical Software, Vol.8 (Issue 14), 2003
// Use an 'Xorshift*' variant, as shown here: http://xorshift.di.unimi.it
RandomGenerator* lovrRandomGeneratorCreate(void) {
RandomGenerator* generator = calloc(1, sizeof(RandomGenerator));
lovrAssert(generator, "Out of memory");
generator->ref = 1;
Seed seed = { .b32 = { .lo = 0xCBBF7A44, .hi = 0x0139408D } };
lovrRandomGeneratorSetSeed(generator, seed);
generator->lastRandomNormal = HUGE_VAL;
return generator;
}
void lovrRandomGeneratorDestroy(void* ref) {
free(ref);
}
Seed lovrRandomGeneratorGetSeed(RandomGenerator* generator) {
return generator->seed;
}
void lovrRandomGeneratorSetSeed(RandomGenerator* generator, Seed seed) {
generator->seed = seed;
do {
seed.b64 = wangHash64(seed.b64);
} while (seed.b64 == 0);
generator->state = seed;
}
void lovrRandomGeneratorGetState(RandomGenerator* generator, char* state, size_t length) {
snprintf(state, length, "0x%" PRIx64, generator->state.b64);
}
int lovrRandomGeneratorSetState(RandomGenerator* generator, const char* state) {
char* end = NULL;
Seed newState;
newState.b64 = strtoull(state, &end, 16);
if (end != NULL && *end != 0) {
return 1;
} else {
generator->state = newState;
return 0;
}
}
double lovrRandomGeneratorRandom(RandomGenerator* generator) {
generator->state.b64 ^= (generator->state.b64 >> 12);
generator->state.b64 ^= (generator->state.b64 << 25);
generator->state.b64 ^= (generator->state.b64 >> 27);
uint64_t r = generator->state.b64 * 2685821657736338717ULL;
union { uint64_t i; double d; } u;
u.i = ((0x3FFULL) << 52) | (r >> 12);
return u.d - 1.;
}
double lovrRandomGeneratorRandomNormal(RandomGenerator* generator) {
if (generator->lastRandomNormal != HUGE_VAL) {
double r = generator->lastRandomNormal;
generator->lastRandomNormal = HUGE_VAL;
return r;
}
double a = lovrRandomGeneratorRandom(generator);
double b = lovrRandomGeneratorRandom(generator);
double r = sqrt(-2. * log(1. - a));
double phi = 2. * M_PI * (1. - b);
generator->lastRandomNormal = r * cos(phi);
return r * sin(phi);
}

View File

@ -1,15 +1,80 @@
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#pragma once
struct RandomGenerator;
typedef struct Curve Curve;
typedef struct Pool Pool;
typedef struct RandomGenerator RandomGenerator;
bool lovrMathInit(void);
void lovrMathDestroy(void);
struct RandomGenerator* lovrMathGetRandomGenerator(void);
float lovrMathGammaToLinear(float x);
float lovrMathLinearToGamma(float x);
double lovrMathNoise1(double x);
double lovrMathNoise2(double x, double y);
double lovrMathNoise3(double x, double y, double z);
double lovrMathNoise4(double x, double y, double z, double w);
RandomGenerator* lovrMathGetRandomGenerator(void);
// Curve
Curve* lovrCurveCreate(void);
void lovrCurveDestroy(void* ref);
void lovrCurveEvaluate(Curve* curve, float t, float* point);
void lovrCurveGetTangent(Curve* curve, float t, float* point);
Curve* lovrCurveSlice(Curve* curve, float t1, float t2);
size_t lovrCurveGetPointCount(Curve* curve);
void lovrCurveGetPoint(Curve* curve, size_t index, float* point);
void lovrCurveSetPoint(Curve* curve, size_t index, float* point);
void lovrCurveAddPoint(Curve* curve, float* point, size_t index);
void lovrCurveRemovePoint(Curve* curve, size_t index);
// Pool
typedef enum {
V_NONE,
V_VEC2,
V_VEC3,
V_VEC4,
V_QUAT,
V_MAT4,
MAX_VECTOR_TYPES
} VectorType;
typedef union {
void* pointer;
struct {
unsigned type : 4;
unsigned generation : 4;
unsigned index : 24;
unsigned padding : 32;
} handle;
} Vector;
Pool* lovrPoolCreate(void);
void lovrPoolDestroy(void* ref);
void lovrPoolGrow(Pool* pool, size_t count);
Vector lovrPoolAllocate(Pool* pool, VectorType type, float** data);
float* lovrPoolResolve(Pool* pool, Vector vector);
void lovrPoolDrain(Pool* pool);
// RandomGenerator
typedef union {
uint64_t b64;
struct {
uint32_t lo;
uint32_t hi;
} b32;
} Seed;
RandomGenerator* lovrRandomGeneratorCreate(void);
void lovrRandomGeneratorDestroy(void* ref);
Seed lovrRandomGeneratorGetSeed(RandomGenerator* generator);
void lovrRandomGeneratorSetSeed(RandomGenerator* generator, Seed seed);
void lovrRandomGeneratorGetState(RandomGenerator* generator, char* state, size_t length);
int lovrRandomGeneratorSetState(RandomGenerator* generator, const char* state);
double lovrRandomGeneratorRandom(RandomGenerator* generator);
double lovrRandomGeneratorRandomNormal(RandomGenerator* generator);

View File

@ -1,70 +0,0 @@
#include "math/pool.h"
#include "util.h"
#include <stdlib.h>
static const size_t vectorComponents[] = {
[V_VEC2] = 2,
[V_VEC3] = 4,
[V_VEC4] = 4,
[V_QUAT] = 4,
[V_MAT4] = 16
};
struct Pool {
uint32_t ref;
float* data;
size_t count;
size_t cursor;
size_t generation;
};
Pool* lovrPoolCreate() {
Pool* pool = calloc(1, sizeof(Pool));
lovrAssert(pool, "Out of memory");
pool->ref = 1;
lovrPoolGrow(pool, 1 << 12);
return pool;
}
void lovrPoolDestroy(void* ref) {
Pool* pool = ref;
free(pool->data);
free(pool);
}
void lovrPoolGrow(Pool* pool, size_t count) {
lovrAssert(count <= (1 << 16), "Temporary vector space exhausted. Try using lovr.math.drain to drain the vector pool periodically.");
pool->count = count;
pool->data = realloc(pool->data, pool->count * sizeof(float));
lovrAssert(pool->data, "Out of memory");
}
Vector lovrPoolAllocate(Pool* pool, VectorType type, float** data) {
size_t count = vectorComponents[type];
if (pool->cursor + count > pool->count) {
lovrPoolGrow(pool, pool->count * 2);
}
Vector v = {
.handle = {
.type = type,
.generation = (uint8_t) pool->generation,
.index = (uint16_t) pool->cursor
}
};
*data = pool->data + pool->cursor;
pool->cursor += count;
return v;
}
float* lovrPoolResolve(Pool* pool, Vector vector) {
lovrAssert(vector.handle.generation == pool->generation, "Attempt to use a temporary vector from a previous frame");
return pool->data + vector.handle.index;
}
void lovrPoolDrain(Pool* pool) {
pool->cursor = 0;
pool->generation = (pool->generation + 1) & 0xff;
}

View File

@ -1,32 +0,0 @@
#include <stdint.h>
#include <stddef.h>
#pragma once
typedef enum {
V_NONE,
V_VEC2,
V_VEC3,
V_VEC4,
V_QUAT,
V_MAT4,
MAX_VECTOR_TYPES
} VectorType;
typedef union {
void* pointer;
struct {
uint8_t type;
uint8_t generation;
uint16_t index;
uint32_t padding;
} handle;
} Vector;
typedef struct Pool Pool;
Pool* lovrPoolCreate(void);
void lovrPoolDestroy(void* ref);
void lovrPoolGrow(Pool* pool, size_t count);
Vector lovrPoolAllocate(Pool* pool, VectorType type, float** data);
float* lovrPoolResolve(Pool* pool, Vector vector);
void lovrPoolDrain(Pool* pool);

Some files were not shown because too many files have changed in this diff Show More