mirror of https://github.com/bjornbytes/lovr.git
Merge branch 'dev' into master
This commit is contained in:
commit
f8f9c833b1
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
134
CMakeLists.txt
134
CMakeLists.txt
|
@ -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()
|
||||
|
|
28
Tupfile.lua
28
Tupfile.lua
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit de4daf201a2acd83ad523b4c6148e4d8e5b7dab4
|
||||
Subproject commit cdafe1c6028e01d669a02576b291e111b5e08d78
|
|
@ -1 +1 @@
|
|||
Subproject commit 05db1d35d8a35f74ce40d0ba5e387717ad91b24d
|
||||
Subproject commit 113eb1d172dfb6b91f67e9d0478a113a3ccced80
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 56205f0f2c3358896fdb5c8da87736bf0f20705d
|
|
@ -1 +1 @@
|
|||
Subproject commit dd3e3fc1f16a6746c0bfaa343899cb3a418b0cab
|
||||
Subproject commit 3f3b4da5dc81a623dc80315369093d0f1f9656f7
|
|
@ -1 +1 @@
|
|||
Subproject commit 1ca7bec6b531185530c9b4f1e7a50e1fd55e7641
|
||||
Subproject commit 58a00cf85c39ad5ec4dc43a769624e420c06179a
|
|
@ -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 {
|
||||
|
|
97
etc/boot.lua
97
etc/boot.lua
|
@ -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
|
||||
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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]; };
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
34
etc/webxr.js
34
etc/webxr.js
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
|
@ -0,0 +1 @@
|
|||
Subproject commit daaee4ef0056958c33b204a83a97c770ff0a2cbc
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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') {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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 }
|
||||
};
|
|
@ -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
|
@ -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) {
|
||||
|
|
|
@ -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 }
|
||||
};
|
|
@ -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 }
|
||||
};
|
||||
|
|
|
@ -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");
|
||||
|
|
105
src/api/l_math.c
105
src/api/l_math.c
|
@ -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);
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "api.h"
|
||||
#include "math/curve.h"
|
||||
#include "util.h"
|
||||
|
||||
static int l_lovrCurveEvaluate(lua_State* L) {
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
@ -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];
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
#include "gpu.h"
|
||||
#include <webgpu/webgpu.h>
|
||||
|
||||
bool gpu_init(gpu_config* config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void gpu_destroy(void) {
|
||||
//
|
||||
}
|
474
src/core/maf.h
474
src/core/maf.h
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
388
src/core/spv.c
388
src/core/spv.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
** $Id: lutf8lib.c $
|
||||
** Standard library for UTF-8 manipulation
|
||||
**
|
||||
** Copyright © 1994–2021 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;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
struct lua_State;
|
||||
|
||||
int luaopen_utf8(struct lua_State *L);
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
@ -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
|
||||
};
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue