Untested merge of Oculus Mobile branch and Oct 2018 lovr master

This commit is contained in:
mcc 2018-10-22 10:44:04 -04:00
commit 56f8f1e26b
150 changed files with 21790 additions and 17658 deletions

36
.appveyor.yml Normal file
View File

@ -0,0 +1,36 @@
skip_branch_with_pr: true
clone_depth: 1
only_commits:
files:
- src/
- deps/
before_build:
- cd C:\projects\lovr
- git submodule update --init
- md build
- cd build
- cmake -DCMAKE_BUILD_TYPE=%configuration% ..
configuration: Release
build:
project: build\lovr.sln
verbosity: quiet
after_build:
- cd C:\projects\lovr
- 7z a lovr.zip C:\projects\lovr\build\Release\*.dll C:\projects\lovr\build\Release\lovr.exe
artifacts:
- path: lovr.zip
deploy:
provider: Webhook
url: https://lovr.org/nightly
on:
branch: master
authorization:
secure: zbxwiHmtJRVP9FdiqxepLdLGjxhtpjVytb+yLSqrz+8Bl1bH6k5Zts809bLrEjzn

6
.gitignore vendored
View File

@ -1,10 +1,10 @@
.tup .tup
build
/lovr /lovr
/data /data
/*.lua
src/obj src/obj
src/Tupfile **/Tupfile
Tuprules.tup
/watch.sh /watch.sh
*.glsl
.DS_Store .DS_Store
bin bin

3
.gitmodules vendored
View File

@ -1,9 +1,6 @@
[submodule "deps/enet"] [submodule "deps/enet"]
path = deps/enet path = deps/enet
url = https://github.com/lsalzman/enet url = https://github.com/lsalzman/enet
[submodule "deps/freetype"]
path = deps/freetype
url = https://github.com/aseprite/freetype2
[submodule "deps/glfw"] [submodule "deps/glfw"]
path = deps/glfw path = deps/glfw
url = https://github.com/glfw/glfw url = https://github.com/glfw/glfw

15
.travis.yml Normal file
View File

@ -0,0 +1,15 @@
language: node_js
node_js:
- node
sudo: required
services:
- docker
before_install:
- docker run -dit --name emscripten -v $(pwd):/src trzeci/emscripten:sdk-incoming-64bit bash
script:
- docker exec -it emscripten emcmake cmake
- docker exec -it emscripten emmake make

View File

@ -1,234 +1,309 @@
cmake_minimum_required(VERSION 3.0.0) cmake_minimum_required(VERSION 3.1.0)
project(lovr) project(lovr)
# Options
option(LOVR_ENABLE_AUDIO "Enable the audio module" ON)
option(LOVR_ENABLE_DATA "Enable the data module" ON)
option(LOVR_ENABLE_EVENT "Enable the event module" ON)
option(LOVR_ENABLE_FILESYSTEM "Enable the filesystem module" ON)
option(LOVR_ENABLE_GRAPHICS "Enable the graphics module" ON)
option(LOVR_ENABLE_HEADSET "Enable the headset module" ON)
option(LOVR_ENABLE_MATH "Enable the math module" ON)
option(LOVR_ENABLE_PHYSICS "Enable the physics module" ON)
option(LOVR_ENABLE_THREAD "Enable the thread module" ON)
option(LOVR_ENABLE_TIMER "Enable the timer module" ON)
option(LOVR_ENABLE_ENET "Bundle with lua-enet" ON)
option(LOVR_ENABLE_JSON "Bundle with lua-cjson" ON)
option(LOVR_USE_LUAJIT "Use LuaJIT instead of Lua" ON)
option(LOVR_USE_ASSIMP "Enable model loading with Assimp" ON)
option(LOVR_USE_OPENVR "Enable the OpenVR backend for the headset module" ON)
option(LOVR_USE_WEBVR "Enable the WebVR backend for the headset module" OFF)
option(LOVR_USE_OCULUS "Enable the LibOVR backend for the headset module (be sure to also set LOVR_OCULUS_PATH to point to the Oculus SDK)" OFF)
option(LOVR_USE_FAKE_HEADSET "Enable the keyboard/mouse backend for the headset module" ON)
option(LOVR_USE_SSE "Enable SIMD use of intrinsics" ON)
option(LOVR_SYSTEM_PHYSFS "Use the system-provided PhysFS" OFF)
option(LOVR_SYSTEM_ASSIMP "Use the system-provided Assimp" OFF)
option(LOVR_SYSTEM_ENET "Use the system-provided enet" OFF)
option(LOVR_SYSTEM_GLFW "Use the system-provided glfw" OFF)
option(LOVR_SYSTEM_LUA "Use the system-provided Lua" OFF)
option(LOVR_SYSTEM_ODE "Use the system-provided ODE" OFF)
option(LOVR_SYSTEM_OPENAL "Use the system-provided OpenAL" OFF)
# Setup # Setup
if(EMSCRIPTEN) if(EMSCRIPTEN)
string(CONCAT LOVR_EMSCRIPTEN_FLAGS string(CONCAT LOVR_EMSCRIPTEN_FLAGS
"-O3 " "-O3 "
"-s USE_WEBGL2=1 " "-s WASM=1 "
"-s FULL_ES3=1 "
"-s USE_FREETYPE=1 "
"-s USE_GLFW=3 " "-s USE_GLFW=3 "
"-s USE_WEBGL2=1 "
"-s GL_PREINITIALIZED_CONTEXT=1 "
"-s USE_ZLIB=1 " "-s USE_ZLIB=1 "
"-s FULL_ES3=1 "
"-s FORCE_FILESYSTEM=1 "
"-s ALLOW_MEMORY_GROWTH=1 " "-s ALLOW_MEMORY_GROWTH=1 "
"-s 'EXTRA_EXPORTED_RUNTIME_METHODS=[\"getValue\", \"setValue\"]' " "-s \"EXPORTED_FUNCTIONS=[ "
"-s WASM=1" "'_main','_lovrRun','_lovrQuit','_lovrDestroy',"
"'_mat4_identity','_mat4_invert','_mat4_multiply','_mat4_rotateQuat','_mat4_transform','_mat4_transformDirection','_mat4_translate',"
"'_quat_fromMat4','_quat_getAngleAxis'"
"]\" "
"-s \"EXTRA_EXPORTED_RUNTIME_METHODS=['getValue','setValue']\" "
"--js-library \"${CMAKE_CURRENT_SOURCE_DIR}/src/resources/webvr.js\" "
"--shell-file \"${CMAKE_CURRENT_SOURCE_DIR}/src/resources/lovr.html\""
) )
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LOVR_EMSCRIPTEN_FLAGS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LOVR_EMSCRIPTEN_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LOVR_EMSCRIPTEN_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LOVR_EMSCRIPTEN_FLAGS}")
set(CMAKE_EXECUTABLE_SUFFIX ".html")
set(LOVR_USE_WEBVR ON)
set(LOVR_USE_OPENVR OFF)
set(LOVR_USE_OCULUS OFF)
set(LOVR_USE_SSE OFF)
elseif(UNIX) elseif(UNIX)
find_package(PkgConfig REQUIRED) if(APPLE)
set(CMAKE_MACOSX_RPATH 1)
endif()
find_package(PkgConfig)
endif() endif()
# PhysicsFS # PhysicsFS
set(PHYSFS_BUILD_STATIC OFF CACHE BOOL "") if(LOVR_ENABLE_FILESYSTEM)
set(PHYSFS_ARCHIVE_7Z OFF CACHE BOOL "") if(LOVR_SYSTEM_PHYSFS)
set(PHYSFS_ARCHIVE_GRP OFF CACHE BOOL "") find_package(PhysFS REQUIRED)
set(PHYSFS_ARCHIVE_WAD OFF CACHE BOOL "") include_directories(${PHYSFS_INCLUDE_DIR})
set(PHYSFS_ARCHIVE_HOG OFF CACHE BOOL "") set(LOVR_PHYSFS ${PHYSFS_LIBRARY})
set(PHYSFS_ARCHIVE_MVL OFF CACHE BOOL "") else()
set(PHYSFS_ARCHIVE_QPAK OFF CACHE BOOL "") set(PHYSFS_BUILD_STATIC OFF CACHE BOOL "")
set(PHYSFS_BUILD_TEST OFF CACHE BOOL "") set(PHYSFS_ARCHIVE_7Z OFF CACHE BOOL "")
set(PHYSFS_BUILD_WX_TEST FALSE CACHE BOOL "") set(PHYSFS_ARCHIVE_GRP OFF CACHE BOOL "")
if(WIN32 OR EMSCRIPTEN OR OCULUS_ANDROID_EMBED) set(PHYSFS_ARCHIVE_WAD OFF CACHE BOOL "")
add_subdirectory(deps/physfs physfs) set(PHYSFS_ARCHIVE_HOG OFF CACHE BOOL "")
include_directories(deps/physfs/src) set(PHYSFS_ARCHIVE_MVL OFF CACHE BOOL "")
set(LOVR_PHYSFS physfs) set(PHYSFS_ARCHIVE_QPAK OFF CACHE BOOL "")
else() set(PHYSFS_ARCHIVE_SLB OFF CACHE BOOL "")
find_package(PhysFS REQUIRED) set(PHYSFS_ARCHIVE_ISO9660 OFF CACHE BOOL "")
include_directories(${PHYSFS_INCLUDE_DIR}) set(PHYSFS_ARCHIVE_VDF OFF CACHE BOOL "")
set(LOVR_PHYSFS ${PHYSFS_LIBRARY}) set(PHYSFS_BUILD_TEST OFF CACHE BOOL "")
set(PHYSFS_BUILD_WX_TEST FALSE CACHE BOOL "")
add_subdirectory(deps/physfs physfs)
include_directories(deps/physfs/src)
set(LOVR_PHYSFS physfs)
endif()
endif() endif()
# Assimp # Assimp
set(ASSIMP_BUILD_ASSIMP_TOOLS OFF CACHE BOOL "") if(LOVR_ENABLE_DATA AND LOVR_USE_ASSIMP)
set(ASSIMP_BUILD_TESTS OFF CACHE BOOL "") if(LOVR_SYSTEM_ASSIMP)
#set(ASSIMP_BUILD_ZLIB ON CACHE BOOL "") pkg_search_module(ASSIMP REQUIRED assimp)
set(ASSIMP_NO_EXPORT ON OFF CACHE BOOL "") include_directories(${ASSIMP_INCLUDE_DIRS})
set(ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT OFF CACHE BOOL "") set(LOVR_ASSIMP ${ASSIMP_LIBRARIES})
set(ASSIMP_BUILD_COLLADA_IMPORTER ON CACHE BOOL "") else()
set(ASSIMP_BUILD_FBX_IMPORTER ON CACHE BOOL "") if(EMSCRIPTEN)
set(ASSIMP_BUILD_GLTF_IMPORTER ON CACHE BOOL "") set(ZLIB_FOUND 1)
set(ASSIMP_BUILD_OBJ_IMPORTER ON CACHE BOOL "") set(ZLIB_LIBRARIES "-s USE_ZLIB=1")
if(EMSCRIPTEN) set(ZLIB_INCLUDE_DIR "assimp/contrib/zlib")
set(ZLIB_FOUND 1) endif()
set(ZLIB_LIBRARIES "-s USE_ZLIB=1") set(ASSIMP_BUILD_ASSIMP_TOOLS OFF CACHE BOOL "")
set(ZLIB_INCLUDE_DIR "assimp/contrib/zlib") set(ASSIMP_BUILD_TESTS OFF CACHE BOOL "")
add_subdirectory(deps/assimp assimp) set(ASSIMP_NO_EXPORT ON CACHE BOOL "")
include_directories(deps/assimp/include ${CMAKE_BINARY_DIR}/assimp/include) set(ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT OFF CACHE BOOL "")
set(LOVR_ASSIMP assimp) set(ASSIMP_BUILD_FBX_IMPORTER ON CACHE BOOL "")
elseif (OCULUS_ANDROID_EMBED) set(ASSIMP_BUILD_GLTF_IMPORTER ON CACHE BOOL "")
set(ASSIMP_BUILD_ZLIB 1) set(ASSIMP_BUILD_OBJ_IMPORTER ON CACHE BOOL "")
add_subdirectory(deps/assimp assimp) add_subdirectory(deps/assimp assimp)
include_directories(deps/assimp/include ${CMAKE_BINARY_DIR}/assimp/include) include_directories(deps/assimp/include ${CMAKE_BINARY_DIR}/assimp/include)
set(LOVR_ASSIMP assimp) set(LOVR_ASSIMP assimp)
elseif(WIN32) endif()
add_subdirectory(deps/assimp assimp)
include_directories(deps/assimp/include ${CMAKE_BINARY_DIR}/assimp/include)
set(LOVR_ASSIMP assimp)
else()
pkg_search_module(ASSIMP REQUIRED assimp)
include_directories(${ASSIMP_INCLUDE_DIRS})
set(LOVR_ASSIMP ${ASSIMP_LIBRARIES})
endif() endif()
# enet # enet
if(EMSCRIPTEN) if(LOVR_ENABLE_ENET)
set(HAVE_HAS_SOCKLEN_T TRUE CACHE BOOL "") if(LOVR_SYSTEM_ENET)
add_definitions(-D__APPLE__) pkg_search_module(ENET REQUIRED enet)
add_subdirectory(deps/enet enet) include_directories(${ENET_INCLUDE_DIRS})
include_directories(deps/enet/include) set(LOVR_ENET ${ENET_LIBRARIES})
set(LOVR_ENET enet) else()
remove_definitions(-D__APPLE__) if(EMSCRIPTEN)
else() set(HAVE_HAS_SOCKLEN_T TRUE CACHE BOOL "")
add_subdirectory(deps/enet enet) endif()
include_directories(deps/enet/include) add_subdirectory(deps/enet enet)
set(LOVR_ENET enet) include_directories(deps/enet/include)
if(WIN32) set(LOVR_ENET enet)
set(LOVR_ENET ${LOVR_ENET} ws2_32 winmm) if(WIN32)
set(LOVR_ENET ${LOVR_ENET} ws2_32 winmm)
endif()
if(EMSCRIPTEN)
target_compile_definitions(enet PRIVATE __APPLE__)
endif()
endif() endif()
endif() endif()
# FreeType
if (EMSCRIPTEN)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s USE_FREETYPE=1")
elseif (WIN32 OR OCULUS_ANDROID_EMBED)
set(FREETYPE_NO_DIST TRUE CACHE BOOL "")
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(deps/freetype freetype)
set(BUILD_SHARED_LIBS ON)
include_directories(deps/freetype/include)
set(LOVR_FREETYPE freetype)
else()
pkg_search_module(FREETYPE REQUIRED freetype2)
include_directories(${FREETYPE_INCLUDE_DIRS})
set(LOVR_FREETYPE ${FREETYPE_LIBRARIES})
endif()
# GLFW # GLFW
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "") if(NOT (EMSCRIPTEN OR OCULUS_ANDROID_EMBED))
set(GLFW_BUILD_TESTS OFF CACHE BOOL "") if(LOVR_SYSTEM_GLFW)
set(GLFW_BUILD_DOCS OFF CACHE BOOL "") pkg_search_module(GLFW REQUIRED glfw3)
if(WIN32) include_directories(${GLFW_INCLUDE_DIRS})
add_subdirectory(deps/glfw glfw) set(LOVR_GLFW ${GLFW_LIBRARIES})
include_directories(deps/glfw/include) else()
set(LOVR_GLFW glfw ${GLFW_LIBRARIES}) set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "")
elseif(NOT (EMSCRIPTEN OR OCULUS_ANDROID_EMBED)) set(GLFW_BUILD_TESTS OFF CACHE BOOL "")
pkg_search_module(GLFW REQUIRED glfw3) set(GLFW_BUILD_DOCS OFF CACHE BOOL "")
include_directories(${GLFW_INCLUDE_DIRS}) add_subdirectory(deps/glfw glfw)
set(LOVR_GLFW ${GLFW_LIBRARIES}) include_directories(deps/glfw/include)
set(LOVR_GLFW glfw ${GLFW_LIBRARIES})
endif()
unset(LIB_SUFFIX CACHE)
endif() endif()
unset(LIB_SUFFIX CACHE)
# Lua # Lua
if(EMSCRIPTEN) if(LOVR_USE_LUAJIT AND NOT (EMSCRIPTEN OR OCULUS_ANDROID_EMBED))
option(LUA_USE_RELATIVE_LOADLIB OFF)
option(LUA_USE_ULONGJMP OFF)
add_subdirectory(deps/lua lua)
set_target_properties(lua luac liblua liblua_static PROPERTIES EXCLUDE_FROM_ALL 1)
include_directories(deps/lua/src ${CMAKE_BINARY_DIR}/lua)
set(LOVR_LUA liblua_static)
elseif (OCULUS_ANDROID_EMBED)
if (FALSE)
# The LuaJIT CMakeLists is going to build a helper tool named minilua,
# but then it won't be able to execute it because it's an Android executable.
# There is no known workaround to this yet.
set(TARGET_LJARCH ARM)
add_subdirectory(deps/luajit luajit)
include_directories(deps/luajit/luajit/src ${CMAKE_BINARY_DIR}/luajit)
set(LOVR_LUA luajit-5.1)
else()
add_subdirectory(deps/lua lua)
set_target_properties(lua luac liblua liblua_static PROPERTIES EXCLUDE_FROM_ALL 1)
include_directories(deps/lua/src ${CMAKE_BINARY_DIR}/lua)
set(LOVR_LUA liblua_static)
endif()
elseif(WIN32)
add_subdirectory(deps/luajit luajit)
include_directories(deps/luajit/src ${CMAKE_BINARY_DIR}/luajit)
set(LOVR_LUA liblua)
else()
if (APPLE) if (APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pagezero_size 10000 -image_base 100000000") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pagezero_size 10000 -image_base 100000000")
endif() endif()
pkg_search_module(LUAJIT REQUIRED luajit) if(LOVR_SYSTEM_LUA)
include_directories(${LUAJIT_INCLUDE_DIRS}) pkg_search_module(LUAJIT REQUIRED luajit)
set(LOVR_LUA ${LUAJIT_LIBRARIES}) include_directories(${LUAJIT_INCLUDE_DIRS})
set(LOVR_LUA ${LUAJIT_LIBRARIES})
else()
add_subdirectory(deps/luajit luajit)
include_directories(deps/luajit/src ${CMAKE_BINARY_DIR}/luajit)
set(LOVR_LUA liblua)
endif()
set_target_properties(luajit minilua liblua PROPERTIES EXCLUDE_FROM_ALL 1)
if(WIN32)
set_target_properties(wluajit PROPERTIES EXCLUDE_FROM_ALL 1)
endif()
else()
if(EMSCRIPTEN)
option(LUA_USE_RELATIVE_LOADLIB OFF)
option(LUA_USE_ULONGJMP OFF)
option(LUA_USE_POPEN OFF)
endif()
if (OCULUS_ANDROID_EMBED)
set_target_properties(lua luac liblua liblua_static PROPERTIES EXCLUDE_FROM_ALL 1)
endif ()
if(LOVR_SYSTEM_LUA)
pkg_search_module(LUA REQUIRED lua)
include_directories(${LUA_INCLUDE_DIRS})
set(LOVR_LUA ${LUA_LIBRARIES})
else()
add_subdirectory(deps/lua lua)
include_directories(deps/lua/src ${CMAKE_BINARY_DIR}/lua)
set(LOVR_LUA liblua_static)
set_target_properties(lua luac liblua liblua_static PROPERTIES EXCLUDE_FROM_ALL 1)
endif()
endif() endif()
# MSDF # MSDF
set(BUILD_SHARED_LIBS OFF) if(LOVR_ENABLE_DATA)
add_subdirectory(deps/msdfgen lib_msdfgen) set(BUILD_SHARED_LIBS OFF)
set(BUILD_SHARED_LIBS ON) add_subdirectory(deps/msdfgen lib_msdfgen)
include_directories(deps/msdfgen) set(BUILD_SHARED_LIBS ON)
set(LOVR_MSDF lib_msdfgen) include_directories(deps/msdfgen)
set(LOVR_MSDF lib_msdfgen)
endif()
# ODE # ODE
if(EMSCRIPTEN OR OCULUS_ANDROID_EMBED) if(LOVR_ENABLE_PHYSICS)
set(ODE_BUILD_SHARED OFF CACHE BOOL "") if(LOVR_SYSTEM_ODE)
add_subdirectory(deps/ode ode) pkg_search_module(ODE REQUIRED ode)
include_directories(deps/ode/include "${CMAKE_CURRENT_BINARY_DIR}/ode/include") pkg_search_module(CCD REQUIRED ccd)
set(LOVR_ODE ode) include_directories(${ODE_INCLUDE_DIRS} ${CCD_INCLUDE_DIRS})
elseif(WIN32) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++")
set(ODE_BUILD_SHARED ON CACHE BOOL "") set(LOVR_ODE ode ccd)
add_subdirectory(deps/ode ode) else()
include_directories(deps/ode/include "${CMAKE_CURRENT_BINARY_DIR}/ode/include") if(EMSCRIPTEN)
set(LOVR_ODE ode) set(ODE_BUILD_SHARED OFF CACHE BOOL "")
else() else()
pkg_search_module(ODE REQUIRED ode) set(ODE_BUILD_SHARED ON CACHE BOOL "")
include_directories(${ODE_INCLUDE_DIRS}) endif()
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++") add_subdirectory(deps/ode ode)
set(LOVR_ODE ode) if(NOT WIN32)
set_target_properties(ode PROPERTIES COMPILE_FLAGS "-Wno-unused-volatile-lvalue -Wno-array-bounds -Wno-undefined-var-template")
endif()
include_directories(deps/ode/include "${CMAKE_CURRENT_BINARY_DIR}/ode/include")
set(LOVR_ODE ode)
endif()
endif() endif()
# OpenAL # OpenAL
set(ALSOFT_UTILS OFF CACHE BOOL "") if(LOVR_ENABLE_AUDIO)
set(ALSOFT_EXAMPLES OFF CACHE BOOL "") if(LOVR_SYSTEM_OPENAL)
set(ALSOFT_TESTS OFF CACHE BOOL "") pkg_search_module(OPENAL openal-soft)
if(EMSCRIPTEN)
include_directories(deps/openal-soft/include)
elseif(OCULUS_ANDROID_EMBED)
include_directories(deps/openal-soft/include)
#include_directories(../../deps/openal-soft/jni/OpenAL/include)
elseif(WIN32)
add_subdirectory(deps/openal-soft openal)
include_directories(deps/openal-soft/include)
set_target_properties(OpenAL32 PROPERTIES COMPILE_FLAGS "/wd4005 /wd4098")
set(LOVR_OPENAL OpenAL32)
else()
pkg_search_module(OPENAL openal-soft)
if (NOT OPENAL_FOUND)
pkg_search_module(OPENAL openal)
if (NOT OPENAL_FOUND) if (NOT OPENAL_FOUND)
message(FATAL_ERROR "OpenAL not found.") pkg_search_module(OPENAL openal)
if (NOT OPENAL_FOUND)
message(FATAL_ERROR "OpenAL not found.")
endif()
endif() endif()
include_directories(${OPENAL_INCLUDE_DIRS})
string(REPLACE ";" " " OPENAL_LDFLAGS_STR "${OPENAL_LDFLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OPENAL_LDFLAGS_STR}")
set(LOVR_OPENAL ${OPENAL_LIBRARIES})
else()
set(ALSOFT_UTILS OFF CACHE BOOL "")
set(ALSOFT_EXAMPLES OFF CACHE BOOL "")
set(ALSOFT_TESTS OFF CACHE BOOL "")
if(NOT EMSCRIPTEN)
add_subdirectory(deps/openal-soft openal)
set(LOVR_OPENAL OpenAL)
if(WIN32)
set_target_properties(OpenAL PROPERTIES COMPILE_FLAGS "/wd4005 /wd4098")
endif()
endif()
include_directories(deps/openal-soft/include)
endif() endif()
include_directories(${OPENAL_INCLUDE_DIRS})
string(REPLACE ";" " " OPENAL_LDFLAGS_STR "${OPENAL_LDFLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OPENAL_LDFLAGS_STR}")
set(LOVR_OPENAL ${OPENAL_LIBRARIES})
endif() endif()
# OpenGL # OpenGL
if(NOT (WIN32 OR OCULUS_ANDROID_EMBED)) if(NOT (WIN32 OR EMSCRIPTEN OR OCULUS_ANDROID_EMBED))
find_package(OpenGL REQUIRED) find_package(OpenGL REQUIRED)
include_directories(${OPENGL_INCLUDE_DIRS}) include_directories(${OPENGL_INCLUDE_DIRS})
set(LOVR_OPENGL ${OPENGL_LIBRARIES}) set(LOVR_OPENGL ${OPENGL_LIBRARIES})
endif() endif()
if(NOT (EMSCRIPTEN OR LOVR_USE_OCULUS OR OCULUS_ANDROID_EMBED)) # OpenVR
if(LOVR_ENABLE_HEADSET AND LOVR_USE_OPENVR)
set(BUILD_SHARED ON CACHE BOOL "") set(BUILD_SHARED ON CACHE BOOL "")
set(BUILD_UNIVERSAL OFF CACHE BOOL "") set(BUILD_UNIVERSAL OFF CACHE BOOL "")
include_directories(deps/openvr/headers) include_directories(deps/openvr/headers)
include_directories(deps/openvr/src) include_directories(deps/openvr/src)
include_directories(deps/openvr/src/vrcommon) include_directories(deps/openvr/src/vrcommon)
add_subdirectory(deps/openvr openvr_api) if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
set_target_properties(openvr_api PROPERTIES set(LOVR_OPENVR openvr_api64)
else()
set(LOVR_OPENVR openvr_api)
endif()
add_subdirectory(deps/openvr)
set_target_properties(${LOVR_OPENVR} PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/openvr_api" LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/openvr_api"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/openvr_api" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/openvr_api"
) )
set(LOVR_OPENVR openvr_api) endif()
# Oculus SDK
if (LOVR_ENABLE_HEADSET AND LOVR_USE_OCULUS AND LOVR_OCULUS_PATH)
if(CMAKE_BUILD_TYPE STREQUAL "Release")
set(OCULUS_BUILD_TYPE "Release")
else()
set(OCULUS_BUILD_TYPE "Debug")
endif()
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(OCULUS_ARCH "x64")
else()
set(OCULUS_ARCH "Win32")
endif()
include_directories("${LOVR_OCULUS_PATH}/LibOVR/Include")
link_directories("${LOVR_OCULUS_PATH}/LibOVR/Lib/Windows/${OCULUS_ARCH}/${OCULUS_BUILD_TYPE}/VS2017")
set(LOVR_OCULUS LibOVR)
endif()
# pthreads
if(NOT WIN32)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
set(LOVR_PTHREADS Threads::Threads)
endif() endif()
if (NOT OCULUS_ANDROID_EMBED) if (NOT OCULUS_ANDROID_EMBED)
@ -236,166 +311,225 @@ set(FAKE_SRC src/headset/fake.c)
endif() endif()
# LÖVR # LÖVR
set(LOVR_SRC
src/api/audio.c
src/api/data.c
src/api/event.c add_executable(lovr
src/api/filesystem.c
src/api/graphics.c
src/api/headset.c
src/api/math.c
src/api/physics.c
src/api/timer.c
src/api/types/animator.c
src/api/types/audioStream.c
src/api/types/blob.c
src/api/types/canvas.c
src/api/types/collider.c
src/api/types/controller.c
src/api/types/font.c
src/api/types/joints.c
src/api/types/material.c
src/api/types/mesh.c
src/api/types/model.c
src/api/types/modelData.c
src/api/types/randomGenerator.c
src/api/types/rasterizer.c
src/api/types/shader.c
src/api/types/shapes.c
src/api/types/source.c
src/api/types/texture.c
src/api/types/textureData.c
src/api/types/transform.c
src/api/types/vertexData.c
src/api/types/world.c
src/audio/audio.c
src/audio/source.c
src/data/audioStream.c
src/data/data.c
src/data/modelData.c
src/data/rasterizer.c
src/data/textureData.c
src/data/vertexData.c
src/event/event.c
src/filesystem/blob.c
src/filesystem/file.c
src/filesystem/filesystem.c
src/graphics/animator.c
src/graphics/canvas.c
src/graphics/font.c
src/graphics/graphics.c
src/graphics/material.c
src/graphics/mesh.c
src/graphics/model.c
src/graphics/shader.c
src/graphics/texture.c
${FAKE_SRC}
src/headset/headset.c
src/lib/glad/glad.c
src/lib/lua-cjson/fpconv.c
src/lib/lua-cjson/lua_cjson.c
src/lib/lua-cjson/strbuf.c
src/lib/lua-enet/enet.c
src/lib/map/map.c
src/lib/stb/stb_image.c
src/lib/stb/stb_image_write.c
src/lib/stb/stb_vorbis.c
src/lib/vec/vec.c
src/lovr.c
src/luax.c
src/main.c src/main.c
src/math/mat4.c
src/math/math.c
src/math/quat.c
src/math/randomGenerator.c
src/math/transform.c
src/math/vec3.c
src/physics/physics.c
src/resources/shaders.c
src/timer/timer.c
src/util.c src/util.c
src/luax.c
src/api/lovr.c
src/lib/map/map.c
src/lib/vec/vec.c
) )
set(LOVR_USE_OCULUS OFF CACHE BOOL "") set_target_properties(lovr PROPERTIES C_STANDARD 99)
if(EMSCRIPTEN) target_include_directories(lovr PRIVATE src)
set(LOVR_HEADSET src/headset/webvr.c)
elseif(LOVR_USE_OCULUS)
set(LOVR_HEADSET src/headset/oculus.c)
include_directories(C:/Users/Andi/Downloads/ovr_sdk_win_1.26.0_public/OculusSDK/LibOVR/Include)
add_definitions(-DLOVR_OVR) # FIXME: NO
elseif (OCULUS_ANDROID_EMBED)
add_definitions(-DLOVR_OVR_MOBILE=1)
add_definitions(-DNO_WINDOW=1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-narrowing")
set(LOVR_SRC ${LOVR_SRC} src/headset/oculus_mobile.c ../BridgeLovr.cpp ../BridgeLovrPrintOverride.cpp)
include_directories(../include)
else()
set(LOVR_SRC ${LOVR_SRC} src/headset/openvr.c)
endif()
if(WIN32)
if(MSVC_VERSION VERSION_LESS 1900)
add_definitions(-Dinline=__inline)
add_definitions(-Dsnprintf=_snprintf)
endif()
endif()
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
if (OCULUS_ANDROID_EMBED)
get_filename_component(CURRENT_PARENT_DIR ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
get_filename_component(CURRENT_GRANDPARENT_DIR ${CURRENT_PARENT_DIR} DIRECTORY)
if(CMAKE_BUILD_TYPE STREQUAL "Release")
set(OCULUS_MOBILE_BUILD_TYPE "release")
else()
set(OCULUS_MOBILE_BUILD_TYPE "debug")
endif()
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(OCULUS_MOBILE_ARCH "arm64-v8a")
else()
set(OCULUS_MOBILE_ARCH "armeabi-v7a")
endif()
# For Oculus mobile ONLY we build as a library.
add_library(lovr SHARED ${LOVR_SRC} ${LOVR_HEADSET})
target_link_libraries(lovr log EGL
${CURRENT_GRANDPARENT_DIR}/deps/openal-soft-gradle/build/intermediates/ndkBuild/${OCULUS_MOBILE_BUILD_TYPE}/obj/local/${OCULUS_MOBILE_ARCH}/libopenal.so
)
else()
add_executable(lovr ${LOVR_SRC} ${LOVR_HEADSET})
endif()
target_link_libraries(lovr target_link_libraries(lovr
${LOVR_ASSIMP} ${LOVR_ASSIMP}
${LOVR_ENET} ${LOVR_ENET}
${LOVR_FREETYPE}
${LOVR_GLFW} ${LOVR_GLFW}
${LOVR_LUA} ${LOVR_LUA}
${LOVR_MSDF} ${LOVR_MSDF}
${LOVR_ODE} ${LOVR_ODE}
${LOVR_OPENAL} ${LOVR_OPENAL}
${LOVR_OPENGL} ${LOVR_OPENGL}
${LOVR_OPENVR}
${LOVR_OCULUS}
${LOVR_PHYSFS} ${LOVR_PHYSFS}
${LOVR_PTHREADS}
${LOVR_EMSCRIPTEN_FLAGS} ${LOVR_EMSCRIPTEN_FLAGS}
) )
if (NOT (LOVR_USE_OCULUS OR OCULUS_ANDROID_EMBED)) if(LOVR_ENABLE_AUDIO)
target_link_libraries(lovr ${LOVR_OPENVR}) add_definitions(-DLOVR_ENABLE_AUDIO)
target_sources(lovr PRIVATE
src/audio/audio.c
src/audio/microphone.c
src/audio/source.c
src/api/audio.c
src/api/types/source.c
src/api/types/microphone.c
)
endif() endif()
if(LOVR_ENABLE_DATA)
add_definitions(-DLOVR_ENABLE_DATA)
target_sources(lovr PRIVATE
src/data/audioStream.c
src/data/blob.c
src/data/modelData.c
src/data/rasterizer.c
src/data/soundData.c
src/data/textureData.c
src/data/vertexData.c
src/api/data.c
src/api/types/audioStream.c
src/api/types/blob.c
src/api/types/modelData.c
src/api/types/rasterizer.c
src/api/types/soundData.c
src/api/types/textureData.c
src/api/types/vertexData.c
src/lib/stb/stb_image.c
src/lib/stb/stb_image_write.c
src/lib/stb/stb_truetype.c
src/lib/stb/stb_vorbis.c
)
if (LOVR_USE_ASSIMP)
add_definitions(-DLOVR_USE_ASSIMP)
endif()
endif()
if(LOVR_ENABLE_EVENT)
add_definitions(-DLOVR_ENABLE_EVENT)
target_sources(lovr PRIVATE
src/event/event.c
src/api/event.c
)
endif()
if(LOVR_ENABLE_FILESYSTEM)
add_definitions(-DLOVR_ENABLE_FILESYSTEM)
target_sources(lovr PRIVATE
src/filesystem/file.c
src/filesystem/filesystem.c
src/api/filesystem.c
)
endif()
if(LOVR_ENABLE_GRAPHICS)
add_definitions(-DLOVR_ENABLE_GRAPHICS)
target_sources(lovr PRIVATE
src/graphics/animator.c
src/graphics/font.c
src/graphics/graphics.c
src/graphics/material.c
src/graphics/model.c
src/graphics/opengl.c
src/api/graphics.c
src/api/types/animator.c
src/api/types/canvas.c
src/api/types/font.c
src/api/types/material.c
src/api/types/mesh.c
src/api/types/model.c
src/api/types/shader.c
src/api/types/shaderBlock.c
src/api/types/texture.c
src/resources/shaders.c
src/lib/glad/glad.c
)
endif()
if(LOVR_ENABLE_HEADSET)
add_definitions(-DLOVR_ENABLE_HEADSET)
target_sources(lovr PRIVATE
src/api/headset.c
src/api/types/controller.c
src/headset/headset.c
)
if(LOVR_USE_OPENVR)
add_definitions(-DLOVR_USE_OPENVR)
target_sources(lovr PRIVATE src/headset/openvr.c)
endif()
if(LOVR_USE_OCULUS)
add_definitions(-DLOVR_USE_OCULUS)
target_sources(lovr PRIVATE src/headset/oculus.c)
endif()
if(LOVR_USE_WEBVR)
add_definitions(-DLOVR_USE_WEBVR)
target_sources(lovr PRIVATE src/headset/webvr.c)
endif()
if(LOVR_USE_FAKE_HEADSET)
add_definitions(-DLOVR_USE_FAKE_HEADSET)
target_sources(lovr PRIVATE src/headset/fake.c)
endif()
endif()
if(LOVR_ENABLE_MATH)
add_definitions(-DLOVR_ENABLE_MATH)
target_sources(lovr PRIVATE
src/math/mat4.c
src/math/math.c
src/math/quat.c
src/math/randomGenerator.c
src/math/transform.c
src/math/vec3.c
src/api/math.c
src/api/types/randomGenerator.c
src/api/types/transform.c
src/lib/noise1234/noise1234.c
)
if(LOVR_USE_SSE)
add_definitions(-DLOVR_USE_SSE)
endif()
endif()
if(LOVR_ENABLE_PHYSICS)
add_definitions(-DLOVR_ENABLE_PHYSICS)
target_sources(lovr PRIVATE
src/physics/physics.c
src/api/physics.c
src/api/types/collider.c
src/api/types/joints.c
src/api/types/shapes.c
src/api/types/world.c
)
endif()
if(LOVR_ENABLE_THREAD)
add_definitions(-DLOVR_ENABLE_THREAD)
target_sources(lovr PRIVATE
src/thread/channel.c
src/thread/thread.c
src/api/thread.c
src/api/types/channel.c
src/api/types/thread.c
src/lib/tinycthread/tinycthread.c
)
endif()
if(LOVR_ENABLE_TIMER)
add_definitions(-DLOVR_ENABLE_TIMER)
target_sources(lovr PRIVATE src/timer/timer.c src/api/timer.c)
endif()
if(LOVR_ENABLE_ENET)
add_definitions(-DLOVR_ENABLE_ENET)
target_sources(lovr PRIVATE src/lib/lua-enet/enet.c)
endif()
if(LOVR_ENABLE_JSON)
add_definitions(-DLOVR_ENABLE_JSON)
target_sources(lovr PRIVATE
src/lib/lua-cjson/fpconv.c
src/lib/lua-cjson/lua_cjson.c
src/lib/lua-cjson/strbuf.c
)
endif()
# Yay Windows
if(WIN32) if(WIN32)
set_target_properties(lovr PROPERTIES COMPILE_FLAGS "/wd4244") set_target_properties(lovr PROPERTIES COMPILE_FLAGS "/wd4244")
set_target_properties(lovr PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE") set_target_properties(lovr PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
set_target_properties(lovr PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:windows /ENTRY:mainCRTStartup") set_target_properties(lovr PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
if(MSVC_VERSION VERSION_LESS 1900)
target_compile_definitions(lovr PUBLIC -Dinline=__inline -Dsnprintf=_snprintf)
endif()
function(move_dll ARG_TARGET) function(move_dll ARG_TARGET)
add_custom_command(TARGET lovr POST_BUILD if(TARGET ${ARG_TARGET})
COMMAND ${CMAKE_COMMAND} -E copy add_custom_command(TARGET lovr POST_BUILD
$<TARGET_FILE:${ARG_TARGET}> COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>/$<TARGET_FILE_NAME:${ARG_TARGET}> $<TARGET_FILE:${ARG_TARGET}>
) ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>/$<TARGET_FILE_NAME:${ARG_TARGET}>
)
endif()
endfunction() endfunction()
move_dll(${LOVR_ASSIMP}) move_dll(${LOVR_ASSIMP})

View File

@ -1,159 +0,0 @@
Compiling LÖVR
===
You might want to compile LÖVR from source so you can use LÖVR on other operating systems or create
a custom build. Below is a guide for setting up all the dependencies and compiling the code on
various operating systems.
Dependencies
---
- Lua or LuaJIT
- GLFW (3.2+)
- OpenGL (3.3, ES3, or WebGL 2)
- assimp
- OpenVR (1.0.9, for `lovr.headset`)
- PhysicsFS
- OpenAL (1.17+ recommended for HRTF support)
- FreeType
- msdfgen
- ODE (for `lovr.physics`)
- Emscripten (optional, for compiling for web)
These can be found as submodules in the `deps` directory of the repository. To initialize the
submodules, clone LÖVR with the `--recursive` option or run this command in an existing repo:
```sh
git submodule update --init
```
Windows
---
From the `lovr` folder, run these commands to create a build folder and compile the project using
CMake:
```sh
mkdir build
cd build
cmake ..
cmake --build .
```
The executable will then exist at `/path/to/lovr/build/Debug/lovr.exe`. A LÖVR project (a folder
containing a `main.lua` script) can then be dropped onto `lovr.exe` to run it, or it can be run
via the command line as `lovr.exe path/to/project`.
macOS
---
Install the dependencies using your package manager of choice:
```sh
brew install assimp glfw3 luajit physfs freetype openal-soft ode
```
Next, build using CMake, as above:
```sh
mkdir build
cd build
cmake ..
cmake --build .
```
The `lovr` executable should exist in `lovr/build` now. It's recommended to set up an alias or
symlink so that this executable exists on your path. Once that's done, you can run a game like this:
```sh
lovr /path/to/myGame
```
You may need to set the `PKG_CONFIG_PATH` environment variable for OpenAL to be located properly.
If you run into this, see [Troubleshooting](#troubleshooting) below for more info.
Linux
---
First, install the dependencies using your package manager of choice.
#### Arch Linux
```sh
pacman -S assimp glfw-x11 luajit physfs freetype2 openal ode
```
#### Debian/Ubuntu
```sh
sudo apt-get install build-essential cmake libassimp-dev libglfw3-dev libluajit-5.1-dev libphysfs-dev libfreetype6-dev libopenal-dev libode-dev
```
Then, build with CMake:
```sh
mkdir build
cd build
cmake ..
cmake --build .
```
On Linux, LÖVR needs to run within the Steam Runtime. To do this, first [install
Steam](https://wiki.archlinux.org/index.php/Steam#Installation). Next, [install the Steam udev
rules](https://github.com/ValveSoftware/SteamVR-for-Linux#usb-device-requirements). Then, run LÖVR
within the Steam runtime:
```sh
~/.steam/steam/ubuntu12_32/steam-runtime/run.sh lovr
```
If you receive errors related to `libstdc++`, set the `LD_PRELOAD` environment variable when running
the command:
```sh
LD_PRELOAD='/usr/$LIB/libstdc++.so.6 /usr/$LIB/libgcc_s.so.1' ~/.steam/steam/ubuntu12_32/steam-runtime/run.sh lovr
```
WebVR
---
First, install the Emscripten SDK. Make sure you're running [this
branch](https://github.com/bjornbytes/emscripten/tree/lovr) of Emscripten.
Unix:
```sh
mkdir build
cd build
emcmake cmake ..
emmake make -j2
```
Windows (from a Visual Studio Command Prompt, make sure the Emscripten SDK is on PATH):
```sh
mkdir build
cd build
emcmake cmake -G "NMake Makefiles" ..
emmake nmake
```
The above commands will output `lovr.js` and `lovr.wasm`. To package a game, run:
```
python /path/to/emscripten/tools/file_packager.py game.data --no-heap-copy --preload /path/to/game@/ --js-output=game.js
```
Which will output `game.js` and `game.data`. You can then include `lovr.js` and `game.js` on an
HTML page with a canvas element. Check out [`lovr-webvr-server`](https://github.com/bjornbytes/lovr-webvr-server/blob/master/views/index.ejs) for an example.
Troubleshooting
---
- If you get "CMake no CMAKE_CXX_COMPILER found" on Windows, then install Visual Studio and create a
blank C++ project, which will prompt you to install the compilers and tools necessary to compile
LÖVR.
- On macOS, if you run into an error message about not being able to find OpenAL, make sure you've
added the proper OpenAL directory (usually something like
`/usr/local/opt/openal-soft/lib/pkgconfig`) to your `PKG_CONFIG_PATH` environment variable.
Installing openal-soft with brew will print out a message telling you how to do this.

View File

@ -18,9 +18,9 @@ Contributing Documentation
--- ---
If you see any typos or inconsistencies in the docs, submitting an issue or pull request in the If you see any typos or inconsistencies in the docs, submitting an issue or pull request in the
[lovr.org repo](https://github.com/bjornbytes/lovr.org) would be greatly appreciated! There, each [lovr-docs repo](https://github.com/bjornbytes/lovr-docs) would be greatly appreciated! The `api`
documentation page is a markdown file in the `docs` folder, and examples can be found in the folder has Lua files for each function, the `guides` folder contains tutorials in markdown format,
`examples` folder. and the `examples` folder has source code for sample projects and other demos.
Contributing Code Contributing Code
--- ---

112
README.md
View File

@ -1,40 +1,42 @@
<p align="center"><a href="http://lovr.org"><img src="src/resources/logo.png" width="160"></a></p> # <p align="center"><a href="https://lovr.org"><img src="https://lovr.org/static/img/README.png" width="256"/></a></p>
<h1 align="center">LÖVR</h1> > **A simple Lua framework for rapidly building VR experiences.**
LÖVR is a simple framework for creating virtual reality experiences with Lua, based on [LÖVE](http://love2d.org). Inspired by [LÖVE](https://love2d.org), a 2D game framework.
[**Homepage**](https://lovr.org) | [**Documentation**](https://lovr.org/docs) | [**Slack**](https://join.slack.com/ifyouwannabemylovr/shared_invite/MTc5ODk2MjE0NDM3LTE0OTQxMTIyMDEtMzdhOGVlODFhYg)
[![Build status](https://ci.appveyor.com/api/projects/status/alx3kdi35bmxka8c/branch/master?svg=true)](https://ci.appveyor.com/project/bjornbytes/lovr/branch/master) [![Build status](https://ci.appveyor.com/api/projects/status/alx3kdi35bmxka8c/branch/master?svg=true)](https://ci.appveyor.com/project/bjornbytes/lovr/branch/master)
[![Version](https://img.shields.io/github/release/bjornbytes/lovr.svg?label=version)](https://github.com/bjornbytes/lovr/releases) [![Version](https://img.shields.io/github/release/bjornbytes/lovr.svg?label=version)](https://github.com/bjornbytes/lovr/releases)
[![Slack](https://img.shields.io/badge/chat-slack-7e4e76.svg)](https://lovr.org/slack)
Features [**Website**](https://lovr.org) | [**Documentation**](https://lovr.org/docs) | [**FAQ**](https://lovr.org/docs/FAQ)
---
- Easily create VR using simple Lua scripts <p align="left">
- Automatically detects and renders to connected VR headsets (works without a headset too!)
- 3D graphics API supporting primitives, fonts, shaders, skyboxes, framebuffers, etc.
- Import 3D models from obj, fbx, collada, or glTF files, including materials and animations.
- Create projects for Windows, macOS, Linux, or WebVR
- Spatialized audio
- 3D physics
Screenshots
---
<p align="center">
<span><img src="http://lovr.org/static/img/wattle.jpg" width="32%"/></span> <span><img src="http://lovr.org/static/img/wattle.jpg" width="32%"/></span>
<span><img src="http://lovr.org/static/img/levrage.jpg" width="32%"/></span> <span><img src="http://lovr.org/static/img/levrage.jpg" width="32%"/></span>
<span><img src="http://lovr.org/static/img/planets.jpg" width="32%"/></span> <span><img src="http://lovr.org/static/img/planets.jpg" width="32%"/></span>
</p> </p>
Features
---
- **Cross-Platform** - Runs on Windows, Mac, Linux, and even on the web using WebAssembly and WebVR.
- **Cross-Device** - Support for many HMDs including HTC Vive, Oculus Touch, and Windows MR headsets. There's also a keyboard/mouse VR simulator so you can prototype without hardware.
- **Beginner-friendly** - Simple VR scenes can be created in just a few lines of Lua.
- **Fast** - Writen in C99 and scripted with LuaJIT, includes optimized single-pass stereo rendering.
- **Asset Import** - Supports 3D models (glTF, FBX, OBJ), skeletal animation, HDR textures, cubemaps, fonts, etc.
- **Spatialized Audio** - Audio is automatically spatialized using HRTFs.
- **3D Rigid Body Physics** - Including 4 collider shapes and 4 joint types.
- **Compute Shaders** - For high performance GPU tasks, like particles.
- **Multiplayer** - Includes enet for multi-user VR experiences.
- **Umlauts** - !!!
Getting Started Getting Started
--- ---
You can download precompiled binaries from the [website](https://lovr.org). There, you It's really easy to get started making things with LÖVR! Grab a copy of the executable from <https://lovr.org/download>,
can also find documentation and a set of tutorials and examples. Here is the hello world example then write a `main.lua` script and drag its parent folder onto the executable. Here are some example projects to try:
for LÖVR:
#### Hello World
```lua ```lua
function lovr.draw() function lovr.draw()
@ -42,10 +44,6 @@ function lovr.draw()
end end
``` ```
To run it, first create a folder for your project and put the code in a file called `main.lua`.
Then, just drop the `project` folder onto `lovr.exe` (or run `lovr.exe path/to/project` on the
command line). Put on your headset and you should see the text at the front of your play area!
#### Spinning Cube #### Spinning Cube
```lua ```lua
@ -54,6 +52,19 @@ function lovr.draw()
end end
``` ```
#### Hand Tracking
```lua
function lovr.draw()
controllers = lovr.headset.getControllers()
for _, controller in ipairs(controllers) do
x, y, z = controller:getPosition()
lovr.graphics.sphere(x, y, z, .1)
end
end
```
#### 3D Models #### 3D Models
```lua ```lua
@ -62,42 +73,35 @@ function lovr.load()
end end
function lovr.draw() function lovr.draw()
model:draw() local x, y, z = 0, 0, 0
model:draw(x, y, z)
end end
``` ```
#### Audio You can also find lots of other WebVR examples on the [docs page](https://lovr.org/docs/Hello_World).
```lua Building
function lovr.load() ---
local sound = lovr.audio.newSource('darudeSandstorm.ogg')
sound:play() Here's how to compile LÖVR using CMake:
end
```console
cd build
cmake ..
cmake --build .
``` ```
Documentation For more help, see the [Compiling Guide](https://lovr.org/docs/Compiling).
Resources
--- ---
Documentation and examples are available on the [website](https://lovr.org/docs). - [**Documentation**](https://lovr.org/docs): Guides, tutorials, examples, and API documentation.
- [**FAQ**](https://lovr.org/docs/FAQ): Frequently Asked Questions.
Community - [**Slack Group**](https://lovr.org/slack): For general LÖVR discussion and support.
--- - [**Nightly Builds**](https://lovr.org/download/nightly): Nightly builds for Windows.
- [**Compiling Guide**](https://lovr.org/docs/Compiling): Information on compiling LÖVR from source.
> If you wanna be my LÖVR, you gotta get with my friends - [**Contributing**](CONTRIBUTING.md): Guide for helping out with development :heart:
> *- Spice Girls*
Feel free to join the [LÖVR Slack](https://join.slack.com/ifyouwannabemylovr/shared_invite/MTc5ODk2MjE0NDM3LTE0OTQxMTIyMDEtMzdhOGVlODFhYg) for questions, info, and other discussion.
Compiling
---
To compile from source to create a custom build or contribute to LÖVR, see
[`COMPILING`](COMPILING.md).
Contributing
---
Contributions are welcome! See [`CONTRIBUTING`](CONTRIBUTING.md) for more information.
License License
--- ---

View File

@ -1,17 +0,0 @@
clone_depth: 1
only_commits:
files:
- src/
- deps/
before_build:
- cd C:\projects\lovr
- git submodule update --init
- md build
- cd build
- cmake -DCMAKE_BUILD_TYPE=%configuration% ..
build:
project: build\lovr.sln
verbosity: quiet

1
build/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*

1
deps/freetype vendored

@ -1 +0,0 @@
Subproject commit 8529e84fb4bb22abfc5c4d9cafbb06025a8b909d

2
deps/msdfgen vendored

@ -1 +1 @@
Subproject commit 8577a0211b442805214eeeb264a8ca7cb8d9439e Subproject commit 9ba5b173f55df905cf765d5d5de99613b678ec1d

2
deps/openal-soft vendored

@ -1 +1 @@
Subproject commit 18bb46163af68a5d7299733cd1e5f194be61d496 Subproject commit 9c5307a48a58959a564be1999b119a87b7cdb8e0

123
src/api.h
View File

@ -1,32 +1,23 @@
#include "luax.h" #include "luax.h"
#include "filesystem/blob.h"
#include "graphics/mesh.h" #ifdef _WIN32
#include "math/math.h" #define LOVR_API __declspec(dllexport)
#include "math/randomGenerator.h" #else
#include "physics/physics.h" #define LOVR_API
#include "lib/map/map.h" #endif
// Module loaders // Module loaders
int l_lovrAudioInit(lua_State* L); LOVR_API int luaopen_lovr(lua_State* L);
int l_lovrDataInit(lua_State* L); LOVR_API int luaopen_lovr_audio(lua_State* L);
int l_lovrEventInit(lua_State* L); LOVR_API int luaopen_lovr_data(lua_State* L);
int l_lovrFilesystemInit(lua_State* L); LOVR_API int luaopen_lovr_event(lua_State* L);
int l_lovrGraphicsInit(lua_State* L); LOVR_API int luaopen_lovr_filesystem(lua_State* L);
int l_lovrHeadsetInit(lua_State* L); LOVR_API int luaopen_lovr_graphics(lua_State* L);
int l_lovrMathInit(lua_State* L); LOVR_API int luaopen_lovr_headset(lua_State* L);
int l_lovrPhysicsInit(lua_State* L); LOVR_API int luaopen_lovr_math(lua_State* L);
int l_lovrTimerInit(lua_State* L); LOVR_API int luaopen_lovr_physics(lua_State* L);
LOVR_API int luaopen_lovr_thread(lua_State* L);
// Modules LOVR_API int luaopen_lovr_timer(lua_State* L);
extern const luaL_Reg lovrAudio[];
extern const luaL_Reg lovrData[];
extern const luaL_Reg lovrEvent[];
extern const luaL_Reg lovrFilesystem[];
extern const luaL_Reg lovrGraphics[];
extern const luaL_Reg lovrHeadset[];
extern const luaL_Reg lovrMath[];
extern const luaL_Reg lovrPhysics[];
extern const luaL_Reg lovrTimer[];
// Objects // Objects
extern const luaL_Reg lovrAnimator[]; extern const luaL_Reg lovrAnimator[];
@ -36,6 +27,7 @@ extern const luaL_Reg lovrBlob[];
extern const luaL_Reg lovrBoxShape[]; extern const luaL_Reg lovrBoxShape[];
extern const luaL_Reg lovrCanvas[]; extern const luaL_Reg lovrCanvas[];
extern const luaL_Reg lovrCapsuleShape[]; extern const luaL_Reg lovrCapsuleShape[];
extern const luaL_Reg lovrChannel[];
extern const luaL_Reg lovrController[]; extern const luaL_Reg lovrController[];
extern const luaL_Reg lovrCylinderShape[]; extern const luaL_Reg lovrCylinderShape[];
extern const luaL_Reg lovrCollider[]; extern const luaL_Reg lovrCollider[];
@ -45,61 +37,56 @@ extern const luaL_Reg lovrHingeJoint[];
extern const luaL_Reg lovrJoint[]; extern const luaL_Reg lovrJoint[];
extern const luaL_Reg lovrMaterial[]; extern const luaL_Reg lovrMaterial[];
extern const luaL_Reg lovrMesh[]; extern const luaL_Reg lovrMesh[];
extern const luaL_Reg lovrMicrophone[];
extern const luaL_Reg lovrModel[]; extern const luaL_Reg lovrModel[];
extern const luaL_Reg lovrModelData[]; extern const luaL_Reg lovrModelData[];
extern const luaL_Reg lovrRandomGenerator[]; extern const luaL_Reg lovrRandomGenerator[];
extern const luaL_Reg lovrRasterizer[]; extern const luaL_Reg lovrRasterizer[];
extern const luaL_Reg lovrShader[]; extern const luaL_Reg lovrShader[];
extern const luaL_Reg lovrShaderBlock[];
extern const luaL_Reg lovrShape[]; extern const luaL_Reg lovrShape[];
extern const luaL_Reg lovrSliderJoint[]; extern const luaL_Reg lovrSliderJoint[];
extern const luaL_Reg lovrSoundData[];
extern const luaL_Reg lovrSource[]; extern const luaL_Reg lovrSource[];
extern const luaL_Reg lovrSphereShape[]; extern const luaL_Reg lovrSphereShape[];
extern const luaL_Reg lovrTexture[]; extern const luaL_Reg lovrTexture[];
extern const luaL_Reg lovrTextureData[]; extern const luaL_Reg lovrTextureData[];
extern const luaL_Reg lovrThread[];
extern const luaL_Reg lovrTransform[]; extern const luaL_Reg lovrTransform[];
extern const luaL_Reg lovrVertexData[]; extern const luaL_Reg lovrVertexData[];
extern const luaL_Reg lovrWorld[]; extern const luaL_Reg lovrWorld[];
// Enums // Enums
extern map_int_t ArcModes; extern const char* ArcModes[];
extern map_int_t AttributeTypes; extern const char* AttributeTypes[];
extern map_int_t BlendAlphaModes; extern const char* BlendAlphaModes[];
extern map_int_t BlendModes; extern const char* BlendModes[];
extern map_int_t CanvasTypes; extern const char* BufferUsages[];
extern map_int_t CompareModes; extern const char* CompareModes[];
extern map_int_t ControllerAxes; extern const char* ControllerAxes[];
extern map_int_t ControllerButtons; extern const char* ControllerButtons[];
extern map_int_t ControllerHands; extern const char* ControllerHands[];
extern map_int_t DrawModes; extern const char* DrawModes[];
extern map_int_t EventTypes; extern const char* EventTypes[];
extern map_int_t FilterModes; extern const char* FilterModes[];
extern map_int_t HeadsetEyes; extern const char* HeadsetDrivers[];
extern map_int_t HeadsetOrigins; extern const char* HeadsetEyes[];
extern map_int_t HeadsetTypes; extern const char* HeadsetOrigins[];
extern map_int_t HorizontalAligns; extern const char* HeadsetTypes[];
extern map_int_t JointTypes; extern const char* HorizontalAligns[];
extern map_int_t MaterialColors; extern const char* JointTypes[];
extern map_int_t MaterialScalars; extern const char* MaterialColors[];
extern map_int_t MaterialTextures; extern const char* MaterialScalars[];
extern map_int_t MatrixTypes; extern const char* MaterialTextures[];
extern map_int_t MeshDrawModes; extern const char* MeshDrawModes[];
extern map_int_t MeshUsages; extern const char* ShaderTypes[];
extern map_int_t PolygonWindings; extern const char* ShapeTypes[];
extern map_int_t ShapeTypes; extern const char* SourceTypes[];
extern map_int_t TextureFormats; extern const char* StencilActions[];
extern map_int_t TimeUnits; extern const char* TextureFormats[];
extern map_int_t VerticalAligns; extern const char* TextureTypes[];
extern map_int_t WrapModes; extern const char* TimeUnits[];
extern const char* UniformAccesses[];
// Shared helpers extern const char* VerticalAligns[];
void luax_checkvertexformat(lua_State* L, int index, VertexFormat* format); extern const char* Windings[];
int luax_pushvertexformat(lua_State* L, VertexFormat* format); extern const char* WrapModes[];
int luax_pushvertexattribute(lua_State* L, VertexPointer* vertex, Attribute attribute);
int luax_pushvertex(lua_State* L, VertexPointer* vertex, VertexFormat* format);
void luax_setvertexattribute(lua_State* L, int index, VertexPointer* vertex, Attribute attribute);
void luax_setvertex(lua_State* L, int index, VertexPointer* vertex, VertexFormat* format);
int luax_readtransform(lua_State* L, int index, mat4 transform, bool uniformScale);
Blob* luax_readblob(lua_State* L, int index, const char* debug);
int luax_pushshape(lua_State* L, Shape* shape);
int luax_pushjoint(lua_State* L, Joint* joint);
Seed luax_checkrandomseed(lua_State* L, int index);

View File

@ -1,29 +1,55 @@
#include "api.h" #include "api.h"
#include "api/data.h"
#include "audio/audio.h" #include "audio/audio.h"
#include "audio/source.h" #include "audio/source.h"
#include "data/audioStream.h" #include "data/audioStream.h"
map_int_t TimeUnits; const char* SourceTypes[] = {
[SOURCE_STATIC] = "static",
[SOURCE_STREAM] = "stream",
NULL
};
int l_lovrAudioInit(lua_State* L) { const char* TimeUnits[] = {
lua_newtable(L); [UNIT_SECONDS] = "seconds",
luaL_register(L, NULL, lovrAudio); [UNIT_SAMPLES] = "samples",
luax_registertype(L, "Source", lovrSource); NULL
};
map_init(&TimeUnits); static int l_lovrAudioUpdate(lua_State* L) {
map_set(&TimeUnits, "seconds", UNIT_SECONDS);
map_set(&TimeUnits, "samples", UNIT_SAMPLES);
lovrAudioInit();
return 1;
}
int l_lovrAudioUpdate(lua_State* L) {
lovrAudioUpdate(); lovrAudioUpdate();
return 0; return 0;
} }
int l_lovrAudioGetOrientation(lua_State* L) { static int l_lovrAudioGetDopplerEffect(lua_State* L) {
float factor, speedOfSound;
lovrAudioGetDopplerEffect(&factor, &speedOfSound);
lua_pushnumber(L, factor);
lua_pushnumber(L, speedOfSound);
return 2;
}
static int l_lovrAudioGetMicrophoneNames(lua_State* L) {
const char* names[MAX_MICROPHONES];
uint8_t count;
lovrAudioGetMicrophoneNames(names, &count);
if (lua_istable(L, 1)) {
lua_settop(L, 1);
} else {
lua_settop(L, 0);
lua_createtable(L, count, 0);
}
for (int i = 0; i < count; i++) {
lua_pushstring(L, names[i]);
lua_rawseti(L, -2, i + 1);
}
return 1;
}
static int l_lovrAudioGetOrientation(lua_State* L) {
float angle, ax, ay, az; float angle, ax, ay, az;
lovrAudioGetOrientation(&angle, &ax, &ay, &az); lovrAudioGetOrientation(&angle, &ax, &ay, &az);
lua_pushnumber(L, angle); lua_pushnumber(L, angle);
@ -33,7 +59,7 @@ int l_lovrAudioGetOrientation(lua_State* L) {
return 4; return 4;
} }
int l_lovrAudioGetPosition(lua_State* L) { static int l_lovrAudioGetPosition(lua_State* L) {
float x, y, z; float x, y, z;
lovrAudioGetPosition(&x, &y, &z); lovrAudioGetPosition(&x, &y, &z);
lua_pushnumber(L, x); lua_pushnumber(L, x);
@ -42,7 +68,7 @@ int l_lovrAudioGetPosition(lua_State* L) {
return 3; return 3;
} }
int l_lovrAudioGetVelocity(lua_State* L) { static int l_lovrAudioGetVelocity(lua_State* L) {
float x, y, z; float x, y, z;
lovrAudioGetVelocity(&x, &y, &z); lovrAudioGetVelocity(&x, &y, &z);
lua_pushnumber(L, x); lua_pushnumber(L, x);
@ -51,53 +77,91 @@ int l_lovrAudioGetVelocity(lua_State* L) {
return 3; return 3;
} }
int l_lovrAudioGetVolume(lua_State* L) { static int l_lovrAudioGetVolume(lua_State* L) {
lua_pushnumber(L, lovrAudioGetVolume()); lua_pushnumber(L, lovrAudioGetVolume());
return 1; return 1;
} }
int l_lovrAudioIsSpatialized(lua_State* L) { static int l_lovrAudioIsSpatialized(lua_State* L) {
lua_pushboolean(L, lovrAudioIsSpatialized()); lua_pushboolean(L, lovrAudioIsSpatialized());
return 1; return 1;
} }
int l_lovrAudioNewSource(lua_State* L) { static int l_lovrAudioNewMicrophone(lua_State* L) {
void** type; const char* name = luaL_optstring(L, 1, NULL);
AudioStream* stream; int samples = luaL_optinteger(L, 2, 1024);
if ((type = luax_totype(L, 1, AudioStream)) != NULL) { int sampleRate = luaL_optinteger(L, 3, 8000);
stream = *type; int bitDepth = luaL_optinteger(L, 4, 16);
} else { int channelCount = luaL_optinteger(L, 5, 1);
Blob* blob = luax_readblob(L, 1, "Source"); Microphone* microphone = lovrMicrophoneCreate(name, samples, sampleRate, bitDepth, channelCount);
stream = lovrAudioStreamCreate(blob, 4096); luax_pushobject(L, microphone);
lovrRelease(&blob->ref); lovrRelease(microphone);
if (!stream) {
luaL_error(L, "Could not decode Ogg audio source at '%s'", luaL_checkstring(L, 1));
return 0;
}
}
Source* source = lovrSourceCreate(stream);
luax_pushtype(L, Source, source);
lovrRelease(&source->ref);
return 1; return 1;
} }
int l_lovrAudioPause(lua_State* L) { static int l_lovrAudioNewSource(lua_State* L) {
Source* source = NULL;
SoundData* soundData = luax_totype(L, 1, SoundData);
AudioStream* stream = luax_totype(L, 1, AudioStream);
bool isStatic = soundData || luaL_checkoption(L, 2, NULL, SourceTypes) == SOURCE_STATIC;
if (isStatic) {
if (soundData) {
source = lovrSourceCreateStatic(soundData);
} else {
if (stream) {
soundData = lovrSoundDataCreateFromAudioStream(stream);
} else {
Blob* blob = luax_readblob(L, 1, "Source");
soundData = lovrSoundDataCreateFromBlob(blob);
lovrRelease(blob);
}
lovrAssert(soundData, "Could not create static Source");
source = lovrSourceCreateStatic(soundData);
lovrRelease(soundData);
}
} else {
if (stream) {
source = lovrSourceCreateStream(stream);
} else {
Blob* blob = luax_readblob(L, 1, "Source");
stream = lovrAudioStreamCreate(blob, 4096);
lovrAssert(stream, "Could not create stream Source");
source = lovrSourceCreateStream(stream);
lovrRelease(blob);
lovrRelease(stream);
}
}
luax_pushobject(L, source);
lovrRelease(source);
return 1;
}
static int l_lovrAudioPause(lua_State* L) {
lovrAudioPause(); lovrAudioPause();
return 0; return 0;
} }
int l_lovrAudioResume(lua_State* L) { static int l_lovrAudioResume(lua_State* L) {
lovrAudioResume(); lovrAudioResume();
return 0; return 0;
} }
int l_lovrAudioRewind(lua_State* L) { static int l_lovrAudioRewind(lua_State* L) {
lovrAudioRewind(); lovrAudioRewind();
return 0; return 0;
} }
int l_lovrAudioSetOrientation(lua_State* L) { static int l_lovrAudioSetDopplerEffect(lua_State* L) {
float factor = luaL_optnumber(L, 1, 1.);
float speedOfSound = luaL_optnumber(L, 2, 343.29);
lovrAudioSetDopplerEffect(factor, speedOfSound);
return 0;
}
static int l_lovrAudioSetOrientation(lua_State* L) {
float angle = luaL_checknumber(L, 1); float angle = luaL_checknumber(L, 1);
float ax = luaL_checknumber(L, 2); float ax = luaL_checknumber(L, 2);
float ay = luaL_checknumber(L, 3); float ay = luaL_checknumber(L, 3);
@ -106,7 +170,7 @@ int l_lovrAudioSetOrientation(lua_State* L) {
return 0; return 0;
} }
int l_lovrAudioSetPosition(lua_State* L) { static int l_lovrAudioSetPosition(lua_State* L) {
float x = luaL_checknumber(L, 1); float x = luaL_checknumber(L, 1);
float y = luaL_checknumber(L, 2); float y = luaL_checknumber(L, 2);
float z = luaL_checknumber(L, 3); float z = luaL_checknumber(L, 3);
@ -114,7 +178,7 @@ int l_lovrAudioSetPosition(lua_State* L) {
return 0; return 0;
} }
int l_lovrAudioSetVelocity(lua_State* L) { static int l_lovrAudioSetVelocity(lua_State* L) {
float x = luaL_checknumber(L, 1); float x = luaL_checknumber(L, 1);
float y = luaL_checknumber(L, 2); float y = luaL_checknumber(L, 2);
float z = luaL_checknumber(L, 3); float z = luaL_checknumber(L, 3);
@ -122,28 +186,32 @@ int l_lovrAudioSetVelocity(lua_State* L) {
return 0; return 0;
} }
int l_lovrAudioSetVolume(lua_State* L) { static int l_lovrAudioSetVolume(lua_State* L) {
float volume = luaL_checknumber(L, 1); float volume = luaL_checknumber(L, 1);
lovrAudioSetVolume(volume); lovrAudioSetVolume(volume);
return 0; return 0;
} }
int l_lovrAudioStop(lua_State* L) { static int l_lovrAudioStop(lua_State* L) {
lovrAudioStop(); lovrAudioStop();
return 0; return 0;
} }
const luaL_Reg lovrAudio[] = { static const luaL_Reg lovrAudio[] = {
{ "update", l_lovrAudioUpdate }, { "update", l_lovrAudioUpdate },
{ "getDopplerEffect", l_lovrAudioGetDopplerEffect },
{ "getMicrophoneNames", l_lovrAudioGetMicrophoneNames },
{ "getOrientation", l_lovrAudioGetOrientation }, { "getOrientation", l_lovrAudioGetOrientation },
{ "getPosition", l_lovrAudioGetPosition }, { "getPosition", l_lovrAudioGetPosition },
{ "getVelocity", l_lovrAudioGetVelocity }, { "getVelocity", l_lovrAudioGetVelocity },
{ "getVolume", l_lovrAudioGetVolume }, { "getVolume", l_lovrAudioGetVolume },
{ "isSpatialized", l_lovrAudioIsSpatialized }, { "isSpatialized", l_lovrAudioIsSpatialized },
{ "newMicrophone", l_lovrAudioNewMicrophone },
{ "newSource", l_lovrAudioNewSource }, { "newSource", l_lovrAudioNewSource },
{ "pause", l_lovrAudioPause }, { "pause", l_lovrAudioPause },
{ "resume", l_lovrAudioResume }, { "resume", l_lovrAudioResume },
{ "rewind", l_lovrAudioRewind }, { "rewind", l_lovrAudioRewind },
{ "setDopplerEffect", l_lovrAudioSetDopplerEffect },
{ "setOrientation", l_lovrAudioSetOrientation }, { "setOrientation", l_lovrAudioSetOrientation },
{ "setPosition", l_lovrAudioSetPosition }, { "setPosition", l_lovrAudioSetPosition },
{ "setVelocity", l_lovrAudioSetVelocity }, { "setVelocity", l_lovrAudioSetVelocity },
@ -151,3 +219,13 @@ const luaL_Reg lovrAudio[] = {
{ "stop", l_lovrAudioStop }, { "stop", l_lovrAudioStop },
{ NULL, NULL } { NULL, NULL }
}; };
int luaopen_lovr_audio(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrAudio);
luax_atexit(L, lovrAudioDestroy);
luax_registertype(L, "Microphone", lovrMicrophone);
luax_registertype(L, "Source", lovrSource);
lovrAudioInit();
return 1;
}

View File

@ -1,41 +1,56 @@
#include "api.h" #include "api.h"
#include "data/data.h" #include "api/data.h"
#include "data/audioStream.h" #include "data/audioStream.h"
#include "data/modelData.h" #include "data/modelData.h"
#include "data/rasterizer.h" #include "data/rasterizer.h"
#include "data/soundData.h"
#include "data/textureData.h" #include "data/textureData.h"
#include "data/vertexData.h"
int l_lovrDataInit(lua_State* L) { static int l_lovrDataNewBlob(lua_State* L) {
lua_newtable(L); size_t size;
luaL_register(L, NULL, lovrData); uint8_t* data = NULL;
luax_registertype(L, "AudioStream", lovrAudioStream); int type = lua_type(L, 1);
luax_registertype(L, "ModelData", lovrModelData); if (type == LUA_TNUMBER) {
luax_registertype(L, "Rasterizer", lovrRasterizer); size = lua_tonumber(L, 1);
luax_registertype(L, "TextureData", lovrTextureData); data = calloc(1, size);
luax_registertype(L, "VertexData", lovrVertexData); } else if (type == LUA_TSTRING) {
const char* str = luaL_checklstring(L, 1, &size);
data = malloc(size + 1);
memcpy(data, str, size);
data[size] = '\0';
} else {
Blob* blob = luax_checktype(L, 1, Blob);
size = blob->size;
data = malloc(size);
}
const char* name = luaL_optstring(L, 2, "");
Blob* blob = lovrBlobCreate(data, size, name);
luax_pushobject(L, blob);
lovrRelease(blob);
return 1; return 1;
} }
int l_lovrDataNewAudioStream(lua_State* L) { static int l_lovrDataNewAudioStream(lua_State* L) {
Blob* blob = luax_readblob(L, 1, "Sound"); Blob* blob = luax_readblob(L, 1, "AudioStream");
size_t bufferSize = luaL_optinteger(L, 2, 4096); size_t bufferSize = luaL_optinteger(L, 2, 4096);
AudioStream* stream = lovrAudioStreamCreate(blob, bufferSize); AudioStream* stream = lovrAudioStreamCreate(blob, bufferSize);
luax_pushtype(L, AudioStream, stream); luax_pushobject(L, stream);
lovrRelease(&blob->ref); lovrRelease(blob);
lovrRelease(&stream->ref); lovrRelease(stream);
return 1; return 1;
} }
int l_lovrDataNewModelData(lua_State* L) { static int l_lovrDataNewModelData(lua_State* L) {
Blob* blob = luax_readblob(L, 1, "Model"); Blob* blob = luax_readblob(L, 1, "Model");
ModelData* modelData = lovrModelDataCreate(blob); ModelData* modelData = lovrModelDataCreate(blob);
luax_pushtype(L, ModelData, modelData); luax_pushobject(L, modelData);
lovrRelease(&blob->ref); lovrRelease(blob);
lovrRelease(&modelData->ref); lovrRelease(modelData);
return 1; return 1;
} }
int l_lovrDataNewRasterizer(lua_State* L) { static int l_lovrDataNewRasterizer(lua_State* L) {
Blob* blob = NULL; Blob* blob = NULL;
float size; float size;
@ -47,49 +62,114 @@ int l_lovrDataNewRasterizer(lua_State* L) {
} }
Rasterizer* rasterizer = lovrRasterizerCreate(blob, size); Rasterizer* rasterizer = lovrRasterizerCreate(blob, size);
luax_pushtype(L, Rasterizer, rasterizer); luax_pushobject(L, rasterizer);
lovrRelease(blob);
if (blob) { lovrRelease(rasterizer);
lovrRelease(&blob->ref);
}
lovrRelease(&rasterizer->ref);
return 1; return 1;
} }
int l_lovrDataNewTextureData(lua_State* L) { static int l_lovrDataNewSoundData(lua_State* L) {
if (lua_type(L, 1) == LUA_TNUMBER) {
int samples = luaL_checkinteger(L, 1);
int sampleRate = luaL_optinteger(L, 2, 44100);
int bitDepth = luaL_optinteger(L, 3, 16);
int channelCount = luaL_optinteger(L, 4, 2);
SoundData* soundData = lovrSoundDataCreate(samples, sampleRate, bitDepth, channelCount);
luax_pushobject(L, soundData);
lovrRelease(soundData);
return 1;
}
AudioStream* audioStream = luax_totype(L, 1, AudioStream);
if (audioStream) {
SoundData* soundData = lovrSoundDataCreateFromAudioStream(audioStream);
luax_pushobject(L, soundData);
lovrRelease(soundData);
return 1;
}
Blob* blob = luax_readblob(L, 1, "SoundData");
SoundData* soundData = lovrSoundDataCreateFromBlob(blob);
luax_pushobject(L, soundData);
lovrRelease(blob);
lovrRelease(soundData);
return 1;
}
static int l_lovrDataNewTextureData(lua_State* L) {
TextureData* textureData = NULL; TextureData* textureData = NULL;
if (lua_type(L, 1) == LUA_TNUMBER) { if (lua_type(L, 1) == LUA_TNUMBER) {
int width = luaL_checknumber(L, 1); int width = luaL_checknumber(L, 1);
int height = luaL_checknumber(L, 2); int height = luaL_checknumber(L, 2);
textureData = lovrTextureDataGetBlank(width, height, 0x0, FORMAT_RGBA); TextureFormat format = luaL_checkoption(L, 3, "rgba", TextureFormats);
textureData = lovrTextureDataCreate(width, height, 0x0, format);
} else { } else {
Blob* blob = luax_readblob(L, 1, "Texture"); Blob* blob = luax_readblob(L, 1, "Texture");
textureData = lovrTextureDataFromBlob(blob); textureData = lovrTextureDataCreateFromBlob(blob, true);
lovrRelease(&blob->ref); lovrRelease(blob);
} }
luax_pushtype(L, TextureData, textureData); luax_pushobject(L, textureData);
lovrRelease(&textureData->ref); lovrRelease(textureData);
return 1; return 1;
} }
int l_lovrDataNewVertexData(lua_State* L) { static int l_lovrDataNewVertexData(lua_State* L) {
uint32_t count = luaL_checkinteger(L, 1); uint32_t count;
int dataIndex = 0;
bool hasFormat = false;
VertexFormat format; VertexFormat format;
vertexFormatInit(&format); vertexFormatInit(&format);
luax_checkvertexformat(L, 2, &format);
VertexData* vertexData = lovrVertexDataCreate(count, format.count > 0 ? &format : NULL, true); if (lua_isnumber(L, 1)) {
luax_pushtype(L, VertexData, vertexData); count = lua_tointeger(L, 1);
lovrRelease(&vertexData->ref); } else if (lua_istable(L, 1)) {
if (lua_isnumber(L, 2)) {
hasFormat = luax_checkvertexformat(L, 1, &format);
count = lua_tointeger(L, 2);
dataIndex = 0;
} else if (lua_istable(L, 2)) {
hasFormat = luax_checkvertexformat(L, 1, &format);
count = lua_objlen(L, 2);
dataIndex = 2;
} else {
count = lua_objlen(L, 1);
dataIndex = 1;
}
} else {
return luaL_argerror(L, 1, "table or number expected");
}
VertexData* vertexData = lovrVertexDataCreate(count, hasFormat ? &format : NULL);
if (dataIndex) {
luax_loadvertices(L, dataIndex, &vertexData->format, (VertexPointer) { .raw = vertexData->blob.data });
}
luax_pushobject(L, vertexData);
lovrRelease(vertexData);
return 1; return 1;
} }
const luaL_Reg lovrData[] = { static const luaL_Reg lovrData[] = {
{ "newBlob", l_lovrDataNewBlob },
{ "newAudioStream", l_lovrDataNewAudioStream }, { "newAudioStream", l_lovrDataNewAudioStream },
{ "newModelData", l_lovrDataNewModelData }, { "newModelData", l_lovrDataNewModelData },
{ "newRasterizer", l_lovrDataNewRasterizer }, { "newRasterizer", l_lovrDataNewRasterizer },
{ "newSoundData", l_lovrDataNewSoundData },
{ "newTextureData", l_lovrDataNewTextureData }, { "newTextureData", l_lovrDataNewTextureData },
{ "newVertexData", l_lovrDataNewVertexData }, { "newVertexData", l_lovrDataNewVertexData },
{ NULL, NULL } { NULL, NULL }
}; };
int luaopen_lovr_data(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrData);
luax_registertype(L, "Blob", lovrBlob);
luax_registertype(L, "AudioStream", lovrAudioStream);
luax_registertype(L, "ModelData", lovrModelData);
luax_registertype(L, "Rasterizer", lovrRasterizer);
luax_extendtype(L, "Blob", "SoundData", lovrBlob, lovrSoundData);
luax_extendtype(L, "Blob", "TextureData", lovrBlob, lovrTextureData);
luax_extendtype(L, "Blob", "VertexData", lovrBlob, lovrVertexData);
return 1;
}

12
src/api/data.h Normal file
View File

@ -0,0 +1,12 @@
#include "data/blob.h"
#include "data/vertexData.h"
#include <stdbool.h>
int luax_loadvertices(lua_State* L, int index, VertexFormat* format, VertexPointer vertices);
bool luax_checkvertexformat(lua_State* L, int index, VertexFormat* format);
int luax_pushvertexformat(lua_State* L, VertexFormat* format);
int luax_pushvertexattribute(lua_State* L, VertexPointer* vertex, Attribute attribute);
int luax_pushvertex(lua_State* L, VertexPointer* vertex, VertexFormat* format);
void luax_setvertexattribute(lua_State* L, int index, VertexPointer* vertex, Attribute attribute);
void luax_setvertex(lua_State* L, int index, VertexPointer* vertex, VertexFormat* format);
Blob* luax_readblob(lua_State* L, int index, const char* debug);

View File

@ -1,9 +1,66 @@
#include "api.h" #include "api.h"
#include "api/event.h"
#include "event/event.h" #include "event/event.h"
map_int_t EventTypes; const char* EventTypes[] = {
[EVENT_QUIT] = "quit",
[EVENT_FOCUS] = "focus",
[EVENT_MOUNT] = "mount",
[EVENT_THREAD_ERROR] = "threaderror",
[EVENT_CONTROLLER_ADDED] = "controlleradded",
[EVENT_CONTROLLER_REMOVED] = "controllerremoved",
[EVENT_CONTROLLER_PRESSED] = "controllerpressed",
[EVENT_CONTROLLER_RELEASED] = "controllerreleased",
};
static int pollRef; static _Thread_local int pollRef;
void luax_checkvariant(lua_State* L, int index, Variant* variant) {
int type = lua_type(L, index);
switch (type) {
case LUA_TNIL:
variant->type = TYPE_NIL;
break;
case LUA_TBOOLEAN:
variant->type = TYPE_BOOLEAN;
variant->value.boolean = lua_toboolean(L, index);
break;
case LUA_TNUMBER:
variant->type = TYPE_NUMBER;
variant->value.number = lua_tonumber(L, index);
break;
case LUA_TSTRING:
variant->type = TYPE_STRING;
size_t length;
const char* string = lua_tolstring(L, index, &length);
variant->value.string = malloc(length + 1);
strcpy(variant->value.string, string);
break;
case LUA_TUSERDATA:
variant->type = TYPE_OBJECT;
variant->value.ref = lua_touserdata(L, index);
lovrRetain(variant->value.ref);
break;
default:
lovrThrow("Bad type for Channel:push: %s", lua_typename(L, type));
return;
}
}
int luax_pushvariant(lua_State* L, Variant* variant) {
switch (variant->type) {
case TYPE_NIL: lua_pushnil(L); return 1;
case TYPE_BOOLEAN: lua_pushboolean(L, variant->value.boolean); return 1;
case TYPE_NUMBER: lua_pushnumber(L, variant->value.number); return 1;
case TYPE_STRING: lua_pushstring(L, variant->value.string); free(variant->value.string); return 1;
case TYPE_OBJECT: luax_pushobject(L, variant->value.ref); lovrRelease(variant->value.ref); return 1;
}
}
static int nextEvent(lua_State* L) { static int nextEvent(lua_State* L) {
Event event; Event event;
@ -12,122 +69,84 @@ static int nextEvent(lua_State* L) {
return 0; return 0;
} }
if (event.type == EVENT_CUSTOM) {
lua_pushstring(L, event.data.custom.name);
} else {
lua_pushstring(L, EventTypes[event.type]);
}
switch (event.type) { switch (event.type) {
case EVENT_QUIT: { case EVENT_QUIT:
lua_pushstring(L, "quit"); if (event.data.quit.restart) {
if (event.data.quit.restart) lua_pushstring(L, "restart");
lua_pushliteral(L, "restart"); } else {
else
lua_pushnumber(L, event.data.quit.exitCode); lua_pushnumber(L, event.data.quit.exitCode);
}
return 2; return 2;
}
case EVENT_FOCUS: { case EVENT_FOCUS:
lua_pushstring(L, "focus"); case EVENT_MOUNT:
lua_pushboolean(L, event.data.focus.isFocused); lua_pushboolean(L, event.data.boolean.value);
return 2; return 2;
};
case EVENT_CONTROLLER_ADDED: { case EVENT_THREAD_ERROR:
lua_pushstring(L, "controlleradded"); luax_pushobject(L, event.data.thread.thread);
luax_pushtype(L, Controller, event.data.controlleradded.controller); lua_pushstring(L, event.data.thread.error);
lovrRelease(&event.data.controlleradded.controller->ref); free((void*) event.data.thread.error);
return 2;
}
case EVENT_CONTROLLER_REMOVED: {
lua_pushstring(L, "controllerremoved");
luax_pushtype(L, Controller, event.data.controllerremoved.controller);
lovrRelease(&event.data.controlleradded.controller->ref);
return 2;
}
case EVENT_CONTROLLER_PRESSED: {
lua_pushstring(L, "controllerpressed");
luax_pushtype(L, Controller, event.data.controllerpressed.controller);
luax_pushenum(L, &ControllerButtons, event.data.controllerpressed.button);
return 3; return 3;
}
case EVENT_CONTROLLER_RELEASED: { case EVENT_CONTROLLER_ADDED:
lua_pushstring(L, "controllerreleased"); case EVENT_CONTROLLER_REMOVED:
luax_pushtype(L, Controller, event.data.controllerreleased.controller); luax_pushobject(L, event.data.controller.controller);
luax_pushenum(L, &ControllerButtons, event.data.controllerreleased.button); lovrRelease(event.data.controller.controller);
return 2;
case EVENT_CONTROLLER_PRESSED:
case EVENT_CONTROLLER_RELEASED:
luax_pushobject(L, event.data.controller.controller);
lua_pushstring(L, ControllerButtons[event.data.controller.button]);
return 3; return 3;
}
case EVENT_CUSTOM:
for (int i = 0; i < event.data.custom.count; i++) {
luax_pushvariant(L, &event.data.custom.data[i]);
}
return event.data.custom.count + 1;
default: default:
return 0; return 1;
} }
} }
int l_lovrEventInit(lua_State* L) { static int l_lovrEventClear(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrEvent);
// Store nextEvent in the registry to avoid creating a closure every time we poll for events.
lua_pushcfunction(L, nextEvent);
pollRef = luaL_ref(L, LUA_REGISTRYINDEX);
map_init(&EventTypes);
map_set(&EventTypes, "quit", EVENT_QUIT);
lovrEventInit();
return 1;
}
int l_lovrEventClear(lua_State* L) {
lovrEventClear(); lovrEventClear();
return 0; return 0;
} }
int l_lovrEventPoll(lua_State* L) { static int l_lovrEventPoll(lua_State* L) {
lua_rawgeti(L, LUA_REGISTRYINDEX, pollRef); lua_rawgeti(L, LUA_REGISTRYINDEX, pollRef);
return 1; return 1;
} }
int l_lovrEventPump(lua_State* L) { static int l_lovrEventPump(lua_State* L) {
lovrEventPump(); lovrEventPump();
return 0; return 0;
} }
int l_lovrEventPush(lua_State* L) { static int l_lovrEventPush(lua_State* L) {
EventType type = *(EventType*) luax_checkenum(L, 1, &EventTypes, "event type"); CustomEvent eventData;
EventData data; const char* name = luaL_checkstring(L, 1);
strncpy(eventData.name, name, MAX_EVENT_NAME_LENGTH - 1);
switch (type) { eventData.count = MIN(lua_gettop(L) - 1, 4);
case EVENT_QUIT: for (int i = 0; i < eventData.count; i++) {
data.quit.exitCode = luaL_optint(L, 2, 0); luax_checkvariant(L, 2 + i, &eventData.data[i]);
break;
case EVENT_FOCUS:
data.focus.isFocused = lua_toboolean(L, 2);
break;
case EVENT_CONTROLLER_ADDED:
data.controlleradded.controller = luax_checktype(L, 2, Controller);
break;
case EVENT_CONTROLLER_REMOVED:
data.controllerremoved.controller = luax_checktype(L, 2, Controller);
break;
case EVENT_CONTROLLER_PRESSED:
data.controllerpressed.controller = luax_checktype(L, 2, Controller);
data.controllerpressed.button = *(ControllerButton*) luax_checkenum(L, 3, &ControllerButtons, "button");
break;
case EVENT_CONTROLLER_RELEASED:
data.controllerreleased.controller = luax_checktype(L, 2, Controller);
data.controllerreleased.button = *(ControllerButton*) luax_checkenum(L, 3, &ControllerButtons, "button");
break;
} }
Event event = { .type = type, .data = data }; lovrEventPush((Event) { .type = EVENT_CUSTOM, .data.custom = eventData });
lovrEventPush(event);
return 0; return 0;
} }
int l_lovrEventQuit(lua_State* L) { static int l_lovrEventQuit(lua_State* L) {
EventData data; EventData data;
int argType = lua_type(L, 1); int argType = lua_type(L, 1);
@ -147,7 +166,7 @@ int l_lovrEventQuit(lua_State* L) {
return 0; return 0;
} }
const luaL_Reg lovrEvent[] = { static const luaL_Reg lovrEvent[] = {
{ "clear", l_lovrEventClear }, { "clear", l_lovrEventClear },
{ "poll", l_lovrEventPoll }, { "poll", l_lovrEventPoll },
{ "pump", l_lovrEventPump }, { "pump", l_lovrEventPump },
@ -155,3 +174,16 @@ const luaL_Reg lovrEvent[] = {
{ "quit", l_lovrEventQuit }, { "quit", l_lovrEventQuit },
{ NULL, NULL } { NULL, NULL }
}; };
int luaopen_lovr_event(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrEvent);
luax_atexit(L, lovrEventDestroy);
// Store nextEvent in the registry to avoid creating a closure every time we poll for events.
lua_pushcfunction(L, nextEvent);
pollRef = luaL_ref(L, LUA_REGISTRYINDEX);
lovrEventInit();
return 1;
}

4
src/api/event.h Normal file
View File

@ -0,0 +1,4 @@
#include "event/event.h"
void luax_checkvariant(lua_State* L, int index, Variant* variant);
int luax_pushvariant(lua_State* L, Variant* variant);

View File

@ -1,14 +1,13 @@
#include "api.h" #include "api.h"
#include "filesystem/filesystem.h" #include "filesystem/filesystem.h"
#include "filesystem/blob.h" #include "data/blob.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
// Returns a Blob, leaving stack unchanged. The Blob must be released when finished. // Returns a Blob, leaving stack unchanged. The Blob must be released when finished.
Blob* luax_readblob(lua_State* L, int index, const char* debug) { Blob* luax_readblob(lua_State* L, int index, const char* debug) {
if (lua_type(L, index) == LUA_TUSERDATA) { if (lua_type(L, index) == LUA_TUSERDATA) {
Blob* blob = luax_checktype(L, index, Blob); Blob* blob = luax_checktype(L, index, Blob);
lovrRetain(&blob->ref); lovrRetain(blob);
return blob; return blob;
} else { } else {
const char* path = luaL_checkstring(L, index); const char* path = luaL_checkstring(L, index);
@ -31,41 +30,56 @@ static int pushDirectoryItem(void* userdata, const char* path, const char* filen
return 1; return 1;
} }
int l_lovrFilesystemLoad(lua_State* L); static int l_lovrFilesystemLoad(lua_State* L);
// Loader to help Lua's require understand PhysFS. static int moduleLoader(lua_State* L) {
static int filesystemLoader(lua_State* L) { const char* module = luaL_gsub(L, lua_tostring(L, -1), ".", "/");
const char* module = luaL_checkstring(L, -1); lua_pop(L, 2);
char* dot;
char path[LOVR_PATH_MAX]; char* path; int i;
strncpy(path, module, LOVR_PATH_MAX); vec_foreach(lovrFilesystemGetRequirePath(), path, i) {
const char* filename = luaL_gsub(L, path, "?", module);
while ((dot = strchr(path, '.')) != NULL) { if (lovrFilesystemIsFile(filename)) {
*dot = '/'; return l_lovrFilesystemLoad(L);
}
lua_pop(L, 1);
} }
const char* requirePath[] = { return 0;
"?.lua", }
"?/init.lua"
};
for (size_t i = 0; i < sizeof(requirePath) / sizeof(char*); i++) { static const char* libraryExtensions[] = {
char filename[LOVR_PATH_MAX]; #ifdef _WIN32
char* sub = strchr(requirePath[i], '?'); ".dll", NULL
#elif __APPLE__
".so", ".dylib", NULL
#else
".so", NULL
#endif
};
memset(filename, 0, LOVR_PATH_MAX); static int libraryLoader(lua_State* L) {
const char* modulePath = luaL_gsub(L, lua_tostring(L, -1), ".", "/");
const char* moduleFunction = luaL_gsub(L, lua_tostring(L, -1), ".", "_");
char* hyphen = strchr(moduleFunction, '-');
moduleFunction = hyphen ? hyphen + 1 : moduleFunction;
lua_pop(L, 3);
if (sub) { char* path; int i;
int index = (int) (sub - requirePath[i]); vec_foreach(lovrFilesystemGetCRequirePath(), path, i) {
strncat(filename, requirePath[i], index); for (const char** extension = libraryExtensions; *extension != NULL; extension++) {
strncat(filename, path, strlen(path)); char buffer[64];
strncat(filename, requirePath[i] + index + 1, strlen(requirePath[i]) - index); snprintf(buffer, 63, "%s%s", modulePath, *extension);
const char* filename = luaL_gsub(L, path, "??", buffer);
filename = luaL_gsub(L, filename, "?", modulePath);
lua_pop(L, 2);
if (lovrFilesystemIsFile(filename)) { if (lovrFilesystemIsFile(filename)) {
lua_pop(L, 1); char fullPath[LOVR_PATH_MAX];
lua_pushstring(L, filename); const char* realPath = lovrFilesystemGetRealDirectory(filename);
return l_lovrFilesystemLoad(L); snprintf(fullPath, LOVR_PATH_MAX - 1, "%s%c%s", realPath, lovrDirSep, filename);
luax_loadlib(L, fullPath, moduleFunction);
return 1;
} }
} }
} }
@ -73,37 +87,7 @@ static int filesystemLoader(lua_State* L) {
return 0; return 0;
} }
int l_lovrFilesystemInit(lua_State* L) { static int l_lovrFilesystemAppend(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrFilesystem);
luax_registertype(L, "Blob", lovrBlob);
lua_getglobal(L, "arg");
lua_rawgeti(L, -1, -2);
lua_rawgeti(L, -2, 1);
const char* arg0 = lua_tostring(L, -2);
const char* arg1 = lua_tostring(L, -1);
lovrFilesystemInit(arg0, arg1);
lua_pop(L, 3);
// Add custom package loader
lua_getglobal(L, "table");
lua_getfield(L, -1, "insert");
lua_getglobal(L, "package");
lua_getfield(L, -1, "loaders");
lua_remove(L, -2);
if (lua_istable(L, -1)) {
lua_pushinteger(L, 2); // Insert our loader after package.preload
lua_pushcfunction(L, filesystemLoader);
lua_call(L, 3, 0);
}
lua_pop(L, 1);
return 1;
}
int l_lovrFilesystemAppend(lua_State* L) {
size_t size; size_t size;
const char* path = luaL_checkstring(L, 1); const char* path = luaL_checkstring(L, 1);
const char* content = luaL_checklstring(L, 2, &size); const char* content = luaL_checklstring(L, 2, &size);
@ -111,14 +95,14 @@ int l_lovrFilesystemAppend(lua_State* L) {
return 1; return 1;
} }
int l_lovrFilesystemCreateDirectory(lua_State* L) { static int l_lovrFilesystemCreateDirectory(lua_State* L) {
const char* path = luaL_checkstring(L, 1); const char* path = luaL_checkstring(L, 1);
lua_pushboolean(L, !lovrFilesystemCreateDirectory(path)); lua_pushboolean(L, !lovrFilesystemCreateDirectory(path));
return 1; return 1;
} }
int l_lovrFilesystemGetAppdataDirectory(lua_State* L) { static int l_lovrFilesystemGetAppdataDirectory(lua_State* L) {
char buffer[1024]; char buffer[LOVR_PATH_MAX];
if (lovrFilesystemGetAppdataDirectory(buffer, sizeof(buffer))) { if (lovrFilesystemGetAppdataDirectory(buffer, sizeof(buffer))) {
lua_pushnil(L); lua_pushnil(L);
@ -129,15 +113,15 @@ int l_lovrFilesystemGetAppdataDirectory(lua_State* L) {
return 1; return 1;
} }
int l_lovrFilesystemGetDirectoryItems(lua_State* L) { static int l_lovrFilesystemGetDirectoryItems(lua_State* L) {
const char* path = luaL_checkstring(L, 1); const char* path = luaL_checkstring(L, 1);
lua_newtable(L); lua_newtable(L);
lovrFilesystemGetDirectoryItems(path, pushDirectoryItem, L); lovrFilesystemGetDirectoryItems(path, pushDirectoryItem, L);
return 1; return 1;
} }
int l_lovrFilesystemGetExecutablePath(lua_State* L) { static int l_lovrFilesystemGetExecutablePath(lua_State* L) {
char buffer[1024]; char buffer[LOVR_PATH_MAX];
if (lovrFilesystemGetExecutablePath(buffer, sizeof(buffer))) { if (lovrFilesystemGetExecutablePath(buffer, sizeof(buffer))) {
lua_pushnil(L); lua_pushnil(L);
@ -148,7 +132,7 @@ int l_lovrFilesystemGetExecutablePath(lua_State* L) {
return 1; return 1;
} }
int l_lovrFilesystemGetIdentity(lua_State* L) { static int l_lovrFilesystemGetIdentity(lua_State* L) {
const char* identity = lovrFilesystemGetIdentity(); const char* identity = lovrFilesystemGetIdentity();
if (identity) { if (identity) {
lua_pushstring(L, identity); lua_pushstring(L, identity);
@ -158,7 +142,7 @@ int l_lovrFilesystemGetIdentity(lua_State* L) {
return 1; return 1;
} }
int l_lovrFilesystemGetLastModified(lua_State* L) { static int l_lovrFilesystemGetLastModified(lua_State* L) {
const char* path = luaL_checkstring(L, 1); const char* path = luaL_checkstring(L, 1);
int lastModified = lovrFilesystemGetLastModified(path); int lastModified = lovrFilesystemGetLastModified(path);
@ -171,18 +155,34 @@ int l_lovrFilesystemGetLastModified(lua_State* L) {
return 1; return 1;
} }
int l_lovrFilesystemGetRealDirectory(lua_State* L) { static int l_lovrFilesystemGetRealDirectory(lua_State* L) {
const char* path = luaL_checkstring(L, 1); const char* path = luaL_checkstring(L, 1);
lua_pushstring(L, lovrFilesystemGetRealDirectory(path)); lua_pushstring(L, lovrFilesystemGetRealDirectory(path));
return 1; return 1;
} }
int l_lovrFilesystemGetSaveDirectory(lua_State* L) { static void pushRequirePath(lua_State* L, vec_str_t* path) {
char* pattern; int i;
vec_foreach(path, pattern, i) {
lua_pushstring(L, pattern);
lua_pushstring(L, ";");
}
lua_pop(L, 1);
lua_concat(L, path->length * 2 - 1);
}
static int l_lovrFilesystemGetRequirePath(lua_State* L) {
pushRequirePath(L, lovrFilesystemGetRequirePath());
pushRequirePath(L, lovrFilesystemGetCRequirePath());
return 2;
}
static int l_lovrFilesystemGetSaveDirectory(lua_State* L) {
lua_pushstring(L, lovrFilesystemGetSaveDirectory()); lua_pushstring(L, lovrFilesystemGetSaveDirectory());
return 1; return 1;
} }
int l_lovrFilesystemGetSize(lua_State* L) { static int l_lovrFilesystemGetSize(lua_State* L) {
const char* path = luaL_checkstring(L, 1); const char* path = luaL_checkstring(L, 1);
size_t size = lovrFilesystemGetSize(path); size_t size = lovrFilesystemGetSize(path);
if ((int) size == -1) { if ((int) size == -1) {
@ -192,7 +192,7 @@ int l_lovrFilesystemGetSize(lua_State* L) {
return 1; return 1;
} }
int l_lovrFilesystemGetSource(lua_State* L) { static int l_lovrFilesystemGetSource(lua_State* L) {
const char* source = lovrFilesystemGetSource(); const char* source = lovrFilesystemGetSource();
if (source) { if (source) {
@ -204,29 +204,41 @@ int l_lovrFilesystemGetSource(lua_State* L) {
return 1; return 1;
} }
int l_lovrFilesystemGetUserDirectory(lua_State* L) { static int l_lovrFilesystemGetUserDirectory(lua_State* L) {
lua_pushstring(L, lovrFilesystemGetUserDirectory()); lua_pushstring(L, lovrFilesystemGetUserDirectory());
return 1; return 1;
} }
int l_lovrFilesystemIsDirectory(lua_State* L) { static int l_lovrFilesystemGetWorkingDirectory(lua_State* L) {
char buffer[LOVR_PATH_MAX];
if (lovrFilesystemGetWorkingDirectory(buffer, sizeof(buffer))) {
lua_pushnil(L);
} else {
lua_pushstring(L, buffer);
}
return 1;
}
static int l_lovrFilesystemIsDirectory(lua_State* L) {
const char* path = luaL_checkstring(L, 1); const char* path = luaL_checkstring(L, 1);
lua_pushboolean(L, lovrFilesystemIsDirectory(path)); lua_pushboolean(L, lovrFilesystemIsDirectory(path));
return 1; return 1;
} }
int l_lovrFilesystemIsFile(lua_State* L) { static int l_lovrFilesystemIsFile(lua_State* L) {
const char* path = luaL_checkstring(L, 1); const char* path = luaL_checkstring(L, 1);
lua_pushboolean(L, lovrFilesystemIsFile(path)); lua_pushboolean(L, lovrFilesystemIsFile(path));
return 1; return 1;
} }
int l_lovrFilesystemIsFused(lua_State* L) { static int l_lovrFilesystemIsFused(lua_State* L) {
lua_pushboolean(L, lovrFilesystemIsFused()); lua_pushboolean(L, lovrFilesystemIsFused());
return 1; return 1;
} }
int l_lovrFilesystemLoad(lua_State* L) { static int l_lovrFilesystemLoad(lua_State* L) {
const char* path = luaL_checkstring(L, 1); const char* path = luaL_checkstring(L, 1);
size_t size; size_t size;
char* content = lovrFilesystemRead(path, &size); char* content = lovrFilesystemRead(path, &size);
@ -235,7 +247,10 @@ int l_lovrFilesystemLoad(lua_State* L) {
return luaL_error(L, "Could not read file '%s'", path); return luaL_error(L, "Could not read file '%s'", path);
} }
int status = luaL_loadbuffer(L, content, size, path); char debug[LOVR_PATH_MAX];
snprintf(debug, LOVR_PATH_MAX, "@%s", path);
int status = luaL_loadbuffer(L, content, size, debug);
free(content); free(content);
switch (status) { switch (status) {
case LUA_ERRMEM: return luaL_error(L, "Memory allocation error: %s", lua_tostring(L, -1)); case LUA_ERRMEM: return luaL_error(L, "Memory allocation error: %s", lua_tostring(L, -1));
@ -244,7 +259,7 @@ int l_lovrFilesystemLoad(lua_State* L) {
} }
} }
int l_lovrFilesystemMount(lua_State* L) { static int l_lovrFilesystemMount(lua_State* L) {
const char* path = luaL_checkstring(L, 1); const char* path = luaL_checkstring(L, 1);
const char* mountpoint = luaL_optstring(L, 2, NULL); const char* mountpoint = luaL_optstring(L, 2, NULL);
bool append = lua_isnoneornil(L, 3) ? 0 : lua_toboolean(L, 3); bool append = lua_isnoneornil(L, 3) ? 0 : lua_toboolean(L, 3);
@ -252,48 +267,34 @@ int l_lovrFilesystemMount(lua_State* L) {
return 1; return 1;
} }
int l_lovrFilesystemNewBlob(lua_State* L) { static int l_lovrFilesystemNewBlob(lua_State* L) {
const char* path;
char* data;
size_t size; size_t size;
const char* path = luaL_checkstring(L, 1);
if (lua_gettop(L) == 1) { uint8_t* data = lovrFilesystemRead(path, &size);
path = luaL_checkstring(L, 1); lovrAssert(data, "Could not load file '%s'", path);
data = lovrFilesystemRead(path, &size);
} else {
const char* str = luaL_checklstring(L, 1, &size);
data = malloc(size + 1); // Copy the Lua string so we can hold onto it
memcpy(data, str, size);
data[size] = '\0';
path = luaL_checkstring(L, 2);
}
Blob* blob = lovrBlobCreate((void*) data, size, path); Blob* blob = lovrBlobCreate((void*) data, size, path);
luax_pushtype(L, Blob, blob); luax_pushobject(L, blob);
lovrRelease(&blob->ref); lovrRelease(blob);
return 1; return 1;
} }
int l_lovrFilesystemRead(lua_State* L) { static int l_lovrFilesystemRead(lua_State* L) {
const char* path = luaL_checkstring(L, 1); const char* path = luaL_checkstring(L, 1);
size_t size; size_t size;
char* content = lovrFilesystemRead(path, &size); char* content = lovrFilesystemRead(path, &size);
if (!content) { lovrAssert(content, "Could not read file '%s'", path);
return luaL_error(L, "Could not read file '%s'", path);
}
lua_pushlstring(L, content, size); lua_pushlstring(L, content, size);
free(content); free(content);
return 1; return 1;
} }
int l_lovrFilesystemRemove(lua_State* L) { static int l_lovrFilesystemRemove(lua_State* L) {
const char* path = luaL_checkstring(L, 1); const char* path = luaL_checkstring(L, 1);
lua_pushboolean(L, !lovrFilesystemRemove(path)); lua_pushboolean(L, !lovrFilesystemRemove(path));
return 1; return 1;
} }
int l_lovrFilesystemSetIdentity(lua_State* L) { static int l_lovrFilesystemSetIdentity(lua_State* L) {
if (lua_isnoneornil(L, 1)) { if (lua_isnoneornil(L, 1)) {
lovrFilesystemSetIdentity(NULL); lovrFilesystemSetIdentity(NULL);
} else { } else {
@ -303,13 +304,19 @@ int l_lovrFilesystemSetIdentity(lua_State* L) {
return 0; return 0;
} }
int l_lovrFilesystemUnmount(lua_State* L) { static int l_lovrFilesystemSetRequirePath(lua_State* L) {
if (lua_type(L, 1) == LUA_TSTRING) lovrFilesystemSetRequirePath(luaL_checkstring(L, 1));
if (lua_type(L, 2) == LUA_TSTRING) lovrFilesystemSetCRequirePath(luaL_checkstring(L, 2));
return 0;
}
static int l_lovrFilesystemUnmount(lua_State* L) {
const char* path = luaL_checkstring(L, 1); const char* path = luaL_checkstring(L, 1);
lua_pushboolean(L, !lovrFilesystemUnmount(path)); lua_pushboolean(L, !lovrFilesystemUnmount(path));
return 1; return 1;
} }
int l_lovrFilesystemWrite(lua_State* L) { static int l_lovrFilesystemWrite(lua_State* L) {
size_t size; size_t size;
const char* path = luaL_checkstring(L, 1); const char* path = luaL_checkstring(L, 1);
const char* content = luaL_checklstring(L, 2, &size); const char* content = luaL_checklstring(L, 2, &size);
@ -317,7 +324,7 @@ int l_lovrFilesystemWrite(lua_State* L) {
return 1; return 1;
} }
const luaL_Reg lovrFilesystem[] = { static const luaL_Reg lovrFilesystem[] = {
{ "append", l_lovrFilesystemAppend }, { "append", l_lovrFilesystemAppend },
{ "createDirectory", l_lovrFilesystemCreateDirectory }, { "createDirectory", l_lovrFilesystemCreateDirectory },
{ "getAppdataDirectory", l_lovrFilesystemGetAppdataDirectory }, { "getAppdataDirectory", l_lovrFilesystemGetAppdataDirectory },
@ -326,10 +333,12 @@ const luaL_Reg lovrFilesystem[] = {
{ "getIdentity", l_lovrFilesystemGetIdentity }, { "getIdentity", l_lovrFilesystemGetIdentity },
{ "getLastModified", l_lovrFilesystemGetLastModified }, { "getLastModified", l_lovrFilesystemGetLastModified },
{ "getRealDirectory", l_lovrFilesystemGetRealDirectory }, { "getRealDirectory", l_lovrFilesystemGetRealDirectory },
{ "getRequirePath", l_lovrFilesystemGetRequirePath },
{ "getSaveDirectory", l_lovrFilesystemGetSaveDirectory }, { "getSaveDirectory", l_lovrFilesystemGetSaveDirectory },
{ "getSize", l_lovrFilesystemGetSize }, { "getSize", l_lovrFilesystemGetSize },
{ "getSource", l_lovrFilesystemGetSource }, { "getSource", l_lovrFilesystemGetSource },
{ "getUserDirectory", l_lovrFilesystemGetUserDirectory }, { "getUserDirectory", l_lovrFilesystemGetUserDirectory },
{ "getWorkingDirectory", l_lovrFilesystemGetWorkingDirectory },
{ "isDirectory", l_lovrFilesystemIsDirectory }, { "isDirectory", l_lovrFilesystemIsDirectory },
{ "isFile", l_lovrFilesystemIsFile }, { "isFile", l_lovrFilesystemIsFile },
{ "isFused", l_lovrFilesystemIsFused }, { "isFused", l_lovrFilesystemIsFused },
@ -338,7 +347,27 @@ const luaL_Reg lovrFilesystem[] = {
{ "newBlob", l_lovrFilesystemNewBlob }, { "newBlob", l_lovrFilesystemNewBlob },
{ "read", l_lovrFilesystemRead }, { "read", l_lovrFilesystemRead },
{ "remove", l_lovrFilesystemRemove }, { "remove", l_lovrFilesystemRemove },
{ "setRequirePath", l_lovrFilesystemSetRequirePath },
{ "setIdentity", l_lovrFilesystemSetIdentity }, { "setIdentity", l_lovrFilesystemSetIdentity },
{ "unmount", l_lovrFilesystemUnmount },
{ "write", l_lovrFilesystemWrite }, { "write", l_lovrFilesystemWrite },
{ NULL, NULL } { NULL, NULL }
}; };
int luaopen_lovr_filesystem(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrFilesystem);
luax_atexit(L, lovrFilesystemDestroy);
lua_getglobal(L, "arg");
lua_rawgeti(L, -1, -2);
lua_rawgeti(L, -2, 1);
const char* arg0 = lua_tostring(L, -2);
const char* arg1 = lua_tostring(L, -1);
lovrFilesystemInit(arg0, arg1);
lua_pop(L, 3);
luax_registerloader(L, moduleLoader, 2);
luax_registerloader(L, libraryLoader, 3);
return 1;
}

File diff suppressed because it is too large Load Diff

9
src/api/graphics.h Normal file
View File

@ -0,0 +1,9 @@
#include "graphics/canvas.h"
#include "graphics/shader.h"
#include "graphics/texture.h"
int luax_checkuniform(lua_State* L, int index, const Uniform* uniform, void* dest, const char* debug);
void luax_checkuniformtype(lua_State* L, int index, UniformType* baseType, int* components);
int luax_optmipmap(lua_State* L, int index, Texture* texture);
Texture* luax_checktexture(lua_State* L, int index);
void luax_readattachments(lua_State* L, int index, Attachment* attachments, int* count);

View File

@ -1,13 +1,61 @@
#include "api.h" #include "api.h"
#include "headset/headset.h" #include "headset/headset.h"
map_int_t ControllerAxes; const char* ControllerAxes[] = {
map_int_t ControllerButtons; [CONTROLLER_AXIS_TRIGGER] = "trigger",
map_int_t ControllerHands; [CONTROLLER_AXIS_GRIP] = "grip",
map_int_t HeadsetDrivers; [CONTROLLER_AXIS_TOUCHPAD_X] = "touchx",
map_int_t HeadsetEyes; [CONTROLLER_AXIS_TOUCHPAD_Y] = "touchy",
map_int_t HeadsetOrigins; NULL
map_int_t HeadsetTypes; };
const char* ControllerButtons[] = {
[CONTROLLER_BUTTON_SYSTEM] = "system",
[CONTROLLER_BUTTON_MENU] = "menu",
[CONTROLLER_BUTTON_TRIGGER] = "trigger",
[CONTROLLER_BUTTON_GRIP] = "grip",
[CONTROLLER_BUTTON_TOUCHPAD] = "touchpad",
[CONTROLLER_BUTTON_A] = "a",
[CONTROLLER_BUTTON_B] = "b",
[CONTROLLER_BUTTON_X] = "x",
[CONTROLLER_BUTTON_Y] = "y",
NULL
};
const char* ControllerHands[] = {
[HAND_UNKNOWN] = "unknown",
[HAND_LEFT] = "left",
[HAND_RIGHT] = "right",
NULL
};
const char* HeadsetDrivers[] = {
[DRIVER_FAKE] = "fake",
[DRIVER_OCULUS] = "oculus",
[DRIVER_OPENVR] = "openvr",
[DRIVER_WEBVR] = "webvr",
NULL
};
const char* HeadsetEyes[] = {
[EYE_LEFT] = "left",
[EYE_RIGHT] = "right",
NULL
};
const char* HeadsetOrigins[] = {
[ORIGIN_HEAD] = "head",
[ORIGIN_FLOOR] = "floor",
NULL
};
const char* HeadsetTypes[] = {
[HEADSET_UNKNOWN] = "unknown",
[HEADSET_VIVE] = "vive",
[HEADSET_RIFT] = "rift",
[HEADSET_WINDOWS_MR] = "windowsmr",
NULL
};
typedef struct { typedef struct {
lua_State* L; lua_State* L;
@ -16,200 +64,149 @@ typedef struct {
static HeadsetRenderData headsetRenderData; static HeadsetRenderData headsetRenderData;
static void renderHelper(HeadsetEye eye, void* userdata) { static void renderHelper(void* userdata) {
HeadsetRenderData* renderData = userdata; HeadsetRenderData* renderData = userdata;
lua_State* L = renderData->L; lua_State* L = renderData->L;
#ifdef EMSCRIPTEN
lua_rawgeti(L, LUA_REGISTRYINDEX, renderData->ref); lua_rawgeti(L, LUA_REGISTRYINDEX, renderData->ref);
luax_pushenum(L, &HeadsetEyes, eye); #else
lua_call(L, 1, 0); lua_pushvalue(L, -1);
#endif
lua_call(L, 0, 0);
} }
int l_lovrHeadsetInit(lua_State* L) { static int l_lovrHeadsetGetDriver(lua_State* L) {
lua_newtable(L); lua_pushstring(L, HeadsetDrivers[lovrHeadsetDriver->driverType]);
luaL_register(L, NULL, lovrHeadset);
luax_registertype(L, "Controller", lovrController);
map_init(&ControllerAxes);
map_set(&ControllerAxes, "trigger", CONTROLLER_AXIS_TRIGGER);
map_set(&ControllerAxes, "grip", CONTROLLER_AXIS_GRIP);
map_set(&ControllerAxes, "touchx", CONTROLLER_AXIS_TOUCHPAD_X);
map_set(&ControllerAxes, "touchy", CONTROLLER_AXIS_TOUCHPAD_Y);
map_init(&ControllerButtons);
map_set(&ControllerButtons, "unknown", CONTROLLER_BUTTON_UNKNOWN);
map_set(&ControllerButtons, "system", CONTROLLER_BUTTON_SYSTEM);
map_set(&ControllerButtons, "menu", CONTROLLER_BUTTON_MENU);
map_set(&ControllerButtons, "trigger", CONTROLLER_BUTTON_TRIGGER);
map_set(&ControllerButtons, "grip", CONTROLLER_BUTTON_GRIP);
map_set(&ControllerButtons, "touchpad", CONTROLLER_BUTTON_TOUCHPAD);
map_set(&ControllerButtons, "a", CONTROLLER_BUTTON_A);
map_set(&ControllerButtons, "b", CONTROLLER_BUTTON_B);
map_set(&ControllerButtons, "x", CONTROLLER_BUTTON_X);
map_set(&ControllerButtons, "y", CONTROLLER_BUTTON_Y);
map_init(&ControllerHands);
map_set(&ControllerHands, "unknown", HAND_UNKNOWN);
map_set(&ControllerHands, "left", HAND_LEFT);
map_set(&ControllerHands, "right", HAND_RIGHT);
map_init(&HeadsetEyes);
map_set(&HeadsetEyes, "left", EYE_LEFT);
map_set(&HeadsetEyes, "right", EYE_RIGHT);
map_init(&HeadsetOrigins);
map_set(&HeadsetOrigins, "head", ORIGIN_HEAD);
map_set(&HeadsetOrigins, "floor", ORIGIN_FLOOR);
map_init(&HeadsetTypes);
map_set(&HeadsetTypes, "unknown", HEADSET_UNKNOWN);
map_set(&HeadsetTypes, "vive", HEADSET_VIVE);
map_set(&HeadsetTypes, "oculusmobile", HEADSET_OCULUS_MOBILE);
map_set(&HeadsetTypes, "rift", HEADSET_RIFT);
map_set(&HeadsetTypes, "windowsmr", HEADSET_WINDOWS_MR);
map_init(&HeadsetDrivers);
map_set(&HeadsetDrivers, "fake", DRIVER_FAKE);
map_set(&HeadsetDrivers, "openvr", DRIVER_OPENVR);
map_set(&HeadsetDrivers, "oculusvr", DRIVER_OVR);
map_set(&HeadsetDrivers, "oculusvrmobile", DRIVER_OVR_MOBILE);
map_set(&HeadsetDrivers, "webvr", DRIVER_WEBVR);
luax_pushconf(L);
lua_getfield(L, -1, "headset");
vec_t(HeadsetDriver) drivers;
vec_init(&drivers);
bool mirror = false;
if (lua_istable(L, -1)) {
// Drivers
lua_getfield(L, -1, "drivers");
int n = lua_objlen(L, -1);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
vec_push(&drivers, *(HeadsetDriver*) luax_checkenum(L, -1, &HeadsetDrivers, "headset driver"));
lua_pop(L, 1);
}
lua_pop(L, 1);
// Mirror
lua_getfield(L, -1, "mirror");
mirror = lua_toboolean(L, -1);
lua_pop(L, 1);
}
lovrHeadsetInit(drivers.data, drivers.length);
lovrHeadsetSetMirrored(mirror);
vec_deinit(&drivers);
lua_pop(L, 2);
headsetRenderData.ref = LUA_NOREF;
return 1; return 1;
} }
int l_lovrHeadsetIsPresent(lua_State* L) { static int l_lovrHeadsetGetType(lua_State* L) {
lua_pushboolean(L, lovrHeadsetIsPresent()); lua_pushstring(L, HeadsetTypes[lovrHeadsetDriver->getType()]);
return 1; return 1;
} }
int l_lovrHeadsetGetDriver(lua_State* L) { static int l_lovrHeadsetGetOriginType(lua_State* L) {
const HeadsetDriver* driver = lovrHeadsetGetDriver(); lua_pushstring(L, HeadsetOrigins[lovrHeadsetDriver->getOriginType()]);
if (driver) { return 1;
luax_pushenum(L, &HeadsetDrivers, *driver); }
static int l_lovrHeadsetIsMounted(lua_State* L) {
lua_pushboolean(L, lovrHeadsetDriver->isMounted());
return 1;
}
static int l_lovrHeadsetIsMirrored(lua_State* L) {
bool mirrored;
HeadsetEye eye;
lovrHeadsetDriver->isMirrored(&mirrored, &eye);
if (mirrored && eye != EYE_BOTH) {
lua_pushstring(L, HeadsetEyes[eye]);
} else { } else {
lua_pushnil(L); lua_pushboolean(L, mirrored);
} }
return 1; return 1;
} }
int l_lovrHeadsetGetType(lua_State* L) { static int l_lovrHeadsetSetMirrored(lua_State* L) {
luax_pushenum(L, &HeadsetTypes, lovrHeadsetGetType()); bool mirror = lua_toboolean(L, 1);
return 1; HeadsetEye eye = lua_type(L, 2) == LUA_TSTRING ? luaL_checkoption(L, 2, NULL, HeadsetEyes) : EYE_BOTH;
} lovrHeadsetDriver->setMirrored(mirror, eye);
int l_lovrHeadsetGetOriginType(lua_State* L) {
luax_pushenum(L, &HeadsetOrigins, lovrHeadsetGetOriginType());
return 1;
}
int l_lovrHeadsetIsMirrored(lua_State* L) {
lua_pushboolean(L, lovrHeadsetIsMirrored());
return 1;
}
int l_lovrHeadsetSetMirrored(lua_State* L) {
int mirror = lua_toboolean(L, 1);
lovrHeadsetSetMirrored(mirror);
return 0; return 0;
} }
int l_lovrHeadsetGetDisplayWidth(lua_State* L) { static int l_lovrHeadsetGetDisplayWidth(lua_State* L) {
int width; uint32_t width, height;
lovrHeadsetGetDisplayDimensions(&width, NULL); lovrHeadsetDriver->getDisplayDimensions(&width, &height);
lua_pushinteger(L, width);
return 1;
}
static int l_lovrHeadsetGetDisplayHeight(lua_State* L) {
uint32_t width, height;
lovrHeadsetDriver->getDisplayDimensions(&width, &height);
lua_pushinteger(L, height);
return 1;
}
static int l_lovrHeadsetGetDisplayDimensions(lua_State* L) {
uint32_t width, height;
lovrHeadsetDriver->getDisplayDimensions(&width, &height);
lua_pushinteger(L, width);
lua_pushinteger(L, height);
return 2;
}
static int l_lovrHeadsetGetClipDistance(lua_State* L) {
float clipNear, clipFar;
lovrHeadsetDriver->getClipDistance(&clipNear, &clipFar);
lua_pushnumber(L, clipNear);
lua_pushnumber(L, clipFar);
return 2;
}
static int l_lovrHeadsetSetClipDistance(lua_State* L) {
float clipNear = luaL_checknumber(L, 1);
float clipFar = luaL_checknumber(L, 2);
lovrHeadsetDriver->setClipDistance(clipNear, clipFar);
return 0;
}
static int l_lovrHeadsetGetBoundsWidth(lua_State* L) {
float width, depth;
lovrHeadsetDriver->getBoundsDimensions(&width, &depth);
lua_pushnumber(L, width); lua_pushnumber(L, width);
return 1; return 1;
} }
int l_lovrHeadsetGetDisplayHeight(lua_State* L) { static int l_lovrHeadsetGetBoundsDepth(lua_State* L) {
int height; float width, depth;
lovrHeadsetGetDisplayDimensions(NULL, &height); lovrHeadsetDriver->getBoundsDimensions(&width, &depth);
lua_pushnumber(L, height); lua_pushnumber(L, depth);
return 1; return 1;
} }
int l_lovrHeadsetGetDisplayDimensions(lua_State* L) { static int l_lovrHeadsetGetBoundsDimensions(lua_State* L) {
int width, height; float width, depth;
lovrHeadsetGetDisplayDimensions(&width, &height); lovrHeadsetDriver->getBoundsDimensions(&width, &depth);
lua_pushnumber(L, width); lua_pushnumber(L, width);
lua_pushnumber(L, height); lua_pushnumber(L, depth);
return 2; return 2;
} }
int l_lovrHeadsetGetClipDistance(lua_State* L) { static int l_lovrHeadsetGetBoundsGeometry(lua_State* L) {
float near, far; int count;
lovrHeadsetGetClipDistance(&near, &far); const float* points = lovrHeadsetDriver->getBoundsGeometry(&count);
lua_pushnumber(L, near);
lua_pushnumber(L, far);
return 2;
}
int l_lovrHeadsetSetClipDistance(lua_State* L) { if (!points) {
float near = luaL_checknumber(L, 1); lua_pushnil(L);
float far = luaL_checknumber(L, 2); return 1;
lovrHeadsetSetClipDistance(near, far); }
return 0;
} if (lua_type(L, 1) == LUA_TTABLE) {
lua_settop(L, 1);
} else {
lua_settop(L, 0);
lua_createtable(L, count, 0);
}
for (int i = 0; i < count; i++) {
lua_pushnumber(L, points[i]);
lua_rawseti(L, 1, i + 1);
}
int l_lovrHeadsetGetBoundsWidth(lua_State* L) {
lua_pushnumber(L, lovrHeadsetGetBoundsWidth());
return 1; return 1;
} }
int l_lovrHeadsetGetBoundsDepth(lua_State* L) {
lua_pushnumber(L, lovrHeadsetGetBoundsDepth());
return 1;
}
int l_lovrHeadsetGetBoundsDimensions(lua_State* L) {
lua_pushnumber(L, lovrHeadsetGetBoundsWidth());
lua_pushnumber(L, lovrHeadsetGetBoundsDepth());
return 2;
}
static void luax_getPose(lua_State* L, float* x, float* y, float* z, float* angle, float* ax, float* ay, float* az) { static void luax_getPose(lua_State* L, float* x, float* y, float* z, float* angle, float* ax, float* ay, float* az) {
if (lua_type(L, 1) == LUA_TSTRING) { if (lua_type(L, 1) == LUA_TSTRING) {
HeadsetEye eye = *(HeadsetEye*) luax_checkenum(L, 1, &HeadsetEyes, "eye"); HeadsetEye eye = luaL_checkoption(L, 1, NULL, HeadsetEyes);
lovrHeadsetGetEyePose(eye, x, y, z, angle, ax, ay, az); lovrHeadsetDriver->getEyePose(eye, x, y, z, angle, ax, ay, az);
} else { } else {
lovrHeadsetGetPose(x, y, z, angle, ax, ay, az); lovrHeadsetDriver->getPose(x, y, z, angle, ax, ay, az);
} }
} }
int l_lovrHeadsetGetPose(lua_State* L) { static int l_lovrHeadsetGetPose(lua_State* L) {
float x, y, z, angle, ax, ay, az; float x, y, z, angle, ax, ay, az;
luax_getPose(L, &x, &y, &z, &angle, &ax, &ay, &az); luax_getPose(L, &x, &y, &z, &angle, &ax, &ay, &az);
lua_pushnumber(L, x); lua_pushnumber(L, x);
@ -222,7 +219,7 @@ int l_lovrHeadsetGetPose(lua_State* L) {
return 7; return 7;
} }
int l_lovrHeadsetGetPosition(lua_State* L) { static int l_lovrHeadsetGetPosition(lua_State* L) {
float x, y, z, angle, ax, ay, az; float x, y, z, angle, ax, ay, az;
luax_getPose(L, &x, &y, &z, &angle, &ax, &ay, &az); luax_getPose(L, &x, &y, &z, &angle, &ax, &ay, &az);
lua_pushnumber(L, x); lua_pushnumber(L, x);
@ -231,7 +228,7 @@ int l_lovrHeadsetGetPosition(lua_State* L) {
return 3; return 3;
} }
int l_lovrHeadsetGetOrientation(lua_State* L) { static int l_lovrHeadsetGetOrientation(lua_State* L) {
float x, y, z, angle, ax, ay, az; float x, y, z, angle, ax, ay, az;
luax_getPose(L, &x, &y, &z, &angle, &ax, &ay, &az); luax_getPose(L, &x, &y, &z, &angle, &ax, &ay, &az);
lua_pushnumber(L, angle); lua_pushnumber(L, angle);
@ -241,75 +238,71 @@ int l_lovrHeadsetGetOrientation(lua_State* L) {
return 4; return 4;
} }
int l_lovrHeadsetGetVelocity(lua_State* L) { static int l_lovrHeadsetGetVelocity(lua_State* L) {
float x, y, z; float x, y, z;
lovrHeadsetGetVelocity(&x, &y, &z); lovrHeadsetDriver->getVelocity(&x, &y, &z);
lua_pushnumber(L, x); lua_pushnumber(L, x);
lua_pushnumber(L, y); lua_pushnumber(L, y);
lua_pushnumber(L, z); lua_pushnumber(L, z);
return 3; return 3;
} }
int l_lovrHeadsetGetAngularVelocity(lua_State* L) { static int l_lovrHeadsetGetAngularVelocity(lua_State* L) {
float x, y, z; float x, y, z;
lovrHeadsetGetAngularVelocity(&x, &y, &z); lovrHeadsetDriver->getAngularVelocity(&x, &y, &z);
lua_pushnumber(L, x); lua_pushnumber(L, x);
lua_pushnumber(L, y); lua_pushnumber(L, y);
lua_pushnumber(L, z); lua_pushnumber(L, z);
return 3; return 3;
} }
int l_lovrHeadsetGetControllers(lua_State* L) { static int l_lovrHeadsetGetControllers(lua_State* L) {
vec_controller_t* controllers = lovrHeadsetGetControllers(); uint8_t count;
if (!controllers) { Controller** controllers = lovrHeadsetDriver->getControllers(&count);
lua_newtable(L); lua_createtable(L, count, 0);
return 1; for (uint8_t i = 0; i < count; i++) {
} luax_pushobject(L, controllers[i]);
lua_newtable(L);
Controller* controller; int i;
vec_foreach(controllers, controller, i) {
luax_pushtype(L, Controller, controller);
lua_rawseti(L, -2, i + 1); lua_rawseti(L, -2, i + 1);
} }
return 1; return 1;
} }
int l_lovrHeadsetGetControllerCount(lua_State* L) { static int l_lovrHeadsetGetControllerCount(lua_State* L) {
vec_controller_t* controllers = lovrHeadsetGetControllers(); uint8_t count;
if (controllers) { lovrHeadsetDriver->getControllers(&count);
lua_pushnumber(L, controllers->length); lua_pushnumber(L, count);
} else {
lua_pushnumber(L, 0);
}
return 1; return 1;
} }
int l_lovrHeadsetRenderTo(lua_State* L) { static int l_lovrHeadsetRenderTo(lua_State* L) {
lua_settop(L, 1); lua_settop(L, 1);
luaL_checktype(L, 1, LUA_TFUNCTION); luaL_checktype(L, 1, LUA_TFUNCTION);
#ifdef EMSCRIPTEN
if (headsetRenderData.ref != LUA_NOREF) { if (headsetRenderData.ref != LUA_NOREF) {
luaL_unref(L, LUA_REGISTRYINDEX, headsetRenderData.ref); luaL_unref(L, LUA_REGISTRYINDEX, headsetRenderData.ref);
} }
headsetRenderData.ref = luaL_ref(L, LUA_REGISTRYINDEX); headsetRenderData.ref = luaL_ref(L, LUA_REGISTRYINDEX);
#endif
headsetRenderData.L = L; headsetRenderData.L = L;
lovrHeadsetRenderTo(renderHelper, &headsetRenderData); lovrHeadsetDriver->renderTo(renderHelper, &headsetRenderData);
return 0; return 0;
} }
int l_lovrHeadsetUpdate(lua_State* L) { static int l_lovrHeadsetUpdate(lua_State* L) {
float dt = luaL_checknumber(L, 1); if (lovrHeadsetDriver->update) {
lovrHeadsetUpdate(dt); lovrHeadsetDriver->update(luaL_checknumber(L, 1));
}
return 0; return 0;
} }
const luaL_Reg lovrHeadset[] = { static const luaL_Reg lovrHeadset[] = {
{ "isPresent", l_lovrHeadsetIsPresent },
{ "getDriver", l_lovrHeadsetGetDriver }, { "getDriver", l_lovrHeadsetGetDriver },
{ "getType", l_lovrHeadsetGetType }, { "getType", l_lovrHeadsetGetType },
{ "getOriginType", l_lovrHeadsetGetOriginType }, { "getOriginType", l_lovrHeadsetGetOriginType },
{ "isMounted", l_lovrHeadsetIsMounted },
{ "isMirrored", l_lovrHeadsetIsMirrored }, { "isMirrored", l_lovrHeadsetIsMirrored },
{ "setMirrored", l_lovrHeadsetSetMirrored }, { "setMirrored", l_lovrHeadsetSetMirrored },
{ "getDisplayWidth", l_lovrHeadsetGetDisplayWidth }, { "getDisplayWidth", l_lovrHeadsetGetDisplayWidth },
@ -320,6 +313,7 @@ const luaL_Reg lovrHeadset[] = {
{ "getBoundsWidth", l_lovrHeadsetGetBoundsWidth }, { "getBoundsWidth", l_lovrHeadsetGetBoundsWidth },
{ "getBoundsDepth", l_lovrHeadsetGetBoundsDepth }, { "getBoundsDepth", l_lovrHeadsetGetBoundsDepth },
{ "getBoundsDimensions", l_lovrHeadsetGetBoundsDimensions }, { "getBoundsDimensions", l_lovrHeadsetGetBoundsDimensions },
{ "getBoundsGeometry", l_lovrHeadsetGetBoundsGeometry },
{ "getPose", l_lovrHeadsetGetPose }, { "getPose", l_lovrHeadsetGetPose },
{ "getPosition", l_lovrHeadsetGetPosition }, { "getPosition", l_lovrHeadsetGetPosition },
{ "getOrientation", l_lovrHeadsetGetOrientation }, { "getOrientation", l_lovrHeadsetGetOrientation },
@ -331,3 +325,59 @@ const luaL_Reg lovrHeadset[] = {
{ "update", l_lovrHeadsetUpdate }, { "update", l_lovrHeadsetUpdate },
{ NULL, NULL } { NULL, NULL }
}; };
int luaopen_lovr_headset(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrHeadset);
luax_atexit(L, lovrHeadsetDestroy);
luax_registertype(L, "Controller", lovrController);
luax_pushconf(L);
lua_getfield(L, -1, "headset");
vec_t(HeadsetDriver) drivers;
vec_init(&drivers);
bool mirror = false;
HeadsetEye mirrorEye = EYE_BOTH;
float offset = 1.7;
int msaa = 4;
if (lua_istable(L, -1)) {
// Drivers
lua_getfield(L, -1, "drivers");
int n = lua_objlen(L, -1);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
vec_push(&drivers, luaL_checkoption(L, -1, NULL, HeadsetDrivers));
lua_pop(L, 1);
}
lua_pop(L, 1);
// Mirror
lua_getfield(L, -1, "mirror");
mirror = lua_toboolean(L, -1);
mirrorEye = lua_type(L, -1) == LUA_TSTRING ? luaL_checkoption(L, -1, NULL, HeadsetEyes) : EYE_BOTH;
lua_pop(L, 1);
// Offset
lua_getfield(L, -1, "offset");
offset = luaL_optnumber(L, -1, 1.7);
lua_pop(L, 1);
// MSAA
lua_getfield(L, -1, "msaa");
msaa = luaL_optnumber(L, -1, 4);
lua_pop(L, 1);
}
lovrHeadsetInit(drivers.data, drivers.length, offset, msaa);
lovrHeadsetDriver->setMirrored(mirror, mirrorEye);
vec_deinit(&drivers);
lua_pop(L, 2);
headsetRenderData.ref = LUA_NOREF;
return 1;
}

40
src/api/lovr.c Normal file
View File

@ -0,0 +1,40 @@
#include "api.h"
#include "version.h"
#include "resources/logo.png.h"
static int l_lovrGetOS(lua_State* L) {
#ifdef _WIN32
lua_pushstring(L, "Windows");
#elif __APPLE__
lua_pushstring(L, "macOS");
#elif EMSCRIPTEN
lua_pushstring(L, "Web");
#elif __linux__
lua_pushstring(L, "Linux");
#else
lua_pushnil(L);
#endif
return 1;
}
static int l_lovrGetVersion(lua_State* L) {
lua_pushinteger(L, LOVR_VERSION_MAJOR);
lua_pushinteger(L, LOVR_VERSION_MINOR);
lua_pushinteger(L, LOVR_VERSION_PATCH);
return 3;
}
static const luaL_Reg lovr[] = {
{ "_setConf", luax_setconf },
{ "getOS", l_lovrGetOS },
{ "getVersion", l_lovrGetVersion },
{ NULL, NULL }
};
int luaopen_lovr(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovr);
lua_pushlstring(L, (const char*) logo_png, logo_png_len);
lua_setfield(L, -2, "_logo");
return 1;
}

View File

@ -1,45 +1,32 @@
#include "api.h" #include "api.h"
#include "api/math.h"
#include "math/math.h" #include "math/math.h"
#include "math/mat4.h" #include "math/mat4.h"
#include "math/quat.h" #include "math/quat.h"
#include "math/randomGenerator.h" #include "math/randomGenerator.h"
#include "math/transform.h" #include "math/transform.h"
extern int l_lovrRandomGeneratorRandom(lua_State* L); static int l_lovrMathNewRandomGenerator(lua_State* L) {
extern int l_lovrRandomGeneratorRandomNormal(lua_State* L);
extern int l_lovrRandomGeneratorGetSeed(lua_State* L);
extern int l_lovrRandomGeneratorSetSeed(lua_State* L);
int l_lovrMathInit(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrMath);
luax_registertype(L, "RandomGenerator", lovrRandomGenerator);
luax_registertype(L, "Transform", lovrTransform);
lovrMathInit();
return 1;
}
int l_lovrMathNewRandomGenerator(lua_State* L) {
RandomGenerator* generator = lovrRandomGeneratorCreate(); RandomGenerator* generator = lovrRandomGeneratorCreate();
if (lua_gettop(L) > 0){ if (lua_gettop(L) > 0){
Seed seed = luax_checkrandomseed(L, 1); Seed seed = luax_checkrandomseed(L, 1);
lovrRandomGeneratorSetSeed(generator, seed); lovrRandomGeneratorSetSeed(generator, seed);
} }
luax_pushtype(L, RandomGenerator, generator); luax_pushobject(L, generator);
lovrRelease(&generator->ref); lovrRelease(generator);
return 1; return 1;
} }
int l_lovrMathNewTransform(lua_State* L) { static int l_lovrMathNewTransform(lua_State* L) {
float matrix[16]; float matrix[16];
luax_readtransform(L, 1, matrix, 0); luax_readtransform(L, 1, matrix, 3);
Transform* transform = lovrTransformCreate(matrix); Transform* transform = lovrTransformCreate(matrix);
luax_pushtype(L, Transform, transform); luax_pushobject(L, transform);
lovrRelease(&transform->ref); lovrRelease(transform);
return 1; return 1;
} }
int l_lovrMathLookAt(lua_State* L) { static int l_lovrMathLookAt(lua_State* L) {
float from[3] = { luaL_checknumber(L, 1), luaL_checknumber(L, 2), luaL_checknumber(L, 3) }; float from[3] = { luaL_checknumber(L, 1), luaL_checknumber(L, 2), luaL_checknumber(L, 3) };
float to[3] = { luaL_checknumber(L, 4), luaL_checknumber(L, 5), luaL_checknumber(L, 6) }; float to[3] = { luaL_checknumber(L, 4), luaL_checknumber(L, 5), luaL_checknumber(L, 6) };
float up[3] = { luaL_optnumber(L, 7, 0), luaL_optnumber(L, 8, 1), luaL_optnumber(L, 9, 0) }; float up[3] = { luaL_optnumber(L, 7, 0), luaL_optnumber(L, 8, 1), luaL_optnumber(L, 9, 0) };
@ -54,7 +41,7 @@ int l_lovrMathLookAt(lua_State* L) {
return 4; return 4;
} }
int l_lovrMathOrientationToDirection(lua_State* L) { static int l_lovrMathOrientationToDirection(lua_State* L) {
float angle = luaL_checknumber(L, 1); float angle = luaL_checknumber(L, 1);
float ax = luaL_optnumber(L, 2, 0); float ax = luaL_optnumber(L, 2, 0);
float ay = luaL_optnumber(L, 3, 1); float ay = luaL_optnumber(L, 3, 1);
@ -67,31 +54,44 @@ int l_lovrMathOrientationToDirection(lua_State* L) {
return 3; return 3;
} }
int l_lovrMathRandom(lua_State* L) { static int l_lovrMathNoise(lua_State* L) {
luax_pushtype(L, RandomGenerator, lovrMathGetRandomGenerator()); switch (lua_gettop(L)) {
case 0:
case 1: lua_pushnumber(L, lovrMathNoise1(luaL_checknumber(L, 1))); return 1;
case 2: lua_pushnumber(L, lovrMathNoise2(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); return 1;
case 3: lua_pushnumber(L, lovrMathNoise3(luaL_checknumber(L, 1), luaL_checknumber(L, 2), luaL_checknumber(L, 3))); return 1;
case 4:
default:
lua_pushnumber(L, lovrMathNoise4(luaL_checknumber(L, 1), luaL_checknumber(L, 2), luaL_checknumber(L, 3), luaL_checknumber(L, 4)));
return 1;
}
}
static int l_lovrMathRandom(lua_State* L) {
luax_pushobject(L, lovrMathGetRandomGenerator());
lua_insert(L, 1); lua_insert(L, 1);
return l_lovrRandomGeneratorRandom(L); return l_lovrRandomGeneratorRandom(L);
} }
int l_lovrMathRandomNormal(lua_State* L) { static int l_lovrMathRandomNormal(lua_State* L) {
luax_pushtype(L, RandomGenerator, lovrMathGetRandomGenerator()); luax_pushobject(L, lovrMathGetRandomGenerator());
lua_insert(L, 1); lua_insert(L, 1);
return l_lovrRandomGeneratorRandomNormal(L); return l_lovrRandomGeneratorRandomNormal(L);
} }
int l_lovrMathGetRandomSeed(lua_State* L) { static int l_lovrMathGetRandomSeed(lua_State* L) {
luax_pushtype(L, RandomGenerator, lovrMathGetRandomGenerator()); luax_pushobject(L, lovrMathGetRandomGenerator());
lua_insert(L, 1); lua_insert(L, 1);
return l_lovrRandomGeneratorGetSeed(L); return l_lovrRandomGeneratorGetSeed(L);
} }
int l_lovrMathSetRandomSeed(lua_State* L) { static int l_lovrMathSetRandomSeed(lua_State* L) {
luax_pushtype(L, RandomGenerator, lovrMathGetRandomGenerator()); luax_pushobject(L, lovrMathGetRandomGenerator());
lua_insert(L, 1); lua_insert(L, 1);
return l_lovrRandomGeneratorSetSeed(L); return l_lovrRandomGeneratorSetSeed(L);
} }
int l_lovrMathGammaToLinear(lua_State* L) { static int l_lovrMathGammaToLinear(lua_State* L) {
if (lua_istable(L, 1)) { if (lua_istable(L, 1)) {
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
lua_rawgeti(L, 1, i + 1); lua_rawgeti(L, 1, i + 1);
@ -108,7 +108,7 @@ int l_lovrMathGammaToLinear(lua_State* L) {
} }
} }
int l_lovrMathLinearToGamma(lua_State* L) { static int l_lovrMathLinearToGamma(lua_State* L) {
if (lua_istable(L, 1)) { if (lua_istable(L, 1)) {
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
lua_rawgeti(L, 1, i + 1); lua_rawgeti(L, 1, i + 1);
@ -125,11 +125,12 @@ int l_lovrMathLinearToGamma(lua_State* L) {
} }
} }
const luaL_Reg lovrMath[] = { static const luaL_Reg lovrMath[] = {
{ "newRandomGenerator", l_lovrMathNewRandomGenerator }, { "newRandomGenerator", l_lovrMathNewRandomGenerator },
{ "newTransform", l_lovrMathNewTransform }, { "newTransform", l_lovrMathNewTransform },
{ "orientationToDirection", l_lovrMathOrientationToDirection }, { "orientationToDirection", l_lovrMathOrientationToDirection },
{ "lookAt", l_lovrMathLookAt }, { "lookAt", l_lovrMathLookAt },
{ "noise", l_lovrMathNoise },
{ "random", l_lovrMathRandom }, { "random", l_lovrMathRandom },
{ "randomNormal", l_lovrMathRandomNormal }, { "randomNormal", l_lovrMathRandomNormal },
{ "getRandomSeed", l_lovrMathGetRandomSeed }, { "getRandomSeed", l_lovrMathGetRandomSeed },
@ -139,3 +140,12 @@ const luaL_Reg lovrMath[] = {
{ NULL, NULL } { NULL, NULL }
}; };
int luaopen_lovr_math(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrMath);
luax_atexit(L, lovrMathDestroy);
luax_registertype(L, "RandomGenerator", lovrRandomGenerator);
luax_registertype(L, "Transform", lovrTransform);
lovrMathInit();
return 1;
}

10
src/api/math.h Normal file
View File

@ -0,0 +1,10 @@
#include "math/mat4.h"
#include "math/randomGenerator.h"
int luax_readtransform(lua_State* L, int index, mat4 transform, int scaleComponents);
Seed luax_checkrandomseed(lua_State* L, int index);
int l_lovrRandomGeneratorRandom(lua_State* L);
int l_lovrRandomGeneratorRandomNormal(lua_State* L);
int l_lovrRandomGeneratorGetSeed(lua_State* L);
int l_lovrRandomGeneratorSetSeed(lua_State* L);

View File

@ -1,40 +1,23 @@
#include "api.h" #include "api.h"
#include "physics/physics.h" #include "physics/physics.h"
map_int_t ShapeTypes; const char* ShapeTypes[] = {
map_int_t JointTypes; [SHAPE_SPHERE] = "sphere",
[SHAPE_BOX] = "box",
[SHAPE_CAPSULE] = "capsule",
[SHAPE_CYLINDER] = "cylinder",
NULL
};
int l_lovrPhysicsInit(lua_State* L) { const char* JointTypes[] = {
lua_newtable(L); [JOINT_BALL] = "ball",
luaL_register(L, NULL, lovrPhysics); [JOINT_DISTANCE] = "distance",
luax_registertype(L, "World", lovrWorld); [JOINT_HINGE] = "hinge",
luax_registertype(L, "Collider", lovrCollider); [JOINT_SLIDER] = "slider",
luax_extendtype(L, "Joint", "BallJoint", lovrJoint, lovrBallJoint); NULL
luax_extendtype(L, "Joint", "DistanceJoint", lovrJoint, lovrDistanceJoint); };
luax_extendtype(L, "Joint", "HingeJoint", lovrJoint, lovrHingeJoint);
luax_extendtype(L, "Joint", "SliderJoint", lovrJoint, lovrSliderJoint);
luax_extendtype(L, "Shape", "SphereShape", lovrShape, lovrSphereShape);
luax_extendtype(L, "Shape", "BoxShape", lovrShape, lovrBoxShape);
luax_extendtype(L, "Shape", "CapsuleShape", lovrShape, lovrCapsuleShape);
luax_extendtype(L, "Shape", "CylinderShape", lovrShape, lovrCylinderShape);
map_init(&JointTypes); static int l_lovrPhysicsNewWorld(lua_State* L) {
map_set(&JointTypes, "ball", JOINT_BALL);
map_set(&JointTypes, "distance", JOINT_DISTANCE);
map_set(&JointTypes, "hinge", JOINT_HINGE);
map_set(&JointTypes, "slider", JOINT_SLIDER);
map_init(&ShapeTypes);
map_set(&ShapeTypes, "sphere", SHAPE_SPHERE);
map_set(&ShapeTypes, "box", SHAPE_BOX);
map_set(&ShapeTypes, "capsule", SHAPE_CAPSULE);
map_set(&ShapeTypes, "cylinder", SHAPE_CYLINDER);
lovrPhysicsInit();
return 1;
}
int l_lovrPhysicsNewWorld(lua_State* L) {
float xg = luaL_optnumber(L, 1, 0.f); float xg = luaL_optnumber(L, 1, 0.f);
float yg = luaL_optnumber(L, 2, -9.81); float yg = luaL_optnumber(L, 2, -9.81);
float zg = luaL_optnumber(L, 3, 0.f); float zg = luaL_optnumber(L, 3, 0.f);
@ -56,47 +39,52 @@ int l_lovrPhysicsNewWorld(lua_State* L) {
tagCount = 0; tagCount = 0;
} }
World* world = lovrWorldCreate(xg, yg, zg, allowSleep, tags, tagCount); World* world = lovrWorldCreate(xg, yg, zg, allowSleep, tags, tagCount);
luax_pushtype(L, World, world); luax_pushobject(L, world);
lovrRelease(world);
return 1; return 1;
} }
int l_lovrPhysicsNewBallJoint(lua_State* L) { static int l_lovrPhysicsNewBallJoint(lua_State* L) {
Collider* a = luax_checktype(L, 1, Collider); Collider* a = luax_checktype(L, 1, Collider);
Collider* b = luax_checktype(L, 2, Collider); Collider* b = luax_checktype(L, 2, Collider);
float x = luaL_checknumber(L, 3); float x = luaL_checknumber(L, 3);
float y = luaL_checknumber(L, 4); float y = luaL_checknumber(L, 4);
float z = luaL_checknumber(L, 5); float z = luaL_checknumber(L, 5);
BallJoint* joint = lovrBallJointCreate(a, b, x, y, z); BallJoint* joint = lovrBallJointCreate(a, b, x, y, z);
luax_pushtype(L, BallJoint, joint); luax_pushobject(L, joint);
lovrRelease(joint);
return 1; return 1;
} }
int l_lovrPhysicsNewBoxShape(lua_State* L) { static int l_lovrPhysicsNewBoxShape(lua_State* L) {
float x = luaL_optnumber(L, 1, 1.f); float x = luaL_optnumber(L, 1, 1.f);
float y = luaL_optnumber(L, 2, x); float y = luaL_optnumber(L, 2, x);
float z = luaL_optnumber(L, 3, x); float z = luaL_optnumber(L, 3, x);
BoxShape* box = lovrBoxShapeCreate(x, y, z); BoxShape* box = lovrBoxShapeCreate(x, y, z);
luax_pushtype(L, BoxShape, box); luax_pushobject(L, box);
lovrRelease(box);
return 1; return 1;
} }
int l_lovrPhysicsNewCapsuleShape(lua_State* L) { static int l_lovrPhysicsNewCapsuleShape(lua_State* L) {
float radius = luaL_optnumber(L, 1, 1.f); float radius = luaL_optnumber(L, 1, 1.f);
float length = luaL_optnumber(L, 2, 1.f); float length = luaL_optnumber(L, 2, 1.f);
CapsuleShape* capsule = lovrCapsuleShapeCreate(radius, length); CapsuleShape* capsule = lovrCapsuleShapeCreate(radius, length);
luax_pushtype(L, CapsuleShape, capsule); luax_pushobject(L, capsule);
lovrRelease(capsule);
return 1; return 1;
} }
int l_lovrPhysicsNewCylinderShape(lua_State* L) { static int l_lovrPhysicsNewCylinderShape(lua_State* L) {
float radius = luaL_optnumber(L, 1, 1.f); float radius = luaL_optnumber(L, 1, 1.f);
float length = luaL_optnumber(L, 2, 1.f); float length = luaL_optnumber(L, 2, 1.f);
CylinderShape* cylinder = lovrCylinderShapeCreate(radius, length); CylinderShape* cylinder = lovrCylinderShapeCreate(radius, length);
luax_pushtype(L, CylinderShape, cylinder); luax_pushobject(L, cylinder);
lovrRelease(cylinder);
return 1; return 1;
} }
int l_lovrPhysicsNewDistanceJoint(lua_State* L) { static int l_lovrPhysicsNewDistanceJoint(lua_State* L) {
Collider* a = luax_checktype(L, 1, Collider); Collider* a = luax_checktype(L, 1, Collider);
Collider* b = luax_checktype(L, 2, Collider); Collider* b = luax_checktype(L, 2, Collider);
float x1 = luaL_checknumber(L, 3); float x1 = luaL_checknumber(L, 3);
@ -106,11 +94,12 @@ int l_lovrPhysicsNewDistanceJoint(lua_State* L) {
float y2 = luaL_checknumber(L, 7); float y2 = luaL_checknumber(L, 7);
float z2 = luaL_checknumber(L, 8); float z2 = luaL_checknumber(L, 8);
DistanceJoint* joint = lovrDistanceJointCreate(a, b, x1, y1, z1, x2, y2, z2); DistanceJoint* joint = lovrDistanceJointCreate(a, b, x1, y1, z1, x2, y2, z2);
luax_pushtype(L, DistanceJoint, joint); luax_pushobject(L, joint);
lovrRelease(joint);
return 1; return 1;
} }
int l_lovrPhysicsNewHingeJoint(lua_State* L) { static int l_lovrPhysicsNewHingeJoint(lua_State* L) {
Collider* a = luax_checktype(L, 1, Collider); Collider* a = luax_checktype(L, 1, Collider);
Collider* b = luax_checktype(L, 2, Collider); Collider* b = luax_checktype(L, 2, Collider);
float x = luaL_checknumber(L, 3); float x = luaL_checknumber(L, 3);
@ -120,29 +109,32 @@ int l_lovrPhysicsNewHingeJoint(lua_State* L) {
float ay = luaL_checknumber(L, 7); float ay = luaL_checknumber(L, 7);
float az = luaL_checknumber(L, 8); float az = luaL_checknumber(L, 8);
HingeJoint* joint = lovrHingeJointCreate(a, b, x, y, z, ax, ay, az); HingeJoint* joint = lovrHingeJointCreate(a, b, x, y, z, ax, ay, az);
luax_pushtype(L, HingeJoint, joint); luax_pushobject(L, joint);
lovrRelease(joint);
return 1; return 1;
} }
int l_lovrPhysicsNewSliderJoint(lua_State* L) { static int l_lovrPhysicsNewSliderJoint(lua_State* L) {
Collider* a = luax_checktype(L, 1, Collider); Collider* a = luax_checktype(L, 1, Collider);
Collider* b = luax_checktype(L, 2, Collider); Collider* b = luax_checktype(L, 2, Collider);
float ax = luaL_checknumber(L, 3); float ax = luaL_checknumber(L, 3);
float ay = luaL_checknumber(L, 4); float ay = luaL_checknumber(L, 4);
float az = luaL_checknumber(L, 5); float az = luaL_checknumber(L, 5);
SliderJoint* joint = lovrSliderJointCreate(a, b, ax, ay, az); SliderJoint* joint = lovrSliderJointCreate(a, b, ax, ay, az);
luax_pushtype(L, SliderJoint, joint); luax_pushobject(L, joint);
lovrRelease(joint);
return 1; return 1;
} }
int l_lovrPhysicsNewSphereShape(lua_State* L) { static int l_lovrPhysicsNewSphereShape(lua_State* L) {
float radius = luaL_optnumber(L, 1, 1.f); float radius = luaL_optnumber(L, 1, 1.f);
SphereShape* sphere = lovrSphereShapeCreate(radius); SphereShape* sphere = lovrSphereShapeCreate(radius);
luax_pushtype(L, SphereShape, sphere); luax_pushobject(L, sphere);
lovrRelease(sphere);
return 1; return 1;
} }
const luaL_Reg lovrPhysics[] = { static const luaL_Reg lovrPhysics[] = {
{ "newWorld", l_lovrPhysicsNewWorld }, { "newWorld", l_lovrPhysicsNewWorld },
{ "newBallJoint", l_lovrPhysicsNewBallJoint }, { "newBallJoint", l_lovrPhysicsNewBallJoint },
{ "newBoxShape", l_lovrPhysicsNewBoxShape }, { "newBoxShape", l_lovrPhysicsNewBoxShape },
@ -154,3 +146,21 @@ const luaL_Reg lovrPhysics[] = {
{ "newSphereShape", l_lovrPhysicsNewSphereShape }, { "newSphereShape", l_lovrPhysicsNewSphereShape },
{ NULL, NULL } { NULL, NULL }
}; };
int luaopen_lovr_physics(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrPhysics);
luax_atexit(L, lovrPhysicsDestroy);
luax_registertype(L, "World", lovrWorld);
luax_registertype(L, "Collider", lovrCollider);
luax_extendtype(L, "Joint", "BallJoint", lovrJoint, lovrBallJoint);
luax_extendtype(L, "Joint", "DistanceJoint", lovrJoint, lovrDistanceJoint);
luax_extendtype(L, "Joint", "HingeJoint", lovrJoint, lovrHingeJoint);
luax_extendtype(L, "Joint", "SliderJoint", lovrJoint, lovrSliderJoint);
luax_extendtype(L, "Shape", "SphereShape", lovrShape, lovrSphereShape);
luax_extendtype(L, "Shape", "BoxShape", lovrShape, lovrBoxShape);
luax_extendtype(L, "Shape", "CapsuleShape", lovrShape, lovrCapsuleShape);
luax_extendtype(L, "Shape", "CylinderShape", lovrShape, lovrCylinderShape);
lovrPhysicsInit();
return 1;
}

70
src/api/thread.c Normal file
View File

@ -0,0 +1,70 @@
#include "api.h"
#include "event/event.h"
#include "thread/thread.h"
static int threadRunner(void* data) {
Thread* thread = (Thread*) data;
lovrRetain(thread);
mtx_lock(&thread->lock);
thread->running = true;
thread->error = NULL;
mtx_unlock(&thread->lock);
// Lua state
lua_State* L = luaL_newstate();
luaL_openlibs(L);
lovrSetErrorCallback((lovrErrorHandler) luax_vthrow, L);
if (luaL_loadbuffer(L, thread->body, strlen(thread->body), "thread") || lua_pcall(L, 0, 0, 0)) {
thread->error = lua_tostring(L, -1);
}
mtx_lock(&thread->lock);
thread->running = false;
mtx_unlock(&thread->lock);
lovrRelease(thread);
if (thread->error) {
lovrEventPush((Event) {
.type = EVENT_THREAD_ERROR,
.data.thread = { thread, strdup(thread->error) }
});
lua_close(L);
return 1;
}
lua_close(L);
return 0;
}
static int l_lovrThreadNewThread(lua_State* L) {
const char* body = luaL_checkstring(L, 1);
Thread* thread = lovrThreadCreate(threadRunner, body);
luax_pushobject(L, thread);
lovrRelease(thread);
return 1;
}
static int l_lovrThreadGetChannel(lua_State* L) {
const char* name = luaL_checkstring(L, 1);
Channel* channel = lovrThreadGetChannel(name);
luax_pushobject(L, channel);
return 1;
}
static const luaL_Reg lovrThreadModule[] = {
{ "newThread", l_lovrThreadNewThread },
{ "getChannel", l_lovrThreadGetChannel },
{ NULL, NULL }
};
int luaopen_lovr_thread(lua_State* L) {
lua_newtable(L);
luax_atexit(L, lovrThreadDeinit);
luaL_register(L, NULL, lovrThreadModule);
luax_registertype(L, "Thread", lovrThread);
luax_registertype(L, "Channel", lovrChannel);
lovrThreadInit();
return 1;
}

View File

@ -1,45 +1,38 @@
#include "api.h" #include "api.h"
#include "timer/timer.h" #include "timer/timer.h"
int l_lovrTimerInit(lua_State* L) { static int l_lovrTimerGetDelta(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrTimer);
lovrTimerInit();
return 1;
}
int l_lovrTimerGetDelta(lua_State* L) {
lua_pushnumber(L, lovrTimerGetDelta()); lua_pushnumber(L, lovrTimerGetDelta());
return 1; return 1;
} }
int l_lovrTimerGetAverageDelta(lua_State* L) { static int l_lovrTimerGetAverageDelta(lua_State* L) {
lua_pushnumber(L, lovrTimerGetAverageDelta()); lua_pushnumber(L, lovrTimerGetAverageDelta());
return 1; return 1;
} }
int l_lovrTimerGetFPS(lua_State* L) { static int l_lovrTimerGetFPS(lua_State* L) {
lua_pushnumber(L, lovrTimerGetFPS()); lua_pushnumber(L, lovrTimerGetFPS());
return 1; return 1;
} }
int l_lovrTimerGetTime(lua_State* L) { static int l_lovrTimerGetTime(lua_State* L) {
lua_pushnumber(L, lovrTimerGetTime()); lua_pushnumber(L, lovrTimerGetTime());
return 1; return 1;
} }
int l_lovrTimerStep(lua_State* L) { static int l_lovrTimerStep(lua_State* L) {
lua_pushnumber(L, lovrTimerStep()); lua_pushnumber(L, lovrTimerStep());
return 1; return 1;
} }
int l_lovrTimerSleep(lua_State* L) { static int l_lovrTimerSleep(lua_State* L) {
double duration = luaL_checknumber(L, 1); double duration = luaL_checknumber(L, 1);
lovrTimerSleep(duration); lovrTimerSleep(duration);
return 0; return 0;
} }
const luaL_Reg lovrTimer[] = { static const luaL_Reg lovrTimer[] = {
{ "getDelta", l_lovrTimerGetDelta }, { "getDelta", l_lovrTimerGetDelta },
{ "getAverageDelta", l_lovrTimerGetAverageDelta }, { "getAverageDelta", l_lovrTimerGetAverageDelta },
{ "getFPS", l_lovrTimerGetFPS }, { "getFPS", l_lovrTimerGetFPS },
@ -48,3 +41,11 @@ const luaL_Reg lovrTimer[] = {
{ "sleep", l_lovrTimerSleep }, { "sleep", l_lovrTimerSleep },
{ NULL, NULL } { NULL, NULL }
}; };
int luaopen_lovr_timer(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrTimer);
luax_atexit(L, lovrTimerDestroy);
lovrTimerInit();
return 1;
}

View File

@ -1,5 +1,20 @@
#include "api.h" #include "api.h"
#include "data/audioStream.h" #include "data/audioStream.h"
#include "data/soundData.h"
int l_lovrAudioStreamDecode(lua_State* L) {
AudioStream* stream = luax_checktype(L, 1, AudioStream);
int samples = lovrAudioStreamDecode(stream, NULL, 0);
if (samples > 0) {
SoundData* soundData = lovrSoundDataCreate(samples / stream->channelCount, stream->sampleRate, stream->bitDepth, stream->channelCount);
memcpy(soundData->blob.data, stream->buffer, samples * (stream->bitDepth / 8));
luax_pushobject(L, soundData);
lovrRelease(soundData);
} else {
lua_pushnil(L);
}
return 1;
}
int l_lovrAudioStreamGetBitDepth(lua_State* L) { int l_lovrAudioStreamGetBitDepth(lua_State* L) {
AudioStream* stream = luax_checktype(L, 1, AudioStream); AudioStream* stream = luax_checktype(L, 1, AudioStream);
@ -25,18 +40,11 @@ int l_lovrAudioStreamGetSampleRate(lua_State* L) {
return 1; return 1;
} }
int l_lovrAudioStreamSeek(lua_State* L) {
AudioStream* stream = luax_checktype(L, 1, AudioStream);
float seconds = luaL_checknumber(L, 2);
lovrAudioStreamSeek(stream, (int) (seconds * stream->sampleRate + .5));
return 0;
}
const luaL_Reg lovrAudioStream[] = { const luaL_Reg lovrAudioStream[] = {
{ "decode", l_lovrAudioStreamDecode },
{ "getBitDepth", l_lovrAudioStreamGetBitDepth }, { "getBitDepth", l_lovrAudioStreamGetBitDepth },
{ "getChannelCount", l_lovrAudioStreamGetChannelCount }, { "getChannelCount", l_lovrAudioStreamGetChannelCount },
{ "getDuration", l_lovrAudioStreamGetDuration }, { "getDuration", l_lovrAudioStreamGetDuration },
{ "getSampleRate", l_lovrAudioStreamGetSampleRate }, { "getSampleRate", l_lovrAudioStreamGetSampleRate },
{ "seek", l_lovrAudioStreamSeek },
{ NULL, NULL } { NULL, NULL }
}; };

View File

@ -1,7 +1,7 @@
#include "api.h" #include "api.h"
#include "filesystem/blob.h" #include "data/blob.h"
int l_lovrBlobGetFilename(lua_State* L) { int l_lovrBlobGetName(lua_State* L) {
Blob* blob = luax_checktype(L, 1, Blob); Blob* blob = luax_checktype(L, 1, Blob);
lua_pushstring(L, blob->name); lua_pushstring(L, blob->name);
return 1; return 1;
@ -26,7 +26,7 @@ int l_lovrBlobGetString(lua_State* L) {
} }
const luaL_Reg lovrBlob[] = { const luaL_Reg lovrBlob[] = {
{ "getFilename", l_lovrBlobGetFilename }, { "getName", l_lovrBlobGetName },
{ "getPointer", l_lovrBlobGetPointer }, { "getPointer", l_lovrBlobGetPointer },
{ "getSize", l_lovrBlobGetSize }, { "getSize", l_lovrBlobGetSize },
{ "getString", l_lovrBlobGetString }, { "getString", l_lovrBlobGetString },

View File

@ -1,35 +1,158 @@
#include "api.h" #include "api.h"
#include "graphics/graphics.h" #include "api/graphics.h"
#include "graphics/canvas.h" #include "graphics/canvas.h"
#include "graphics/graphics.h"
Texture* luax_checktexture(lua_State* L, int index) {
Canvas* canvas = luax_totype(L, index, Canvas);
if (canvas) {
const Attachment* attachment = lovrCanvasGetAttachments(canvas, NULL);
return attachment->texture;
} else {
return luax_checktype(L, index, Texture);
}
}
static int luax_checkattachment(lua_State* L, int index, Attachment* attachment) {
if (lua_istable(L, index)) {
lua_rawgeti(L, index, 1);
attachment->texture = luax_checktype(L, -1, Texture);
lua_pop(L, 1);
lua_rawgeti(L, index, 2);
attachment->slice = luaL_optinteger(L, -1, 1) - 1;
lua_pop(L, 1);
lua_rawgeti(L, index, 3);
attachment->level = luax_optmipmap(L, -1, attachment->texture);
lua_pop(L, 1);
index++;
} else {
attachment->texture = luax_checktype(L, index++, Texture);
attachment->slice = lua_type(L, index) == LUA_TNUMBER ? lua_tointeger(L, index++) - 1 : 0;
attachment->level = lua_type(L, index) == LUA_TNUMBER ? luax_optmipmap(L, index++, attachment->texture) : 0;
}
bool isValidSlice = attachment->slice >= 0 && attachment->slice < lovrTextureGetDepth(attachment->texture, 0);
lovrAssert(isValidSlice, "Invalid slice %d\n", attachment->slice + 1);
return index;
}
void luax_readattachments(lua_State* L, int index, Attachment* attachments, int* count) {
bool table = lua_istable(L, index);
int top = table ? -1 : lua_gettop(L);
int n;
if (lua_istable(L, index)) {
n = lua_objlen(L, index);
n = MIN(n, 3 * MAX_CANVAS_ATTACHMENTS);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, index, i + 1);
}
index = -n;
}
for (*count = 0; *count < MAX_CANVAS_ATTACHMENTS && index <= top; (*count)++) {
index = luax_checkattachment(L, index, attachments + *count);
}
if (table) {
lua_pop(L, n);
}
}
int l_lovrCanvasNewTextureData(lua_State* L) {
Canvas* canvas = luax_checktype(L, 1, Canvas);
int index = luaL_optinteger(L, 2, 1) - 1;
int count;
lovrCanvasGetAttachments(canvas, &count);
lovrAssert(index >= 0 && index < count, "Can not create a TextureData from Texture #%d of Canvas (it only has %d textures)", index, count);
TextureData* textureData = lovrCanvasNewTextureData(canvas, index);
luax_pushobject(L, textureData);
lovrRelease(textureData);
return 1;
}
int l_lovrCanvasRenderTo(lua_State* L) { int l_lovrCanvasRenderTo(lua_State* L) {
Canvas* canvas = luax_checktype(L, 1, Canvas); Canvas* canvas = luax_checktype(L, 1, Canvas);
luaL_checktype(L, 2, LUA_TFUNCTION); luaL_checktype(L, 2, LUA_TFUNCTION);
int nargs = lua_gettop(L) - 2; int argumentCount = lua_gettop(L) - 2;
lovrGraphicsPushView(); Canvas* old = lovrGraphicsGetCanvas();
lovrCanvasBind(canvas); lovrGraphicsSetCanvas(canvas);
lua_call(L, nargs, 0); lua_call(L, argumentCount, 0);
lovrCanvasResolveMSAA(canvas); lovrGraphicsSetCanvas(old);
lovrGraphicsPopView();
return 0; return 0;
} }
int l_lovrCanvasGetFormat(lua_State* L) { int l_lovrCanvasGetTexture(lua_State* L) {
Canvas* canvas = luax_checktype(L, 1, Canvas); Canvas* canvas = luax_checktype(L, 1, Canvas);
TextureFormat format = lovrCanvasGetFormat(canvas); int count;
luax_pushenum(L, &TextureFormats, format); const Attachment* attachments = lovrCanvasGetAttachments(canvas, &count);
for (int i = 0; i < count; i++) {
luax_pushobject(L, attachments[i].texture);
}
return count;
}
int l_lovrCanvasSetTexture(lua_State* L) {
Canvas* canvas = luax_checktype(L, 1, Canvas);
Attachment attachments[MAX_CANVAS_ATTACHMENTS];
int count;
luax_readattachments(L, 2, attachments, &count);
lovrCanvasSetAttachments(canvas, attachments, count);
return 0;
}
int l_lovrCanvasGetWidth(lua_State* L) {
Canvas* canvas = luax_checktype(L, 1, Canvas);
lua_pushinteger(L, lovrCanvasGetWidth(canvas));
return 1;
}
int l_lovrCanvasGetHeight(lua_State* L) {
Canvas* canvas = luax_checktype(L, 1, Canvas);
lua_pushinteger(L, lovrCanvasGetHeight(canvas));
return 1;
}
int l_lovrCanvasGetDimensions(lua_State* L) {
Canvas* canvas = luax_checktype(L, 1, Canvas);
lua_pushinteger(L, lovrCanvasGetWidth(canvas));
lua_pushinteger(L, lovrCanvasGetHeight(canvas));
return 2;
}
int l_lovrCanvasGetDepthTexture(lua_State* L) {
Canvas* canvas = luax_checktype(L, 1, Canvas);
Texture* texture = lovrCanvasGetDepthTexture(canvas);
luax_pushobject(L, texture);
return 1; return 1;
} }
int l_lovrCanvasGetMSAA(lua_State* L) { int l_lovrCanvasGetMSAA(lua_State* L) {
Canvas* canvas = luax_checktype(L, 1, Canvas); Canvas* canvas = luax_checktype(L, 1, Canvas);
lua_pushinteger(L, lovrCanvasGetMSAA(canvas)); int msaa = lovrCanvasGetMSAA(canvas);
lua_pushinteger(L, msaa);
return 1;
}
int l_lovrCanvasIsStereo(lua_State* L) {
Canvas* canvas = luax_checktype(L, 1, Canvas);
bool stereo = lovrCanvasIsStereo(canvas);
lua_pushboolean(L, stereo);
return 1; return 1;
} }
const luaL_Reg lovrCanvas[] = { const luaL_Reg lovrCanvas[] = {
{ "newTextureData", l_lovrCanvasNewTextureData },
{ "renderTo", l_lovrCanvasRenderTo }, { "renderTo", l_lovrCanvasRenderTo },
{ "getFormat", l_lovrCanvasGetFormat }, { "getTexture", l_lovrCanvasGetTexture },
{ "setTexture", l_lovrCanvasSetTexture },
{ "getWidth", l_lovrCanvasGetWidth },
{ "getHeight", l_lovrCanvasGetHeight },
{ "getDimensions", l_lovrCanvasGetDimensions },
{ "getDepthTexture", l_lovrCanvasGetDepthTexture },
{ "getMSAA", l_lovrCanvasGetMSAA }, { "getMSAA", l_lovrCanvasGetMSAA },
{ "isStereo", l_lovrCanvasIsStereo },
{ NULL, NULL } { NULL, NULL }
}; };

83
src/api/types/channel.c Normal file
View File

@ -0,0 +1,83 @@
#include "api.h"
#include "api/event.h"
#include "thread/channel.h"
#include <math.h>
static void luax_checktimeout(lua_State* L, int index, double* timeout) {
switch (lua_type(L, index)) {
case LUA_TNONE:
case LUA_TNIL:
*timeout = NAN;
break;
case LUA_TBOOLEAN:
*timeout = lua_toboolean(L, index) ? INFINITY : NAN;
break;
default:
*timeout = luaL_checknumber(L, index);
break;
}
}
int l_lovrChannelPush(lua_State* L) {
Variant variant;
double timeout;
Channel* channel = luax_checktype(L, 1, Channel);
luax_checkvariant(L, 2, &variant);
luax_checktimeout(L, 3, &timeout);
uint64_t id;
bool read = lovrChannelPush(channel, variant, timeout, &id);
lua_pushnumber(L, id);
lua_pushboolean(L, read);
return 2;
}
int l_lovrChannelPop(lua_State* L) {
Variant variant;
double timeout;
Channel* channel = luax_checktype(L, 1, Channel);
luax_checktimeout(L, 2, &timeout);
if (lovrChannelPop(channel, &variant, timeout)) {
return luax_pushvariant(L, &variant);
}
lua_pushnil(L);
return 1;
}
int l_lovrChannelPeek(lua_State* L) {
Variant variant;
Channel* channel = luax_checktype(L, 1, Channel);
if (lovrChannelPeek(channel, &variant)) {
return luax_pushvariant(L, &variant);
}
lua_pushnil(L);
return 1;
}
int l_lovrChannelClear(lua_State* L) {
Channel* channel = luax_checktype(L, 1, Channel);
lovrChannelClear(channel);
return 0;
}
int l_lovrChannelGetCount(lua_State* L) {
Channel* channel = luax_checktype(L, 1, Channel);
lua_pushinteger(L, lovrChannelGetCount(channel));
return 1;
}
int l_lovrChannelHasRead(lua_State* L) {
Channel* channel = luax_checktype(L, 1, Channel);
uint64_t id = luaL_checkinteger(L, 2);
lua_pushboolean(L, lovrChannelHasRead(channel, id));
return 1;
}
const luaL_Reg lovrChannel[] = {
{ "push", l_lovrChannelPush },
{ "pop", l_lovrChannelPop },
{ "peek", l_lovrChannelPeek },
{ "clear", l_lovrChannelClear },
{ "getCount", l_lovrChannelGetCount },
{ "hasRead", l_lovrChannelHasRead },
{ NULL, NULL }
};

View File

@ -11,41 +11,41 @@ int l_lovrColliderDestroy(lua_State* L) {
int l_lovrColliderGetWorld(lua_State* L) { int l_lovrColliderGetWorld(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider); Collider* collider = luax_checktype(L, 1, Collider);
World* world = lovrColliderGetWorld(collider); World* world = lovrColliderGetWorld(collider);
luax_pushtype(L, World, world); luax_pushobject(L, world);
return 1; return 1;
} }
int l_lovrColliderAddShape(lua_State* L) { int l_lovrColliderAddShape(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider); Collider* collider = luax_checktype(L, 1, Collider);
Shape* shape = luax_checktypeof(L, 2, Shape); Shape* shape = luax_checktype(L, 2, Shape);
lovrColliderAddShape(collider, shape); lovrColliderAddShape(collider, shape);
return 0; return 0;
} }
int l_lovrColliderRemoveShape(lua_State* L) { int l_lovrColliderRemoveShape(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider); Collider* collider = luax_checktype(L, 1, Collider);
Shape* shape = luax_checktypeof(L, 2, Shape); Shape* shape = luax_checktype(L, 2, Shape);
lovrColliderRemoveShape(collider, shape); lovrColliderRemoveShape(collider, shape);
return 0; return 0;
} }
int l_lovrColliderGetShapeList(lua_State* L) { int l_lovrColliderGetShapes(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider); Collider* collider = luax_checktype(L, 1, Collider);
lua_newtable(L); lua_newtable(L);
vec_void_t* shapes = lovrColliderGetShapes(collider); vec_void_t* shapes = lovrColliderGetShapes(collider);
for (int i = 0; i < shapes->length; i++) { for (int i = 0; i < shapes->length; i++) {
luax_pushshape(L, shapes->data[i]); luax_pushobject(L, shapes->data[i]);
lua_rawseti(L, -2, i + 1); lua_rawseti(L, -2, i + 1);
} }
return 1; return 1;
} }
int l_lovrColliderGetJointList(lua_State* L) { int l_lovrColliderGetJoints(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider); Collider* collider = luax_checktype(L, 1, Collider);
lua_newtable(L); lua_newtable(L);
vec_void_t* joints = lovrColliderGetJoints(collider); vec_void_t* joints = lovrColliderGetJoints(collider);
for (int i = 0; i < joints->length; i++) { for (int i = 0; i < joints->length; i++) {
luax_pushshape(L, joints->data[i]); luax_pushobject(L, joints->data[i]);
lua_rawseti(L, -2, i + 1); lua_rawseti(L, -2, i + 1);
} }
return 1; return 1;
@ -476,7 +476,8 @@ const luaL_Reg lovrCollider[] = {
{ "getWorld", l_lovrColliderGetWorld }, { "getWorld", l_lovrColliderGetWorld },
{ "addShape", l_lovrColliderAddShape }, { "addShape", l_lovrColliderAddShape },
{ "removeShape", l_lovrColliderRemoveShape }, { "removeShape", l_lovrColliderRemoveShape },
{ "getShapeList", l_lovrColliderGetShapeList }, { "getShapes", l_lovrColliderGetShapes },
{ "getJoints", l_lovrColliderGetJoints },
{ "getUserData", l_lovrColliderGetUserData }, { "getUserData", l_lovrColliderGetUserData },
{ "setUserData", l_lovrColliderSetUserData }, { "setUserData", l_lovrColliderSetUserData },
{ "isKinematic", l_lovrColliderIsKinematic }, { "isKinematic", l_lovrColliderIsKinematic },

View File

@ -3,23 +3,23 @@
#include "data/modelData.h" #include "data/modelData.h"
#include "graphics/model.h" #include "graphics/model.h"
int l_lovrControllerIsPresent(lua_State* L) { int l_lovrControllerIsConnected(lua_State* L) {
Controller* controller = luax_checktype(L, 1, Controller); Controller* controller = luax_checktype(L, 1, Controller);
lua_pushboolean(L, lovrHeadsetControllerIsPresent(controller)); lua_pushboolean(L, lovrHeadsetDriver->controllerIsConnected(controller));
return 1; return 1;
} }
int l_lovrControllerGetHand(lua_State* L) { int l_lovrControllerGetHand(lua_State* L) {
Controller* controller = luax_checktype(L, 1, Controller); Controller* controller = luax_checktype(L, 1, Controller);
ControllerHand hand = lovrHeadsetControllerGetHand(controller); ControllerHand hand = lovrHeadsetDriver->controllerGetHand(controller);
luax_pushenum(L, &ControllerHands, hand); lua_pushstring(L, ControllerHands[hand]);
return 1; return 1;
} }
int l_lovrControllerGetPose(lua_State* L) { int l_lovrControllerGetPose(lua_State* L) {
Controller* controller = luax_checktype(L, 1, Controller); Controller* controller = luax_checktype(L, 1, Controller);
float x, y, z, angle, ax, ay, az; float x, y, z, angle, ax, ay, az;
lovrHeadsetControllerGetPose(controller, &x, &y, &z, &angle, &ax, &ay, &az); lovrHeadsetDriver->controllerGetPose(controller, &x, &y, &z, &angle, &ax, &ay, &az);
lua_pushnumber(L, x); lua_pushnumber(L, x);
lua_pushnumber(L, y); lua_pushnumber(L, y);
lua_pushnumber(L, z); lua_pushnumber(L, z);
@ -33,7 +33,7 @@ int l_lovrControllerGetPose(lua_State* L) {
int l_lovrControllerGetPosition(lua_State* L) { int l_lovrControllerGetPosition(lua_State* L) {
Controller* controller = luax_checktype(L, 1, Controller); Controller* controller = luax_checktype(L, 1, Controller);
float x, y, z, angle, ax, ay, az; float x, y, z, angle, ax, ay, az;
lovrHeadsetControllerGetPose(controller, &x, &y, &z, &angle, &ax, &ay, &az); lovrHeadsetDriver->controllerGetPose(controller, &x, &y, &z, &angle, &ax, &ay, &az);
lua_pushnumber(L, x); lua_pushnumber(L, x);
lua_pushnumber(L, y); lua_pushnumber(L, y);
lua_pushnumber(L, z); lua_pushnumber(L, z);
@ -43,7 +43,7 @@ int l_lovrControllerGetPosition(lua_State* L) {
int l_lovrControllerGetOrientation(lua_State* L) { int l_lovrControllerGetOrientation(lua_State* L) {
Controller* controller = luax_checktype(L, 1, Controller); Controller* controller = luax_checktype(L, 1, Controller);
float x, y, z, angle, ax, ay, az; float x, y, z, angle, ax, ay, az;
lovrHeadsetControllerGetPose(controller, &x, &y, &z, &angle, &ax, &ay, &az); lovrHeadsetDriver->controllerGetPose(controller, &x, &y, &z, &angle, &ax, &ay, &az);
lua_pushnumber(L, angle); lua_pushnumber(L, angle);
lua_pushnumber(L, ax); lua_pushnumber(L, ax);
lua_pushnumber(L, ay); lua_pushnumber(L, ay);
@ -53,22 +53,22 @@ int l_lovrControllerGetOrientation(lua_State* L) {
int l_lovrControllerGetAxis(lua_State* L) { int l_lovrControllerGetAxis(lua_State* L) {
Controller* controller = luax_checktype(L, 1, Controller); Controller* controller = luax_checktype(L, 1, Controller);
ControllerAxis* axis = (ControllerAxis*) luax_checkenum(L, 2, &ControllerAxes, "controller axis"); ControllerAxis axis = luaL_checkoption(L, 2, NULL, ControllerAxes);
lua_pushnumber(L, lovrHeadsetControllerGetAxis(controller, *axis)); lua_pushnumber(L, lovrHeadsetDriver->controllerGetAxis(controller, axis));
return 1; return 1;
} }
int l_lovrControllerIsDown(lua_State* L) { int l_lovrControllerIsDown(lua_State* L) {
Controller* controller = luax_checktype(L, 1, Controller); Controller* controller = luax_checktype(L, 1, Controller);
ControllerButton* button = (ControllerButton*) luax_checkenum(L, 2, &ControllerButtons, "controller button"); ControllerButton button = luaL_checkoption(L, 2, NULL, ControllerButtons);
lua_pushboolean(L, lovrHeadsetControllerIsDown(controller, *button)); lua_pushboolean(L, lovrHeadsetDriver->controllerIsDown(controller, button));
return 1; return 1;
} }
int l_lovrControllerIsTouched(lua_State* L) { int l_lovrControllerIsTouched(lua_State* L) {
Controller* controller = luax_checktype(L, 1, Controller); Controller* controller = luax_checktype(L, 1, Controller);
ControllerButton* button = (ControllerButton*) luax_checkenum(L, 2, &ControllerButtons, "controller button"); ControllerButton button = luaL_checkoption(L, 2, NULL, ControllerButtons);
lua_pushboolean(L, lovrHeadsetControllerIsTouched(controller, *button)); lua_pushboolean(L, lovrHeadsetDriver->controllerIsTouched(controller, button));
return 1; return 1;
} }
@ -76,17 +76,18 @@ int l_lovrControllerVibrate(lua_State* L) {
Controller* controller = luax_checktype(L, 1, Controller); Controller* controller = luax_checktype(L, 1, Controller);
float duration = luaL_optnumber(L, 2, .5); float duration = luaL_optnumber(L, 2, .5);
float power = luaL_optnumber(L, 3, 1); float power = luaL_optnumber(L, 3, 1);
lovrHeadsetControllerVibrate(controller, duration, power); lovrHeadsetDriver->controllerVibrate(controller, duration, power);
return 0; return 0;
} }
int l_lovrControllerNewModel(lua_State* L) { int l_lovrControllerNewModel(lua_State* L) {
Controller* controller = luax_checktype(L, 1, Controller); Controller* controller = luax_checktype(L, 1, Controller);
ModelData* modelData = lovrHeadsetControllerNewModelData(controller); ModelData* modelData = lovrHeadsetDriver->controllerNewModelData(controller);
if (modelData) { if (modelData) {
Model* model = lovrModelCreate(modelData); Model* model = lovrModelCreate(modelData);
luax_pushtype(L, Model, model); luax_pushobject(L, model);
lovrRelease(&model->ref); lovrRelease(modelData);
lovrRelease(model);
} else { } else {
lua_pushnil(L); lua_pushnil(L);
} }
@ -94,7 +95,7 @@ int l_lovrControllerNewModel(lua_State* L) {
} }
const luaL_Reg lovrController[] = { const luaL_Reg lovrController[] = {
{ "isPresent", l_lovrControllerIsPresent }, { "isConnected", l_lovrControllerIsConnected },
{ "getHand", l_lovrControllerGetHand }, { "getHand", l_lovrControllerGetHand },
{ "getPose", l_lovrControllerGetPose }, { "getPose", l_lovrControllerGetPose },
{ "getPosition", l_lovrControllerGetPosition }, { "getPosition", l_lovrControllerGetPosition },

View File

@ -1,25 +1,15 @@
#include "api.h" #include "api.h"
#include "physics/physics.h" #include "physics/physics.h"
int luax_pushjoint(lua_State* L, Joint* joint) {
switch (lovrJointGetType(joint)) {
case JOINT_BALL: luax_pushtype(L, BallJoint, joint); return 1;
case JOINT_DISTANCE: luax_pushtype(L, DistanceJoint, joint); return 1;
case JOINT_HINGE: luax_pushtype(L, HingeJoint, joint); return 1;
case JOINT_SLIDER: luax_pushtype(L, SliderJoint, joint); return 1;
default: return 0;
}
}
int l_lovrJointDestroy(lua_State* L) { int l_lovrJointDestroy(lua_State* L) {
Joint* joint = luax_checktypeof(L, 1, Joint); Joint* joint = luax_checktype(L, 1, Joint);
lovrJointDestroyData(joint); lovrJointDestroyData(joint);
return 0; return 0;
} }
int l_lovrJointGetType(lua_State* L) { int l_lovrJointGetType(lua_State* L) {
Joint* joint = luax_checktypeof(L, 1, Joint); Joint* joint = luax_checktype(L, 1, Joint);
luax_pushenum(L, &JointTypes, lovrJointGetType(joint)); lua_pushstring(L, JointTypes[lovrJointGetType(joint)]);
return 1; return 1;
} }
@ -28,20 +18,20 @@ int l_lovrJointGetColliders(lua_State* L) {
Collider* a; Collider* a;
Collider* b; Collider* b;
lovrJointGetColliders(joint, &a, &b); lovrJointGetColliders(joint, &a, &b);
luax_pushtype(L, Collider, a); luax_pushobject(L, a);
luax_pushtype(L, Collider, b); luax_pushobject(L, b);
return 2; return 2;
} }
int l_lovrJointGetUserData(lua_State* L) { int l_lovrJointGetUserData(lua_State* L) {
Joint* joint = luax_checktypeof(L, 1, Joint); Joint* joint = luax_checktype(L, 1, Joint);
int ref = (int) lovrJointGetUserData(joint); int ref = (int) lovrJointGetUserData(joint);
lua_rawgeti(L, LUA_REGISTRYINDEX, ref); lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
return 1; return 1;
} }
int l_lovrJointSetUserData(lua_State* L) { int l_lovrJointSetUserData(lua_State* L) {
Joint* joint = luax_checktypeof(L, 1, Joint); Joint* joint = luax_checktype(L, 1, Joint);
uint64_t ref = (int) lovrJointGetUserData(joint); uint64_t ref = (int) lovrJointGetUserData(joint);
if (ref) { if (ref) {
luaL_unref(L, LUA_REGISTRYINDEX, ref); luaL_unref(L, LUA_REGISTRYINDEX, ref);

View File

@ -1,9 +1,10 @@
#include "api.h" #include "api.h"
#include "api/graphics.h"
#include "graphics/material.h" #include "graphics/material.h"
int l_lovrMaterialGetColor(lua_State* L) { int l_lovrMaterialGetColor(lua_State* L) {
Material* material = luax_checktype(L, 1, Material); Material* material = luax_checktype(L, 1, Material);
MaterialColor colorType = *(MaterialColor*) luax_optenum(L, 2, "diffuse", &MaterialColors, "color"); MaterialColor colorType = luaL_checkoption(L, 2, "diffuse", MaterialColors);
Color color = lovrMaterialGetColor(material, colorType); Color color = lovrMaterialGetColor(material, colorType);
lua_pushnumber(L, color.r); lua_pushnumber(L, color.r);
lua_pushnumber(L, color.g); lua_pushnumber(L, color.g);
@ -17,7 +18,7 @@ int l_lovrMaterialSetColor(lua_State* L) {
MaterialColor colorType = COLOR_DIFFUSE; MaterialColor colorType = COLOR_DIFFUSE;
int index = 2; int index = 2;
if (lua_type(L, index) == LUA_TSTRING) { if (lua_type(L, index) == LUA_TSTRING) {
colorType = *(MaterialColor*) luax_checkenum(L, index, &MaterialColors, "color"); colorType = luaL_checkoption(L, index, NULL, MaterialColors);
index++; index++;
} }
Color color = luax_checkcolor(L, index); Color color = luax_checkcolor(L, index);
@ -27,7 +28,7 @@ int l_lovrMaterialSetColor(lua_State* L) {
int l_lovrMaterialGetScalar(lua_State* L) { int l_lovrMaterialGetScalar(lua_State* L) {
Material* material = luax_checktype(L, 1, Material); Material* material = luax_checktype(L, 1, Material);
MaterialScalar scalarType = *(MaterialScalar*) luax_checkenum(L, 2, &MaterialScalars, "scalar"); MaterialScalar scalarType = luaL_checkoption(L, 2, NULL, MaterialScalars);
float value = lovrMaterialGetScalar(material, scalarType); float value = lovrMaterialGetScalar(material, scalarType);
lua_pushnumber(L, value); lua_pushnumber(L, value);
return 1; return 1;
@ -35,7 +36,7 @@ int l_lovrMaterialGetScalar(lua_State* L) {
int l_lovrMaterialSetScalar(lua_State* L) { int l_lovrMaterialSetScalar(lua_State* L) {
Material* material = luax_checktype(L, 1, Material); Material* material = luax_checktype(L, 1, Material);
MaterialScalar scalarType = *(MaterialScalar*) luax_checkenum(L, 2, &MaterialScalars, "scalar"); MaterialScalar scalarType = luaL_checkoption(L, 2, NULL, MaterialScalars);
float value = luaL_checknumber(L, 3); float value = luaL_checknumber(L, 3);
lovrMaterialSetScalar(material, scalarType, value); lovrMaterialSetScalar(material, scalarType, value);
return 0; return 0;
@ -43,9 +44,9 @@ int l_lovrMaterialSetScalar(lua_State* L) {
int l_lovrMaterialGetTexture(lua_State* L) { int l_lovrMaterialGetTexture(lua_State* L) {
Material* material = luax_checktype(L, 1, Material); Material* material = luax_checktype(L, 1, Material);
MaterialTexture textureType = *(MaterialTexture*) luax_optenum(L, 2, "diffuse", &MaterialTextures, "texture"); MaterialTexture textureType = luaL_checkoption(L, 2, "diffuse", MaterialTextures);
Texture* texture = lovrMaterialGetTexture(material, textureType); Texture* texture = lovrMaterialGetTexture(material, textureType);
luax_pushtype(L, Texture, texture); luax_pushobject(L, texture);
return 1; return 1;
} }
@ -54,14 +55,37 @@ int l_lovrMaterialSetTexture(lua_State* L) {
MaterialTexture textureType = TEXTURE_DIFFUSE; MaterialTexture textureType = TEXTURE_DIFFUSE;
int index = 2; int index = 2;
if (lua_type(L, index) == LUA_TSTRING) { if (lua_type(L, index) == LUA_TSTRING) {
textureType = *(MaterialTexture*) luax_checkenum(L, index, &MaterialTextures, "texture"); textureType = luaL_checkoption(L, index, NULL, MaterialTextures);
index++; index++;
} }
Texture* texture = lua_isnoneornil(L, index) ? NULL : luax_checktypeof(L, index, Texture); Texture* texture = lua_isnoneornil(L, index) ? NULL : luax_checktexture(L, index);
lovrMaterialSetTexture(material, textureType, texture); lovrMaterialSetTexture(material, textureType, texture);
return 0; return 0;
} }
int l_lovrMaterialGetTransform(lua_State* L) {
Material* material = luax_checktype(L, 1, Material);
float ox, oy, sx, sy, angle;
lovrMaterialGetTransform(material, &ox, &oy, &sx, &sy, &angle);
lua_pushnumber(L, ox);
lua_pushnumber(L, oy);
lua_pushnumber(L, sx);
lua_pushnumber(L, sy);
lua_pushnumber(L, angle);
return 5;
}
int l_lovrMaterialSetTransform(lua_State* L) {
Material* material = luax_checktype(L, 1, Material);
float ox = luaL_optnumber(L, 2, 0.f);
float oy = luaL_optnumber(L, 3, 0.f);
float sx = luaL_optnumber(L, 4, 1.f);
float sy = luaL_optnumber(L, 5, sx);
float angle = luaL_optnumber(L, 6, 0.f);
lovrMaterialSetTransform(material, ox, oy, sx, sy, angle);
return 0;
}
const luaL_Reg lovrMaterial[] = { const luaL_Reg lovrMaterial[] = {
{ "getColor", l_lovrMaterialGetColor }, { "getColor", l_lovrMaterialGetColor },
{ "setColor", l_lovrMaterialSetColor }, { "setColor", l_lovrMaterialSetColor },
@ -69,5 +93,7 @@ const luaL_Reg lovrMaterial[] = {
{ "setScalar", l_lovrMaterialSetScalar }, { "setScalar", l_lovrMaterialSetScalar },
{ "getTexture", l_lovrMaterialGetTexture }, { "getTexture", l_lovrMaterialGetTexture },
{ "setTexture", l_lovrMaterialSetTexture }, { "setTexture", l_lovrMaterialSetTexture },
{ "getTransform", l_lovrMaterialGetTransform },
{ "setTransform", l_lovrMaterialSetTransform },
{ NULL, NULL } { NULL, NULL }
}; };

View File

@ -1,11 +1,70 @@
#include "api.h" #include "api.h"
#include "api/data.h"
#include "api/math.h"
#include "graphics/graphics.h"
#include <limits.h>
int l_lovrMeshAttachAttributes(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
Mesh* other = luax_checktype(L, 2, Mesh);
int instanceDivisor = luaL_optinteger(L, 3, 0);
if (lua_isnoneornil(L, 4)) {
VertexFormat* format = lovrMeshGetVertexFormat(other);
for (int i = 0; i < format->count; i++) {
lovrMeshAttachAttribute(mesh, other, format->attributes[i].name, instanceDivisor);
}
} else if (lua_istable(L, 4)) {
int length = lua_objlen(L, 4);
for (int i = 0; i < length; i++) {
lua_rawgeti(L, 4, i + 1);
lovrMeshAttachAttribute(mesh, other, lua_tostring(L, -1), instanceDivisor);
lua_pop(L, 1);
}
} else {
int top = lua_gettop(L);
for (int i = 4; i <= top; i++) {
lovrMeshAttachAttribute(mesh, other, lua_tostring(L, i), instanceDivisor);
}
}
return 0;
}
int l_lovrMeshDetachAttributes(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
if (lua_isuserdata(L, 2)) {
Mesh* other = luax_checktype(L, 2, Mesh);
VertexFormat* format = lovrMeshGetVertexFormat(other);
for (int i = 0; i < format->count; i++) {
lovrMeshDetachAttribute(mesh, format->attributes[i].name);
}
} else if (lua_istable(L, 2)) {
int length = lua_objlen(L, 2);
for (int i = 0; i < length; i++) {
lua_rawgeti(L, 2, i + 1);
lovrMeshDetachAttribute(mesh, lua_tostring(L, -1));
lua_pop(L, 1);
}
} else {
int top = lua_gettop(L);
for (int i = 2; i <= top; i++) {
lovrMeshDetachAttribute(mesh, lua_tostring(L, i));
}
}
return 0;
}
int l_lovrMeshDrawInstanced(lua_State* L) { int l_lovrMeshDrawInstanced(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh); Mesh* mesh = luax_checktype(L, 1, Mesh);
int instances = luaL_checkinteger(L, 2); int instances = luaL_checkinteger(L, 2);
float transform[16]; float transform[16];
luax_readtransform(L, 3, transform, 1); luax_readtransform(L, 3, transform, 1);
lovrMeshDraw(mesh, transform, NULL, instances); lovrGraphicsDraw(&(DrawCommand) {
.transform = transform,
.mesh = mesh,
.material = lovrMeshGetMaterial(mesh),
.instances = instances
});
return 0; return 0;
} }
@ -17,14 +76,14 @@ int l_lovrMeshDraw(lua_State* L) {
int l_lovrMeshGetDrawMode(lua_State* L) { int l_lovrMeshGetDrawMode(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh); Mesh* mesh = luax_checktype(L, 1, Mesh);
luax_pushenum(L, &MeshDrawModes, lovrMeshGetDrawMode(mesh)); lua_pushstring(L, MeshDrawModes[lovrMeshGetDrawMode(mesh)]);
return 1; return 1;
} }
int l_lovrMeshSetDrawMode(lua_State* L) { int l_lovrMeshSetDrawMode(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh); Mesh* mesh = luax_checktype(L, 1, Mesh);
MeshDrawMode* drawMode = (MeshDrawMode*) luax_checkenum(L, 2, &MeshDrawModes, "mesh draw mode"); MeshDrawMode drawMode = luaL_checkoption(L, 2, NULL, MeshDrawModes);
lovrMeshSetDrawMode(mesh, *drawMode); lovrMeshSetDrawMode(mesh, drawMode);
return 0; return 0;
} }
@ -43,7 +102,7 @@ int l_lovrMeshGetVertexCount(lua_State* L) {
int l_lovrMeshGetVertex(lua_State* L) { int l_lovrMeshGetVertex(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh); Mesh* mesh = luax_checktype(L, 1, Mesh);
int index = luaL_checkint(L, 2) - 1; int index = luaL_checkint(L, 2) - 1;
VertexPointer vertex = lovrMeshMap(mesh, index, 1, true, false); VertexPointer vertex = lovrMeshMapVertices(mesh, index, 1, true, false);
VertexFormat* format = lovrMeshGetVertexFormat(mesh); VertexFormat* format = lovrMeshGetVertexFormat(mesh);
return luax_pushvertex(L, &vertex, format); return luax_pushvertex(L, &vertex, format);
} }
@ -53,7 +112,7 @@ int l_lovrMeshSetVertex(lua_State* L) {
int index = luaL_checkint(L, 2) - 1; int index = luaL_checkint(L, 2) - 1;
lovrAssert(index >= 0 && index < lovrMeshGetVertexCount(mesh), "Invalid mesh vertex index: %d", index + 1); lovrAssert(index >= 0 && index < lovrMeshGetVertexCount(mesh), "Invalid mesh vertex index: %d", index + 1);
VertexFormat* format = lovrMeshGetVertexFormat(mesh); VertexFormat* format = lovrMeshGetVertexFormat(mesh);
VertexPointer vertex = lovrMeshMap(mesh, index, 1, false, true); VertexPointer vertex = lovrMeshMapVertices(mesh, index, 1, false, true);
luax_setvertex(L, 3, &vertex, format); luax_setvertex(L, 3, &vertex, format);
return 0; return 0;
} }
@ -66,7 +125,7 @@ int l_lovrMeshGetVertexAttribute(lua_State* L) {
lovrAssert(vertexIndex >= 0 && vertexIndex < lovrMeshGetVertexCount(mesh), "Invalid mesh vertex: %d", vertexIndex + 1); lovrAssert(vertexIndex >= 0 && vertexIndex < lovrMeshGetVertexCount(mesh), "Invalid mesh vertex: %d", vertexIndex + 1);
lovrAssert(attributeIndex >= 0 && attributeIndex < format->count, "Invalid mesh attribute: %d", attributeIndex + 1); lovrAssert(attributeIndex >= 0 && attributeIndex < format->count, "Invalid mesh attribute: %d", attributeIndex + 1);
Attribute attribute = format->attributes[attributeIndex]; Attribute attribute = format->attributes[attributeIndex];
VertexPointer vertex = lovrMeshMap(mesh, vertexIndex, 1, true, false); VertexPointer vertex = lovrMeshMapVertices(mesh, vertexIndex, 1, true, false);
vertex.bytes += attribute.offset; vertex.bytes += attribute.offset;
return luax_pushvertexattribute(L, &vertex, attribute); return luax_pushvertexattribute(L, &vertex, attribute);
} }
@ -79,7 +138,7 @@ int l_lovrMeshSetVertexAttribute(lua_State* L) {
lovrAssert(vertexIndex >= 0 && vertexIndex < lovrMeshGetVertexCount(mesh), "Invalid mesh vertex: %d", vertexIndex + 1); lovrAssert(vertexIndex >= 0 && vertexIndex < lovrMeshGetVertexCount(mesh), "Invalid mesh vertex: %d", vertexIndex + 1);
lovrAssert(attributeIndex >= 0 && attributeIndex < format->count, "Invalid mesh attribute: %d", attributeIndex + 1); lovrAssert(attributeIndex >= 0 && attributeIndex < format->count, "Invalid mesh attribute: %d", attributeIndex + 1);
Attribute attribute = format->attributes[attributeIndex]; Attribute attribute = format->attributes[attributeIndex];
VertexPointer vertex = lovrMeshMap(mesh, vertexIndex, 1, false, true); VertexPointer vertex = lovrMeshMapVertices(mesh, vertexIndex, 1, false, true);
vertex.bytes += attribute.offset; vertex.bytes += attribute.offset;
luax_setvertexattribute(L, 4, &vertex, attribute); luax_setvertexattribute(L, 4, &vertex, attribute);
return 0; return 0;
@ -88,18 +147,35 @@ int l_lovrMeshSetVertexAttribute(lua_State* L) {
int l_lovrMeshSetVertices(lua_State* L) { int l_lovrMeshSetVertices(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh); Mesh* mesh = luax_checktype(L, 1, Mesh);
VertexFormat* format = lovrMeshGetVertexFormat(mesh); VertexFormat* format = lovrMeshGetVertexFormat(mesh);
luaL_checktype(L, 2, LUA_TTABLE); uint32_t capacity = lovrMeshGetVertexCount(mesh);
int vertexCount = lua_objlen(L, 2);
int start = luaL_optnumber(L, 3, 1) - 1;
int maxVertices = lovrMeshGetVertexCount(mesh);
lovrAssert(start + vertexCount <= maxVertices, "Overflow in Mesh:setVertices: Mesh can only hold %d vertices", maxVertices);
VertexPointer vertices = lovrMeshMap(mesh, start, vertexCount, false, true);
for (int i = 0; i < vertexCount; i++) { VertexData* vertexData = NULL;
lua_rawgeti(L, 2, i + 1); uint32_t sourceSize;
luaL_checktype(L, -1, LUA_TTABLE); if (lua_istable(L, 2)) {
luax_setvertex(L, -1, &vertices, format); sourceSize = lua_objlen(L, 2);
lua_pop(L, 1); } else {
vertexData = luax_checktype(L, 2, VertexData);
sourceSize = vertexData->count;
bool sameFormat = !memcmp(&vertexData->format, format, sizeof(VertexFormat));
lovrAssert(sameFormat, "Mesh and VertexData must have the same format to copy vertices");
}
uint32_t start = luaL_optnumber(L, 3, 1) - 1;
uint32_t count = luaL_optinteger(L, 4, sourceSize);
lovrAssert(start + count <= capacity, "Overflow in Mesh:setVertices: Mesh can only hold %d vertices", capacity);
lovrAssert(count <= sourceSize, "Cannot set %d vertices on Mesh: source only has %d vertices", count, sourceSize);
VertexPointer vertices = lovrMeshMapVertices(mesh, start, count, false, true);
if (vertexData) {
memcpy(vertices.raw, vertexData->blob.data, count * format->stride);
} else {
for (uint32_t i = 0; i < count; i++) {
lua_rawgeti(L, 2, i + 1);
luaL_checktype(L, -1, LUA_TTABLE);
luax_setvertex(L, -1, &vertices, format);
lua_pop(L, 1);
}
} }
return 0; return 0;
@ -107,19 +183,31 @@ int l_lovrMeshSetVertices(lua_State* L) {
int l_lovrMeshGetVertexMap(lua_State* L) { int l_lovrMeshGetVertexMap(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh); Mesh* mesh = luax_checktype(L, 1, Mesh);
size_t count; uint32_t count;
IndexPointer indices = lovrMeshGetVertexMap(mesh, &count); size_t size;
IndexPointer indices = lovrMeshReadIndices(mesh, &count, &size);
if (count == 0) { if (count == 0 || !indices.raw) {
lua_pushnil(L); lua_pushnil(L);
return 1; return 1;
} }
lua_newtable(L); if (lua_istable(L, 2)) {
lua_settop(L, 2);
} else if (lua_isuserdata(L, 2)) {
Blob* blob = luax_checktype(L, 2, Blob);
lovrAssert(size * count <= blob->size, "Mesh vertex map is %zu bytes, but Blob can only hold %zu", size * count, blob->size);
memcpy(blob->data, indices.raw, size * count);
return 0;
} else {
lua_settop(L, 1);
lua_createtable(L, count, 0);
}
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
uint32_t index = mesh->indexSize == sizeof(uint32_t) ? indices.ints[i] : indices.shorts[i]; uint32_t index = size == sizeof(uint32_t) ? indices.ints[i] : indices.shorts[i];
lua_pushinteger(L, index + 1); lua_pushinteger(L, index + 1);
lua_rawseti(L, -2, i + 1); lua_rawseti(L, 2, i + 1);
} }
return 1; return 1;
@ -129,38 +217,45 @@ int l_lovrMeshSetVertexMap(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh); Mesh* mesh = luax_checktype(L, 1, Mesh);
if (lua_isnoneornil(L, 2)) { if (lua_isnoneornil(L, 2)) {
lovrMeshSetVertexMap(mesh, NULL, 0); lovrMeshWriteIndices(mesh, 0, 0);
return 0; return 0;
} }
luaL_checktype(L, 2, LUA_TTABLE); if (lua_type(L, 2) == LUA_TUSERDATA) {
int count = lua_objlen(L, 2); Blob* blob = luax_checktype(L, 2, Blob);
int vertexCount = lovrMeshGetVertexCount(mesh); size_t size = luaL_optinteger(L, 3, 4);
int indexSize = mesh->indexSize; lovrAssert(size == 2 || size == 4, "Size of Mesh indices should be 2 bytes or 4 bytes");
IndexPointer indices = lovrMeshGetVertexMap(mesh, NULL); uint32_t count = blob->size / size;
indices.raw = realloc(indices.raw, indexSize * count); IndexPointer indices = lovrMeshWriteIndices(mesh, count, size);
memcpy(indices.raw, blob->data, blob->size);
} else {
luaL_checktype(L, 2, LUA_TTABLE);
uint32_t count = lua_objlen(L, 2);
uint32_t vertexCount = lovrMeshGetVertexCount(mesh);
size_t size = vertexCount > USHRT_MAX ? sizeof(uint32_t) : sizeof(uint16_t);
IndexPointer indices = lovrMeshWriteIndices(mesh, count, size);
for (int i = 0; i < count; i++) { for (uint32_t i = 0; i < count; i++) {
lua_rawgeti(L, 2, i + 1); lua_rawgeti(L, 2, i + 1);
if (!lua_isnumber(L, -1)) { if (!lua_isnumber(L, -1)) {
return luaL_error(L, "Mesh vertex map index #%d must be numeric", i); return luaL_error(L, "Mesh vertex map index #%d must be numeric", i);
}
uint32_t index = lua_tointeger(L, -1);
if (index > vertexCount || index < 1) {
return luaL_error(L, "Invalid vertex map value: %d", index);
}
if (size == sizeof(uint16_t)) {
indices.shorts[i] = index - 1;
} else {
indices.ints[i] = index - 1;
}
lua_pop(L, 1);
} }
int index = lua_tointeger(L, -1);
if (index > vertexCount || index < 1) {
return luaL_error(L, "Invalid vertex map value: %d", index);
}
if (indexSize == sizeof(uint16_t)) {
indices.shorts[i] = index - 1;
} else if (indexSize == sizeof(uint32_t)) {
indices.ints[i] = index - 1;
}
lua_pop(L, 1);
} }
lovrMeshSetVertexMap(mesh, indices.raw, count);
return 0; return 0;
} }
@ -181,13 +276,14 @@ int l_lovrMeshSetAttributeEnabled(lua_State* L) {
int l_lovrMeshGetDrawRange(lua_State* L) { int l_lovrMeshGetDrawRange(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh); Mesh* mesh = luax_checktype(L, 1, Mesh);
if (!lovrMeshIsRangeEnabled(mesh)) { uint32_t start, count;
lovrMeshGetDrawRange(mesh, &start, &count);
if (count == 0) {
lua_pushnil(L); lua_pushnil(L);
return 1; return 1;
} }
int start, count;
lovrMeshGetDrawRange(mesh, &start, &count);
lua_pushinteger(L, start + 1); lua_pushinteger(L, start + 1);
lua_pushinteger(L, count); lua_pushinteger(L, count);
return 2; return 2;
@ -196,11 +292,10 @@ int l_lovrMeshGetDrawRange(lua_State* L) {
int l_lovrMeshSetDrawRange(lua_State* L) { int l_lovrMeshSetDrawRange(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh); Mesh* mesh = luax_checktype(L, 1, Mesh);
if (lua_isnoneornil(L, 2)) { if (lua_isnoneornil(L, 2)) {
lovrMeshSetRangeEnabled(mesh, 0); lovrMeshSetDrawRange(mesh, 0, 0);
return 0; return 0;
} }
lovrMeshSetRangeEnabled(mesh, 1);
int rangeStart = luaL_checkinteger(L, 2) - 1; int rangeStart = luaL_checkinteger(L, 2) - 1;
int rangeCount = luaL_checkinteger(L, 3); int rangeCount = luaL_checkinteger(L, 3);
lovrMeshSetDrawRange(mesh, rangeStart, rangeCount); lovrMeshSetDrawRange(mesh, rangeStart, rangeCount);
@ -210,11 +305,7 @@ int l_lovrMeshSetDrawRange(lua_State* L) {
int l_lovrMeshGetMaterial(lua_State* L) { int l_lovrMeshGetMaterial(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh); Mesh* mesh = luax_checktype(L, 1, Mesh);
Material* material = lovrMeshGetMaterial(mesh); Material* material = lovrMeshGetMaterial(mesh);
if (material) { luax_pushobject(L, material);
luax_pushtype(L, Material, material);
} else {
lua_pushnil(L);
}
return 1; return 1;
} }
@ -230,6 +321,8 @@ int l_lovrMeshSetMaterial(lua_State* L) {
} }
const luaL_Reg lovrMesh[] = { const luaL_Reg lovrMesh[] = {
{ "attachAttributes", l_lovrMeshAttachAttributes },
{ "detachAttributes", l_lovrMeshDetachAttributes },
{ "drawInstanced", l_lovrMeshDrawInstanced }, { "drawInstanced", l_lovrMeshDrawInstanced },
{ "draw", l_lovrMeshDraw }, { "draw", l_lovrMeshDraw },
{ "getVertexFormat", l_lovrMeshGetVertexFormat }, { "getVertexFormat", l_lovrMeshGetVertexFormat },

View File

@ -0,0 +1,71 @@
#include "api.h"
#include "audio/microphone.h"
int l_lovrMicrophoneGetBitDepth(lua_State* L) {
Microphone* microphone = luax_checktype(L, 1, Microphone);
lua_pushinteger(L, lovrMicrophoneGetBitDepth(microphone));
return 1;
}
int l_lovrMicrophoneGetChannelCount(lua_State* L) {
Microphone* microphone = luax_checktype(L, 1, Microphone);
lua_pushinteger(L, lovrMicrophoneGetChannelCount(microphone));
return 1;
}
int l_lovrMicrophoneGetData(lua_State* L) {
Microphone* microphone = luax_checktype(L, 1, Microphone);
SoundData* soundData = lovrMicrophoneGetData(microphone);
luax_pushobject(L, soundData);
lovrRelease(soundData);
return 1;
}
int l_lovrMicrophoneGetName(lua_State* L) {
Microphone* microphone = luax_checktype(L, 1, Microphone);
lua_pushstring(L, lovrMicrophoneGetName(microphone));
return 1;
}
int l_lovrMicrophoneGetSampleCount(lua_State* L) {
Microphone* microphone = luax_checktype(L, 1, Microphone);
lua_pushinteger(L, lovrMicrophoneGetSampleCount(microphone));
return 1;
}
int l_lovrMicrophoneGetSampleRate(lua_State* L) {
Microphone* microphone = luax_checktype(L, 1, Microphone);
lua_pushinteger(L, lovrMicrophoneGetSampleRate(microphone));
return 1;
}
int l_lovrMicrophoneIsRecording(lua_State* L) {
Microphone* microphone = luax_checktype(L, 1, Microphone);
lua_pushboolean(L, lovrMicrophoneIsRecording(microphone));
return 1;
}
int l_lovrMicrophoneStartRecording(lua_State* L) {
Microphone* microphone = luax_checktype(L, 1, Microphone);
lovrMicrophoneStartRecording(microphone);
return 0;
}
int l_lovrMicrophoneStopRecording(lua_State* L) {
Microphone* microphone = luax_checktype(L, 1, Microphone);
lovrMicrophoneStopRecording(microphone);
return 0;
}
const luaL_Reg lovrMicrophone[] = {
{ "getBitDepth", l_lovrMicrophoneGetBitDepth },
{ "getChannelCount", l_lovrMicrophoneGetChannelCount },
{ "getData", l_lovrMicrophoneGetData },
{ "getName", l_lovrMicrophoneGetName },
{ "getSampleCount", l_lovrMicrophoneGetSampleCount },
{ "getSampleRate", l_lovrMicrophoneGetSampleRate },
{ "isRecording", l_lovrMicrophoneIsRecording },
{ "startRecording", l_lovrMicrophoneStartRecording },
{ "stopRecording", l_lovrMicrophoneStopRecording },
{ NULL, NULL }
};

View File

@ -1,4 +1,5 @@
#include "api.h" #include "api.h"
#include "api/math.h"
#include "graphics/model.h" #include "graphics/model.h"
int l_lovrModelDrawInstanced(lua_State* L) { int l_lovrModelDrawInstanced(lua_State* L) {
@ -28,7 +29,7 @@ int l_lovrModelGetAABB(lua_State* L) {
int l_lovrModelGetAnimator(lua_State* L) { int l_lovrModelGetAnimator(lua_State* L) {
Model* model = luax_checktype(L, 1, Model); Model* model = luax_checktype(L, 1, Model);
Animator* animator = lovrModelGetAnimator(model); Animator* animator = lovrModelGetAnimator(model);
luax_pushtype(L, Animator, animator); luax_pushobject(L, animator);
return 1; return 1;
} }
@ -53,7 +54,7 @@ int l_lovrModelGetMaterial(lua_State* L) {
Model* model = luax_checktype(L, 1, Model); Model* model = luax_checktype(L, 1, Model);
Material* material = lovrModelGetMaterial(model); Material* material = lovrModelGetMaterial(model);
if (material) { if (material) {
luax_pushtype(L, Material, material); luax_pushobject(L, material);
} else { } else {
lua_pushnil(L); lua_pushnil(L);
} }
@ -74,7 +75,7 @@ int l_lovrModelSetMaterial(lua_State* L) {
int l_lovrModelGetMesh(lua_State* L) { int l_lovrModelGetMesh(lua_State* L) {
Model* model = luax_checktype(L, 1, Model); Model* model = luax_checktype(L, 1, Model);
Mesh* mesh = lovrModelGetMesh(model); Mesh* mesh = lovrModelGetMesh(model);
luax_pushtype(L, Mesh, mesh); luax_pushobject(L, mesh);
return 1; return 1;
} }

View File

@ -2,12 +2,10 @@
#include "data/modelData.h" #include "data/modelData.h"
#include "math/transform.h" #include "math/transform.h"
#include "math/mat4.h" #include "math/mat4.h"
#include "math/quat.h"
#include "math/vec3.h"
int l_lovrModelDataGetVertexData(lua_State* L) { int l_lovrModelDataGetVertexData(lua_State* L) {
ModelData* modelData = luax_checktype(L, 1, ModelData); ModelData* modelData = luax_checktype(L, 1, ModelData);
luax_pushtype(L, VertexData, modelData->vertexData); luax_pushobject(L, modelData->vertexData);
return 1; return 1;
} }
@ -17,6 +15,21 @@ int l_lovrModelDataGetTriangleCount(lua_State* L) {
return 1; return 1;
} }
int l_lovrModelDataGetTriangle(lua_State* L) {
ModelData* modelData = luax_checktype(L, 1, ModelData);
int index = luaL_checkinteger(L, 2);
if (modelData->indexSize == sizeof(uint16_t)) {
lua_pushinteger(L, modelData->indices.shorts[index]);
lua_pushinteger(L, modelData->indices.shorts[index]);
lua_pushinteger(L, modelData->indices.shorts[index]);
} else {
lua_pushinteger(L, modelData->indices.ints[index]);
lua_pushinteger(L, modelData->indices.ints[index]);
lua_pushinteger(L, modelData->indices.ints[index]);
}
return 3;
}
int l_lovrModelDataGetNodeCount(lua_State* L) { int l_lovrModelDataGetNodeCount(lua_State* L) {
ModelData* modelData = luax_checktype(L, 1, ModelData); ModelData* modelData = luax_checktype(L, 1, ModelData);
lua_pushinteger(L, modelData->nodeCount); lua_pushinteger(L, modelData->nodeCount);
@ -33,8 +46,8 @@ int l_lovrModelDataGetNodeName(lua_State* L) {
} }
static int luax_writenodetransform(lua_State* L, mat4 m, int transformIndex) { static int luax_writenodetransform(lua_State* L, mat4 m, int transformIndex) {
Transform* transform; Transform* transform = luax_totype(L, transformIndex, Transform);
if ((transform = luax_totype(L, transformIndex, Transform)) != NULL) { if (transform) {
lovrTransformSetMatrix(transform, m); lovrTransformSetMatrix(transform, m);
lua_settop(L, transformIndex); lua_settop(L, transformIndex);
return 1; return 1;
@ -183,7 +196,7 @@ int l_lovrModelDataGetDiffuseTexture(lua_State* L) {
ModelData* modelData = luax_checktype(L, 1, ModelData); ModelData* modelData = luax_checktype(L, 1, ModelData);
ModelMaterial* material = luax_checkmodelmaterial(L, 1); ModelMaterial* material = luax_checkmodelmaterial(L, 1);
TextureData* textureData = modelData->textures.data[material->diffuseTexture]; TextureData* textureData = modelData->textures.data[material->diffuseTexture];
luax_pushtype(L, TextureData, textureData); luax_pushobject(L, textureData);
return 1; return 1;
} }
@ -191,7 +204,7 @@ int l_lovrModelDataGetEmissiveTexture(lua_State* L) {
ModelData* modelData = luax_checktype(L, 1, ModelData); ModelData* modelData = luax_checktype(L, 1, ModelData);
ModelMaterial* material = luax_checkmodelmaterial(L, 1); ModelMaterial* material = luax_checkmodelmaterial(L, 1);
TextureData* textureData = modelData->textures.data[material->emissiveTexture]; TextureData* textureData = modelData->textures.data[material->emissiveTexture];
luax_pushtype(L, TextureData, textureData); luax_pushobject(L, textureData);
return 1; return 1;
} }
@ -199,7 +212,7 @@ int l_lovrModelDataGetMetalnessTexture(lua_State* L) {
ModelData* modelData = luax_checktype(L, 1, ModelData); ModelData* modelData = luax_checktype(L, 1, ModelData);
ModelMaterial* material = luax_checkmodelmaterial(L, 1); ModelMaterial* material = luax_checkmodelmaterial(L, 1);
TextureData* textureData = modelData->textures.data[material->metalnessTexture]; TextureData* textureData = modelData->textures.data[material->metalnessTexture];
luax_pushtype(L, TextureData, textureData); luax_pushobject(L, textureData);
return 1; return 1;
} }
@ -207,7 +220,7 @@ int l_lovrModelDataGetRoughnessTexture(lua_State* L) {
ModelData* modelData = luax_checktype(L, 1, ModelData); ModelData* modelData = luax_checktype(L, 1, ModelData);
ModelMaterial* material = luax_checkmodelmaterial(L, 1); ModelMaterial* material = luax_checkmodelmaterial(L, 1);
TextureData* textureData = modelData->textures.data[material->roughnessTexture]; TextureData* textureData = modelData->textures.data[material->roughnessTexture];
luax_pushtype(L, TextureData, textureData); luax_pushobject(L, textureData);
return 1; return 1;
} }
@ -215,7 +228,7 @@ int l_lovrModelDataGetOcclusionTexture(lua_State* L) {
ModelData* modelData = luax_checktype(L, 1, ModelData); ModelData* modelData = luax_checktype(L, 1, ModelData);
ModelMaterial* material = luax_checkmodelmaterial(L, 1); ModelMaterial* material = luax_checkmodelmaterial(L, 1);
TextureData* textureData = modelData->textures.data[material->occlusionTexture]; TextureData* textureData = modelData->textures.data[material->occlusionTexture];
luax_pushtype(L, TextureData, textureData); luax_pushobject(L, textureData);
return 1; return 1;
} }
@ -223,13 +236,14 @@ int l_lovrModelDataGetNormalTexture(lua_State* L) {
ModelData* modelData = luax_checktype(L, 1, ModelData); ModelData* modelData = luax_checktype(L, 1, ModelData);
ModelMaterial* material = luax_checkmodelmaterial(L, 1); ModelMaterial* material = luax_checkmodelmaterial(L, 1);
TextureData* textureData = modelData->textures.data[material->normalTexture]; TextureData* textureData = modelData->textures.data[material->normalTexture];
luax_pushtype(L, TextureData, textureData); luax_pushobject(L, textureData);
return 1; return 1;
} }
const luaL_Reg lovrModelData[] = { const luaL_Reg lovrModelData[] = {
{ "getVertexData", l_lovrModelDataGetVertexData }, { "getVertexData", l_lovrModelDataGetVertexData },
{ "getTriangleCount", l_lovrModelDataGetTriangleCount }, { "getTriangleCount", l_lovrModelDataGetTriangleCount },
{ "getTriangle", l_lovrModelDataGetTriangle },
{ "getNodeCount", l_lovrModelDataGetNodeCount }, { "getNodeCount", l_lovrModelDataGetNodeCount },
{ "getNodeName", l_lovrModelDataGetNodeName }, { "getNodeName", l_lovrModelDataGetNodeName },
{ "getLocalNodeTransform", l_lovrModelDataGetLocalNodeTransform }, { "getLocalNodeTransform", l_lovrModelDataGetLocalNodeTransform },

View File

@ -1,5 +1,7 @@
#include "api.h" #include "api.h"
#include "api/math.h"
#include "math/randomGenerator.h" #include "math/randomGenerator.h"
#include <math.h>
static double luax_checkrandomseedpart(lua_State* L, int index) { static double luax_checkrandomseedpart(lua_State* L, int index) {
double x = luaL_checknumber(L, index); double x = luaL_checknumber(L, index);
@ -50,9 +52,8 @@ int l_lovrRandomGeneratorGetState(lua_State* L) {
int l_lovrRandomGeneratorSetState(lua_State* L) { int l_lovrRandomGeneratorSetState(lua_State* L) {
RandomGenerator* generator = luax_checktype(L, 1, RandomGenerator); RandomGenerator* generator = luax_checktype(L, 1, RandomGenerator);
size_t length; const char* state = luaL_checklstring(L, 2, NULL);
const char* state = luaL_checklstring(L, 2, &length); if (lovrRandomGeneratorSetState(generator, state)) {
if (lovrRandomGeneratorSetState(generator, state, length)) {
return luaL_error(L, "invalid random state %s", state); return luaL_error(L, "invalid random state %s", state);
} }
return 0; return 0;

View File

@ -1,147 +1,258 @@
#include "api.h" #include "api.h"
#include "graphics/graphics.h" #include "api/graphics.h"
#include "graphics/shader.h" #include "graphics/shader.h"
#include "math/transform.h" #include "math/transform.h"
struct TempData { struct TempData {
void* data; void* data;
size_t size; int size;
}; };
// Not thread safe
static struct TempData tempData; static struct TempData tempData;
int luax_checkuniform(lua_State* L, int index, const Uniform* uniform, void* dest, const char* debug) {
Blob* blob = luax_totype(L, index, Blob);
int components = uniform->components;
int count = uniform->count;
if (uniform->type == UNIFORM_MATRIX) {
components *= components;
}
if (blob) {
size_t elements = count * components;
const char* s = elements == 1 ? "" : "s";
size_t capacity;
switch (uniform->type) {
case UNIFORM_FLOAT:
case UNIFORM_MATRIX:
capacity = blob->size / sizeof(float);
lovrAssert(capacity >= elements, "Blob can only hold %d float%s, at least %d needed for uniform '%s'", capacity, s, elements, debug);
memcpy(dest, blob->data, elements * sizeof(float));
break;
case UNIFORM_INT:
capacity = blob->size / sizeof(int);
lovrAssert(capacity >= elements, "Blob can only hold %d int%s, at least %d needed for uniform '%s'", capacity, s, elements, debug);
memcpy(dest, blob->data, elements * sizeof(int));
break;
case UNIFORM_SAMPLER: lovrThrow("Sampler uniform '%s' can not be updated with a Blob", debug);
case UNIFORM_IMAGE: lovrThrow("Image uniform '%s' can not be updated with a Blob", debug);
}
return 0;
}
if (components == 1) {
bool isTable = lua_istable(L, index);
int length = isTable ? lua_objlen(L, index) : count;
length = MIN(length, count);
for (int i = 0; i < count; i++) {
int j = index + i;
if (isTable) {
lua_rawgeti(L, index, i + 1);
j = -1;
}
switch (uniform->type) {
case UNIFORM_FLOAT: *((float*) dest + i) = luaL_optnumber(L, j, 0.); break;
case UNIFORM_INT: *((int*) dest + i) = luaL_optinteger(L, j, 0); break;
case UNIFORM_SAMPLER:
*((Texture**) dest + i) = luax_checktype(L, j, Texture);
TextureType type = lovrTextureGetType(*((Texture**) dest + i));
lovrAssert(type == uniform->textureType, "Attempt to send %s texture to %s sampler uniform", TextureTypes[type], TextureTypes[uniform->textureType]);
break;
case UNIFORM_IMAGE: {
Image* image = (Image*) dest + i;
image->texture = luax_checktype(L, j, Texture);
image->slice = -1;
image->mipmap = 0;
image->access = ACCESS_READ_WRITE;
TextureType type = lovrTextureGetType(image->texture);
lovrAssert(type == uniform->textureType, "Attempt to send %s texture to %s image uniform", TextureTypes[type], TextureTypes[uniform->textureType]);
break;
}
default: break;
}
if (isTable) {
lua_pop(L, 1);
}
}
} else {
luaL_checktype(L, index, LUA_TTABLE);
lua_rawgeti(L, index, 1);
bool wrappedTable = lua_istable(L, -1) || luax_totype(L, -1, Transform);
lua_pop(L, 1);
if (wrappedTable) {
int length = lua_objlen(L, index);
length = MIN(length, count);
for (int i = 0; i < length; i++) {
lua_rawgeti(L, index, i + 1);
if (uniform->type == UNIFORM_MATRIX && lua_isuserdata(L, -1)) {
Transform* transform = luax_checktype(L, -1, Transform);
memcpy((float*) dest + i * components, transform->matrix, 16 * sizeof(float));
lua_pop(L, 1);
continue;
}
for (int j = 0; j < components; j++) {
lua_rawgeti(L, -1, j + 1);
switch (uniform->type) {
case UNIFORM_FLOAT:
case UNIFORM_MATRIX:
*((float*) dest + i * components + j) = luaL_optnumber(L, -1, 0.);
break;
case UNIFORM_INT:
*((int*) dest + i * components + j) = luaL_optinteger(L, -1, 0);
break;
case UNIFORM_SAMPLER:
case UNIFORM_IMAGE:
lovrThrow("Unreachable");
}
lua_pop(L, 1);
}
lua_pop(L, 1);
}
} else {
for (int i = 0; i < count; i++) {
if (uniform->type == UNIFORM_MATRIX && lua_isuserdata(L, index + i)) {
Transform* transform = luax_checktype(L, index + i, Transform);
memcpy((float*) dest + i * components, transform->matrix, 16 * sizeof(float));
continue;
}
luaL_checktype(L, index + i, LUA_TTABLE);
for (int j = 0; j < components; j++) {
lua_rawgeti(L, index + i, j + 1);
switch (uniform->type) {
case UNIFORM_FLOAT:
case UNIFORM_MATRIX:
*((float*) dest + i * components + j) = luaL_optnumber(L, -1, 0.);
break;
case UNIFORM_INT:
*((float*) dest + i * components + j) = luaL_optinteger(L, -1, 0);
break;
case UNIFORM_SAMPLER:
case UNIFORM_IMAGE:
lovrThrow("Unreachable");
}
}
}
}
}
return 0;
}
void luax_checkuniformtype(lua_State* L, int index, UniformType* baseType, int* components) {
size_t length;
lovrAssert(lua_type(L, index) == LUA_TSTRING, "Uniform types must be strings, got %s", lua_typename(L, index));
const char* type = lua_tolstring(L, index, &length);
if (!strcmp(type, "float")) {
*baseType = UNIFORM_FLOAT;
*components = 1;
} else if (!strcmp(type, "int")) {
*baseType = UNIFORM_INT;
*components = 1;
} else {
int n = type[length - 1] - '0';
lovrAssert(n >= 2 && n <= 4, "Unknown uniform type '%s'", type);
if (type[0] == 'v' && type[1] == 'e' && type[2] == 'c' && length == 4) {
*baseType = UNIFORM_FLOAT;
*components = n;
} else if (type[0] == 'i' && type[1] == 'v' && type[2] == 'e' && type[3] == 'c' && length == 5) {
*baseType = UNIFORM_INT;
*components = n;
} else if (type[0] == 'm' && type[1] == 'a' && type[2] == 't' && length == 4) {
*baseType = UNIFORM_MATRIX;
*components = n;
} else {
lovrThrow("Unknown uniform type '%s'", type);
}
}
}
int l_lovrShaderGetType(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader);
lua_pushstring(L, ShaderTypes[lovrShaderGetType(shader)]);
return 1;
}
int l_lovrShaderHasUniform(lua_State* L) { int l_lovrShaderHasUniform(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader); Shader* shader = luax_checktype(L, 1, Shader);
const char* name = luaL_checkstring(L, 2); const char* name = luaL_checkstring(L, 2);
lua_pushboolean(L, lovrShaderGetUniform(shader, name) != NULL); lua_pushboolean(L, lovrShaderHasUniform(shader, name));
return 1; return 1;
} }
int l_lovrShaderSend(lua_State* L) { int l_lovrShaderSend(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader); Shader* shader = luax_checktype(L, 1, Shader);
const char* name = luaL_checkstring(L, 2); const char* name = luaL_checkstring(L, 2);
lua_settop(L, 3); const Uniform* uniform = lovrShaderGetUniform(shader, name);
lovrAssert(uniform, "Unknown shader variable '%s'", name);
Uniform* uniform = lovrShaderGetUniform(shader, name);
if (!uniform) {
return luaL_error(L, "Unknown shader variable '%s'", name);
}
if (tempData.size < uniform->size) { if (tempData.size < uniform->size) {
tempData.size = uniform->size; tempData.size = uniform->size;
tempData.data = realloc(tempData.data, tempData.size); tempData.data = realloc(tempData.data, tempData.size);
} }
int* ints = (int*) tempData.data; luax_checkuniform(L, 3, uniform, tempData.data, name);
float* floats = (float*) tempData.data;
Texture** textures = (Texture**) tempData.data;
int n = 1;
int components = uniform->components;
if (components > 1) {
luaL_checktype(L, 3, LUA_TTABLE);
lua_rawgeti(L, 3, 1);
if (!lua_istable(L, -1)) {
lua_newtable(L);
lua_pushvalue(L, 3);
lua_rawseti(L, -2, 1);
} else {
lua_pop(L, 1);
}
n = lua_objlen(L, -1);
if (n != uniform->count) {
const char* elements = uniform->count == 1 ? "element" : "elements";
return luaL_error(L, "Expected %d %s for array '%s', got %d", uniform->count, elements, uniform->name, n);
}
}
switch (uniform->type) { switch (uniform->type) {
case UNIFORM_FLOAT: case UNIFORM_FLOAT: lovrShaderSetFloats(shader, uniform->name, tempData.data, 0, uniform->count * uniform->components); break;
if (components == 1) { case UNIFORM_INT: lovrShaderSetInts(shader, uniform->name, tempData.data, 0, uniform->count * uniform->components); break;
floats[0] = luaL_checknumber(L, 3); case UNIFORM_MATRIX: lovrShaderSetMatrices(shader, uniform->name, tempData.data, 0, uniform->count * uniform->components * uniform->components); break;
} else { case UNIFORM_SAMPLER: lovrShaderSetTextures(shader, uniform->name, tempData.data, 0, uniform->count); break;
luaL_checktype(L, 3, LUA_TTABLE); case UNIFORM_IMAGE: lovrShaderSetImages(shader, uniform->name, tempData.data, 0, uniform->count); break;
for (int i = 0; i < n; i++) { }
lua_rawgeti(L, -1, i + 1); return 0;
if (lua_type(L, -1) != LUA_TTABLE || (int) lua_objlen(L, -1) != components) { }
return luaL_error(L, "Expected %d components for uniform '%s' #%d, got %d", components, uniform->name, lua_objlen(L, -1));
}
for (int j = 0; j < components; j++) {
lua_rawgeti(L, -1, j + 1);
floats[i * components + j] = luaL_checknumber(L, -1);
lua_pop(L, 1);
}
lua_pop(L, 1);
}
}
lovrShaderSetFloat(shader, name, floats, n * components);
break;
case UNIFORM_INT: int l_lovrShaderSendBlock(lua_State* L) {
if (components == 1) { Shader* shader = luax_checktype(L, 1, Shader);
ints[0] = luaL_checkinteger(L, 3); const char* name = luaL_checkstring(L, 2);
} else { ShaderBlock* block = luax_checktype(L, 3, ShaderBlock);
luaL_checktype(L, 3, LUA_TTABLE); UniformAccess access = luaL_checkoption(L, 4, "readwrite", UniformAccesses);
for (int i = 0; i < n; i++) { lovrShaderSetBlock(shader, name, block, access);
lua_rawgeti(L, -1, i + 1); return 0;
if (lua_type(L, -1) != LUA_TTABLE || (int) lua_objlen(L, -1) != components) { }
return luaL_error(L, "Expected %d components for uniform '%s' #%d, got %d", components, uniform->name, lua_objlen(L, -1));
}
for (int j = 0; j < components; j++) {
lua_rawgeti(L, -1, j + 1);
ints[i * components + j] = luaL_checkinteger(L, -1);
lua_pop(L, 1);
}
lua_pop(L, 1);
}
}
lovrShaderSetInt(shader, name, ints, n * components);
break;
case UNIFORM_MATRIX: int l_lovrShaderSendImage(lua_State* L) {
if (components == 4 && lua_isuserdata(L, 3)) { int index = 1;
Transform* transform = luax_checktype(L, 3, Transform); Shader* shader = luax_checktype(L, index++, Shader);
memcpy(floats, transform->matrix, 16 * sizeof(float)); const char* name = luaL_checkstring(L, index++);
} else {
luaL_checktype(L, 3, LUA_TTABLE);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
for (int j = 0; j < components * components; j++) {
lua_rawgeti(L, -1, j + 1);
floats[i * components * components + j] = luaL_checknumber(L, -1);
lua_pop(L, 1);
}
lua_pop(L, 1);
}
}
lovrShaderSetMatrix(shader, name, floats, n * components * components);
break;
case UNIFORM_SAMPLER: int start = 0;
if (components == 1) { if (lua_type(L, index) == LUA_TNUMBER) {
textures[0] = luax_checktypeof(L, 3, Texture); start = lua_tointeger(L, index++);
} else {
luaL_checktype(L, 3, LUA_TTABLE);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
textures[i] = luax_checktypeof(L, -1, Texture);
lua_pop(L, 1);
}
}
lovrShaderSetTexture(shader, name, textures, n);
break;
default: break;
} }
Texture* texture = luax_checktype(L, index++, Texture);
int slice = luaL_optinteger(L, index++, 0) - 1; // Default is -1
int mipmap = luax_optmipmap(L, index++, texture);
UniformAccess access = luaL_checkoption(L, index++, "readwrite", UniformAccesses);
Image image = { .texture = texture, .slice = slice, .mipmap = mipmap, .access = access };
lovrShaderSetImages(shader, name, &image, start, 1);
return 0; return 0;
} }
const luaL_Reg lovrShader[] = { const luaL_Reg lovrShader[] = {
{ "getType", l_lovrShaderGetType },
{ "hasUniform", l_lovrShaderHasUniform }, { "hasUniform", l_lovrShaderHasUniform },
{ "send", l_lovrShaderSend }, { "send", l_lovrShaderSend },
{ "sendBlock", l_lovrShaderSendBlock },
{ "sendImage", l_lovrShaderSendImage },
{ NULL, NULL } { NULL, NULL }
}; };

View File

@ -0,0 +1,62 @@
#include "api.h"
#include "api/graphics.h"
#include "graphics/shader.h"
#include "math/transform.h"
int l_lovrShaderBlockIsWritable(lua_State* L) {
ShaderBlock* block = luax_checktype(L, 1, ShaderBlock);
lua_pushboolean(L, lovrShaderBlockGetType(block) == BLOCK_STORAGE);
return 1;
}
int l_lovrShaderBlockGetSize(lua_State* L) {
ShaderBlock* block = luax_checktype(L, 1, ShaderBlock);
lua_pushinteger(L, lovrShaderBlockGetSize(block));
return 1;
}
int l_lovrShaderBlockGetOffset(lua_State* L) {
ShaderBlock* block = luax_checktype(L, 1, ShaderBlock);
const char* field = luaL_checkstring(L, 2);
const Uniform* uniform = lovrShaderBlockGetUniform(block, field);
lua_pushinteger(L, uniform->offset);
return 1;
}
int l_lovrShaderBlockSend(lua_State* L) {
ShaderBlock* block = luax_checktype(L, 1, ShaderBlock);
if (lua_type(L, 2) == LUA_TSTRING) {
const char* name = luaL_checkstring(L, 2);
const Uniform* uniform = lovrShaderBlockGetUniform(block, name);
lovrAssert(uniform, "Unknown uniform for ShaderBlock '%s'", name);
uint8_t* data = ((uint8_t*) lovrShaderBlockMap(block)) + uniform->offset;
luax_checkuniform(L, 3, uniform, data, name);
return 0;
} else {
Blob* blob = luax_checktype(L, 1, Blob);
void* data = lovrShaderBlockMap(block);
size_t blockSize = lovrShaderBlockGetSize(block);
size_t copySize = MIN(blockSize, blob->size);
memcpy(data, blob->data, copySize);
lua_pushinteger(L, copySize);
return 1;
}
}
int l_lovrShaderBlockGetShaderCode(lua_State* L) {
ShaderBlock* block = luax_checktype(L, 1, ShaderBlock);
const char* blockName = luaL_checkstring(L, 2);
size_t length;
char* code = lovrShaderBlockGetShaderCode(block, blockName, &length);
lua_pushlstring(L, code, length);
return 1;
}
const luaL_Reg lovrShaderBlock[] = {
{ "isWritable", l_lovrShaderBlockIsWritable },
{ "getSize", l_lovrShaderBlockGetSize },
{ "getOffset", l_lovrShaderBlockGetOffset },
{ "send", l_lovrShaderBlockSend },
{ "getShaderCode", l_lovrShaderBlockGetShaderCode },
{ NULL, NULL }
};

View File

@ -1,56 +1,46 @@
#include "api.h" #include "api.h"
#include "physics/physics.h" #include "physics/physics.h"
int luax_pushshape(lua_State* L, Shape* shape) {
switch (lovrShapeGetType(shape)) {
case SHAPE_SPHERE: luax_pushtype(L, SphereShape, shape); return 1;
case SHAPE_BOX: luax_pushtype(L, BoxShape, shape); return 1;
case SHAPE_CAPSULE: luax_pushtype(L, CapsuleShape, shape); return 1;
case SHAPE_CYLINDER: luax_pushtype(L, CylinderShape, shape); return 1;
default: return 0;
}
}
int l_lovrShapeDestroy(lua_State* L) { int l_lovrShapeDestroy(lua_State* L) {
Shape* shape = luax_checktypeof(L, 1, Shape); Shape* shape = luax_checktype(L, 1, Shape);
lovrShapeDestroyData(shape); lovrShapeDestroyData(shape);
return 0; return 0;
} }
int l_lovrShapeGetType(lua_State* L) { int l_lovrShapeGetType(lua_State* L) {
Shape* shape = luax_checktypeof(L, 1, Shape); Shape* shape = luax_checktype(L, 1, Shape);
luax_pushenum(L, &ShapeTypes, lovrShapeGetType(shape)); lua_pushstring(L, ShapeTypes[lovrShapeGetType(shape)]);
return 1; return 1;
} }
int l_lovrShapeGetCollider(lua_State* L) { int l_lovrShapeGetCollider(lua_State* L) {
Shape* shape = luax_checktypeof(L, 1, Shape); Shape* shape = luax_checktype(L, 1, Shape);
luax_pushtype(L, Collider, lovrShapeGetCollider(shape)); luax_pushobject(L, lovrShapeGetCollider(shape));
return 1; return 1;
} }
int l_lovrShapeIsEnabled(lua_State* L) { int l_lovrShapeIsEnabled(lua_State* L) {
Shape* shape = luax_checktypeof(L, 1, Shape); Shape* shape = luax_checktype(L, 1, Shape);
lua_pushboolean(L, lovrShapeIsEnabled(shape)); lua_pushboolean(L, lovrShapeIsEnabled(shape));
return 1; return 1;
} }
int l_lovrShapeSetEnabled(lua_State* L) { int l_lovrShapeSetEnabled(lua_State* L) {
Shape* shape = luax_checktypeof(L, 1, Shape); Shape* shape = luax_checktype(L, 1, Shape);
bool enabled = lua_toboolean(L, 2); bool enabled = lua_toboolean(L, 2);
lovrShapeSetEnabled(shape, enabled); lovrShapeSetEnabled(shape, enabled);
return 0; return 0;
} }
int l_lovrShapeGetUserData(lua_State* L) { int l_lovrShapeGetUserData(lua_State* L) {
Shape* shape = luax_checktypeof(L, 1, Shape); Shape* shape = luax_checktype(L, 1, Shape);
int ref = (int) lovrShapeGetUserData(shape); int ref = (int) lovrShapeGetUserData(shape);
lua_rawgeti(L, LUA_REGISTRYINDEX, ref); lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
return 1; return 1;
} }
int l_lovrShapeSetUserData(lua_State* L) { int l_lovrShapeSetUserData(lua_State* L) {
Shape* shape = luax_checktypeof(L, 1, Shape); Shape* shape = luax_checktype(L, 1, Shape);
uint64_t ref = (int) lovrShapeGetUserData(shape); uint64_t ref = (int) lovrShapeGetUserData(shape);
if (ref) { if (ref) {
luaL_unref(L, LUA_REGISTRYINDEX, ref); luaL_unref(L, LUA_REGISTRYINDEX, ref);
@ -67,7 +57,7 @@ int l_lovrShapeSetUserData(lua_State* L) {
} }
int l_lovrShapeGetPosition(lua_State* L) { int l_lovrShapeGetPosition(lua_State* L) {
Shape* shape = luax_checktypeof(L, 1, Shape); Shape* shape = luax_checktype(L, 1, Shape);
float x, y, z; float x, y, z;
lovrShapeGetPosition(shape, &x, &y, &z); lovrShapeGetPosition(shape, &x, &y, &z);
lua_pushnumber(L, x); lua_pushnumber(L, x);
@ -77,7 +67,7 @@ int l_lovrShapeGetPosition(lua_State* L) {
} }
int l_lovrShapeSetPosition(lua_State* L) { int l_lovrShapeSetPosition(lua_State* L) {
Shape* shape = luax_checktypeof(L, 1, Shape); Shape* shape = luax_checktype(L, 1, Shape);
float x = luaL_checknumber(L, 2); float x = luaL_checknumber(L, 2);
float y = luaL_checknumber(L, 3); float y = luaL_checknumber(L, 3);
float z = luaL_checknumber(L, 4); float z = luaL_checknumber(L, 4);
@ -86,7 +76,7 @@ int l_lovrShapeSetPosition(lua_State* L) {
} }
int l_lovrShapeGetOrientation(lua_State* L) { int l_lovrShapeGetOrientation(lua_State* L) {
Shape* shape = luax_checktypeof(L, 1, Shape); Shape* shape = luax_checktype(L, 1, Shape);
float angle, x, y, z; float angle, x, y, z;
lovrShapeGetOrientation(shape, &angle, &x, &y, &z); lovrShapeGetOrientation(shape, &angle, &x, &y, &z);
lua_pushnumber(L, angle); lua_pushnumber(L, angle);
@ -97,7 +87,7 @@ int l_lovrShapeGetOrientation(lua_State* L) {
} }
int l_lovrShapeSetOrientation(lua_State* L) { int l_lovrShapeSetOrientation(lua_State* L) {
Shape* shape = luax_checktypeof(L, 1, Shape); Shape* shape = luax_checktype(L, 1, Shape);
float angle = luaL_checknumber(L, 2); float angle = luaL_checknumber(L, 2);
float x = luaL_checknumber(L, 3); float x = luaL_checknumber(L, 3);
float y = luaL_checknumber(L, 4); float y = luaL_checknumber(L, 4);
@ -107,7 +97,7 @@ int l_lovrShapeSetOrientation(lua_State* L) {
} }
int l_lovrShapeGetMass(lua_State* L) { int l_lovrShapeGetMass(lua_State* L) {
Shape* shape = luax_checktypeof(L, 1, Shape); Shape* shape = luax_checktype(L, 1, Shape);
float density = luaL_checknumber(L, 2); float density = luaL_checknumber(L, 2);
float cx, cy, cz, mass; float cx, cy, cz, mass;
float inertia[6]; float inertia[6];

58
src/api/types/soundData.c Normal file
View File

@ -0,0 +1,58 @@
#include "api.h"
#include "data/soundData.h"
int l_lovrSoundDataGetBitDepth(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
lua_pushinteger(L, soundData->bitDepth);
return 1;
}
int l_lovrSoundDataGetChannelCount(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
lua_pushinteger(L, soundData->channelCount);
return 1;
}
int l_lovrSoundDataGetDuration(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
lua_pushnumber(L, soundData->samples / (float) soundData->sampleRate);
return 1;
}
int l_lovrSoundDataGetSample(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
int index = luaL_checkinteger(L, 2);
lua_pushnumber(L, lovrSoundDataGetSample(soundData, index));
return 1;
}
int l_lovrSoundDataGetSampleCount(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
lua_pushinteger(L, soundData->samples);
return 1;
}
int l_lovrSoundDataGetSampleRate(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
lua_pushinteger(L, soundData->sampleRate);
return 1;
}
int l_lovrSoundDataSetSample(lua_State* L) {
SoundData* soundData = luax_checktype(L, 1, SoundData);
int index = luaL_checkinteger(L, 2);
float value = luaL_checknumber(L, 3);
lovrSoundDataSetSample(soundData, index, value);
return 0;
}
const luaL_Reg lovrSoundData[] = {
{ "getBitDepth", l_lovrSoundDataGetBitDepth },
{ "getChannelCount", l_lovrSoundDataGetChannelCount },
{ "getDuration", l_lovrSoundDataGetDuration },
{ "getSample", l_lovrSoundDataGetSample },
{ "getSampleCount", l_lovrSoundDataGetSampleCount },
{ "getSampleRate", l_lovrSoundDataGetSampleRate },
{ "setSample", l_lovrSoundDataSetSample },
{ NULL, NULL }
};

View File

@ -35,10 +35,10 @@ int l_lovrSourceGetDirection(lua_State* L) {
int l_lovrSourceGetDuration(lua_State* L) { int l_lovrSourceGetDuration(lua_State* L) {
Source* source = luax_checktype(L, 1, Source); Source* source = luax_checktype(L, 1, Source);
TimeUnit* unit = luax_optenum(L, 2, "seconds", &TimeUnits, "unit"); TimeUnit unit = luaL_checkoption(L, 2, "seconds", TimeUnits);
int duration = lovrSourceGetDuration(source); int duration = lovrSourceGetDuration(source);
if (*unit == UNIT_SECONDS) { if (unit == UNIT_SECONDS) {
lua_pushnumber(L, (float) duration / lovrSourceGetSampleRate(source)); lua_pushnumber(L, (float) duration / lovrSourceGetSampleRate(source));
} else { } else {
lua_pushinteger(L, duration); lua_pushinteger(L, duration);
@ -78,6 +78,12 @@ int l_lovrSourceGetSampleRate(lua_State* L) {
return 1; return 1;
} }
int l_lovrSourceGetType(lua_State* L) {
Source* source = luax_checktype(L, 1, Source);
lua_pushstring(L, SourceTypes[lovrSourceGetType(source)]);
return 1;
}
int l_lovrSourceGetVelocity(lua_State* L) { int l_lovrSourceGetVelocity(lua_State* L) {
float x, y, z; float x, y, z;
lovrSourceGetVelocity(luax_checktype(L, 1, Source), &x, &y, &z); lovrSourceGetVelocity(luax_checktype(L, 1, Source), &x, &y, &z);
@ -151,9 +157,9 @@ int l_lovrSourceRewind(lua_State* L) {
int l_lovrSourceSeek(lua_State* L) { int l_lovrSourceSeek(lua_State* L) {
Source* source = luax_checktype(L, 1, Source); Source* source = luax_checktype(L, 1, Source);
TimeUnit* unit = luax_optenum(L, 3, "seconds", &TimeUnits, "unit"); TimeUnit unit = luaL_checkoption(L, 3, "seconds", TimeUnits);
if (*unit == UNIT_SECONDS) { if (unit == UNIT_SECONDS) {
float seconds = luaL_checknumber(L, 2); float seconds = luaL_checknumber(L, 2);
int sampleRate = lovrSourceGetSampleRate(source); int sampleRate = lovrSourceGetSampleRate(source);
lovrSourceSeek(source, (int) (seconds * sampleRate + .5f)); lovrSourceSeek(source, (int) (seconds * sampleRate + .5f));
@ -243,10 +249,10 @@ int l_lovrSourceStop(lua_State* L) {
int l_lovrSourceTell(lua_State* L) { int l_lovrSourceTell(lua_State* L) {
Source* source = luax_checktype(L, 1, Source); Source* source = luax_checktype(L, 1, Source);
TimeUnit* unit = luax_optenum(L, 2, "seconds", &TimeUnits, "unit"); TimeUnit unit = luaL_checkoption(L, 2, "seconds", TimeUnits);
int offset = lovrSourceTell(source); int offset = lovrSourceTell(source);
if (*unit == UNIT_SECONDS) { if (unit == UNIT_SECONDS) {
lua_pushnumber(L, (float) offset / lovrSourceGetSampleRate(source)); lua_pushnumber(L, (float) offset / lovrSourceGetSampleRate(source));
} else { } else {
lua_pushinteger(L, offset); lua_pushinteger(L, offset);
@ -265,6 +271,7 @@ const luaL_Reg lovrSource[] = {
{ "getPitch", l_lovrSourceGetPitch }, { "getPitch", l_lovrSourceGetPitch },
{ "getPosition", l_lovrSourceGetPosition }, { "getPosition", l_lovrSourceGetPosition },
{ "getSampleRate", l_lovrSourceGetSampleRate }, { "getSampleRate", l_lovrSourceGetSampleRate },
{ "getType", l_lovrSourceGetType },
{ "getVelocity", l_lovrSourceGetVelocity }, { "getVelocity", l_lovrSourceGetVelocity },
{ "getVolume", l_lovrSourceGetVolume }, { "getVolume", l_lovrSourceGetVolume },
{ "getVolumeLimits", l_lovrSourceGetVolumeLimits }, { "getVolumeLimits", l_lovrSourceGetVolumeLimits },

View File

@ -1,17 +1,34 @@
#include "api.h" #include "api.h"
#include "graphics/graphics.h" #include "graphics/texture.h"
int luax_optmipmap(lua_State* L, int index, Texture* texture) {
int mipmap = luaL_optinteger(L, index, 1);
lovrAssert(mipmap > 0 && mipmap <= lovrTextureGetMipmapCount(texture), "Invalid mipmap %d\n", mipmap);
return mipmap - 1;
}
int l_lovrTextureGetDepth(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
lua_pushnumber(L, lovrTextureGetDepth(texture, luax_optmipmap(L, 2, texture)));
return 1;
}
int l_lovrTextureGetDimensions(lua_State* L) { int l_lovrTextureGetDimensions(lua_State* L) {
Texture* texture = luax_checktypeof(L, 1, Texture); Texture* texture = luax_checktype(L, 1, Texture);
lua_pushnumber(L, texture->width); int mipmap = luax_optmipmap(L, 2, texture);
lua_pushnumber(L, texture->height); lua_pushinteger(L, lovrTextureGetWidth(texture, mipmap));
lua_pushinteger(L, lovrTextureGetHeight(texture, mipmap));
if (lovrTextureGetType(texture) != TEXTURE_2D) {
lua_pushinteger(L, lovrTextureGetDepth(texture, mipmap));
return 3;
}
return 2; return 2;
} }
int l_lovrTextureGetFilter(lua_State* L) { int l_lovrTextureGetFilter(lua_State* L) {
Texture* texture = luax_checktypeof(L, 1, Texture); Texture* texture = luax_checktype(L, 1, Texture);
TextureFilter filter = lovrTextureGetFilter(texture); TextureFilter filter = lovrTextureGetFilter(texture);
luax_pushenum(L, &FilterModes, filter.mode); lua_pushstring(L, FilterModes[filter.mode]);
if (filter.mode == FILTER_ANISOTROPIC) { if (filter.mode == FILTER_ANISOTROPIC) {
lua_pushnumber(L, filter.anisotropy); lua_pushnumber(L, filter.anisotropy);
return 2; return 2;
@ -19,25 +36,43 @@ int l_lovrTextureGetFilter(lua_State* L) {
return 1; return 1;
} }
int l_lovrTextureGetFormat(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
lua_pushstring(L, TextureFormats[lovrTextureGetFormat(texture)]);
return 1;
}
int l_lovrTextureGetHeight(lua_State* L) { int l_lovrTextureGetHeight(lua_State* L) {
Texture* texture = luax_checktypeof(L, 1, Texture); Texture* texture = luax_checktype(L, 1, Texture);
lua_pushnumber(L, texture->height); lua_pushnumber(L, lovrTextureGetHeight(texture, luax_optmipmap(L, 2, texture)));
return 1;
}
int l_lovrTextureGetMipmapCount(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
lua_pushinteger(L, lovrTextureGetMipmapCount(texture));
return 1;
}
int l_lovrTextureGetType(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
lua_pushstring(L, TextureTypes[lovrTextureGetType(texture)]);
return 1; return 1;
} }
int l_lovrTextureGetWidth(lua_State* L) { int l_lovrTextureGetWidth(lua_State* L) {
Texture* texture = luax_checktypeof(L, 1, Texture); Texture* texture = luax_checktype(L, 1, Texture);
lua_pushnumber(L, texture->width); lua_pushnumber(L, lovrTextureGetWidth(texture, luax_optmipmap(L, 2, texture)));
return 1; return 1;
} }
int l_lovrTextureGetWrap(lua_State* L) { int l_lovrTextureGetWrap(lua_State* L) {
Texture* texture = luax_checktypeof(L, 1, Texture); Texture* texture = luax_checktype(L, 1, Texture);
TextureWrap wrap = lovrTextureGetWrap(texture); TextureWrap wrap = lovrTextureGetWrap(texture);
luax_pushenum(L, &WrapModes, wrap.s); lua_pushstring(L, WrapModes[wrap.s]);
luax_pushenum(L, &WrapModes, wrap.t); lua_pushstring(L, WrapModes[wrap.t]);
if (texture->type == TEXTURE_CUBE) { if (lovrTextureGetType(texture) == TEXTURE_CUBE) {
luax_pushenum(L, &WrapModes, wrap.r); lua_pushstring(L, WrapModes[wrap.r]);
return 3; return 3;
} }
return 2; return 2;
@ -46,14 +81,17 @@ int l_lovrTextureGetWrap(lua_State* L) {
int l_lovrTextureReplacePixels(lua_State* L) { int l_lovrTextureReplacePixels(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture); Texture* texture = luax_checktype(L, 1, Texture);
TextureData* textureData = luax_checktype(L, 2, TextureData); TextureData* textureData = luax_checktype(L, 2, TextureData);
int slice = luaL_optinteger(L, 3, 1); int x = luaL_optinteger(L, 3, 0);
lovrTextureReplacePixels(texture, textureData, slice - 1); int y = luaL_optinteger(L, 4, 0);
int slice = luaL_optinteger(L, 5, 1) - 1;
int mipmap = luaL_optinteger(L, 6, 1) - 1;
lovrTextureReplacePixels(texture, textureData, x, y, slice, mipmap);
return 0; return 0;
} }
int l_lovrTextureSetFilter(lua_State* L) { int l_lovrTextureSetFilter(lua_State* L) {
Texture* texture = luax_checktypeof(L, 1, Texture); Texture* texture = luax_checktype(L, 1, Texture);
FilterMode mode = *(FilterMode*) luax_checkenum(L, 2, &FilterModes, "filter mode"); FilterMode mode = luaL_checkoption(L, 2, NULL, FilterModes);
float anisotropy = luaL_optnumber(L, 3, 1.); float anisotropy = luaL_optnumber(L, 3, 1.);
TextureFilter filter = { .mode = mode, .anisotropy = anisotropy }; TextureFilter filter = { .mode = mode, .anisotropy = anisotropy };
lovrTextureSetFilter(texture, filter); lovrTextureSetFilter(texture, filter);
@ -61,19 +99,23 @@ int l_lovrTextureSetFilter(lua_State* L) {
} }
int l_lovrTextureSetWrap(lua_State* L) { int l_lovrTextureSetWrap(lua_State* L) {
Texture* texture = luax_checktypeof(L, 1, Texture); Texture* texture = luax_checktype(L, 1, Texture);
TextureWrap wrap; TextureWrap wrap;
wrap.s = *(WrapMode*) luax_checkenum(L, 2, &WrapModes, "wrap mode"); wrap.s = luaL_checkoption(L, 2, NULL, WrapModes);
wrap.t = *(WrapMode*) luax_optenum(L, 3, luaL_checkstring(L, 2), &WrapModes, "wrap mode"); wrap.t = luaL_checkoption(L, 3, luaL_checkstring(L, 2), WrapModes);
wrap.r = *(WrapMode*) luax_optenum(L, 4, luaL_checkstring(L, 2), &WrapModes, "wrap mode"); wrap.r = luaL_checkoption(L, 4, luaL_checkstring(L, 2), WrapModes);
lovrTextureSetWrap(texture, wrap); lovrTextureSetWrap(texture, wrap);
return 0; return 0;
} }
const luaL_Reg lovrTexture[] = { const luaL_Reg lovrTexture[] = {
{ "getDepth", l_lovrTextureGetDepth },
{ "getDimensions", l_lovrTextureGetDimensions }, { "getDimensions", l_lovrTextureGetDimensions },
{ "getFilter", l_lovrTextureGetFilter }, { "getFilter", l_lovrTextureGetFilter },
{ "getFormat", l_lovrTextureGetFormat },
{ "getHeight", l_lovrTextureGetHeight }, { "getHeight", l_lovrTextureGetHeight },
{ "getMipmapCount", l_lovrTextureGetMipmapCount },
{ "getType", l_lovrTextureGetType },
{ "getWidth", l_lovrTextureGetWidth }, { "getWidth", l_lovrTextureGetWidth },
{ "getWrap", l_lovrTextureGetWrap }, { "getWrap", l_lovrTextureGetWrap },
{ "replacePixels", l_lovrTextureReplacePixels }, { "replacePixels", l_lovrTextureReplacePixels },

View File

@ -1,4 +1,5 @@
#include "api.h" #include "api.h"
#include "data/textureData.h"
int l_lovrTextureDataEncode(lua_State* L) { int l_lovrTextureDataEncode(lua_State* L) {
TextureData* textureData = luax_checktype(L, 1, TextureData); TextureData* textureData = luax_checktype(L, 1, TextureData);

39
src/api/types/thread.c Normal file
View File

@ -0,0 +1,39 @@
#include "api.h"
#include "thread/thread.h"
int l_lovrThreadStart(lua_State* L) {
Thread* thread = luax_checktype(L, 1, Thread);
lovrThreadStart(thread);
return 0;
}
int l_lovrThreadWait(lua_State* L) {
Thread* thread = luax_checktype(L, 1, Thread);
lovrThreadWait(thread);
return 0;
}
int l_lovrThreadGetError(lua_State* L) {
Thread* thread = luax_checktype(L, 1, Thread);
const char* error = lovrThreadGetError(thread);
if (error) {
lua_pushstring(L, error);
} else {
lua_pushnil(L);
}
return 1;
}
int l_lovrThreadIsRunning(lua_State* L) {
Thread* thread = luax_checktype(L, 1, Thread);
lua_pushboolean(L, thread->running);
return 1;
}
const luaL_Reg lovrThread[] = {
{ "start", l_lovrThreadStart },
{ "wait", l_lovrThreadWait },
{ "getError", l_lovrThreadGetError },
{ "isRunning", l_lovrThreadIsRunning },
{ NULL, NULL }
};

View File

@ -2,24 +2,24 @@
#include "math/mat4.h" #include "math/mat4.h"
#include "math/transform.h" #include "math/transform.h"
int luax_readtransform(lua_State* L, int index, mat4 m, bool uniformScale) { int luax_readtransform(lua_State* L, int index, mat4 m, int scaleComponents) {
if (lua_isnumber(L, index)) { if (lua_isnumber(L, index)) {
float x = luaL_optnumber(L, index++, 0); float x = luaL_optnumber(L, index++, 0);
float y = luaL_optnumber(L, index++, 0); float y = luaL_optnumber(L, index++, 0);
float z = luaL_optnumber(L, index++, 0); float z = luaL_optnumber(L, index++, 0);
float sx, sy, sz; float scale[3] = { 1., 1., 1. };
if (uniformScale) { if (scaleComponents == 1) {
sx = sy = sz = luaL_optnumber(L, index++, 1); scale[0] = scale[1] = scale[2] = luaL_optnumber(L, index++, 1);
} else { } else {
sx = luaL_optnumber(L, index++, 1); for (int i = 0; i < scaleComponents; i++) {
sy = luaL_optnumber(L, index++, 1); scale[i] = luaL_optnumber(L, index++, 1);
sz = luaL_optnumber(L, index++, 1); }
} }
float angle = luaL_optnumber(L, index++, 0); float angle = luaL_optnumber(L, index++, 0);
float ax = luaL_optnumber(L, index++, 0); float ax = luaL_optnumber(L, index++, 0);
float ay = luaL_optnumber(L, index++, 1); float ay = luaL_optnumber(L, index++, 1);
float az = luaL_optnumber(L, index++, 0); float az = luaL_optnumber(L, index++, 0);
mat4_setTransform(m, x, y, z, sx, sy, sz, angle, ax, ay, az); mat4_setTransform(m, x, y, z, scale[0], scale[1], scale[2], angle, ax, ay, az);
return index; return index;
} else if (lua_isnoneornil(L, index)) { } else if (lua_isnoneornil(L, index)) {
mat4_identity(m); mat4_identity(m);
@ -33,15 +33,20 @@ int luax_readtransform(lua_State* L, int index, mat4 m, bool uniformScale) {
int l_lovrTransformGetMatrix(lua_State* L) { int l_lovrTransformGetMatrix(lua_State* L) {
Transform* transform = luax_checktype(L, 1, Transform); Transform* transform = luax_checktype(L, 1, Transform);
bool table = lua_istable(L, 2);
lua_settop(L, 2);
float matrix[16]; float matrix[16];
lovrTransformGetMatrix(transform, matrix); lovrTransformGetMatrix(transform, matrix);
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
lua_pushnumber(L, matrix[i]); lua_pushnumber(L, matrix[i]);
if (table) {
lua_rawseti(L, 2, i + 1);
}
} }
return 16; return table ? 1 : 16;
} }
int l_lovrTransformSetMatrix(lua_State* L) { int l_lovrTransformSetMatrix(lua_State* L) {
@ -67,16 +72,16 @@ int l_lovrTransformSetMatrix(lua_State* L) {
int l_lovrTransformClone(lua_State* L) { int l_lovrTransformClone(lua_State* L) {
Transform* transform = luax_checktype(L, 1, Transform); Transform* transform = luax_checktype(L, 1, Transform);
Transform* clone = lovrTransformCreate(transform->matrix); Transform* clone = lovrTransformCreate(transform->matrix);
luax_pushtype(L, Transform, clone); luax_pushobject(L, clone);
lovrRelease(&clone->ref); lovrRelease(clone);
return 1; return 1;
} }
int l_lovrTransformInverse(lua_State* L) { int l_lovrTransformInverse(lua_State* L) {
Transform* transform = luax_checktype(L, 1, Transform); Transform* transform = luax_checktype(L, 1, Transform);
Transform* inverse = lovrTransformCreate(lovrTransformInverse(transform)); Transform* inverse = lovrTransformCreate(lovrTransformInverse(transform));
luax_pushtype(L, Transform, inverse); luax_pushobject(L, inverse);
lovrRelease(&inverse->ref); lovrRelease(inverse);
return 1; return 1;
} }
@ -125,39 +130,59 @@ int l_lovrTransformScale(lua_State* L) {
return 1; return 1;
} }
int l_lovrTransformSetOrthographic(lua_State* L) {
Transform* transform = luax_checktype(L, 1, Transform);
float left = luaL_checknumber(L, 2);
float right = luaL_checknumber(L, 3);
float top = luaL_checknumber(L, 4);
float bottom = luaL_checknumber(L, 5);
float near = luaL_checknumber(L, 6);
float far = luaL_checknumber(L, 7);
mat4_orthographic(transform->matrix, left, right, top, bottom, near, far);
lua_pushvalue(L, 1);
return 1;
}
int l_lovrTransformSetPerspective(lua_State* L) {
Transform* transform = luax_checktype(L, 1, Transform);
float near = luaL_checknumber(L, 2);
float far = luaL_checknumber(L, 3);
float fov = luaL_checknumber(L, 4);
float aspect = luaL_checknumber(L, 5);
mat4_perspective(transform->matrix, near, far, fov, aspect);
lua_pushvalue(L, 1);
return 1;
}
int l_lovrTransformSetTransformation(lua_State* L) { int l_lovrTransformSetTransformation(lua_State* L) {
Transform* transform = luax_checktype(L, 1, Transform); Transform* transform = luax_checktype(L, 1, Transform);
lovrTransformOrigin(transform); // Dirty the Transform lovrTransformOrigin(transform); // Dirty the Transform
luax_readtransform(L, 2, transform->matrix, 0); luax_readtransform(L, 2, transform->matrix, 3);
lua_pushvalue(L, 1); lua_pushvalue(L, 1);
return 1; return 1;
} }
int l_lovrTransformTransformPoint(lua_State* L) { int l_lovrTransformTransformPoint(lua_State* L) {
Transform* transform = luax_checktype(L, 1, Transform); Transform* transform = luax_checktype(L, 1, Transform);
float point[3] = { float x = luaL_checknumber(L, 2);
luaL_checknumber(L, 2), float y = luaL_checknumber(L, 3);
luaL_checknumber(L, 3), float z = luaL_checknumber(L, 4);
luaL_checknumber(L, 4) lovrTransformTransformPoint(transform, &x, &y, &z);
}; lua_pushnumber(L, x);
lovrTransformTransformPoint(transform, point); lua_pushnumber(L, y);
lua_pushnumber(L, point[0]); lua_pushnumber(L, z);
lua_pushnumber(L, point[1]);
lua_pushnumber(L, point[2]);
return 3; return 3;
} }
int l_lovrTransformInverseTransformPoint(lua_State* L) { int l_lovrTransformInverseTransformPoint(lua_State* L) {
Transform* transform = luax_checktype(L, 1, Transform); Transform* transform = luax_checktype(L, 1, Transform);
float point[3] = { float x = luaL_checknumber(L, 2);
luaL_checknumber(L, 2), float y = luaL_checknumber(L, 3);
luaL_checknumber(L, 3), float z = luaL_checknumber(L, 4);
luaL_checknumber(L, 4) lovrTransformInverseTransformPoint(transform, &x, &y, &z);
}; lua_pushnumber(L, x);
lovrTransformInverseTransformPoint(transform, point); lua_pushnumber(L, y);
lua_pushnumber(L, point[0]); lua_pushnumber(L, z);
lua_pushnumber(L, point[1]);
lua_pushnumber(L, point[2]);
return 3; return 3;
} }
@ -171,7 +196,8 @@ const luaL_Reg lovrTransform[] = {
{ "translate", l_lovrTransformTranslate }, { "translate", l_lovrTransformTranslate },
{ "rotate", l_lovrTransformRotate }, { "rotate", l_lovrTransformRotate },
{ "scale", l_lovrTransformScale }, { "scale", l_lovrTransformScale },
{ "setTransformation", l_lovrTransformSetTransformation }, { "setOrthographic", l_lovrTransformSetOrthographic },
{ "setPerspective", l_lovrTransformSetPerspective },
{ "transformPoint", l_lovrTransformTransformPoint }, { "transformPoint", l_lovrTransformTransformPoint },
{ "inverseTransformPoint", l_lovrTransformInverseTransformPoint }, { "inverseTransformPoint", l_lovrTransformInverseTransformPoint },
{ NULL, NULL } { NULL, NULL }

View File

@ -1,18 +1,48 @@
#include "api.h" #include "api.h"
#include "api/data.h"
void luax_checkvertexformat(lua_State* L, int index, VertexFormat* format) { int luax_loadvertices(lua_State* L, int index, VertexFormat* format, VertexPointer vertices) {
uint32_t count = lua_objlen(L, index);
for (uint32_t i = 0; i < count; i++) {
lua_rawgeti(L, index, i + 1);
if (!lua_istable(L, -1)) {
return luaL_error(L, "Vertex information should be specified as a table");
}
int component = 0;
for (int j = 0; j < format->count; j++) {
Attribute attribute = format->attributes[j];
for (int k = 0; k < attribute.count; k++) {
lua_rawgeti(L, -1, ++component);
switch (attribute.type) {
case ATTR_FLOAT: *vertices.floats++ = luaL_optnumber(L, -1, 0.f); break;
case ATTR_BYTE: *vertices.bytes++ = luaL_optint(L, -1, 255); break;
case ATTR_INT: *vertices.ints++ = luaL_optint(L, -1, 0); break;
}
lua_pop(L, 1);
}
}
lua_pop(L, 1);
}
return 0;
}
bool luax_checkvertexformat(lua_State* L, int index, VertexFormat* format) {
if (!lua_istable(L, index)) { if (!lua_istable(L, index)) {
return; return false;
} }
int length = lua_objlen(L, index); int length = lua_objlen(L, index);
lovrAssert(length <= 8, "Only 8 vertex attributes are supported"); lovrAssert(length <= 8, "Up to 8 vertex attributes are supported");
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
lua_rawgeti(L, index, i + 1); lua_rawgeti(L, index, i + 1);
if (!lua_istable(L, -1) || lua_objlen(L, -1) != 3) { if (!lua_istable(L, -1) || lua_objlen(L, -1) != 3) {
luaL_error(L, "Expected vertex format specified as tables containing name, data type, and size"); luaL_error(L, "Expected vertex format specified as tables containing name, data type, and size");
return; return false;
} }
lua_rawgeti(L, -1, 1); lua_rawgeti(L, -1, 1);
@ -20,11 +50,14 @@ void luax_checkvertexformat(lua_State* L, int index, VertexFormat* format) {
lua_rawgeti(L, -3, 3); lua_rawgeti(L, -3, 3);
const char* name = lua_tostring(L, -3); const char* name = lua_tostring(L, -3);
AttributeType* type = (AttributeType*) luax_checkenum(L, -2, &AttributeTypes, "mesh attribute type"); AttributeType type = luaL_checkoption(L, -2, NULL, AttributeTypes);
int count = lua_tointeger(L, -1); int count = lua_tointeger(L, -1);
vertexFormatAppend(format, name, *type, count); lovrAssert(count >= 1 || count <= 4, "Vertex attribute counts must be between 1 and 4");
vertexFormatAppend(format, name, type, count);
lua_pop(L, 4); lua_pop(L, 4);
} }
return true;
} }
int luax_pushvertexformat(lua_State* L, VertexFormat* format) { int luax_pushvertexformat(lua_State* L, VertexFormat* format) {
@ -38,7 +71,7 @@ int luax_pushvertexformat(lua_State* L, VertexFormat* format) {
lua_rawseti(L, -2, 1); lua_rawseti(L, -2, 1);
// Type // Type
luax_pushenum(L, &AttributeTypes, attribute.type); lua_pushstring(L, AttributeTypes[attribute.type]);
lua_rawseti(L, -2, 2); lua_rawseti(L, -2, 2);
// Count // Count
@ -55,7 +88,7 @@ int luax_pushvertexattribute(lua_State* L, VertexPointer* vertex, Attribute attr
switch (attribute.type) { switch (attribute.type) {
case ATTR_FLOAT: lua_pushnumber(L, *vertex->floats++); break; case ATTR_FLOAT: lua_pushnumber(L, *vertex->floats++); break;
case ATTR_BYTE: lua_pushnumber(L, *vertex->bytes++); break; case ATTR_BYTE: lua_pushnumber(L, *vertex->bytes++); break;
case ATTR_INT: lua_pushnumber(L, *vertex->ints++); break; case ATTR_INT: lua_pushinteger(L, *vertex->ints++); break;
} }
} }
return attribute.count; return attribute.count;
@ -104,24 +137,6 @@ void luax_setvertex(lua_State* L, int index, VertexPointer* vertex, VertexFormat
// //
int l_lovrVertexDataGetPointer(lua_State* L) {
VertexData* vertexData = luax_checktype(L, 1, VertexData);
lua_pushlightuserdata(L, vertexData->data.raw);
return 1;
}
int l_lovrVertexDataGetSize(lua_State* L) {
VertexData* vertexData = luax_checktype(L, 1, VertexData);
lua_pushinteger(L, vertexData->count * vertexData->format.stride);
return 1;
}
int l_lovrVertexDataGetString(lua_State* L) {
VertexData* vertexData = luax_checktype(L, 1, VertexData);
lua_pushlstring(L, vertexData->data.raw, vertexData->count * vertexData->format.stride);
return 1;
}
int l_lovrVertexDataGetCount(lua_State* L) { int l_lovrVertexDataGetCount(lua_State* L) {
VertexData* vertexData = luax_checktype(L, 1, VertexData); VertexData* vertexData = luax_checktype(L, 1, VertexData);
uint32_t count = vertexData->count; uint32_t count = vertexData->count;
@ -137,7 +152,7 @@ int l_lovrVertexDataGetFormat(lua_State* L) {
int l_lovrVertexDataGetVertex(lua_State* L) { int l_lovrVertexDataGetVertex(lua_State* L) {
VertexData* vertexData = luax_checktype(L, 1, VertexData); VertexData* vertexData = luax_checktype(L, 1, VertexData);
uint32_t index = (uint32_t) luaL_checkint(L, 2) - 1; uint32_t index = (uint32_t) luaL_checkint(L, 2) - 1;
VertexPointer vertex = { .raw = vertexData->data.bytes + index * vertexData->format.stride }; VertexPointer vertex = { .raw = (uint8_t*) vertexData->blob.data + index * vertexData->format.stride };
return luax_pushvertex(L, &vertex, &vertexData->format); return luax_pushvertex(L, &vertex, &vertexData->format);
} }
@ -146,9 +161,9 @@ int l_lovrVertexDataSetVertex(lua_State* L) {
uint32_t index = (uint32_t) luaL_checkint(L, 2) - 1; uint32_t index = (uint32_t) luaL_checkint(L, 2) - 1;
lovrAssert(index < vertexData->count, "Invalid vertex index: %d", index + 1); lovrAssert(index < vertexData->count, "Invalid vertex index: %d", index + 1);
VertexFormat* format = &vertexData->format; VertexFormat* format = &vertexData->format;
VertexPointer* vertex = &vertexData->data; VertexPointer vertex = { .raw = vertexData->blob.data };
vertex->bytes += index * format->stride; vertex.bytes += index * format->stride;
luax_setvertex(L, 3, vertex, format); luax_setvertex(L, 3, &vertex, format);
return 0; return 0;
} }
@ -160,7 +175,7 @@ int l_lovrVertexDataGetVertexAttribute(lua_State* L) {
lovrAssert(vertexIndex < vertexData->count, "Invalid vertex index: %d", vertexIndex + 1); lovrAssert(vertexIndex < vertexData->count, "Invalid vertex index: %d", vertexIndex + 1);
lovrAssert(attributeIndex >= 0 && attributeIndex < format->count, "Invalid attribute index: %d", attributeIndex + 1); lovrAssert(attributeIndex >= 0 && attributeIndex < format->count, "Invalid attribute index: %d", attributeIndex + 1);
Attribute attribute = format->attributes[attributeIndex]; Attribute attribute = format->attributes[attributeIndex];
VertexPointer vertex = vertexData->data; VertexPointer vertex = { .raw = vertexData->blob.data };
vertex.bytes += vertexIndex * format->stride + attribute.offset; vertex.bytes += vertexIndex * format->stride + attribute.offset;
return luax_pushvertexattribute(L, &vertex, attribute); return luax_pushvertexattribute(L, &vertex, attribute);
} }
@ -173,7 +188,7 @@ int l_lovrVertexDataSetVertexAttribute(lua_State* L) {
lovrAssert(vertexIndex < vertexData->count, "Invalid vertex index: %d", vertexIndex + 1); lovrAssert(vertexIndex < vertexData->count, "Invalid vertex index: %d", vertexIndex + 1);
lovrAssert(attributeIndex >= 0 && attributeIndex < format->count, "Invalid attribute index: %d", attributeIndex + 1); lovrAssert(attributeIndex >= 0 && attributeIndex < format->count, "Invalid attribute index: %d", attributeIndex + 1);
Attribute attribute = format->attributes[attributeIndex]; Attribute attribute = format->attributes[attributeIndex];
VertexPointer vertex = vertexData->data; VertexPointer vertex = { .raw = vertexData->blob.data };
vertex.bytes += vertexIndex * format->stride + attribute.offset; vertex.bytes += vertexIndex * format->stride + attribute.offset;
luax_setvertexattribute(L, 4, &vertex, attribute); luax_setvertexattribute(L, 4, &vertex, attribute);
return 0; return 0;
@ -186,7 +201,7 @@ int l_lovrVertexDataSetVertices(lua_State* L) {
uint32_t vertexCount = lua_objlen(L, 2); uint32_t vertexCount = lua_objlen(L, 2);
int start = luaL_optnumber(L, 3, 1) - 1; int start = luaL_optnumber(L, 3, 1) - 1;
lovrAssert(start + vertexCount <= vertexData->count, "VertexData can only hold %d vertices", vertexData->count); lovrAssert(start + vertexCount <= vertexData->count, "VertexData can only hold %d vertices", vertexData->count);
VertexPointer vertices = vertexData->data; VertexPointer vertices = { .raw = vertexData->blob.data };
vertices.bytes += start * format->stride; vertices.bytes += start * format->stride;
for (uint32_t i = 0; i < vertexCount; i++) { for (uint32_t i = 0; i < vertexCount; i++) {
@ -200,9 +215,6 @@ int l_lovrVertexDataSetVertices(lua_State* L) {
} }
const luaL_Reg lovrVertexData[] = { const luaL_Reg lovrVertexData[] = {
{ "getPointer", l_lovrVertexDataGetPointer },
{ "getSize", l_lovrVertexDataGetSize },
{ "getString", l_lovrVertexDataGetString },
{ "getCount", l_lovrVertexDataGetCount }, { "getCount", l_lovrVertexDataGetCount },
{ "getFormat", l_lovrVertexDataGetFormat }, { "getFormat", l_lovrVertexDataGetFormat },
{ "getVertex", l_lovrVertexDataGetVertex }, { "getVertex", l_lovrVertexDataGetVertex },

View File

@ -5,7 +5,7 @@
static void collisionResolver(World* world, void* userdata) { static void collisionResolver(World* world, void* userdata) {
lua_State* L = userdata; lua_State* L = userdata;
luaL_checktype(L, -1, LUA_TFUNCTION); luaL_checktype(L, -1, LUA_TFUNCTION);
luax_pushtype(L, World, world); luax_pushobject(L, world);
lua_call(L, 1, 0); lua_call(L, 1, 0);
} }
@ -14,8 +14,8 @@ static int nextOverlap(lua_State* L) {
Shape* a; Shape* a;
Shape* b; Shape* b;
if (lovrWorldGetNextOverlap(world, &a, &b)) { if (lovrWorldGetNextOverlap(world, &a, &b)) {
luax_pushshape(L, a); luax_pushobject(L, a);
luax_pushshape(L, b); luax_pushobject(L, b);
return 2; return 2;
} else { } else {
lua_pushnil(L); lua_pushnil(L);
@ -27,7 +27,7 @@ static void raycastCallback(Shape* shape, float x, float y, float z, float nx, f
lua_State* L = userdata; lua_State* L = userdata;
luaL_checktype(L, -1, LUA_TFUNCTION); luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushvalue(L, -1); lua_pushvalue(L, -1);
luax_pushshape(L, shape); luax_pushobject(L, shape);
lua_pushnumber(L, x); lua_pushnumber(L, x);
lua_pushnumber(L, y); lua_pushnumber(L, y);
lua_pushnumber(L, z); lua_pushnumber(L, z);
@ -43,7 +43,8 @@ int l_lovrWorldNewCollider(lua_State* L) {
float y = luaL_optnumber(L, 3, 0); float y = luaL_optnumber(L, 3, 0);
float z = luaL_optnumber(L, 4, 0); float z = luaL_optnumber(L, 4, 0);
Collider* collider = lovrColliderCreate(world, x, y, z); Collider* collider = lovrColliderCreate(world, x, y, z);
luax_pushtype(L, Collider, collider); luax_pushobject(L, collider);
lovrRelease(collider);
return 1; return 1;
} }
@ -56,8 +57,11 @@ int l_lovrWorldNewBoxCollider(lua_State* L) {
float sy = luaL_optnumber(L, 6, sx); float sy = luaL_optnumber(L, 6, sx);
float sz = luaL_optnumber(L, 7, sx); float sz = luaL_optnumber(L, 7, sx);
Collider* collider = lovrColliderCreate(world, x, y, z); Collider* collider = lovrColliderCreate(world, x, y, z);
lovrColliderAddShape(collider, lovrBoxShapeCreate(sx, sy, sz)); BoxShape* shape = lovrBoxShapeCreate(sx, sy, sz);
luax_pushtype(L, Collider, collider); lovrColliderAddShape(collider, shape);
luax_pushobject(L, collider);
lovrRelease(collider);
lovrRelease(shape);
return 1; return 1;
} }
@ -69,8 +73,11 @@ int l_lovrWorldNewCapsuleCollider(lua_State* L) {
float radius = luaL_optnumber(L, 5, 1.f); float radius = luaL_optnumber(L, 5, 1.f);
float length = luaL_optnumber(L, 6, 1.f); float length = luaL_optnumber(L, 6, 1.f);
Collider* collider = lovrColliderCreate(world, x, y, z); Collider* collider = lovrColliderCreate(world, x, y, z);
lovrColliderAddShape(collider, lovrCapsuleShapeCreate(radius, length)); CapsuleShape* shape = lovrCapsuleShapeCreate(radius, length);
luax_pushtype(L, Collider, collider); lovrColliderAddShape(collider, shape);
luax_pushobject(L, collider);
lovrRelease(collider);
lovrRelease(shape);
return 1; return 1;
} }
@ -82,8 +89,11 @@ int l_lovrWorldNewCylinderCollider(lua_State* L) {
float radius = luaL_optnumber(L, 5, 1.f); float radius = luaL_optnumber(L, 5, 1.f);
float length = luaL_optnumber(L, 6, 1.f); float length = luaL_optnumber(L, 6, 1.f);
Collider* collider = lovrColliderCreate(world, x, y, z); Collider* collider = lovrColliderCreate(world, x, y, z);
lovrColliderAddShape(collider, lovrCylinderShapeCreate(radius, length)); CylinderShape* shape = lovrCylinderShapeCreate(radius, length);
luax_pushtype(L, Collider, collider); lovrColliderAddShape(collider, shape);
luax_pushobject(L, collider);
lovrRelease(collider);
lovrRelease(shape);
return 1; return 1;
} }
@ -94,8 +104,11 @@ int l_lovrWorldNewSphereCollider(lua_State* L) {
float z = luaL_optnumber(L, 4, 0); float z = luaL_optnumber(L, 4, 0);
float radius = luaL_optnumber(L, 5, 1.f); float radius = luaL_optnumber(L, 5, 1.f);
Collider* collider = lovrColliderCreate(world, x, y, z); Collider* collider = lovrColliderCreate(world, x, y, z);
lovrColliderAddShape(collider, lovrSphereShapeCreate(radius)); SphereShape* shape = lovrSphereShapeCreate(radius);
luax_pushtype(L, Collider, collider); lovrColliderAddShape(collider, shape);
luax_pushobject(L, collider);
lovrRelease(collider);
lovrRelease(shape);
return 1; return 1;
} }
@ -129,8 +142,8 @@ int l_lovrWorldOverlaps(lua_State* L) {
int l_lovrWorldCollide(lua_State* L) { int l_lovrWorldCollide(lua_State* L) {
World* world = luax_checktype(L, 1, World); World* world = luax_checktype(L, 1, World);
Shape* a = luax_checktypeof(L, 2, Shape); Shape* a = luax_checktype(L, 2, Shape);
Shape* b = luax_checktypeof(L, 3, Shape); Shape* b = luax_checktype(L, 3, Shape);
float friction = luaL_optnumber(L, 4, -1); float friction = luaL_optnumber(L, 4, -1);
float restitution = luaL_optnumber(L, 5, -1); float restitution = luaL_optnumber(L, 5, -1);
lua_pushboolean(L, lovrWorldCollide(world, a, b, friction, restitution)); lua_pushboolean(L, lovrWorldCollide(world, a, b, friction, restitution));

View File

@ -3,15 +3,25 @@
#include "math/quat.h" #include "math/quat.h"
#include "util.h" #include "util.h"
#include <stdlib.h> #include <stdlib.h>
#include "lovr.h"
static AudioState state; static AudioState state;
static bool audioAlreadyInit = false;
ALenum lovrAudioConvertFormat(int bitDepth, int channelCount) {
if (bitDepth == 8 && channelCount == 1) {
return AL_FORMAT_MONO8;
} else if (bitDepth == 8 && channelCount == 2) {
return AL_FORMAT_STEREO8;
} else if (bitDepth == 16 && channelCount == 1) {
return AL_FORMAT_MONO16;
} else if (bitDepth == 16 && channelCount == 2) {
return AL_FORMAT_STEREO16;
}
return 0;
}
void lovrAudioInit() { void lovrAudioInit() {
if (audioAlreadyInit) { // During a reload, bring down the audio device then recreate it if (state.initialized) return;
lovrAudioDestroy();
}
ALCdevice* device = alcOpenDevice(NULL); ALCdevice* device = alcOpenDevice(NULL);
lovrAssert(device, "Unable to open default audio device"); lovrAssert(device, "Unable to open default audio device");
@ -26,8 +36,7 @@ void lovrAudioInit() {
state.isSpatialized = alcIsExtensionPresent(device, "ALC_SOFT_HRTF"); state.isSpatialized = alcIsExtensionPresent(device, "ALC_SOFT_HRTF");
if (state.isSpatialized) { if (state.isSpatialized) {
ALCint attrs[3] = { ALC_HRTF_SOFT, ALC_TRUE, 0 }; alcResetDeviceSOFT(device, (ALCint[]) { ALC_HRTF_SOFT, ALC_TRUE, 0 });
alcResetDeviceSOFT(device, attrs);
} }
state.device = device; state.device = device;
@ -36,23 +45,28 @@ void lovrAudioInit() {
quat_set(state.orientation, 0, 0, 0, -1); quat_set(state.orientation, 0, 0, 0, -1);
vec3_set(state.position, 0, 0, 0); vec3_set(state.position, 0, 0, 0);
vec3_set(state.velocity, 0, 0, 0); vec3_set(state.velocity, 0, 0, 0);
state.initialized = true;
if (!audioAlreadyInit) {
atexit(lovrAudioDestroy);
audioAlreadyInit = true;
}
} }
void lovrAudioDestroy() { void lovrAudioDestroy() {
if (!state.initialized) return;
alcMakeContextCurrent(NULL); alcMakeContextCurrent(NULL);
alcDestroyContext(state.context); alcDestroyContext(state.context);
alcCloseDevice(state.device); alcCloseDevice(state.device);
for (int i = 0; i < state.sources.length; i++) {
lovrRelease(state.sources.data[i]);
}
vec_deinit(&state.sources); vec_deinit(&state.sources);
memset(&state, 0, sizeof(AudioState));
} }
void lovrAudioUpdate() { void lovrAudioUpdate() {
int i; Source* source; int i; Source* source;
vec_foreach_rev(&state.sources, source, i) { vec_foreach_rev(&state.sources, source, i) {
if (source->type == SOURCE_STATIC) {
continue;
}
bool isStopped = lovrSourceIsStopped(source); bool isStopped = lovrSourceIsStopped(source);
ALint processed; ALint processed;
alGetSourcei(source->id, AL_BUFFERS_PROCESSED, &processed); alGetSourcei(source->id, AL_BUFFERS_PROCESSED, &processed);
@ -67,18 +81,32 @@ void lovrAudioUpdate() {
} else if (isStopped) { } else if (isStopped) {
lovrAudioStreamRewind(source->stream); lovrAudioStreamRewind(source->stream);
vec_splice(&state.sources, i, 1); vec_splice(&state.sources, i, 1);
lovrRelease(&source->ref); lovrRelease(source);
} }
} }
} }
void lovrAudioAdd(Source* source) { void lovrAudioAdd(Source* source) {
if (!lovrAudioHas(source)) { if (!lovrAudioHas(source)) {
lovrRetain(&source->ref); lovrRetain(source);
vec_push(&state.sources, source); vec_push(&state.sources, source);
} }
} }
void lovrAudioGetDopplerEffect(float* factor, float* speedOfSound) {
alGetFloatv(AL_DOPPLER_FACTOR, factor);
alGetFloatv(AL_SPEED_OF_SOUND, speedOfSound);
}
void lovrAudioGetMicrophoneNames(const char* names[MAX_MICROPHONES], uint8_t* count) {
const char* name = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
*count = 0;
while (*name) {
names[(*count)++] = name;
name += strlen(name);
}
}
void lovrAudioGetOrientation(float* angle, float* ax, float* ay, float* az) { void lovrAudioGetOrientation(float* angle, float* ax, float* ay, float* az) {
quat_getAngleAxis(state.orientation, angle, ax, ay, az); quat_getAngleAxis(state.orientation, angle, ax, ay, az);
} }
@ -132,13 +160,17 @@ void lovrAudioRewind() {
} }
} }
void lovrAudioSetDopplerEffect(float factor, float speedOfSound) {
alDopplerFactor(factor);
alSpeedOfSound(speedOfSound);
}
void lovrAudioSetOrientation(float angle, float ax, float ay, float az) { void lovrAudioSetOrientation(float angle, float ax, float ay, float az) {
// Rotate the unit forward/up vectors by the quaternion derived from the specified angle/axis // Rotate the unit forward/up vectors by the quaternion derived from the specified angle/axis
float f[3] = { 0, 0, -1 }; float f[3] = { 0, 0, -1 };
float u[3] = { 0, 1, 0 }; float u[3] = { 0, 1, 0 };
float axis[3] = { ax, ay, az }; quat_fromAngleAxis(state.orientation, angle, (float[3]) { ax, ay, az });
quat_fromAngleAxis(state.orientation, angle, axis);
quat_rotate(state.orientation, f); quat_rotate(state.orientation, f);
quat_rotate(state.orientation, u); quat_rotate(state.orientation, u);

View File

@ -1,4 +1,5 @@
#include "audio/source.h" #include "audio/source.h"
#include "audio/microphone.h"
#include "lib/vec/vec.h" #include "lib/vec/vec.h"
#include <AL/al.h> #include <AL/al.h>
#include <AL/alc.h> #include <AL/alc.h>
@ -7,20 +8,27 @@
#pragma once #pragma once
#define MAX_MICROPHONES 8
typedef struct { typedef struct {
bool initialized;
ALCdevice* device; ALCdevice* device;
ALCcontext* context; ALCcontext* context;
vec_void_t sources; vec_void_t sources;
bool isSpatialized; bool isSpatialized;
float orientation[4]; float orientation[4];
float position[3]; float position[3];
float velocity[4]; float velocity[3];
} AudioState; } AudioState;
ALenum lovrAudioConvertFormat(int bitDepth, int channelCount);
void lovrAudioInit(); void lovrAudioInit();
void lovrAudioDestroy(); void lovrAudioDestroy();
void lovrAudioUpdate(); void lovrAudioUpdate();
void lovrAudioAdd(Source* source); void lovrAudioAdd(Source* source);
void lovrAudioGetDopplerEffect(float* factor, float* speedOfSound);
void lovrAudioGetMicrophoneNames(const char* names[MAX_MICROPHONES], uint8_t* count);
void lovrAudioGetOrientation(float* angle, float* ax, float* ay, float* az); void lovrAudioGetOrientation(float* angle, float* ax, float* ay, float* az);
void lovrAudioGetPosition(float* x, float* y, float* z); void lovrAudioGetPosition(float* x, float* y, float* z);
void lovrAudioGetVelocity(float* x, float* y, float* z); void lovrAudioGetVelocity(float* x, float* y, float* z);
@ -30,6 +38,7 @@ bool lovrAudioIsSpatialized();
void lovrAudioPause(); void lovrAudioPause();
void lovrAudioResume(); void lovrAudioResume();
void lovrAudioRewind(); void lovrAudioRewind();
void lovrAudioSetDopplerEffect(float factor, float speedOfSound);
void lovrAudioSetOrientation(float angle, float ax, float ay, float az); void lovrAudioSetOrientation(float angle, float ax, float ay, float az);
void lovrAudioSetPosition(float x, float y, float z); void lovrAudioSetPosition(float x, float y, float z);
void lovrAudioSetVelocity(float x, float y, float z); void lovrAudioSetVelocity(float x, float y, float z);

92
src/audio/microphone.c Normal file
View File

@ -0,0 +1,92 @@
#include "audio/microphone.h"
#include "audio/audio.h"
Microphone* lovrMicrophoneCreate(const char* name, int samples, int sampleRate, int bitDepth, int channelCount) {
Microphone* microphone = lovrAlloc(Microphone, lovrMicrophoneDestroy);
if (!microphone) return NULL;
ALCdevice* device = alcCaptureOpenDevice(name, sampleRate, lovrAudioConvertFormat(bitDepth, channelCount), samples);
if (!device) {
free(microphone);
return NULL;
}
microphone->device = device;
microphone->name = name ? name : alcGetString(device, ALC_CAPTURE_DEVICE_SPECIFIER);
microphone->sampleRate = sampleRate;
microphone->bitDepth = bitDepth;
microphone->channelCount = channelCount;
return microphone;
}
void lovrMicrophoneDestroy(void* ref) {
Microphone* microphone = ref;
lovrMicrophoneStopRecording(microphone);
alcCaptureCloseDevice(microphone->device);
free(microphone);
}
int lovrMicrophoneGetBitDepth(Microphone* microphone) {
return microphone->bitDepth;
}
int lovrMicrophoneGetChannelCount(Microphone* microphone) {
return microphone->channelCount;
}
SoundData* lovrMicrophoneGetData(Microphone* microphone) {
if (!microphone->isRecording) {
return NULL;
}
int samples = lovrMicrophoneGetSampleCount(microphone);
if (samples == 0) {
return NULL;
}
SoundData* soundData = lovrSoundDataCreate(samples, microphone->sampleRate, microphone->bitDepth, microphone->channelCount);
alcCaptureSamples(microphone->device, soundData->blob.data, samples);
return soundData;
}
const char* lovrMicrophoneGetName(Microphone* microphone) {
return microphone->name;
}
int lovrMicrophoneGetSampleCount(Microphone* microphone) {
if (!microphone->isRecording) {
return 0;
}
ALCint samples;
alcGetIntegerv(microphone->device, ALC_CAPTURE_SAMPLES, sizeof(ALCint), &samples);
return (int) samples;
}
int lovrMicrophoneGetSampleRate(Microphone* microphone) {
return microphone->sampleRate;
}
bool lovrMicrophoneIsRecording(Microphone* microphone) {
return microphone->isRecording;
}
void lovrMicrophoneStartRecording(Microphone* microphone) {
if (microphone->isRecording) {
return;
}
alcCaptureStart(microphone->device);
microphone->isRecording = true;
}
void lovrMicrophoneStopRecording(Microphone* microphone) {
if (!microphone->isRecording) {
return;
}
alcCaptureStop(microphone->device);
microphone->isRecording = false;
}

29
src/audio/microphone.h Normal file
View File

@ -0,0 +1,29 @@
#include "util.h"
#include "data/soundData.h"
#include <AL/al.h>
#include <AL/alc.h>
#include <stdbool.h>
#pragma once
typedef struct {
Ref ref;
ALCdevice* device;
bool isRecording;
const char* name;
int sampleRate;
int bitDepth;
int channelCount;
} Microphone;
Microphone* lovrMicrophoneCreate(const char* name, int samples, int sampleRate, int bitDepth, int channelCount);
void lovrMicrophoneDestroy(void* ref);
int lovrMicrophoneGetBitDepth(Microphone* microphone);
int lovrMicrophoneGetChannelCount(Microphone* microphone);
SoundData* lovrMicrophoneGetData(Microphone* microphone);
const char* lovrMicrophoneGetName(Microphone* microphone);
int lovrMicrophoneGetSampleCount(Microphone* microphone);
int lovrMicrophoneGetSampleRate(Microphone* microphone);
bool lovrMicrophoneIsRecording(Microphone* microphone);
void lovrMicrophoneStartRecording(Microphone* microphone);
void lovrMicrophoneStopRecording(Microphone* microphone);

View File

@ -1,55 +1,60 @@
#include "audio/source.h" #include "audio/source.h"
#include "audio/audio.h"
#include "data/audioStream.h" #include "data/audioStream.h"
#define _USE_MATH_DEFINES #define _USE_MATH_DEFINES
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
static ALenum lovrSourceGetFormat(Source* source) {
int channelCount = source->stream->channelCount;
int bitDepth = source->stream->bitDepth;
if (bitDepth == 8 && channelCount == 1) {
return AL_FORMAT_MONO8;
} else if (bitDepth == 8 && channelCount == 2) {
return AL_FORMAT_STEREO8;
} else if (bitDepth == 16 && channelCount == 1) {
return AL_FORMAT_MONO16;
} else if (bitDepth == 16 && channelCount == 2) {
return AL_FORMAT_STEREO16;
}
return 0;
}
static ALenum lovrSourceGetState(Source* source) { static ALenum lovrSourceGetState(Source* source) {
ALenum state; ALenum state;
alGetSourcei(source->id, AL_SOURCE_STATE, &state); alGetSourcei(source->id, AL_SOURCE_STATE, &state);
return state; return state;
} }
Source* lovrSourceCreate(AudioStream* stream) { Source* lovrSourceCreateStatic(SoundData* soundData) {
Source* source = lovrAlloc(sizeof(Source), lovrSourceDestroy); Source* source = lovrAlloc(Source, lovrSourceDestroy);
if (!source) return NULL; if (!source) return NULL;
source->stream = stream; ALenum format = lovrAudioConvertFormat(soundData->bitDepth, soundData->channelCount);
source->isLooping = false; source->type = SOURCE_STATIC;
source->soundData = soundData;
alGenSources(1, &source->id); alGenSources(1, &source->id);
alGenBuffers(SOURCE_BUFFERS, source->buffers); alGenBuffers(1, source->buffers);
lovrRetain(&stream->ref); alBufferData(source->buffers[0], format, soundData->blob.data, soundData->blob.size, soundData->sampleRate);
alSourcei(source->id, AL_BUFFER, source->buffers[0]);
lovrRetain(soundData);
return source; return source;
} }
void lovrSourceDestroy(const Ref* ref) { Source* lovrSourceCreateStream(AudioStream* stream) {
Source* source = containerof(ref, Source); Source* source = lovrAlloc(Source, lovrSourceDestroy);
if (!source) return NULL;
source->type = SOURCE_STREAM;
source->stream = stream;
alGenSources(1, &source->id);
alGenBuffers(SOURCE_BUFFERS, source->buffers);
lovrRetain(stream);
return source;
}
void lovrSourceDestroy(void* ref) {
Source* source = ref;
alDeleteSources(1, &source->id); alDeleteSources(1, &source->id);
alDeleteBuffers(SOURCE_BUFFERS, source->buffers); alDeleteBuffers(source->type == SOURCE_STATIC ? 1 : SOURCE_BUFFERS, source->buffers);
lovrRelease(&source->stream->ref); lovrRelease(source->soundData);
lovrRelease(source->stream);
free(source); free(source);
} }
SourceType lovrSourceGetType(Source* source) {
return source->type;
}
int lovrSourceGetBitDepth(Source* source) { int lovrSourceGetBitDepth(Source* source) {
return source->stream->bitDepth; return source->type == SOURCE_STATIC ? source->soundData->bitDepth : source->stream->bitDepth;
} }
void lovrSourceGetCone(Source* source, float* innerAngle, float* outerAngle, float* outerGain) { void lovrSourceGetCone(Source* source, float* innerAngle, float* outerAngle, float* outerGain) {
@ -61,7 +66,7 @@ void lovrSourceGetCone(Source* source, float* innerAngle, float* outerAngle, flo
} }
int lovrSourceGetChannelCount(Source* source) { int lovrSourceGetChannelCount(Source* source) {
return source->stream->channelCount; return source->type == SOURCE_STATIC ? source->soundData->channelCount : source->stream->channelCount;
} }
void lovrSourceGetDirection(Source* source, float* x, float* y, float* z) { void lovrSourceGetDirection(Source* source, float* x, float* y, float* z) {
@ -73,7 +78,7 @@ void lovrSourceGetDirection(Source* source, float* x, float* y, float* z) {
} }
int lovrSourceGetDuration(Source* source) { int lovrSourceGetDuration(Source* source) {
return source->stream->samples; return source->type == SOURCE_STATIC ? source->soundData->samples : source->stream->samples;
} }
void lovrSourceGetFalloff(Source* source, float* reference, float* max, float* rolloff) { void lovrSourceGetFalloff(Source* source, float* reference, float* max, float* rolloff) {
@ -97,7 +102,7 @@ void lovrSourceGetPosition(Source* source, float* x, float* y, float* z) {
} }
int lovrSourceGetSampleRate(Source* source) { int lovrSourceGetSampleRate(Source* source) {
return source->stream->sampleRate; return source->type == SOURCE_STATIC ? source->soundData->sampleRate : source->stream->sampleRate;
} }
void lovrSourceGetVelocity(Source* source, float* x, float* y, float* z) { void lovrSourceGetVelocity(Source* source, float* x, float* y, float* z) {
@ -180,12 +185,21 @@ void lovrSourceRewind(Source* source) {
} }
void lovrSourceSeek(Source* source, int sample) { void lovrSourceSeek(Source* source, int sample) {
bool wasPaused = lovrSourceIsPaused(source); switch (source->type) {
lovrSourceStop(source); case SOURCE_STATIC:
lovrAudioStreamSeek(source->stream, sample); alSourcef(source->id, AL_SAMPLE_OFFSET, sample);
lovrSourcePlay(source); break;
if (wasPaused) {
lovrSourcePause(source); case SOURCE_STREAM: {
bool wasPaused = lovrSourceIsPaused(source);
lovrSourceStop(source);
lovrAudioStreamSeek(source->stream, sample);
lovrSourcePlay(source);
if (wasPaused) {
lovrSourcePause(source);
}
break;
}
} }
} }
@ -208,6 +222,9 @@ void lovrSourceSetFalloff(Source* source, float reference, float max, float roll
void lovrSourceSetLooping(Source* source, bool isLooping) { void lovrSourceSetLooping(Source* source, bool isLooping) {
source->isLooping = isLooping; source->isLooping = isLooping;
if (source->type == SOURCE_STATIC) {
alSourcei(source->id, AL_LOOPING, isLooping ? AL_TRUE : AL_FALSE);
}
} }
void lovrSourceSetPitch(Source* source, float pitch) { void lovrSourceSetPitch(Source* source, float pitch) {
@ -241,29 +258,43 @@ void lovrSourceStop(Source* source) {
return; return;
} }
// Empty the buffers switch (source->type) {
int count = 0; case SOURCE_STATIC:
alGetSourcei(source->id, AL_BUFFERS_QUEUED, &count); alSourceStop(source->id);
alSourceUnqueueBuffers(source->id, count, NULL); break;
// Stop the source case SOURCE_STREAM: {
alSourceStop(source->id);
alSourcei(source->id, AL_BUFFER, AL_NONE);
// Rewind the decoder // Empty the buffers
lovrAudioStreamRewind(source->stream); int count = 0;
alGetSourcei(source->id, AL_BUFFERS_QUEUED, &count);
alSourceUnqueueBuffers(source->id, count, NULL);
// Stop the source
alSourceStop(source->id);
alSourcei(source->id, AL_BUFFER, AL_NONE);
// Rewind the decoder
lovrAudioStreamRewind(source->stream);
break;
}
}
} }
// Fills buffers with data and queues them, called once initially and over time to stream more data // Fills buffers with data and queues them, called once initially and over time to stream more data
void lovrSourceStream(Source* source, ALuint* buffers, int count) { void lovrSourceStream(Source* source, ALuint* buffers, int count) {
if (source->type == SOURCE_STATIC) {
return;
}
AudioStream* stream = source->stream; AudioStream* stream = source->stream;
ALenum format = lovrSourceGetFormat(source); ALenum format = lovrAudioConvertFormat(stream->bitDepth, stream->channelCount);
int frequency = stream->sampleRate; int frequency = stream->sampleRate;
int samples = 0; int samples = 0;
int n = 0; int n = 0;
// Keep decoding until there is nothing left to decode or all the buffers are filled // Keep decoding until there is nothing left to decode or all the buffers are filled
while (n < count && (samples = lovrAudioStreamDecode(stream)) != 0) { while (n < count && (samples = lovrAudioStreamDecode(stream, NULL, 0)) != 0) {
alBufferData(buffers[n++], format, stream->buffer, samples * sizeof(ALshort), frequency); alBufferData(buffers[n++], format, stream->buffer, samples * sizeof(ALshort), frequency);
} }
@ -277,17 +308,28 @@ void lovrSourceStream(Source* source, ALuint* buffers, int count) {
} }
int lovrSourceTell(Source* source) { int lovrSourceTell(Source* source) {
int decoderOffset = lovrAudioStreamTell(source->stream); switch (source->type) {
int samplesPerBuffer = source->stream->bufferSize / source->stream->channelCount / sizeof(ALshort); case SOURCE_STATIC: {
int queuedBuffers, sampleOffset; float offset;
alGetSourcei(source->id, AL_BUFFERS_QUEUED, &queuedBuffers); alGetSourcef(source->id, AL_SAMPLE_OFFSET, &offset);
alGetSourcei(source->id, AL_SAMPLE_OFFSET, &sampleOffset); return offset;
}
int offset = decoderOffset - queuedBuffers * samplesPerBuffer + sampleOffset; case SOURCE_STREAM: {
int decoderOffset = lovrAudioStreamTell(source->stream);
int samplesPerBuffer = source->stream->bufferSize / source->stream->channelCount / sizeof(ALshort);
int queuedBuffers, sampleOffset;
alGetSourcei(source->id, AL_BUFFERS_QUEUED, &queuedBuffers);
alGetSourcei(source->id, AL_SAMPLE_OFFSET, &sampleOffset);
if (offset < 0) { int offset = decoderOffset - queuedBuffers * samplesPerBuffer + sampleOffset;
return offset + source->stream->samples;
} else { if (offset < 0) {
return offset; return offset + source->stream->samples;
} else {
return offset;
}
break;
}
} }
} }

View File

@ -1,4 +1,5 @@
#include "data/audioStream.h" #include "data/audioStream.h"
#include "data/soundData.h"
#include "util.h" #include "util.h"
#include <AL/al.h> #include <AL/al.h>
#include <AL/alc.h> #include <AL/alc.h>
@ -8,6 +9,11 @@
#define SOURCE_BUFFERS 4 #define SOURCE_BUFFERS 4
typedef enum {
SOURCE_STATIC,
SOURCE_STREAM
} SourceType;
typedef enum { typedef enum {
UNIT_SECONDS, UNIT_SECONDS,
UNIT_SAMPLES UNIT_SAMPLES
@ -15,14 +21,18 @@ typedef enum {
typedef struct { typedef struct {
Ref ref; Ref ref;
SourceType type;
SoundData* soundData;
AudioStream* stream; AudioStream* stream;
ALuint id; ALuint id;
ALuint buffers[SOURCE_BUFFERS]; ALuint buffers[SOURCE_BUFFERS];
bool isLooping; bool isLooping;
} Source; } Source;
Source* lovrSourceCreate(AudioStream* stream); Source* lovrSourceCreateStatic(SoundData* soundData);
void lovrSourceDestroy(const Ref* ref); Source* lovrSourceCreateStream(AudioStream* stream);
void lovrSourceDestroy(void* ref);
SourceType lovrSourceGetType(Source* source);
int lovrSourceGetBitDepth(Source* source); int lovrSourceGetBitDepth(Source* source);
int lovrSourceGetChannelCount(Source* source); int lovrSourceGetChannelCount(Source* source);
void lovrSourceGetCone(Source* source, float* innerAngle, float* outerAngle, float* outerGain); void lovrSourceGetCone(Source* source, float* innerAngle, float* outerAngle, float* outerGain);

View File

@ -4,7 +4,7 @@
#include <stdlib.h> #include <stdlib.h>
AudioStream* lovrAudioStreamCreate(Blob* blob, size_t bufferSize) { AudioStream* lovrAudioStreamCreate(Blob* blob, size_t bufferSize) {
AudioStream* stream = lovrAlloc(sizeof(AudioStream), lovrAudioStreamDestroy); AudioStream* stream = lovrAlloc(AudioStream, lovrAudioStreamDestroy);
if (!stream) return NULL; if (!stream) return NULL;
stb_vorbis* decoder = stb_vorbis_open_memory(blob->data, blob->size, NULL, NULL); stb_vorbis* decoder = stb_vorbis_open_memory(blob->data, blob->size, NULL, NULL);
@ -24,24 +24,24 @@ AudioStream* lovrAudioStreamCreate(Blob* blob, size_t bufferSize) {
stream->bufferSize = stream->channelCount * bufferSize * sizeof(short); stream->bufferSize = stream->channelCount * bufferSize * sizeof(short);
stream->buffer = malloc(stream->bufferSize); stream->buffer = malloc(stream->bufferSize);
stream->blob = blob; stream->blob = blob;
lovrRetain(&blob->ref); lovrRetain(blob);
return stream; return stream;
} }
void lovrAudioStreamDestroy(const Ref* ref) { void lovrAudioStreamDestroy(void* ref) {
AudioStream* stream = containerof(ref, AudioStream); AudioStream* stream = ref;
stb_vorbis_close(stream->decoder); stb_vorbis_close(stream->decoder);
lovrRelease(&stream->blob->ref); lovrRelease(stream->blob);
free(stream->buffer); free(stream->buffer);
free(stream); free(stream);
} }
int lovrAudioStreamDecode(AudioStream* stream) { int lovrAudioStreamDecode(AudioStream* stream, short* destination, size_t size) {
stb_vorbis* decoder = (stb_vorbis*) stream->decoder; stb_vorbis* decoder = (stb_vorbis*) stream->decoder;
short* buffer = (short*) stream->buffer; short* buffer = destination ? destination : (short*) stream->buffer;
int capacity = destination ? size : (stream->bufferSize / sizeof(short));
int channelCount = stream->channelCount; int channelCount = stream->channelCount;
int capacity = stream->bufferSize / sizeof(short);
int samples = 0; int samples = 0;
while (samples < capacity) { while (samples < capacity) {

View File

@ -1,4 +1,4 @@
#include "filesystem/blob.h" #include "data/blob.h"
#include "util.h" #include "util.h"
#pragma once #pragma once
@ -16,8 +16,8 @@ typedef struct {
} AudioStream; } AudioStream;
AudioStream* lovrAudioStreamCreate(Blob* blob, size_t bufferSize); AudioStream* lovrAudioStreamCreate(Blob* blob, size_t bufferSize);
void lovrAudioStreamDestroy(const Ref* ref); void lovrAudioStreamDestroy(void* ref);
int lovrAudioStreamDecode(AudioStream* stream); int lovrAudioStreamDecode(AudioStream* stream, short* destination, size_t size);
void lovrAudioStreamRewind(AudioStream* stream); void lovrAudioStreamRewind(AudioStream* stream);
void lovrAudioStreamSeek(AudioStream* stream, int sample); void lovrAudioStreamSeek(AudioStream* stream, int sample);
int lovrAudioStreamTell(AudioStream* stream); int lovrAudioStreamTell(AudioStream* stream);

View File

@ -1,19 +1,18 @@
#include "filesystem/blob.h" #include "data/blob.h"
Blob* lovrBlobCreate(void* data, size_t size, const char* name) { Blob* lovrBlobCreate(void* data, size_t size, const char* name) {
Blob* blob = lovrAlloc(sizeof(Blob), lovrBlobDestroy); Blob* blob = lovrAlloc(Blob, lovrBlobDestroy);
if (!blob) return NULL; if (!blob) return NULL;
blob->data = data; blob->data = data;
blob->size = size; blob->size = size;
blob->name = name; blob->name = name;
blob->seek = 0;
return blob; return blob;
} }
void lovrBlobDestroy(const Ref* ref) { void lovrBlobDestroy(void* ref) {
Blob* blob = containerof(ref, Blob); Blob* blob = ref;
free(blob->data); free(blob->data);
free(blob); free(blob);
} }

View File

@ -1,4 +1,5 @@
#include "util.h" #include "util.h"
#include <stdlib.h>
#pragma once #pragma once
@ -11,4 +12,4 @@ typedef struct {
} Blob; } Blob;
Blob* lovrBlobCreate(void* data, size_t size, const char* name); Blob* lovrBlobCreate(void* data, size_t size, const char* name);
void lovrBlobDestroy(const Ref* ref); void lovrBlobDestroy(void* ref);

View File

@ -1,5 +0,0 @@
#include "data/data.h"
void lovrDataInit() {
//
}

View File

@ -1,3 +0,0 @@
#pragma once
void lovrDataInit();

View File

@ -1,7 +1,6 @@
#include "data/modelData.h" #include "data/modelData.h"
#include "filesystem/filesystem.h" #include "filesystem/filesystem.h"
#include "filesystem/file.h" #include "filesystem/file.h"
#include "math/math.h"
#include "math/mat4.h" #include "math/mat4.h"
#include "math/quat.h" #include "math/quat.h"
#include "math/vec3.h" #include "math/vec3.h"
@ -9,6 +8,9 @@
#include <limits.h> #include <limits.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#ifdef LOVR_USE_ASSIMP
#include <assimp/cfileio.h> #include <assimp/cfileio.h>
#include <assimp/cimport.h> #include <assimp/cimport.h>
#include <assimp/config.h> #include <assimp/config.h>
@ -93,6 +95,35 @@ static void assimpNodeTraversal(ModelData* modelData, struct aiNode* assimpNode,
} }
} }
static void aabbIterator(ModelData* modelData, ModelNode* node, float aabb[6]) {
for (int i = 0; i < node->primitives.length; i++) {
ModelPrimitive* primitive = &modelData->primitives[node->primitives.data[i]];
for (int j = 0; j < primitive->drawCount; j++) {
uint32_t index;
if (modelData->indexSize == sizeof(uint16_t)) {
index = modelData->indices.shorts[primitive->drawStart + j];
} else {
index = modelData->indices.ints[primitive->drawStart + j];
}
float vertex[3];
VertexPointer vertices = { .raw = modelData->vertexData->blob.data };
vec3_init(vertex, (float*) (vertices.bytes + index * modelData->vertexData->format.stride));
mat4_transform(node->globalTransform, &vertex[0], &vertex[1], &vertex[2]);
aabb[0] = MIN(aabb[0], vertex[0]);
aabb[1] = MAX(aabb[1], vertex[0]);
aabb[2] = MIN(aabb[2], vertex[1]);
aabb[3] = MAX(aabb[3], vertex[1]);
aabb[4] = MIN(aabb[4], vertex[2]);
aabb[5] = MAX(aabb[5], vertex[2]);
}
}
for (int i = 0; i < node->children.length; i++) {
ModelNode* child = &modelData->nodes[node->children.data[i]];
aabbIterator(modelData, child, aabb);
}
}
static float readMaterialScalar(struct aiMaterial* assimpMaterial, const char* key, unsigned int type, unsigned int index) { static float readMaterialScalar(struct aiMaterial* assimpMaterial, const char* key, unsigned int type, unsigned int index) {
float scalar; float scalar;
if (aiGetMaterialFloatArray(assimpMaterial, key, type, index, &scalar, NULL) == aiReturn_SUCCESS) { if (aiGetMaterialFloatArray(assimpMaterial, key, type, index, &scalar, NULL) == aiReturn_SUCCESS) {
@ -102,21 +133,16 @@ static float readMaterialScalar(struct aiMaterial* assimpMaterial, const char* k
} }
} }
static Color readMaterialColor(struct aiMaterial* assimpMaterial, const char* key, unsigned int type, unsigned int index) { static Color readMaterialColor(struct aiMaterial* assimpMaterial, const char* key, unsigned int type, unsigned int index, Color fallback) {
struct aiColor4D assimpColor; struct aiColor4D assimpColor;
if (aiGetMaterialColor(assimpMaterial, key, type, index, &assimpColor) == aiReturn_SUCCESS) { if (aiGetMaterialColor(assimpMaterial, key, type, index, &assimpColor) == aiReturn_SUCCESS) {
Color color; return (Color) { .r = assimpColor.r, .g = assimpColor.g, .b = assimpColor.b, .a = assimpColor.a };
color.r = assimpColor.r;
color.g = assimpColor.g;
color.b = assimpColor.b;
color.a = assimpColor.a;
return color;
} else { } else {
return (Color) { 1, 1, 1, 1 }; return fallback;
} }
} }
static TextureData* readMaterialTexture(struct aiMaterial* assimpMaterial, enum aiTextureType type, ModelData* modelData, map_int_t* textureCache, const char* dirname) { static int readMaterialTexture(struct aiMaterial* assimpMaterial, enum aiTextureType type, ModelData* modelData, map_int_t* textureCache, const char* dirname) {
struct aiString str; struct aiString str;
if (aiGetMaterialTexture(assimpMaterial, type, 0, &str, NULL, NULL, NULL, NULL, NULL, NULL) != aiReturn_SUCCESS) { if (aiGetMaterialTexture(assimpMaterial, type, 0, &str, NULL, NULL, NULL, NULL, NULL, NULL) != aiReturn_SUCCESS) {
@ -125,7 +151,7 @@ static TextureData* readMaterialTexture(struct aiMaterial* assimpMaterial, enum
char* path = str.data; char* path = str.data;
int* cachedTexture = (TextureData**) map_get(textureCache, path); int* cachedTexture = map_get(textureCache, path);
if (cachedTexture) { if (cachedTexture) {
return *cachedTexture; return *cachedTexture;
} }
@ -135,7 +161,8 @@ static TextureData* readMaterialTexture(struct aiMaterial* assimpMaterial, enum
strncpy(fullPath, dirname, LOVR_PATH_MAX); strncpy(fullPath, dirname, LOVR_PATH_MAX);
char* lastSlash = strrchr(fullPath, '/'); char* lastSlash = strrchr(fullPath, '/');
if (lastSlash) lastSlash[1] = '\0'; if (lastSlash) lastSlash[1] = '\0';
strncat(fullPath, path, LOVR_PATH_MAX); else fullPath[0] = '\0';
strncat(fullPath, path, LOVR_PATH_MAX - 1);
normalizePath(fullPath, normalizedPath, LOVR_PATH_MAX); normalizePath(fullPath, normalizedPath, LOVR_PATH_MAX);
size_t size; size_t size;
@ -145,7 +172,8 @@ static TextureData* readMaterialTexture(struct aiMaterial* assimpMaterial, enum
} }
Blob* blob = lovrBlobCreate(data, size, path); Blob* blob = lovrBlobCreate(data, size, path);
TextureData* textureData = lovrTextureDataFromBlob(blob); TextureData* textureData = lovrTextureDataCreateFromBlob(blob, true);
lovrRelease(blob);
int textureIndex = modelData->textures.length; int textureIndex = modelData->textures.length;
vec_push(&modelData->textures, textureData); vec_push(&modelData->textures, textureData);
map_set(textureCache, path, textureIndex); map_set(textureCache, path, textureIndex);
@ -200,7 +228,7 @@ static aiReturn assimpFileSeek(struct aiFile* assimpFile, size_t position, enum
return lovrFileSeek(file, position) ? aiReturn_FAILURE : aiReturn_SUCCESS; return lovrFileSeek(file, position) ? aiReturn_FAILURE : aiReturn_SUCCESS;
} }
static unsigned long assimpFileTell(struct aiFile* assimpFile) { static size_t assimpFileTell(struct aiFile* assimpFile) {
File* file = (File*) assimpFile->UserData; File* file = (File*) assimpFile->UserData;
return lovrFileTell(file); return lovrFileTell(file);
} }
@ -223,6 +251,7 @@ static struct aiFile* assimpFileOpen(struct aiFileIO* io, const char* path, cons
File* file = lovrFileCreate(normalizedPath); File* file = lovrFileCreate(normalizedPath);
if (lovrFileOpen(file, OPEN_READ)) { if (lovrFileOpen(file, OPEN_READ)) {
lovrRelease(file);
return NULL; return NULL;
} }
@ -241,13 +270,13 @@ static void assimpFileClose(struct aiFileIO* io, struct aiFile* assimpFile) {
if (assimpFile->UserData != blob) { if (assimpFile->UserData != blob) {
File* file = (File*) assimpFile->UserData; File* file = (File*) assimpFile->UserData;
lovrFileClose(file); lovrFileClose(file);
lovrRelease(&file->ref); lovrRelease(file);
} }
free(assimpFile); free(assimpFile);
} }
ModelData* lovrModelDataCreate(Blob* blob) { ModelData* lovrModelDataCreate(Blob* blob) {
ModelData* modelData = lovrAlloc(sizeof(ModelData), lovrModelDataDestroy); ModelData* modelData = lovrAlloc(ModelData, lovrModelDataDestroy);
if (!modelData) return NULL; if (!modelData) return NULL;
struct aiFileIO assimpIO; struct aiFileIO assimpIO;
@ -266,13 +295,11 @@ ModelData* lovrModelDataCreate(Blob* blob) {
lovrThrow("Unable to load model from '%s': %s\n", blob->name, aiGetErrorString()); lovrThrow("Unable to load model from '%s': %s\n", blob->name, aiGetErrorString());
} }
modelData->nodeCount = 0;
modelData->indexCount = 0;
uint32_t vertexCount = 0; uint32_t vertexCount = 0;
bool hasNormals = false; bool hasNormals = false;
bool hasUVs = false; bool hasUVs = false;
bool hasVertexColors = false; bool hasVertexColors = false;
bool hasTangents = false;
bool isSkinned = false; bool isSkinned = false;
for (unsigned int m = 0; m < scene->mNumMeshes; m++) { for (unsigned int m = 0; m < scene->mNumMeshes; m++) {
@ -282,6 +309,7 @@ ModelData* lovrModelDataCreate(Blob* blob) {
hasNormals |= assimpMesh->mNormals != NULL; hasNormals |= assimpMesh->mNormals != NULL;
hasUVs |= assimpMesh->mTextureCoords[0] != NULL; hasUVs |= assimpMesh->mTextureCoords[0] != NULL;
hasVertexColors |= assimpMesh->mColors[0] != NULL; hasVertexColors |= assimpMesh->mColors[0] != NULL;
hasTangents |= assimpMesh->mTangents != NULL;
isSkinned |= assimpMesh->mNumBones > 0; isSkinned |= assimpMesh->mNumBones > 0;
} }
@ -292,12 +320,13 @@ ModelData* lovrModelDataCreate(Blob* blob) {
if (hasNormals) vertexFormatAppend(&format, "lovrNormal", ATTR_FLOAT, 3); if (hasNormals) vertexFormatAppend(&format, "lovrNormal", ATTR_FLOAT, 3);
if (hasUVs) vertexFormatAppend(&format, "lovrTexCoord", ATTR_FLOAT, 2); if (hasUVs) vertexFormatAppend(&format, "lovrTexCoord", ATTR_FLOAT, 2);
if (hasVertexColors) vertexFormatAppend(&format, "lovrVertexColor", ATTR_BYTE, 4); if (hasVertexColors) vertexFormatAppend(&format, "lovrVertexColor", ATTR_BYTE, 4);
if (hasTangents) vertexFormatAppend(&format, "lovrTangent", ATTR_FLOAT, 3);
size_t boneByteOffset = format.stride; size_t boneByteOffset = format.stride;
if (isSkinned) vertexFormatAppend(&format, "lovrBones", ATTR_INT, 4); if (isSkinned) vertexFormatAppend(&format, "lovrBones", ATTR_INT, 4);
if (isSkinned) vertexFormatAppend(&format, "lovrBoneWeights", ATTR_FLOAT, 4); if (isSkinned) vertexFormatAppend(&format, "lovrBoneWeights", ATTR_FLOAT, 4);
// Allocate // Allocate
modelData->vertexData = lovrVertexDataCreate(vertexCount, &format, true); modelData->vertexData = lovrVertexDataCreate(vertexCount, &format);
modelData->indexSize = vertexCount > USHRT_MAX ? sizeof(uint32_t) : sizeof(uint16_t); modelData->indexSize = vertexCount > USHRT_MAX ? sizeof(uint32_t) : sizeof(uint16_t);
modelData->indices.raw = malloc(modelData->indexCount * modelData->indexSize); modelData->indices.raw = malloc(modelData->indexCount * modelData->indexSize);
modelData->primitiveCount = scene->mNumMeshes; modelData->primitiveCount = scene->mNumMeshes;
@ -335,7 +364,7 @@ ModelData* lovrModelDataCreate(Blob* blob) {
// Vertices // Vertices
for (unsigned int v = 0; v < assimpMesh->mNumVertices; v++) { for (unsigned int v = 0; v < assimpMesh->mNumVertices; v++) {
VertexPointer vertices = modelData->vertexData->data; VertexPointer vertices = { .raw = modelData->vertexData->blob.data };
vertices.bytes += vertex * modelData->vertexData->format.stride; vertices.bytes += vertex * modelData->vertexData->format.stride;
*vertices.floats++ = assimpMesh->mVertices[v].x; *vertices.floats++ = assimpMesh->mVertices[v].x;
@ -378,6 +407,18 @@ ModelData* lovrModelDataCreate(Blob* blob) {
} }
} }
if (hasTangents) {
if (assimpMesh->mTangents) {
*vertices.floats++ = assimpMesh->mTangents[v].x;
*vertices.floats++ = assimpMesh->mTangents[v].y;
*vertices.floats++ = assimpMesh->mTangents[v].z;
} else {
*vertices.floats++ = 0;
*vertices.floats++ = 0;
*vertices.floats++ = 0;
}
}
vertex++; vertex++;
} }
@ -396,7 +437,7 @@ ModelData* lovrModelDataCreate(Blob* blob) {
for (unsigned int w = 0; w < assimpBone->mNumWeights; w++) { for (unsigned int w = 0; w < assimpBone->mNumWeights; w++) {
uint32_t vertexIndex = baseVertex + assimpBone->mWeights[w].mVertexId; uint32_t vertexIndex = baseVertex + assimpBone->mWeights[w].mVertexId;
float weight = assimpBone->mWeights[w].mWeight; float weight = assimpBone->mWeights[w].mWeight;
VertexPointer vertices = modelData->vertexData->data; VertexPointer vertices = { .raw = modelData->vertexData->blob.data };
vertices.bytes += vertexIndex * modelData->vertexData->format.stride; vertices.bytes += vertexIndex * modelData->vertexData->format.stride;
uint32_t* bones = (uint32_t*) (vertices.bytes + boneByteOffset); uint32_t* bones = (uint32_t*) (vertices.bytes + boneByteOffset);
float* weights = (float*) (bones + MAX_BONES_PER_VERTEX); float* weights = (float*) (bones + MAX_BONES_PER_VERTEX);
@ -424,8 +465,8 @@ ModelData* lovrModelDataCreate(Blob* blob) {
ModelMaterial* material = &modelData->materials[m]; ModelMaterial* material = &modelData->materials[m];
struct aiMaterial* assimpMaterial = scene->mMaterials[m]; struct aiMaterial* assimpMaterial = scene->mMaterials[m];
material->diffuseColor = readMaterialColor(assimpMaterial, AI_MATKEY_COLOR_DIFFUSE); material->diffuseColor = readMaterialColor(assimpMaterial, AI_MATKEY_COLOR_DIFFUSE, (Color) { 1., 1., 1., 1. });
material->emissiveColor = readMaterialColor(assimpMaterial, AI_MATKEY_COLOR_EMISSIVE); material->emissiveColor = readMaterialColor(assimpMaterial, AI_MATKEY_COLOR_EMISSIVE, (Color) { 0., 0., 0., 0. });
material->diffuseTexture = readMaterialTexture(assimpMaterial, aiTextureType_DIFFUSE, modelData, &textureCache, blob->name); material->diffuseTexture = readMaterialTexture(assimpMaterial, aiTextureType_DIFFUSE, modelData, &textureCache, blob->name);
material->emissiveTexture = readMaterialTexture(assimpMaterial, aiTextureType_EMISSIVE, modelData, &textureCache, blob->name); material->emissiveTexture = readMaterialTexture(assimpMaterial, aiTextureType_EMISSIVE, modelData, &textureCache, blob->name);
material->metalnessTexture = readMaterialTexture(assimpMaterial, aiTextureType_UNKNOWN, modelData, &textureCache, blob->name); material->metalnessTexture = readMaterialTexture(assimpMaterial, aiTextureType_UNKNOWN, modelData, &textureCache, blob->name);
@ -471,28 +512,28 @@ ModelData* lovrModelDataCreate(Blob* blob) {
for (unsigned int k = 0; k < assimpChannel->mNumPositionKeys; k++) { for (unsigned int k = 0; k < assimpChannel->mNumPositionKeys; k++) {
struct aiVectorKey assimpKeyframe = assimpChannel->mPositionKeys[k]; struct aiVectorKey assimpKeyframe = assimpChannel->mPositionKeys[k];
struct aiVector3D position = assimpKeyframe.mValue; struct aiVector3D position = assimpKeyframe.mValue;
Keyframe keyframe; vec_push(&channel.positionKeyframes, ((Keyframe) {
keyframe.time = assimpKeyframe.mTime / ticksPerSecond; .time = assimpKeyframe.mTime / ticksPerSecond,
vec3_set(keyframe.data, position.x, position.y, position.z); .data = { position.x, position.y, position.z }
vec_push(&channel.positionKeyframes, keyframe); }));
} }
for (unsigned int k = 0; k < assimpChannel->mNumRotationKeys; k++) { for (unsigned int k = 0; k < assimpChannel->mNumRotationKeys; k++) {
struct aiQuatKey assimpKeyframe = assimpChannel->mRotationKeys[k]; struct aiQuatKey assimpKeyframe = assimpChannel->mRotationKeys[k];
struct aiQuaternion quaternion = assimpKeyframe.mValue; struct aiQuaternion quaternion = assimpKeyframe.mValue;
Keyframe keyframe; vec_push(&channel.rotationKeyframes, ((Keyframe) {
keyframe.time = assimpKeyframe.mTime / ticksPerSecond; .time = assimpKeyframe.mTime / ticksPerSecond,
quat_set(keyframe.data, quaternion.x, quaternion.y, quaternion.z, quaternion.w); .data = { quaternion.x, quaternion.y, quaternion.z, quaternion.w }
vec_push(&channel.rotationKeyframes, keyframe); }));
} }
for (unsigned int k = 0; k < assimpChannel->mNumScalingKeys; k++) { for (unsigned int k = 0; k < assimpChannel->mNumScalingKeys; k++) {
struct aiVectorKey assimpKeyframe = assimpChannel->mScalingKeys[k]; struct aiVectorKey assimpKeyframe = assimpChannel->mScalingKeys[k];
struct aiVector3D scale = assimpKeyframe.mValue; struct aiVector3D scale = assimpKeyframe.mValue;
Keyframe keyframe; vec_push(&channel.scaleKeyframes, ((Keyframe) {
keyframe.time = assimpKeyframe.mTime / ticksPerSecond; .time = assimpKeyframe.mTime / ticksPerSecond,
vec3_set(keyframe.data, scale.x, scale.y, scale.z); .data = { scale.x, scale.y, scale.z }
vec_push(&channel.scaleKeyframes, keyframe); }));
} }
map_set(&animation->channels, channel.node, channel); map_set(&animation->channels, channel.node, channel);
@ -502,17 +543,32 @@ ModelData* lovrModelDataCreate(Blob* blob) {
aiReleaseImport(scene); aiReleaseImport(scene);
return modelData; return modelData;
} }
#else
static void aabbIterator(ModelData* modelData, ModelNode* node, float aabb[6]) {}
ModelData* lovrModelDataCreate(Blob* blob) {
return NULL;
}
#endif
void lovrModelDataDestroy(const Ref* ref) { ModelData* lovrModelDataCreateEmpty() {
ModelData* modelData = containerof(ref, ModelData); return lovrAlloc(ModelData, lovrModelDataDestroy);
}
void lovrModelDataDestroy(void* ref) {
ModelData* modelData = ref;
for (int i = 0; i < modelData->nodeCount; i++) { for (int i = 0; i < modelData->nodeCount; i++) {
vec_deinit(&modelData->nodes[i].children); vec_deinit(&modelData->nodes[i].children);
vec_deinit(&modelData->nodes[i].primitives); vec_deinit(&modelData->nodes[i].primitives);
free((char*) modelData->nodes[i].name);
} }
for (int i = 0; i < modelData->primitiveCount; i++) { for (int i = 0; i < modelData->primitiveCount; i++) {
map_deinit(&modelData->primitives[i].boneMap); ModelPrimitive* primitive = &modelData->primitives[i];
for (int j = 0; j < primitive->boneCount; j++) {
free((char*) primitive->bones[j].name);
}
map_deinit(&primitive->boneMap);
} }
for (int i = 0; i < modelData->animationCount; i++) { for (int i = 0; i < modelData->animationCount; i++) {
@ -526,19 +582,17 @@ void lovrModelDataDestroy(const Ref* ref) {
vec_deinit(&channel->scaleKeyframes); vec_deinit(&channel->scaleKeyframes);
} }
map_deinit(&animation->channels); map_deinit(&animation->channels);
free((char*) animation->name);
} }
for (int i = 0; i < modelData->textures.length; i++) { for (int i = 0; i < modelData->textures.length; i++) {
TextureData* textureData = modelData->textures.data[i]; lovrRelease(modelData->textures.data[i]);
if (textureData) {
lovrRelease(&textureData->ref);
}
} }
vec_deinit(&modelData->textures); vec_deinit(&modelData->textures);
map_deinit(&modelData->nodeMap); map_deinit(&modelData->nodeMap);
lovrRelease(&modelData->vertexData->ref); lovrRelease(modelData->vertexData);
free(modelData->nodes); free(modelData->nodes);
free(modelData->primitives); free(modelData->primitives);
@ -548,44 +602,12 @@ void lovrModelDataDestroy(const Ref* ref) {
free(modelData); free(modelData);
} }
static void aabbIterator(ModelData* modelData, ModelNode* node, float aabb[6], mat4 transform) {
mat4_multiply(transform, node->transform);
for (int i = 0; i < node->primitives.length; i++) {
ModelPrimitive* primitive = &modelData->primitives[node->primitives.data[i]];
for (int j = 0; j < primitive->drawCount; j++) {
float vertex[3];
uint32_t index;
if (modelData->indexSize == sizeof(uint16_t)) {
index = modelData->indices.shorts[primitive->drawStart + j];
} else {
index = modelData->indices.ints[primitive->drawStart + j];
}
vec3_init(vertex, (float*) (modelData->vertexData->data.bytes + index * modelData->vertexData->format.stride));
mat4_transform(transform, vertex);
aabb[0] = MIN(aabb[0], vertex[0]);
aabb[1] = MAX(aabb[1], vertex[0]);
aabb[2] = MIN(aabb[2], vertex[1]);
aabb[3] = MAX(aabb[3], vertex[1]);
aabb[4] = MIN(aabb[4], vertex[2]);
aabb[5] = MAX(aabb[5], vertex[2]);
}
}
for (int i = 0; i < node->children.length; i++) {
ModelNode* child = &modelData->nodes[node->children.data[i]];
aabbIterator(modelData, child, aabb, transform);
}
}
void lovrModelDataGetAABB(ModelData* modelData, float aabb[6]) { void lovrModelDataGetAABB(ModelData* modelData, float aabb[6]) {
float transform[16];
mat4_identity(transform);
aabb[0] = FLT_MAX; aabb[0] = FLT_MAX;
aabb[1] = -FLT_MAX; aabb[1] = -FLT_MAX;
aabb[2] = FLT_MAX; aabb[2] = FLT_MAX;
aabb[3] = -FLT_MAX; aabb[3] = -FLT_MAX;
aabb[4] = FLT_MAX; aabb[4] = FLT_MAX;
aabb[5] = -FLT_MAX; aabb[5] = -FLT_MAX;
aabbIterator(modelData, &modelData->nodes[0], aabb, transform); aabbIterator(modelData, &modelData->nodes[0], aabb);
} }

View File

@ -1,4 +1,4 @@
#include "filesystem/blob.h" #include "data/blob.h"
#include "data/textureData.h" #include "data/textureData.h"
#include "data/vertexData.h" #include "data/vertexData.h"
#include "util.h" #include "util.h"
@ -24,6 +24,8 @@ typedef struct {
int boneCount; int boneCount;
} ModelPrimitive; } ModelPrimitive;
typedef vec_t(unsigned int) vec_uint_t;
typedef struct ModelNode { typedef struct ModelNode {
const char* name; const char* name;
float transform[16]; float transform[16];
@ -88,5 +90,6 @@ typedef struct {
} ModelData; } ModelData;
ModelData* lovrModelDataCreate(Blob* blob); ModelData* lovrModelDataCreate(Blob* blob);
void lovrModelDataDestroy(const Ref* ref); ModelData* lovrModelDataCreateEmpty();
void lovrModelDataDestroy(void* ref);
void lovrModelDataGetAABB(ModelData* modelData, float aabb[6]); void lovrModelDataGetAABB(ModelData* modelData, float aabb[6]);

View File

@ -1,109 +1,47 @@
#include "data/rasterizer.h" #include "data/rasterizer.h"
#include "resources/Cabin.ttf.h" #include "resources/Cabin.ttf.h"
#include "util.h" #include "util.h"
#include "lib/stb/stb_truetype.h"
#include "msdfgen-c.h" #include "msdfgen-c.h"
#include <ft2build.h> #include <math.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_OUTLINE_H
static FT_Library ft = NULL;
typedef struct {
float x;
float y;
msShape* shape;
msContour* contour;
} ftContext;
static int ftMoveTo(const FT_Vector* to, void* userdata) {
ftContext* context = userdata;
context->contour = msShapeAddContour(context->shape);
context->x = to->x / 64.;
context->y = to->y / 64.;
return 0;
}
static int ftLineTo(const FT_Vector* to, void* userdata) {
ftContext* context = userdata;
float x = to->x / 64.;
float y = to->y / 64.;
msContourAddLinearEdge(context->contour, context->x, context->y, x, y);
context->x = x;
context->y = y;
return 0;
}
static int ftConicTo(const FT_Vector* control, const FT_Vector* to, void* userdata) {
ftContext* context = userdata;
float cx = control->x / 64.;
float cy = control->y / 64.;
float x = to->x / 64.;
float y = to->y / 64.;
msContourAddQuadraticEdge(context->contour, context->x, context->y, cx, cy, x, y);
context->x = x;
context->y = y;
return 0;
}
static int ftCubicTo(const FT_Vector* control1, const FT_Vector* control2, const FT_Vector* to, void* userdata) {
ftContext* context = userdata;
float c1x = control1->x / 64.;
float c1y = control1->y / 64.;
float c2x = control2->x / 64.;
float c2y = control2->y / 64.;
float x = to->x / 64.;
float y = to->y / 64.;
msContourAddCubicEdge(context->contour, context->x, context->y, c1x, c1y, c2x, c2y, x, y);
context->x = x;
context->y = y;
return 0;
}
Rasterizer* lovrRasterizerCreate(Blob* blob, int size) { Rasterizer* lovrRasterizerCreate(Blob* blob, int size) {
if (!ft && FT_Init_FreeType(&ft)) { Rasterizer* rasterizer = lovrAlloc(Rasterizer, lovrRasterizerDestroy);
lovrThrow("Error initializing FreeType"); if (!rasterizer) return NULL;
stbtt_fontinfo* font = &rasterizer->font;
unsigned char* data = blob ? blob->data : Cabin_ttf;
if (!stbtt_InitFont(font, data, stbtt_GetFontOffsetForIndex(data, 0))) {
lovrThrow("Problem loading font");
} }
FT_Face face = NULL; lovrRetain(blob);
FT_Error err = FT_Err_Ok;
if (blob) {
err = err || FT_New_Memory_Face(ft, blob->data, blob->size, 0, &face);
lovrRetain(&blob->ref);
} else {
err = err || FT_New_Memory_Face(ft, Cabin_ttf, Cabin_ttf_len, 0, &face);
}
err = err || FT_Set_Pixel_Sizes(face, 0, size);
lovrAssert(!err, "Problem loading font");
Rasterizer* rasterizer = lovrAlloc(sizeof(Rasterizer), lovrRasterizerDestroy);
rasterizer->ftHandle = face;
rasterizer->blob = blob; rasterizer->blob = blob;
rasterizer->size = size; rasterizer->size = size;
rasterizer->glyphCount = face->num_glyphs; rasterizer->scale = stbtt_ScaleForMappingEmToPixels(font, size);
rasterizer->glyphCount = font->numGlyphs;
FT_Size_Metrics metrics = face->size->metrics; int ascent, descent, linegap;
rasterizer->height = metrics.height >> 6; stbtt_GetFontVMetrics(font, &ascent, &descent, &linegap);
rasterizer->advance = metrics.max_advance >> 6; rasterizer->ascent = roundf(ascent * rasterizer->scale);
rasterizer->ascent = metrics.ascender >> 6; rasterizer->descent = roundf(descent * rasterizer->scale);
rasterizer->descent = metrics.descender >> 6; rasterizer->height = roundf((ascent - descent + linegap) * rasterizer->scale);
int x0, y0, x1, y1;
stbtt_GetFontBoundingBox(font, &x0, &y0, &x1, &y1);
rasterizer->advance = roundf(x1 * rasterizer->scale);
return rasterizer; return rasterizer;
} }
void lovrRasterizerDestroy(const Ref* ref) { void lovrRasterizerDestroy(void* ref) {
Rasterizer* rasterizer = containerof(ref, Rasterizer); Rasterizer* rasterizer = ref;
FT_Done_Face(rasterizer->ftHandle); lovrRelease(rasterizer->blob);
if (rasterizer->blob) {
lovrRelease(&rasterizer->blob->ref);
}
free(rasterizer); free(rasterizer);
} }
bool lovrRasterizerHasGlyph(Rasterizer* rasterizer, uint32_t character) { bool lovrRasterizerHasGlyph(Rasterizer* rasterizer, uint32_t character) {
FT_Face face = rasterizer->ftHandle; return stbtt_FindGlyphIndex(&rasterizer->font, character) != 0;
return FT_Get_Char_Index(face, character) != 0;
} }
bool lovrRasterizerHasGlyphs(Rasterizer* rasterizer, const char* str) { bool lovrRasterizerHasGlyphs(Rasterizer* rasterizer, const char* str) {
@ -121,51 +59,81 @@ bool lovrRasterizerHasGlyphs(Rasterizer* rasterizer, const char* str) {
} }
void lovrRasterizerLoadGlyph(Rasterizer* rasterizer, uint32_t character, Glyph* glyph) { void lovrRasterizerLoadGlyph(Rasterizer* rasterizer, uint32_t character, Glyph* glyph) {
FT_Face face = rasterizer->ftHandle; int glyphIndex = stbtt_FindGlyphIndex(&rasterizer->font, character);
FT_Error err = FT_Err_Ok; lovrAssert(glyphIndex, "Error loading glyph");
FT_Glyph_Metrics* metrics;
FT_Outline_Funcs callbacks = {
.move_to = ftMoveTo,
.line_to = ftLineTo,
.conic_to = ftConicTo,
.cubic_to = ftCubicTo
};
// Trace glyph outline
stbtt_vertex* vertices;
int vertexCount = stbtt_GetGlyphShape(&rasterizer->font, glyphIndex, &vertices);
msShape* shape = msShapeCreate(); msShape* shape = msShapeCreate();
ftContext context = { .x = 0, .y = 0, .shape = shape, .contour = NULL }; msContour* contour = NULL;
float x = 0;
float y = 0;
err = err || FT_Load_Glyph(face, FT_Get_Char_Index(face, character), FT_LOAD_DEFAULT); for (int i = 0; i < vertexCount; i++) {
err = err || FT_Outline_Decompose(&face->glyph->outline, &callbacks, &context); stbtt_vertex vertex = vertices[i];
lovrAssert(!err, "Error loading glyph"); float x2 = vertex.x * rasterizer->scale;
float y2 = vertex.y * rasterizer->scale;
metrics = &face->glyph->metrics; switch (vertex.type) {
case STBTT_vmove:
contour = msShapeAddContour(shape);
break;
// Initialize glyph case STBTT_vline:
msContourAddLinearEdge(contour, x, y, x2, y2);
break;
case STBTT_vcurve: {
float cx = vertex.cx * rasterizer->scale;
float cy = vertex.cy * rasterizer->scale;
msContourAddQuadraticEdge(contour, x, y, cx, cy, x2, y2);
break;
}
case STBTT_vcubic: {
float cx1 = vertex.cx * rasterizer->scale;
float cy1 = vertex.cy * rasterizer->scale;
float cx2 = vertex.cx1 * rasterizer->scale;
float cy2 = vertex.cy1 * rasterizer->scale;
msContourAddCubicEdge(contour, x, y, cx1, cy1, cx2, cy2, x2, y2);
break;
}
}
x = x2;
y = y2;
}
int advance, bearing;
stbtt_GetGlyphHMetrics(&rasterizer->font, glyphIndex, &advance, &bearing);
int x0, y0, x1, y1;
stbtt_GetGlyphBox(&rasterizer->font, glyphIndex, &x0, &y0, &x1, &y1);
bool empty = stbtt_IsGlyphEmpty(&rasterizer->font, glyphIndex);
// Initialize glyph data
glyph->x = 0; glyph->x = 0;
glyph->y = 0; glyph->y = 0;
glyph->w = metrics->width >> 6; glyph->w = empty ? 0 : ceilf((x1 - x0) * rasterizer->scale);
glyph->h = metrics->height >> 6; glyph->h = empty ? 0 : ceilf((y1 - y0) * rasterizer->scale);
glyph->tw = glyph->w + 2 * GLYPH_PADDING; glyph->tw = glyph->w + 2 * GLYPH_PADDING;
glyph->th = glyph->h + 2 * GLYPH_PADDING; glyph->th = glyph->h + 2 * GLYPH_PADDING;
glyph->dx = metrics->horiBearingX >> 6; glyph->dx = empty ? 0 : roundf(bearing * rasterizer->scale);
glyph->dy = metrics->horiBearingY >> 6; glyph->dy = empty ? 0 : roundf(y1 * rasterizer->scale);
glyph->advance = metrics->horiAdvance >> 6; glyph->advance = roundf(advance * rasterizer->scale);
glyph->data = malloc(glyph->tw * glyph->th * 3 * sizeof(uint8_t)); glyph->data = lovrTextureDataCreate(glyph->tw, glyph->th, 0, FORMAT_RGB);
// Render SDF // Render SDF
float tx = GLYPH_PADDING + -glyph->dx; float tx = GLYPH_PADDING + -glyph->dx;
float ty = GLYPH_PADDING + glyph->h - glyph->dy; float ty = GLYPH_PADDING + glyph->h - glyph->dy;
msShapeNormalize(shape); msShapeNormalize(shape);
msEdgeColoringSimple(shape, 3.0, 0); msEdgeColoringSimple(shape, 3.0, 0);
msGenerateMSDF(glyph->data, glyph->tw, glyph->th, shape, 4., 1, 1, tx, ty); msGenerateMSDF(glyph->data->blob.data, glyph->tw, glyph->th, shape, 4., 1, 1, tx, ty);
msShapeDestroy(shape); msShapeDestroy(shape);
} }
int lovrRasterizerGetKerning(Rasterizer* rasterizer, uint32_t left, uint32_t right) { int lovrRasterizerGetKerning(Rasterizer* rasterizer, uint32_t left, uint32_t right) {
FT_Face face = rasterizer->ftHandle; return stbtt_GetCodepointKernAdvance(&rasterizer->font, left, right) * rasterizer->scale;
FT_Vector kerning;
left = FT_Get_Char_Index(face, left);
right = FT_Get_Char_Index(face, right);
FT_Get_Kerning(face, left, right, FT_KERNING_DEFAULT, &kerning);
return kerning.x >> 6;
} }

View File

@ -1,5 +1,7 @@
#include "filesystem/blob.h" #include "data/blob.h"
#include "data/textureData.h"
#include "lib/map/map.h" #include "lib/map/map.h"
#include "lib/stb/stb_truetype.h"
#include "util.h" #include "util.h"
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
@ -10,9 +12,10 @@
typedef struct { typedef struct {
Ref ref; Ref ref;
void* ftHandle; stbtt_fontinfo font;
Blob* blob; Blob* blob;
int size; int size;
float scale;
int glyphCount; int glyphCount;
int height; int height;
int advance; int advance;
@ -30,13 +33,13 @@ typedef struct {
int dx; int dx;
int dy; int dy;
int advance; int advance;
uint8_t* data; TextureData* data;
} Glyph; } Glyph;
typedef map_t(Glyph) map_glyph_t; typedef map_t(Glyph) map_glyph_t;
Rasterizer* lovrRasterizerCreate(Blob* blob, int size); Rasterizer* lovrRasterizerCreate(Blob* blob, int size);
void lovrRasterizerDestroy(const Ref* ref); void lovrRasterizerDestroy(void* ref);
bool lovrRasterizerHasGlyph(Rasterizer* fontData, uint32_t character); bool lovrRasterizerHasGlyph(Rasterizer* fontData, uint32_t character);
bool lovrRasterizerHasGlyphs(Rasterizer* fontData, const char* str); bool lovrRasterizerHasGlyphs(Rasterizer* fontData, const char* str);
void lovrRasterizerLoadGlyph(Rasterizer* fontData, uint32_t character, Glyph* glyph); void lovrRasterizerLoadGlyph(Rasterizer* fontData, uint32_t character, Glyph* glyph);

72
src/data/soundData.c Normal file
View File

@ -0,0 +1,72 @@
#include "data/soundData.h"
#include "lib/stb/stb_vorbis.h"
#include <limits.h>
SoundData* lovrSoundDataCreate(int samples, int sampleRate, int bitDepth, int channelCount) {
SoundData* soundData = lovrAlloc(SoundData, lovrSoundDataDestroy);
if (!soundData) return NULL;
soundData->samples = samples;
soundData->sampleRate = sampleRate;
soundData->bitDepth = bitDepth;
soundData->channelCount = channelCount;
soundData->blob.size = samples * channelCount * (bitDepth / 8);
soundData->blob.data = calloc(1, soundData->blob.size);
return soundData;
}
SoundData* lovrSoundDataCreateFromAudioStream(AudioStream* audioStream) {
SoundData* soundData = lovrAlloc(SoundData, lovrSoundDataDestroy);
if (!soundData) return NULL;
soundData->samples = audioStream->samples;
soundData->sampleRate = audioStream->sampleRate;
soundData->bitDepth = audioStream->bitDepth;
soundData->channelCount = audioStream->channelCount;
soundData->blob.size = audioStream->samples * audioStream->channelCount * (audioStream->bitDepth / 8);
soundData->blob.data = calloc(1, soundData->blob.size);
int samples;
short* buffer = soundData->blob.data;
size_t offset = 0;
lovrAudioStreamRewind(audioStream);
while ((samples = lovrAudioStreamDecode(audioStream, buffer + offset, soundData->blob.size - (offset * sizeof(short)))) != 0) {
offset += samples;
}
return soundData;
}
SoundData* lovrSoundDataCreateFromBlob(Blob* blob) {
SoundData* soundData = lovrAlloc(SoundData, lovrSoundDataDestroy);
if (!soundData) return NULL;
soundData->bitDepth = 16;
soundData->samples = stb_vorbis_decode_memory(blob->data, blob->size, &soundData->channelCount, &soundData->sampleRate, (short**) &soundData->blob.data);
soundData->blob.size = soundData->samples * soundData->channelCount * (soundData->bitDepth / 8);
return soundData;
}
float lovrSoundDataGetSample(SoundData* soundData, int index) {
lovrAssert(index >= 0 && index < (int) soundData->blob.size / (soundData->bitDepth / 8), "Sample index out of range");
switch (soundData->bitDepth) {
case 8: return ((int8_t*) soundData->blob.data)[index] / (float) CHAR_MAX;
case 16: return ((int16_t*) soundData->blob.data)[index] / (float) SHRT_MAX;
default: lovrThrow("Unsupported SoundData bit depth %d\n", soundData->bitDepth); return 0;
}
}
void lovrSoundDataSetSample(SoundData* soundData, int index, float value) {
lovrAssert(index >= 0 && index < (int) soundData->blob.size / (soundData->bitDepth / 8), "Sample index out of range");
switch (soundData->bitDepth) {
case 8: ((int8_t*) soundData->blob.data)[index] = value * CHAR_MAX; break;
case 16: ((int16_t*) soundData->blob.data)[index] = value * SHRT_MAX; break;
default: lovrThrow("Unsupported SoundData bit depth %d\n", soundData->bitDepth); break;
}
}
void lovrSoundDataDestroy(void* ref) {
lovrBlobDestroy(ref);
}

19
src/data/soundData.h Normal file
View File

@ -0,0 +1,19 @@
#include "data/blob.h"
#include "data/audioStream.h"
#pragma once
typedef struct {
Blob blob;
int channelCount;
int sampleRate;
int samples;
int bitDepth;
} SoundData;
SoundData* lovrSoundDataCreate(int samples, int sampleRate, int bitDepth, int channels);
SoundData* lovrSoundDataCreateFromAudioStream(AudioStream* audioStream);
SoundData* lovrSoundDataCreateFromBlob(Blob* blob);
float lovrSoundDataGetSample(SoundData* soundData, int index);
void lovrSoundDataSetSample(SoundData* soundData, int index, float value);
void lovrSoundDataDestroy(void* ref);

View File

@ -1,6 +1,5 @@
#include "data/textureData.h" #include "data/textureData.h"
#include "filesystem/file.h" #include "filesystem/file.h"
#include "math/math.h"
#include "lib/dds.h" #include "lib/dds.h"
#include "lib/stb/stb_image.h" #include "lib/stb/stb_image.h"
#include "lib/stb/stb_image_write.h" #include "lib/stb/stb_image_write.h"
@ -109,23 +108,38 @@ static int parseDDS(uint8_t* data, size_t size, TextureData* textureData) {
height = MAX(height >> 1, 1); height = MAX(height >> 1, 1);
} }
textureData->data = NULL; textureData->blob.data = NULL;
return 0; return 0;
} }
TextureData* lovrTextureDataGetBlank(int width, int height, uint8_t value, TextureFormat format) { TextureData* lovrTextureDataCreate(int width, int height, uint8_t value, TextureFormat format) {
TextureData* textureData = lovrAlloc(sizeof(TextureData), lovrTextureDataDestroy); TextureData* textureData = lovrAlloc(TextureData, lovrTextureDataDestroy);
if (!textureData) return NULL; if (!textureData) return NULL;
size_t pixelSize = 0; size_t pixelSize = 0;
switch (format) { switch (format) {
case FORMAT_RGB: pixelSize = 3; break; case FORMAT_RGB: pixelSize = 3; break;
case FORMAT_RGBA: pixelSize = 4; break; case FORMAT_RGBA: pixelSize = 4; break;
case FORMAT_RGBA4: pixelSize = 2; break;
case FORMAT_RGBA16F: pixelSize = 8; break; case FORMAT_RGBA16F: pixelSize = 8; break;
case FORMAT_RGBA32F: pixelSize = 16; break; case FORMAT_RGBA32F: pixelSize = 16; break;
case FORMAT_R16F: pixelSize = 2; break;
case FORMAT_R32F: pixelSize = 4; break;
case FORMAT_RG16F: pixelSize = 4; break;
case FORMAT_RG32F: pixelSize = 8; break;
case FORMAT_RGB5A1: pixelSize = 2; break;
case FORMAT_RGB10A2: pixelSize = 4; break;
case FORMAT_RG11B10F: pixelSize = 4; break; case FORMAT_RG11B10F: pixelSize = 4; break;
default: lovrThrow("Unable to create a blank compressed texture"); case FORMAT_D16: pixelSize = 2; break;
case FORMAT_D32F: pixelSize = 4; break;
case FORMAT_D24S8: pixelSize = 4; break;
case FORMAT_DXT1:
case FORMAT_DXT3:
case FORMAT_DXT5:
lovrThrow("Unable to create a blank compressed texture");
return NULL;
} }
lovrAssert(width > 0 && height > 0, "TextureData dimensions must be positive"); lovrAssert(width > 0 && height > 0, "TextureData dimensions must be positive");
@ -133,47 +147,34 @@ TextureData* lovrTextureDataGetBlank(int width, int height, uint8_t value, Textu
textureData->width = width; textureData->width = width;
textureData->height = height; textureData->height = height;
textureData->format = format; textureData->format = format;
textureData->data = memset(malloc(size), value, size); textureData->blob.size = size;
textureData->blob = NULL; textureData->blob.data = memset(malloc(size), value, size);
vec_init(&textureData->mipmaps); vec_init(&textureData->mipmaps);
textureData->generateMipmaps = false;
return textureData; return textureData;
} }
TextureData* lovrTextureDataGetEmpty(int width, int height, TextureFormat format) { TextureData* lovrTextureDataCreateFromBlob(Blob* blob, bool flip) {
TextureData* textureData = lovrAlloc(sizeof(TextureData), lovrTextureDataDestroy); TextureData* textureData = lovrAlloc(TextureData, lovrTextureDataDestroy);
if (!textureData) return NULL;
lovrAssert(width > 0 && height > 0, "TextureData dimensions must be positive");
textureData->width = width;
textureData->height = height;
textureData->format = format;
textureData->data = NULL;
textureData->blob = NULL;
vec_init(&textureData->mipmaps);
textureData->generateMipmaps = false;
return textureData;
}
TextureData* lovrTextureDataFromBlob(Blob* blob) {
TextureData* textureData = lovrAlloc(sizeof(TextureData), lovrTextureDataDestroy);
if (!textureData) return NULL; if (!textureData) return NULL;
vec_init(&textureData->mipmaps); vec_init(&textureData->mipmaps);
if (!parseDDS(blob->data, blob->size, textureData)) { if (!parseDDS(blob->data, blob->size, textureData)) {
textureData->blob = blob; textureData->source = blob;
lovrRetain(&blob->ref); lovrRetain(blob);
return textureData; return textureData;
} }
stbi_set_flip_vertically_on_load(1); stbi_set_flip_vertically_on_load(flip);
textureData->format = FORMAT_RGBA; if (stbi_is_hdr_from_memory(blob->data, blob->size)) {
textureData->data = stbi_load_from_memory(blob->data, blob->size, &textureData->width, &textureData->height, NULL, 4); textureData->format = FORMAT_RGBA32F;
textureData->blob = NULL; textureData->blob.data = stbi_loadf_from_memory(blob->data, blob->size, &textureData->width, &textureData->height, NULL, 4);
textureData->generateMipmaps = true; } else {
textureData->format = FORMAT_RGBA;
textureData->blob.data = stbi_load_from_memory(blob->data, blob->size, &textureData->width, &textureData->height, NULL, 4);
}
if (!textureData->data) { if (!textureData->blob.data) {
lovrThrow("Could not load texture data from '%s'", blob->name); lovrThrow("Could not load texture data from '%s'", blob->name);
free(textureData); free(textureData);
return NULL; return NULL;
@ -183,26 +184,26 @@ TextureData* lovrTextureDataFromBlob(Blob* blob) {
} }
Color lovrTextureDataGetPixel(TextureData* textureData, int x, int y) { Color lovrTextureDataGetPixel(TextureData* textureData, int x, int y) {
if (!textureData->data || textureData->format != FORMAT_RGBA) { if (!textureData->blob.data || textureData->format != FORMAT_RGBA) {
return (Color) { 0, 0, 0, 0 }; return (Color) { 0, 0, 0, 0 };
} }
bool inside = x >= 0 && y >= 0 && x <= (textureData->width - 1) && y <= (textureData->height - 1); bool inside = x >= 0 && y >= 0 && x <= (textureData->width - 1) && y <= (textureData->height - 1);
lovrAssert(inside, "getPixel coordinates must be in TextureData bounds"); lovrAssert(inside, "getPixel coordinates must be in TextureData bounds");
size_t offset = 4 * ((textureData->height - (y + 1)) * textureData->width + x); size_t offset = 4 * ((textureData->height - (y + 1)) * textureData->width + x);
uint8_t* data = (uint8_t*) textureData->data + offset; uint8_t* data = (uint8_t*) textureData->blob.data + offset;
return (Color) { data[0] / 255.f, data[1] / 255.f, data[2] / 255.f, data[3] / 255.f }; return (Color) { data[0] / 255.f, data[1] / 255.f, data[2] / 255.f, data[3] / 255.f };
} }
void lovrTextureDataSetPixel(TextureData* textureData, int x, int y, Color color) { void lovrTextureDataSetPixel(TextureData* textureData, int x, int y, Color color) {
if (!textureData->data || textureData->format != FORMAT_RGBA) { if (!textureData->blob.data || textureData->format != FORMAT_RGBA) {
return; return;
} }
bool inside = x >= 0 && y >= 0 && x <= (textureData->width - 1) && y <= (textureData->height - 1); bool inside = x >= 0 && y >= 0 && x <= (textureData->width - 1) && y <= (textureData->height - 1);
lovrAssert(inside, "setPixel coordinates must be in TextureData bounds"); lovrAssert(inside, "setPixel coordinates must be in TextureData bounds");
size_t offset = 4 * ((textureData->height - (y + 1)) * textureData->width + x); size_t offset = 4 * ((textureData->height - (y + 1)) * textureData->width + x);
uint8_t* data = (uint8_t*) textureData->data + offset; uint8_t* data = (uint8_t*) textureData->blob.data + offset;
data[0] = (uint8_t) (color.r * 255.f + .5); data[0] = (uint8_t) (color.r * 255.f + .5);
data[1] = (uint8_t) (color.g * 255.f + .5); data[1] = (uint8_t) (color.g * 255.f + .5);
data[2] = (uint8_t) (color.b * 255.f + .5); data[2] = (uint8_t) (color.b * 255.f + .5);
@ -217,25 +218,24 @@ static void writeCallback(void* context, void* data, int size) {
bool lovrTextureDataEncode(TextureData* textureData, const char* filename) { bool lovrTextureDataEncode(TextureData* textureData, const char* filename) {
File* file = NULL; File* file = NULL;
if ((file = lovrFileCreate(filename)) == NULL || lovrFileOpen(file, OPEN_WRITE)) { if ((file = lovrFileCreate(filename)) == NULL || lovrFileOpen(file, OPEN_WRITE)) {
lovrRelease(file);
return false; return false;
} }
lovrAssert(textureData->format == FORMAT_RGB || textureData->format == FORMAT_RGBA, "Only RGB and RGBA TextureData can be encoded"); lovrAssert(textureData->format == FORMAT_RGB || textureData->format == FORMAT_RGBA, "Only RGB and RGBA TextureData can be encoded");
int components = textureData->format == FORMAT_RGB ? 3 : 4; int components = textureData->format == FORMAT_RGB ? 3 : 4;
int width = textureData->width; int width = textureData->width;
int height = textureData->height; int height = textureData->height;
void* data = (uint8_t*) textureData->data + (textureData->height - 1) * textureData->width * components; void* data = (uint8_t*) textureData->blob.data + (textureData->height - 1) * textureData->width * components;
size_t stride = -textureData->width * components; size_t stride = -textureData->width * components;
bool success = stbi_write_png_to_func(writeCallback, file, width, height, components, data, stride); bool success = stbi_write_png_to_func(writeCallback, file, width, height, components, data, stride);
lovrFileClose(file); lovrFileClose(file);
lovrRelease(file);
return success; return success;
} }
void lovrTextureDataDestroy(const Ref* ref) { void lovrTextureDataDestroy(void* ref) {
TextureData* textureData = containerof(ref, TextureData); TextureData* textureData = ref;
if (textureData->blob) { lovrRelease(textureData->source);
lovrRelease(&textureData->blob->ref);
}
vec_deinit(&textureData->mipmaps); vec_deinit(&textureData->mipmaps);
free(textureData->data); lovrBlobDestroy(ref);
free(textureData);
} }

View File

@ -1,24 +1,27 @@
#include "filesystem/blob.h" #include "data/blob.h"
#include "util.h" #include "util.h"
#include "lib/vec/vec.h"
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#pragma once #pragma once
// WEBGL_compressed_texture_s3tc_srgb isn't ratified yet...
#ifdef EMSCRIPTEN
#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C
#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D
#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E
#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F
#endif
typedef enum { typedef enum {
FORMAT_RGB, FORMAT_RGB,
FORMAT_RGBA, FORMAT_RGBA,
FORMAT_RGBA4,
FORMAT_RGBA16F, FORMAT_RGBA16F,
FORMAT_RGBA32F, FORMAT_RGBA32F,
FORMAT_R16F,
FORMAT_R32F,
FORMAT_RG16F,
FORMAT_RG32F,
FORMAT_RGB5A1,
FORMAT_RGB10A2,
FORMAT_RG11B10F, FORMAT_RG11B10F,
FORMAT_D16,
FORMAT_D32F,
FORMAT_D24S8,
FORMAT_DXT1, FORMAT_DXT1,
FORMAT_DXT3, FORMAT_DXT3,
FORMAT_DXT5 FORMAT_DXT5
@ -34,20 +37,17 @@ typedef struct {
typedef vec_t(Mipmap) vec_mipmap_t; typedef vec_t(Mipmap) vec_mipmap_t;
typedef struct { typedef struct {
Ref ref; Blob blob;
int width; int width;
int height; int height;
void* data; Blob* source;
Blob* blob;
TextureFormat format; TextureFormat format;
bool generateMipmaps;
vec_mipmap_t mipmaps; vec_mipmap_t mipmaps;
} TextureData; } TextureData;
TextureData* lovrTextureDataGetBlank(int width, int height, uint8_t value, TextureFormat format); TextureData* lovrTextureDataCreate(int width, int height, uint8_t value, TextureFormat format);
TextureData* lovrTextureDataGetEmpty(int width, int height, TextureFormat format); TextureData* lovrTextureDataCreateFromBlob(Blob* blob, bool flip);
TextureData* lovrTextureDataFromBlob(Blob* blob);
Color lovrTextureDataGetPixel(TextureData* textureData, int x, int y); Color lovrTextureDataGetPixel(TextureData* textureData, int x, int y);
void lovrTextureDataSetPixel(TextureData* textureData, int x, int y, Color color); void lovrTextureDataSetPixel(TextureData* textureData, int x, int y, Color color);
bool lovrTextureDataEncode(TextureData* textureData, const char* filename); bool lovrTextureDataEncode(TextureData* textureData, const char* filename);
void lovrTextureDataDestroy(const Ref* ref); void lovrTextureDataDestroy(void* ref);

View File

@ -1,8 +1,7 @@
#include "data/vertexData.h" #include "data/vertexData.h"
#include <string.h> #include <string.h>
#include <stdio.h>
static size_t attributeTypeSizes[3] = { 4, 1, 4 }; static const size_t attributeTypeSizes[3] = { 4, 1, 4 };
void vertexFormatInit(VertexFormat* format) { void vertexFormatInit(VertexFormat* format) {
memset(format, 0, sizeof(*format)); memset(format, 0, sizeof(*format));
@ -15,8 +14,8 @@ void vertexFormatAppend(VertexFormat* format, const char* name, AttributeType ty
format->stride += size * count; format->stride += size * count;
} }
VertexData* lovrVertexDataCreate(uint32_t count, VertexFormat* format, bool allocate) { VertexData* lovrVertexDataCreate(uint32_t count, VertexFormat* format) {
VertexData* vertexData = lovrAlloc(sizeof(VertexData), lovrVertexDataDestroy); VertexData* vertexData = lovrAlloc(VertexData, lovrBlobDestroy);
if (!vertexData) return NULL; if (!vertexData) return NULL;
if (format) { if (format) {
@ -30,21 +29,10 @@ VertexData* lovrVertexDataCreate(uint32_t count, VertexFormat* format, bool allo
vertexFormatAppend(&vertexData->format, "lovrVertexColor", ATTR_BYTE, 4); vertexFormatAppend(&vertexData->format, "lovrVertexColor", ATTR_BYTE, 4);
} }
size_t size = format->stride * count;
vertexData->blob.data = calloc(1, size);
vertexData->blob.size = size;
vertexData->count = count; vertexData->count = count;
vertexData->data.raw = NULL;
if (allocate) {
vertexData->data.raw = malloc(format->stride * count);
memset(vertexData->data.raw, 0, format->stride * count);
}
return vertexData; return vertexData;
} }
void lovrVertexDataDestroy(const Ref* ref) {
VertexData* vertexData = containerof(ref, VertexData);
if (vertexData->data.raw) {
free(vertexData->data.raw);
}
free(vertexData);
}

View File

@ -1,7 +1,6 @@
#include "util.h" #include "blob.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#pragma once #pragma once
@ -39,14 +38,12 @@ typedef union {
} IndexPointer; } IndexPointer;
typedef struct { typedef struct {
Ref ref; Blob blob;
VertexFormat format; VertexFormat format;
VertexPointer data;
uint32_t count; uint32_t count;
} VertexData; } VertexData;
void vertexFormatInit(VertexFormat* format); void vertexFormatInit(VertexFormat* format);
void vertexFormatAppend(VertexFormat* format, const char* name, AttributeType type, int count); void vertexFormatAppend(VertexFormat* format, const char* name, AttributeType type, int count);
VertexData* lovrVertexDataCreate(uint32_t count, VertexFormat* format, bool allocate); VertexData* lovrVertexDataCreate(uint32_t count, VertexFormat* format);
void lovrVertexDataDestroy(const Ref* ref);

View File

@ -1,31 +1,34 @@
#include "event/event.h" #include "event/event.h"
#include "lib/glfw.h" #include "lib/glfw.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
static EventState state; static EventState state;
bool eventAlreadyInit = false;
void lovrEventInit() { void lovrEventInit() {
if (eventAlreadyInit) if (state.initialized) return;
lovrEventDestroy();
vec_init(&state.pumps); vec_init(&state.pumps);
vec_init(&state.events); vec_init(&state.events);
lovrEventAddPump(glfwPollEvents); lovrEventAddPump(glfwPollEvents);
if (!eventAlreadyInit) { atexit(lovrEventDestroy);
atexit(lovrEventDestroy); state.initialized = true;
eventAlreadyInit = true;
}
} }
void lovrEventDestroy() { void lovrEventDestroy() {
if (!state.initialized) return;
vec_deinit(&state.pumps); vec_deinit(&state.pumps);
vec_deinit(&state.events); vec_deinit(&state.events);
memset(&state, 0, sizeof(EventState));
} }
void lovrEventAddPump(EventPump pump) { void lovrEventAddPump(EventPump pump) {
vec_push(&state.pumps, pump); vec_push(&state.pumps, pump);
} }
void lovrEventRemovePump(EventPump pump) {
vec_remove(&state.pumps, pump);
}
void lovrEventPump() { void lovrEventPump() {
int i; EventPump pump; int i; EventPump pump;
vec_foreach(&state.pumps, pump, i) { vec_foreach(&state.pumps, pump, i) {

View File

@ -1,52 +1,77 @@
#include "headset/headset.h" #include "headset/headset.h"
#include "thread/thread.h"
#include "lib/vec/vec.h" #include "lib/vec/vec.h"
#include <stdbool.h> #include <stdbool.h>
#pragma once #pragma once
#define MAX_EVENT_NAME_LENGTH 32
typedef enum { typedef enum {
EVENT_QUIT, EVENT_QUIT,
EVENT_FOCUS, EVENT_FOCUS,
EVENT_MOUNT,
EVENT_THREAD_ERROR,
EVENT_CONTROLLER_ADDED, EVENT_CONTROLLER_ADDED,
EVENT_CONTROLLER_REMOVED, EVENT_CONTROLLER_REMOVED,
EVENT_CONTROLLER_PRESSED, EVENT_CONTROLLER_PRESSED,
EVENT_CONTROLLER_RELEASED EVENT_CONTROLLER_RELEASED,
EVENT_CUSTOM
} EventType; } EventType;
typedef enum {
TYPE_NIL,
TYPE_BOOLEAN,
TYPE_NUMBER,
TYPE_STRING,
TYPE_OBJECT
} VariantType;
typedef union {
bool boolean;
double number;
char* string;
Ref* ref;
} VariantValue;
typedef struct {
VariantType type;
VariantValue value;
} Variant;
typedef vec_t(Variant) vec_variant_t;
typedef struct { typedef struct {
bool restart; bool restart;
int exitCode; int exitCode;
} QuitEvent; } QuitEvent;
typedef struct { typedef struct {
bool isFocused; bool value;
} FocusEvent; } BoolEvent;
typedef struct { typedef struct {
Controller* controller; Thread* thread;
} ControllerAddedEvent; const char* error;
} ThreadEvent;
typedef struct {
Controller* controller;
} ControllerRemovedEvent;
typedef struct { typedef struct {
Controller* controller; Controller* controller;
ControllerButton button; ControllerButton button;
} ControllerPressedEvent; } ControllerEvent;
typedef struct { typedef struct {
Controller* controller; char name[MAX_EVENT_NAME_LENGTH];
ControllerButton button; Variant data[4];
} ControllerReleasedEvent; int count;
} CustomEvent;
typedef union { typedef union {
QuitEvent quit; QuitEvent quit;
FocusEvent focus; BoolEvent boolean;
ControllerAddedEvent controlleradded; ThreadEvent thread;
ControllerRemovedEvent controllerremoved; ControllerEvent controller;
ControllerPressedEvent controllerpressed; CustomEvent custom;
ControllerReleasedEvent controllerreleased;
} EventData; } EventData;
typedef struct { typedef struct {
@ -60,13 +85,15 @@ typedef vec_t(EventPump) vec_pump_t;
typedef vec_t(Event) vec_event_t; typedef vec_t(Event) vec_event_t;
typedef struct { typedef struct {
bool initialized;
vec_pump_t pumps; vec_pump_t pumps;
vec_event_t events; vec_event_t events;
} EventState; } EventState;
void lovrEventInit(); void lovrEventInit();
void lovrEventDestroy(); void lovrEventDestroy();
void lovrEventAddPump(void (*pump)(void)); void lovrEventAddPump(EventPump pump);
void lovrEventRemovePump(EventPump pump);
void lovrEventPump(); void lovrEventPump();
void lovrEventPush(Event event); void lovrEventPush(Event event);
bool lovrEventPoll(Event* event); bool lovrEventPoll(Event* event);

View File

@ -1,18 +1,18 @@
#include "filesystem/file.h" #include "filesystem/file.h"
#include <physfs.h> #include <physfs.h>
#include <stdlib.h>
File* lovrFileCreate(const char* path) { File* lovrFileCreate(const char* path) {
File* file = lovrAlloc(sizeof(File), lovrFileDestroy); File* file = lovrAlloc(File, lovrFileDestroy);
if (!file) return NULL; if (!file) return NULL;
file->path = path; file->path = path;
file->handle = NULL;
return file; return file;
} }
void lovrFileDestroy(const Ref* ref) { void lovrFileDestroy(void* ref) {
File* file = containerof(ref, File); File* file = ref;
if (file->handle) { if (file->handle) {
PHYSFS_close(file->handle); PHYSFS_close(file->handle);
} }

View File

@ -16,7 +16,7 @@ typedef struct {
} File; } File;
File* lovrFileCreate(const char* filename); File* lovrFileCreate(const char* filename);
void lovrFileDestroy(const Ref* ref); void lovrFileDestroy(void* ref);
int lovrFileOpen(File* file, FileMode mode); int lovrFileOpen(File* file, FileMode mode);
void lovrFileClose(File* file); void lovrFileClose(File* file);
size_t lovrFileRead(File* file, void* data, size_t bytes); size_t lovrFileRead(File* file, void* data, size_t bytes);

View File

@ -4,7 +4,7 @@
#include <physfs.h> #include <physfs.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "lovr.h" #include <string.h>
#ifdef __APPLE__ #ifdef __APPLE__
#include <mach-o/dyld.h> #include <mach-o/dyld.h>
#endif #endif
@ -22,14 +22,17 @@
#include "BridgeLovr.h" #include "BridgeLovr.h"
#endif #endif
#ifdef _WIN32
const char lovrDirSep = '\\';
#else
const char lovrDirSep = '/';
#endif
static FilesystemState state; static FilesystemState state;
bool filesystemAlreadyInit = false;
void lovrFilesystemInit(const char* arg0, const char* arg1) { void lovrFilesystemInit(const char* arg0, const char* arg1) {
if (filesystemAlreadyInit) // Do not change settings during a reload, and don't try to initialize PhysFS twice if (state.initialized) return;
return; state.initialized = true;
filesystemAlreadyInit = true;
if (!PHYSFS_init(arg0)) { if (!PHYSFS_init(arg0)) {
lovrThrow("Could not initialize filesystem: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); lovrThrow("Could not initialize filesystem: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
@ -38,6 +41,10 @@ void lovrFilesystemInit(const char* arg0, const char* arg1) {
state.source = malloc(LOVR_PATH_MAX * sizeof(char)); state.source = malloc(LOVR_PATH_MAX * sizeof(char));
state.identity = NULL; state.identity = NULL;
state.isFused = true; state.isFused = true;
vec_init(&state.requirePattern[0]);
vec_init(&state.requirePattern[1]);
lovrFilesystemSetRequirePath("?.lua;?/init.lua;lua_modules/?.lua;lua_modules/?/init.lua;deps/?.lua;deps/?/init.lua");
lovrFilesystemSetCRequirePath("??;lua_modules/??;deps/??");
// Try to mount either an archive fused to the executable or an archive from the command line // Try to mount either an archive fused to the executable or an archive from the command line
lovrFilesystemGetExecutablePath(state.source, LOVR_PATH_MAX); lovrFilesystemGetExecutablePath(state.source, LOVR_PATH_MAX);
@ -47,23 +54,26 @@ void lovrFilesystemInit(const char* arg0, const char* arg1) {
if (arg1) { if (arg1) {
strncpy(state.source, arg1, LOVR_PATH_MAX); strncpy(state.source, arg1, LOVR_PATH_MAX);
if (!lovrFilesystemMount(state.source, NULL, 1)) { if (!lovrFilesystemMount(state.source, NULL, 1)) {
goto mounted; return;
} }
} }
free(state.source); free(state.source);
state.source = NULL; state.source = NULL;
} }
mounted:
atexit(lovrFilesystemDestroy);
} }
void lovrFilesystemDestroy() { void lovrFilesystemDestroy() {
if (!state.initialized) return;
free(state.source); free(state.source);
free(state.savePathFull); free(state.savePathFull);
free(state.savePathRelative); free(state.savePathRelative);
for (int i = 0; i < 2; i++) {
free(state.requirePath[i]);
vec_deinit(&state.requirePattern[i]);
}
PHYSFS_deinit(); PHYSFS_deinit();
memset(&state, 0, sizeof(FilesystemState));
} }
int lovrFilesystemCreateDirectory(const char* path) { int lovrFilesystemCreateDirectory(const char* path) {
@ -108,27 +118,6 @@ void lovrFilesystemGetDirectoryItems(const char* path, getDirectoryItemsCallback
PHYSFS_enumerate(path, callback, userdata); PHYSFS_enumerate(path, callback, userdata);
} }
int lovrFilesystemGetExecutablePath(char* dest, unsigned int size) {
#ifdef __APPLE__
if (_NSGetExecutablePath(dest, &size) == 0) {
return 0;
}
#elif _WIN32
return !GetModuleFileName(NULL, dest, size);
#elif EMSCRIPTEN
return 1;
#elif __linux__
memset(dest, 0, size);
if (readlink("/proc/self/exe", dest, size) == -1) {
return 1;
}
#else
#error "This platform is missing an implementation for lovrFilesystemGetExecutablePath"
#endif
return 1;
}
const char* lovrFilesystemGetIdentity() { const char* lovrFilesystemGetIdentity() {
return state.identity; return state.identity;
} }
@ -142,6 +131,14 @@ const char* lovrFilesystemGetRealDirectory(const char* path) {
return PHYSFS_getRealDir(path); return PHYSFS_getRealDir(path);
} }
vec_str_t* lovrFilesystemGetRequirePath() {
return &state.requirePattern[0];
}
vec_str_t* lovrFilesystemGetCRequirePath() {
return &state.requirePattern[1];
}
const char* lovrFilesystemGetSaveDirectory() { const char* lovrFilesystemGetSaveDirectory() {
return state.savePathFull; return state.savePathFull;
} }
@ -171,6 +168,20 @@ const char* lovrFilesystemGetUserDirectory() {
#endif #endif
} }
int lovrFilesystemGetWorkingDirectory(char* dest, unsigned int size) {
#ifdef _WIN32
WCHAR w_cwd[LOVR_PATH_MAX];
_wgetcwd(w_cwd, LOVR_PATH_MAX);
PHYSFS_utf8FromUtf16(w_cwd, dest, size);
return 0;
#else
if (getcwd(dest, size)) {
return 0;
}
#endif
return 1;
}
bool lovrFilesystemIsDirectory(const char* path) { bool lovrFilesystemIsDirectory(const char* path) {
PHYSFS_Stat stat; PHYSFS_Stat stat;
return PHYSFS_stat(path, &stat) ? stat.filetype == PHYSFS_FILETYPE_DIRECTORY : false; return PHYSFS_stat(path, &stat) ? stat.filetype == PHYSFS_FILETYPE_DIRECTORY : false;
@ -194,29 +205,34 @@ void* lovrFilesystemRead(const char* path, size_t* bytesRead) {
// Create file // Create file
File* file = lovrFileCreate(path); File* file = lovrFileCreate(path);
if (!file) { if (!file) {
lovrRelease(file);
return NULL; return NULL;
} }
// Open it // Open it
if (lovrFileOpen(file, OPEN_READ)) { if (lovrFileOpen(file, OPEN_READ)) {
lovrRelease(file);
return NULL; return NULL;
} }
// Get file size // Get file size
size_t size = lovrFileGetSize(file); size_t size = lovrFileGetSize(file);
if (size == (unsigned int) -1) { if (size == (unsigned int) -1) {
lovrRelease(file);
return NULL; return NULL;
} }
// Allocate buffer // Allocate buffer
void* data = malloc(size); void* data = malloc(size);
if (!data) { if (!data) {
lovrRelease(file);
return NULL; return NULL;
} }
// Perform read // Perform read
*bytesRead = lovrFileRead(file, data, size); *bytesRead = lovrFileRead(file, data, size);
lovrFileClose(file); lovrFileClose(file);
lovrRelease(file);
// Make sure we got everything // Make sure we got everything
if (*bytesRead != (size_t) size) { if (*bytesRead != (size_t) size) {
@ -266,6 +282,32 @@ int lovrFilesystemSetIdentity(const char* identity) {
return 0; return 0;
} }
static void setRequirePath(int i, const char* requirePath) {
if (state.requirePath[i]) {
free(state.requirePath[i]);
vec_clear(&state.requirePattern[i]);
}
char* p = state.requirePath[i] = strdup(requirePath);
while (1) {
vec_push(&state.requirePattern[i], p);
if ((p = strchr(p, ';')) != NULL) {
*p++ = '\0';
} else {
break;
}
}
}
void lovrFilesystemSetRequirePath(const char* requirePath) {
setRequirePath(0, requirePath);
}
void lovrFilesystemSetCRequirePath(const char* requirePath) {
setRequirePath(1, requirePath);
}
int lovrFilesystemUnmount(const char* path) { int lovrFilesystemUnmount(const char* path) {
return !PHYSFS_unmount(path); return !PHYSFS_unmount(path);
} }
@ -279,6 +321,6 @@ size_t lovrFilesystemWrite(const char* path, const char* content, size_t size, b
lovrFileOpen(file, append ? OPEN_APPEND : OPEN_WRITE); lovrFileOpen(file, append ? OPEN_APPEND : OPEN_WRITE);
size_t bytesWritten = lovrFileWrite(file, (void*) content, size); size_t bytesWritten = lovrFileWrite(file, (void*) content, size);
lovrFileClose(file); lovrFileClose(file);
lovrRelease(&file->ref); lovrRelease(file);
return bytesWritten; return bytesWritten;
} }

View File

@ -1,3 +1,4 @@
#include "lib/vec/vec.h"
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
@ -5,14 +6,19 @@
#define LOVR_PATH_MAX 1024 #define LOVR_PATH_MAX 1024
extern const char lovrDirSep;
typedef int getDirectoryItemsCallback(void* userdata, const char* dir, const char* file); typedef int getDirectoryItemsCallback(void* userdata, const char* dir, const char* file);
typedef struct { typedef struct {
bool initialized;
char* source; char* source;
const char* identity; const char* identity;
char* savePathRelative; char* savePathRelative;
char* savePathFull; char* savePathFull;
bool isFused; bool isFused;
char* requirePath[2];
vec_str_t requirePattern[2];
} FilesystemState; } FilesystemState;
void lovrFilesystemInit(const char* arg0, const char* arg1); void lovrFilesystemInit(const char* arg0, const char* arg1);
@ -20,14 +26,17 @@ void lovrFilesystemDestroy();
int lovrFilesystemCreateDirectory(const char* path); int lovrFilesystemCreateDirectory(const char* path);
int lovrFilesystemGetAppdataDirectory(char* dest, unsigned int size); int lovrFilesystemGetAppdataDirectory(char* dest, unsigned int size);
void lovrFilesystemGetDirectoryItems(const char* path, getDirectoryItemsCallback callback, void* userdata); void lovrFilesystemGetDirectoryItems(const char* path, getDirectoryItemsCallback callback, void* userdata);
int lovrFilesystemGetExecutablePath(char* dest, unsigned int size); #define lovrFilesystemGetExecutablePath lovrGetExecutablePath
const char* lovrFilesystemGetIdentity(); const char* lovrFilesystemGetIdentity();
long lovrFilesystemGetLastModified(const char* path); long lovrFilesystemGetLastModified(const char* path);
const char* lovrFilesystemGetRealDirectory(const char* path); const char* lovrFilesystemGetRealDirectory(const char* path);
vec_str_t* lovrFilesystemGetRequirePath();
vec_str_t* lovrFilesystemGetCRequirePath();
const char* lovrFilesystemGetSaveDirectory(); const char* lovrFilesystemGetSaveDirectory();
size_t lovrFilesystemGetSize(const char* path); size_t lovrFilesystemGetSize(const char* path);
const char* lovrFilesystemGetSource(); const char* lovrFilesystemGetSource();
const char* lovrFilesystemGetUserDirectory(); const char* lovrFilesystemGetUserDirectory();
int lovrFilesystemGetWorkingDirectory(char* dest, unsigned int size);
bool lovrFilesystemIsDirectory(const char* path); bool lovrFilesystemIsDirectory(const char* path);
bool lovrFilesystemIsFile(const char* path); bool lovrFilesystemIsFile(const char* path);
bool lovrFilesystemIsFused(); bool lovrFilesystemIsFused();
@ -35,6 +44,7 @@ int lovrFilesystemMount(const char* path, const char* mountpoint, bool append);
void* lovrFilesystemRead(const char* path, size_t* bytesRead); void* lovrFilesystemRead(const char* path, size_t* bytesRead);
int lovrFilesystemRemove(const char* path); int lovrFilesystemRemove(const char* path);
int lovrFilesystemSetIdentity(const char* identity); int lovrFilesystemSetIdentity(const char* identity);
int lovrFilesystemSetSource(const char* source); void lovrFilesystemSetRequirePath(const char* requirePath);
void lovrFilesystemSetCRequirePath(const char* requirePath);
int lovrFilesystemUnmount(const char* path); int lovrFilesystemUnmount(const char* path);
size_t lovrFilesystemWrite(const char* path, const char* content, size_t size, bool append); size_t lovrFilesystemWrite(const char* path, const char* content, size_t size, bool append);

View File

@ -1,7 +1,9 @@
#include "graphics/animator.h" #include "graphics/animator.h"
#include "math/mat4.h"
#include "math/vec3.h" #include "math/vec3.h"
#include "math/quat.h" #include "math/quat.h"
#include <math.h> #include <math.h>
#include <stdlib.h>
static Track* lovrAnimatorEnsureTrack(Animator* animator, const char* animation) { static Track* lovrAnimatorEnsureTrack(Animator* animator, const char* animation) {
Track* track = map_get(&animator->trackMap, animation); Track* track = map_get(&animator->trackMap, animation);
@ -14,10 +16,10 @@ static int trackSortCallback(const void* a, const void* b) {
} }
Animator* lovrAnimatorCreate(ModelData* modelData) { Animator* lovrAnimatorCreate(ModelData* modelData) {
Animator* animator = lovrAlloc(sizeof(Animator), lovrAnimatorDestroy); Animator* animator = lovrAlloc(Animator, lovrAnimatorDestroy);
if (!animator) return NULL; if (!animator) return NULL;
lovrRetain(&modelData->ref); lovrRetain(modelData);
animator->modelData = modelData; animator->modelData = modelData;
map_init(&animator->trackMap); map_init(&animator->trackMap);
vec_init(&animator->trackList); vec_init(&animator->trackList);
@ -43,9 +45,9 @@ Animator* lovrAnimatorCreate(ModelData* modelData) {
return animator; return animator;
} }
void lovrAnimatorDestroy(const Ref* ref) { void lovrAnimatorDestroy(void* ref) {
Animator* animator = containerof(ref, Animator); Animator* animator = ref;
lovrRelease(&animator->modelData->ref); lovrRelease(animator->modelData);
map_deinit(&animator->trackMap); map_deinit(&animator->trackMap);
vec_deinit(&animator->trackList); vec_deinit(&animator->trackList);
free(animator); free(animator);

View File

@ -2,6 +2,7 @@
#include "math/mat4.h" #include "math/mat4.h"
#include "util.h" #include "util.h"
#include "lib/map/map.h" #include "lib/map/map.h"
#include "lib/vec/vec.h"
#include <stdbool.h> #include <stdbool.h>
#pragma once #pragma once
@ -27,7 +28,7 @@ typedef struct {
} Animator; } Animator;
Animator* lovrAnimatorCreate(ModelData* modelData); Animator* lovrAnimatorCreate(ModelData* modelData);
void lovrAnimatorDestroy(const Ref* ref); void lovrAnimatorDestroy(void* ref);
void lovrAnimatorReset(Animator* animator); void lovrAnimatorReset(Animator* animator);
void lovrAnimatorUpdate(Animator* animator, float dt); void lovrAnimatorUpdate(Animator* animator, float dt);
bool lovrAnimatorEvaluate(Animator* animator, const char* bone, mat4 transform); bool lovrAnimatorEvaluate(Animator* animator, const char* bone, mat4 transform);

View File

@ -1,136 +0,0 @@
#include "graphics/canvas.h"
#include "graphics/graphics.h"
#include "math/mat4.h"
#include <math.h>
#include <stdlib.h>
bool lovrCanvasSupportsFormat(TextureFormat format) {
switch (format) {
case FORMAT_RGB:
case FORMAT_RGBA:
case FORMAT_RGBA16F:
case FORMAT_RGBA32F:
case FORMAT_RG11B10F:
return true;
case FORMAT_DXT1:
case FORMAT_DXT3:
case FORMAT_DXT5:
return false;
}
}
Canvas* lovrCanvasCreate(int width, int height, TextureFormat format, CanvasType type, int msaa, bool depth, bool stencil) {
TextureData* textureData = lovrTextureDataGetEmpty(width, height, format);
Texture* texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, true);
if (!texture) return NULL;
Canvas* canvas = lovrAlloc(sizeof(Canvas), lovrCanvasDestroy);
canvas->texture = *texture;
canvas->type = type;
canvas->msaa = msaa;
canvas->framebuffer = 0;
canvas->resolveFramebuffer = 0;
canvas->depthStencilBuffer = 0;
canvas->msaaTexture = 0;
// Framebuffer
glGenFramebuffers(1, &canvas->framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, canvas->framebuffer);
// Color attachment
if (msaa > 0) {
GLenum internalFormat = lovrTextureFormatGetGLInternalFormat(format, lovrGraphicsIsGammaCorrect());
glGenRenderbuffers(1, &canvas->msaaTexture);
glBindRenderbuffer(GL_RENDERBUFFER, canvas->msaaTexture);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa, internalFormat, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, canvas->msaaTexture);
} else {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, canvas->texture.id, 0);
}
// Depth/Stencil
if (depth || stencil) {
GLenum depthStencilFormat = stencil ? GL_DEPTH24_STENCIL8 : GL_DEPTH_COMPONENT24;
glGenRenderbuffers(1, &canvas->depthStencilBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, canvas->depthStencilBuffer);
if (msaa > 0) {
glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa, depthStencilFormat, width, height);
} else {
glRenderbufferStorage(GL_RENDERBUFFER, depthStencilFormat, width, height);
}
if (depth) {
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, canvas->depthStencilBuffer);
}
if (stencil) {
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, canvas->depthStencilBuffer);
}
}
// Resolve framebuffer
if (msaa > 0) {
glGenFramebuffers(1, &canvas->resolveFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, canvas->resolveFramebuffer);
glBindTexture(GL_TEXTURE_2D, canvas->texture.id);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, canvas->texture.id, 0);
glBindFramebuffer(GL_FRAMEBUFFER, canvas->framebuffer);
}
lovrAssert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "Error creating Canvas");
lovrGraphicsClear(true, true, true, (Color) { 0, 0, 0, 0 }, 1., 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return canvas;
}
void lovrCanvasDestroy(const Ref* ref) {
Canvas* canvas = (Canvas*) containerof(ref, Texture);
glDeleteFramebuffers(1, &canvas->framebuffer);
if (canvas->resolveFramebuffer) {
glDeleteFramebuffers(1, &canvas->resolveFramebuffer);
}
if (canvas->depthStencilBuffer) {
glDeleteRenderbuffers(1, &canvas->depthStencilBuffer);
}
if (canvas->msaaTexture) {
glDeleteTextures(1, &canvas->msaaTexture);
}
lovrTextureDestroy(ref);
}
void lovrCanvasBind(Canvas* canvas) {
int width = canvas->texture.width;
int height = canvas->texture.height;
lovrGraphicsBindFramebuffer(canvas->framebuffer);
lovrGraphicsSetViewport(0, 0, width, height);
if (canvas->type == CANVAS_2D) {
float projection[16];
mat4_orthographic(projection, 0, width, 0, height, -1, 1);
lovrGraphicsSetProjection(projection);
}
}
void lovrCanvasResolveMSAA(Canvas* canvas) {
if (canvas->msaa == 0) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return;
}
int width = canvas->texture.width;
int height = canvas->texture.height;
glBindFramebuffer(GL_READ_FRAMEBUFFER, canvas->framebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, canvas->resolveFramebuffer);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
TextureFormat lovrCanvasGetFormat(Canvas* canvas) {
return canvas->texture.slices[0]->format;
}
int lovrCanvasGetMSAA(Canvas* canvas) {
return canvas->msaa;
}

View File

@ -1,26 +1,37 @@
#include "graphics/texture.h" #include "graphics/texture.h"
#include "util.h"
typedef enum { #pragma once
CANVAS_3D,
CANVAS_2D #define MAX_CANVAS_ATTACHMENTS 4
} CanvasType;
typedef struct { typedef struct {
Texture texture; Texture* texture;
CanvasType type; int slice;
GLuint framebuffer; int level;
GLuint resolveFramebuffer; } Attachment;
GLuint depthStencilBuffer;
GLuint msaaTexture; typedef struct {
struct {
bool enabled;
bool readable;
TextureFormat format;
} depth;
bool stereo;
int msaa; int msaa;
} Canvas; bool mipmaps;
} CanvasFlags;
bool lovrCanvasSupportsFormat(TextureFormat format); typedef struct Canvas Canvas;
Canvas* lovrCanvasCreate(int width, int height, TextureFormat format, CanvasType type, int msaa, bool depth, bool stencil); Canvas* lovrCanvasCreate(int width, int height, CanvasFlags flags);
void lovrCanvasDestroy(const Ref* ref); void lovrCanvasDestroy(void* ref);
void lovrCanvasBind(Canvas* canvas); const Attachment* lovrCanvasGetAttachments(Canvas* canvas, int* count);
void lovrCanvasResolveMSAA(Canvas* canvas); void lovrCanvasSetAttachments(Canvas* canvas, Attachment* attachments, int count);
TextureFormat lovrCanvasGetFormat(Canvas* canvas); void lovrCanvasBind(Canvas* canvas, bool willDraw);
void lovrCanvasResolve(Canvas* canvas);
bool lovrCanvasIsStereo(Canvas* canvas);
int lovrCanvasGetWidth(Canvas* canvas);
int lovrCanvasGetHeight(Canvas* canvas);
int lovrCanvasGetMSAA(Canvas* canvas); int lovrCanvasGetMSAA(Canvas* canvas);
Texture* lovrCanvasGetDepthTexture(Canvas* canvas);
TextureData* lovrCanvasNewTextureData(Canvas* canvas, int index);

View File

@ -5,30 +5,30 @@
#include "data/textureData.h" #include "data/textureData.h"
#include "util.h" #include "util.h"
#include <string.h> #include <string.h>
#include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
static int lovrFontAlignLine(vec_float_t* vertices, int index, float width, HorizontalAlign halign) { static float* lovrFontAlignLine(float* x, float* lineEnd, float width, HorizontalAlign halign) {
while (index < vertices->length) { while(x < lineEnd) {
if (halign == ALIGN_CENTER) { if (halign == ALIGN_CENTER) {
vertices->data[index] -= width / 2.f; *x -= width / 2.f;
} else if (halign == ALIGN_RIGHT) { } else if (halign == ALIGN_RIGHT) {
vertices->data[index] -= width; *x -= width;
} }
index += 5; x += 8;
} }
return index; return x;
} }
Font* lovrFontCreate(Rasterizer* rasterizer) { Font* lovrFontCreate(Rasterizer* rasterizer) {
Font* font = lovrAlloc(sizeof(Font), lovrFontDestroy); Font* font = lovrAlloc(Font, lovrFontDestroy);
if (!font) return NULL; if (!font) return NULL;
lovrRetain(&rasterizer->ref); lovrRetain(rasterizer);
font->rasterizer = rasterizer; font->rasterizer = rasterizer;
font->texture = NULL;
font->lineHeight = 1.f; font->lineHeight = 1.f;
font->pixelDensity = (float) font->rasterizer->height; font->pixelDensity = (float) font->rasterizer->height;
map_init(&font->kerning); map_init(&font->kerning);
@ -53,16 +53,22 @@ Font* lovrFontCreate(Rasterizer* rasterizer) {
return font; return font;
} }
void lovrFontDestroy(const Ref* ref) { void lovrFontDestroy(void* ref) {
Font* font = containerof(ref, Font); Font* font = ref;
lovrRelease(&font->rasterizer->ref); lovrRelease(font->rasterizer);
lovrRelease(&font->texture->ref); lovrRelease(font->texture);
const char* key;
map_iter_t iter = map_iter(&font->atlas.glyphs);
while ((key = map_next(&font->atlas.glyphs, &iter)) != NULL) {
Glyph* glyph = map_get(&font->atlas.glyphs, key);
lovrRelease(glyph->data);
}
map_deinit(&font->atlas.glyphs); map_deinit(&font->atlas.glyphs);
map_deinit(&font->kerning); map_deinit(&font->kerning);
free(font); free(font);
} }
void lovrFontRender(Font* font, const char* str, float wrap, HorizontalAlign halign, VerticalAlign valign, vec_float_t* vertices, float* offsety) { void lovrFontRender(Font* font, const char* str, float wrap, HorizontalAlign halign, VerticalAlign valign, VertexPointer vertices, float* offsety, uint32_t* vertexCount) {
FontAtlas* atlas = &font->atlas; FontAtlas* atlas = &font->atlas;
float cx = 0; float cx = 0;
@ -78,17 +84,16 @@ void lovrFontRender(Font* font, const char* str, float wrap, HorizontalAlign hal
unsigned int codepoint; unsigned int codepoint;
size_t bytes; size_t bytes;
int linePtr = 0; float* cursor = vertices.floats;
float* lineStart = vertices.floats;
int lineCount = 1; int lineCount = 1;
*vertexCount = 0;
vec_reserve(vertices, len * 30);
vec_clear(vertices);
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) { while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
// Newlines // Newlines
if (codepoint == '\n' || (wrap && cx * scale > wrap && codepoint == ' ')) { if (codepoint == '\n' || (wrap && cx * scale > wrap && codepoint == ' ')) {
linePtr = lovrFontAlignLine(vertices, linePtr, cx, halign); lineStart = lovrFontAlignLine(lineStart, cursor, cx, halign);
lineCount++; lineCount++;
cx = 0; cx = 0;
cy -= font->rasterizer->height * font->lineHeight; cy -= font->rasterizer->height * font->lineHeight;
@ -106,7 +111,7 @@ void lovrFontRender(Font* font, const char* str, float wrap, HorizontalAlign hal
// Start over if texture was repacked // Start over if texture was repacked
if (u != atlas->width || v != atlas->height) { if (u != atlas->width || v != atlas->height) {
lovrFontRender(font, start, wrap, halign, valign, vertices, offsety); lovrFontRender(font, start, wrap, halign, valign, vertices, offsety, vertexCount);
return; return;
} }
@ -121,16 +126,18 @@ void lovrFontRender(Font* font, const char* str, float wrap, HorizontalAlign hal
float s2 = (glyph->x + glyph->tw) / u; float s2 = (glyph->x + glyph->tw) / u;
float t2 = glyph->y / v; float t2 = glyph->y / v;
float quad[30] = { float quad[48] = {
x1, y1, 0, s1, t1, x1, y1, 0, 0, 0, 0, s1, t1,
x1, y2, 0, s1, t2, x1, y2, 0, 0, 0, 0, s1, t2,
x2, y1, 0, s2, t1, x2, y1, 0, 0, 0, 0, s2, t1,
x2, y1, 0, s2, t1, x2, y1, 0, 0, 0, 0, s2, t1,
x1, y2, 0, s1, t2, x1, y2, 0, 0, 0, 0, s1, t2,
x2, y2, 0, s2, t2 x2, y2, 0, 0, 0, 0, s2, t2
}; };
vec_pusharr(vertices, quad, 30); memcpy(cursor, quad, 6 * 8 * sizeof(float));
cursor += 48;
*vertexCount += 6;
} }
// Advance cursor // Advance cursor
@ -139,7 +146,7 @@ void lovrFontRender(Font* font, const char* str, float wrap, HorizontalAlign hal
} }
// Align the last line // Align the last line
lovrFontAlignLine(vertices, linePtr, cx, halign); lovrFontAlignLine(lineStart, cursor, cx, halign);
// Calculate vertical offset // Calculate vertical offset
if (valign == ALIGN_MIDDLE) { if (valign == ALIGN_MIDDLE) {
@ -274,8 +281,7 @@ void lovrFontAddGlyph(Font* font, Glyph* glyph) {
glyph->y = atlas->y; glyph->y = atlas->y;
// Paste glyph into texture // Paste glyph into texture
lovrGraphicsBindTexture(font->texture, TEXTURE_2D, 0); lovrTextureReplacePixels(font->texture, glyph->data, atlas->x, atlas->y, 0, 0);
glTexSubImage2D(GL_TEXTURE_2D, 0, atlas->x, atlas->y, glyph->tw, glyph->th, GL_RGB, GL_UNSIGNED_BYTE, glyph->data);
// Advance atlas cursor // Advance atlas cursor
atlas->x += glyph->tw + atlas->padding; atlas->x += glyph->tw + atlas->padding;
@ -312,19 +318,13 @@ void lovrFontExpandTexture(Font* font) {
} }
} }
// TODO we only need the TextureData here to clear the texture, but it's a big waste of memory.
// Could look into using glClearTexImage when supported to make this more efficient.
void lovrFontCreateTexture(Font* font) { void lovrFontCreateTexture(Font* font) {
if (font->texture) { lovrRelease(font->texture);
lovrRelease(&font->texture->ref); TextureData* textureData = lovrTextureDataCreate(font->atlas.width, font->atlas.height, 0x0, FORMAT_RGB);
} font->texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, false, false, 0);
lovrTextureSetFilter(font->texture, (TextureFilter) { .mode = FILTER_BILINEAR });
int maxTextureSize = lovrGraphicsGetLimits().textureSize;
if (font->atlas.width > maxTextureSize || font->atlas.height > maxTextureSize) {
lovrThrow("Font texture atlas overflow: exceeded %d x %d", maxTextureSize, maxTextureSize);
}
TextureData* textureData = lovrTextureDataGetBlank(font->atlas.width, font->atlas.height, 0x0, FORMAT_RGB);
TextureFilter filter = { .mode = FILTER_BILINEAR };
font->texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, false);
lovrTextureSetFilter(font->texture, filter);
lovrTextureSetWrap(font->texture, (TextureWrap) { .s = WRAP_CLAMP, .t = WRAP_CLAMP }); lovrTextureSetWrap(font->texture, (TextureWrap) { .s = WRAP_CLAMP, .t = WRAP_CLAMP });
lovrRelease(textureData);
} }

View File

@ -1,9 +1,8 @@
#include "data/rasterizer.h" #include "data/rasterizer.h"
#include "data/vertexData.h"
#include "util.h" #include "util.h"
#include "graphics/texture.h" #include "graphics/texture.h"
#include "math/math.h"
#include "lib/map/map.h" #include "lib/map/map.h"
#include "lib/vec/vec.h"
#include <stdint.h> #include <stdint.h>
#pragma once #pragma once
@ -41,8 +40,8 @@ typedef struct {
} Font; } Font;
Font* lovrFontCreate(Rasterizer* rasterizer); Font* lovrFontCreate(Rasterizer* rasterizer);
void lovrFontDestroy(const Ref* ref); void lovrFontDestroy(void* ref);
void lovrFontRender(Font* font, const char* str, float wrap, HorizontalAlign halign, VerticalAlign valign, vec_float_t* vertices, float* offsety); void lovrFontRender(Font* font, const char* str, float wrap, HorizontalAlign halign, VerticalAlign valign, VertexPointer vertices, float* offsety, uint32_t* vertexCount);
float lovrFontGetWidth(Font* font, const char* string, float wrap); float lovrFontGetWidth(Font* font, const char* string, float wrap);
float lovrFontGetHeight(Font* font); float lovrFontGetHeight(Font* font);
float lovrFontGetAscent(Font* font); float lovrFontGetAscent(Font* font);

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,27 @@
#include "graphics/canvas.h"
#include "graphics/font.h" #include "graphics/font.h"
#include "graphics/material.h" #include "graphics/material.h"
#include "graphics/mesh.h" #include "graphics/mesh.h"
#include "graphics/shader.h" #include "graphics/shader.h"
#include "graphics/texture.h" #include "graphics/texture.h"
#include "math/math.h" #include "math/math.h"
#include "lib/glfw.h" #include "util.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#pragma once #pragma once
#define MAX_VIEWS 4 #define MAX_TRANSFORMS 64
#define MAX_TRANSFORMS 60 #define MAX_PIPELINES 16
#define INTERNAL_TRANSFORMS 4
#define DEFAULT_SHADER_COUNT 4
#define MAX_TEXTURES 16
typedef void (*StencilCallback)(void* userdata); typedef void (*StencilCallback)(void* userdata);
typedef enum {
ARC_MODE_PIE,
ARC_MODE_OPEN,
ARC_MODE_CLOSED
} ArcMode;
typedef enum { typedef enum {
BLEND_ALPHA, BLEND_ALPHA,
BLEND_ADD, BLEND_ADD,
@ -33,45 +38,39 @@ typedef enum {
BLEND_PREMULTIPLIED BLEND_PREMULTIPLIED
} BlendAlphaMode; } BlendAlphaMode;
typedef enum {
COMPARE_NONE,
COMPARE_EQUAL,
COMPARE_NEQUAL,
COMPARE_LESS,
COMPARE_LEQUAL,
COMPARE_GREATER,
COMPARE_GEQUAL
} CompareMode;
typedef enum { typedef enum {
DRAW_MODE_FILL, DRAW_MODE_FILL,
DRAW_MODE_LINE DRAW_MODE_LINE
} DrawMode; } DrawMode;
typedef enum { typedef enum {
ARC_MODE_PIE, STENCIL_REPLACE,
ARC_MODE_OPEN, STENCIL_INCREMENT,
ARC_MODE_CLOSED STENCIL_DECREMENT,
} ArcMode; STENCIL_INCREMENT_WRAP,
STENCIL_DECREMENT_WRAP,
typedef enum { STENCIL_INVERT
WINDING_CLOCKWISE = GL_CW,
WINDING_COUNTERCLOCKWISE = GL_CCW
} Winding;
typedef enum {
COMPARE_NONE = 0,
COMPARE_EQUAL = GL_EQUAL,
COMPARE_NOT_EQUAL = GL_NOTEQUAL,
COMPARE_LESS = GL_LESS,
COMPARE_LEQUAL = GL_LEQUAL,
COMPARE_GEQUAL = GL_GEQUAL,
COMPARE_GREATER = GL_GREATER
} CompareMode;
typedef enum {
STENCIL_REPLACE = GL_REPLACE,
STENCIL_INCREMENT = GL_INCR,
STENCIL_DECREMENT = GL_DECR,
STENCIL_INCREMENT_WRAP = GL_INCR_WRAP,
STENCIL_DECREMENT_WRAP = GL_DECR_WRAP,
STENCIL_INVERT = GL_INVERT
} StencilAction; } StencilAction;
typedef enum { typedef enum {
MATRIX_MODEL, WINDING_CLOCKWISE,
MATRIX_VIEW WINDING_COUNTERCLOCKWISE
} MatrixType; } Winding;
typedef struct {
bool computeShaders;
bool singlepass;
} GpuFeatures;
typedef struct { typedef struct {
bool initialized; bool initialized;
@ -79,39 +78,30 @@ typedef struct {
int textureSize; int textureSize;
int textureMSAA; int textureMSAA;
float textureAnisotropy; float textureAnisotropy;
} GraphicsLimits; } GpuLimits;
typedef struct { typedef struct {
int framebuffer;
float projection[16];
int viewport[4];
} View;
typedef struct {
int drawCalls;
int shaderSwitches; int shaderSwitches;
} GraphicsStats; int drawCalls;
} GpuStats;
typedef struct {
bool stereo;
Canvas* canvas;
float viewMatrix[2][16];
float projection[2][16];
} Camera;
typedef struct { typedef struct {
GLFWwindow* window;
Shader* defaultShaders[DEFAULT_SHADER_COUNT];
DefaultShader defaultShader;
Material* defaultMaterial;
Font* defaultFont;
Texture* defaultTexture;
float transforms[MAX_TRANSFORMS + INTERNAL_TRANSFORMS][2][16];
int transform;
Color backgroundColor; Color backgroundColor;
BlendMode blendMode; BlendMode blendMode;
BlendAlphaMode blendAlphaMode; BlendAlphaMode blendAlphaMode;
Canvas* canvas;
Color color; Color color;
bool culling; bool culling;
TextureFilter defaultFilter;
CompareMode depthTest; CompareMode depthTest;
bool depthWrite; bool depthWrite;
Font* font; Font* font;
bool gammaCorrect;
GraphicsLimits limits;
float lineWidth; float lineWidth;
float pointSize; float pointSize;
Shader* shader; Shader* shader;
@ -119,40 +109,71 @@ typedef struct {
int stencilValue; int stencilValue;
Winding winding; Winding winding;
bool wireframe; bool wireframe;
uint32_t streamVAO; } Pipeline;
uint32_t streamVBO;
uint32_t streamIBO; typedef struct {
vec_float_t streamData; Mesh* mesh;
vec_uint_t streamIndices; MeshDrawMode mode;
View views[MAX_VIEWS]; struct {
int view; uint32_t count;
Texture* textures[MAX_TEXTURES]; float* data;
bool stencilEnabled; } vertex;
bool stencilWriting; struct {
uint32_t program; uint32_t count;
uint32_t vertexArray; uint16_t* data;
uint32_t vertexBuffer; } index;
uint32_t indexBuffer; struct {
GraphicsStats stats; int start;
int count;
} range;
DefaultShader shader;
Material* material;
Texture* textures[MAX_MATERIAL_TEXTURES];
mat4 transform;
bool forceMono;
int instances;
} DrawCommand;
typedef struct {
bool initialized;
bool gammaCorrect;
int width;
int height;
void* window;
Camera camera;
Shader* defaultShaders[MAX_DEFAULT_SHADERS];
Material* defaultMaterial;
Font* defaultFont;
Mesh* defaultMesh;
TextureFilter defaultFilter;
float transforms[MAX_TRANSFORMS][16];
int transform;
Pipeline pipelines[MAX_PIPELINES];
int pipeline;
} GraphicsState; } GraphicsState;
// Base // Base
void lovrGraphicsInit(); void lovrGraphicsInit(bool gammaCorrect);
void lovrGraphicsDestroy(); void lovrGraphicsDestroy();
void lovrGraphicsReset();
void lovrGraphicsClear(bool clearColor, bool clearDepth, bool clearStencil, Color color, float depth, int stencil);
void lovrGraphicsPresent(); void lovrGraphicsPresent();
void lovrGraphicsPrepare(Material* material, float* pose);
void lovrGraphicsCreateWindow(int w, int h, bool fullscreen, int msaa, const char* title, const char* icon); void lovrGraphicsCreateWindow(int w, int h, bool fullscreen, int msaa, const char* title, const char* icon);
int lovrGraphicsGetWidth(); int lovrGraphicsGetWidth();
int lovrGraphicsGetHeight(); int lovrGraphicsGetHeight();
GraphicsStats lovrGraphicsGetStats(); void lovrGraphicsSetCamera(Camera* camera, bool clear);
#define lovrGraphicsGetSupported lovrGpuGetSupported
#define lovrGraphicsGetLimits lovrGpuGetLimits
#define lovrGraphicsGetStats lovrGpuGetStats
// State // State
void lovrGraphicsReset();
void lovrGraphicsPushPipeline();
void lovrGraphicsPopPipeline();
Color lovrGraphicsGetBackgroundColor(); Color lovrGraphicsGetBackgroundColor();
void lovrGraphicsSetBackgroundColor(Color color); void lovrGraphicsSetBackgroundColor(Color color);
void lovrGraphicsGetBlendMode(BlendMode* mode, BlendAlphaMode* alphaMode); void lovrGraphicsGetBlendMode(BlendMode* mode, BlendAlphaMode* alphaMode);
void lovrGraphicsSetBlendMode(BlendMode mode, BlendAlphaMode alphaMode); void lovrGraphicsSetBlendMode(BlendMode mode, BlendAlphaMode alphaMode);
Canvas* lovrGraphicsGetCanvas();
void lovrGraphicsSetCanvas(Canvas* canvas);
Color lovrGraphicsGetColor(); Color lovrGraphicsGetColor();
void lovrGraphicsSetColor(Color color); void lovrGraphicsSetColor(Color color);
bool lovrGraphicsIsCullingEnabled(); bool lovrGraphicsIsCullingEnabled();
@ -164,8 +185,6 @@ void lovrGraphicsSetDepthTest(CompareMode depthTest, bool write);
Font* lovrGraphicsGetFont(); Font* lovrGraphicsGetFont();
void lovrGraphicsSetFont(Font* font); void lovrGraphicsSetFont(Font* font);
bool lovrGraphicsIsGammaCorrect(); bool lovrGraphicsIsGammaCorrect();
void lovrGraphicsSetGammaCorrect(bool gammaCorrect);
GraphicsLimits lovrGraphicsGetLimits();
float lovrGraphicsGetLineWidth(); float lovrGraphicsGetLineWidth();
void lovrGraphicsSetLineWidth(float width); void lovrGraphicsSetLineWidth(float width);
float lovrGraphicsGetPointSize(); float lovrGraphicsGetPointSize();
@ -183,17 +202,19 @@ void lovrGraphicsSetWireframe(bool wireframe);
void lovrGraphicsPush(); void lovrGraphicsPush();
void lovrGraphicsPop(); void lovrGraphicsPop();
void lovrGraphicsOrigin(); void lovrGraphicsOrigin();
void lovrGraphicsTranslate(MatrixType type, float x, float y, float z); void lovrGraphicsTranslate(float x, float y, float z);
void lovrGraphicsRotate(MatrixType type, float angle, float ax, float ay, float az); void lovrGraphicsRotate(float angle, float ax, float ay, float az);
void lovrGraphicsScale(MatrixType type, float x, float y, float z); void lovrGraphicsScale(float x, float y, float z);
void lovrGraphicsMatrixTransform(MatrixType type, mat4 transform); void lovrGraphicsMatrixTransform(mat4 transform);
// Primitives // Rendering
void lovrGraphicsPoints(float* points, int count); VertexPointer lovrGraphicsGetVertexPointer(uint32_t capacity);
void lovrGraphicsLine(float* points, int count); void lovrGraphicsClear(Color* color, float* depth, int* stencil);
void lovrGraphicsTriangle(DrawMode mode, Material* material, float* points); void lovrGraphicsDraw(DrawCommand* draw);
void lovrGraphicsPoints(uint32_t count);
void lovrGraphicsLine(uint32_t count);
void lovrGraphicsTriangle(DrawMode mode, Material* material, float points[9]);
void lovrGraphicsPlane(DrawMode mode, Material* material, mat4 transform); void lovrGraphicsPlane(DrawMode mode, Material* material, mat4 transform);
void lovrGraphicsPlaneFullscreen(Texture* texture);
void lovrGraphicsBox(DrawMode mode, Material* material, mat4 transform); void lovrGraphicsBox(DrawMode mode, Material* material, mat4 transform);
void lovrGraphicsArc(DrawMode mode, ArcMode, Material* material, mat4 transform, float theta1, float theta2, int segments); void lovrGraphicsArc(DrawMode mode, ArcMode, Material* material, mat4 transform, float theta1, float theta2, int segments);
void lovrGraphicsCircle(DrawMode mode, Material* material, mat4 transform, int segments); void lovrGraphicsCircle(DrawMode mode, Material* material, mat4 transform, int segments);
@ -201,23 +222,23 @@ void lovrGraphicsCylinder(Material* material, float x1, float y1, float z1, floa
void lovrGraphicsSphere(Material* material, mat4 transform, int segments); void lovrGraphicsSphere(Material* material, mat4 transform, int segments);
void lovrGraphicsSkybox(Texture* texture, float angle, float ax, float ay, float az); void lovrGraphicsSkybox(Texture* texture, float angle, float ax, float ay, float az);
void lovrGraphicsPrint(const char* str, mat4 transform, float wrap, HorizontalAlign halign, VerticalAlign valign); void lovrGraphicsPrint(const char* str, mat4 transform, float wrap, HorizontalAlign halign, VerticalAlign valign);
void lovrGraphicsStencil(StencilAction action, int replaceValue, StencilCallback callback, void* userdata); void lovrGraphicsFill(Texture* texture, float u, float v, float w, float h);
#define lovrGraphicsStencil lovrGpuStencil
#define lovrGraphicsCompute lovrGpuCompute
// Internal State // GPU
void lovrGraphicsPushView();
void lovrGraphicsPopView(); typedef void (*gpuProc)(void);
mat4 lovrGraphicsGetProjection();
void lovrGraphicsSetProjection(mat4 projection); void lovrGpuInit(bool srgb, gpuProc (*getProcAddress)(const char*));
void lovrGraphicsSetViewport(int x, int y, int w, int h); void lovrGpuDestroy();
void lovrGraphicsBindFramebuffer(int framebuffer); void lovrGpuBindPipeline(Pipeline* pipeline);
Texture* lovrGraphicsGetTexture(int slot); void lovrGpuSetViewports(float* viewports, int count);
void lovrGraphicsBindTexture(Texture* texture, TextureType type, int slot); void lovrGpuClear(Canvas* canvas, Color* color, float* depth, int* stencil);
Material* lovrGraphicsGetDefaultMaterial(); void lovrGpuStencil(StencilAction action, int replaceValue, StencilCallback callback, void* userdata);
void lovrGraphicsSetDefaultShader(DefaultShader defaultShader); void lovrGpuCompute(Shader* shader, int x, int y, int z);
Shader* lovrGraphicsGetActiveShader(); void lovrGpuPresent();
void lovrGraphicsUseProgram(uint32_t program); void lovrGpuDirtyTexture();
void lovrGraphicsBindVertexArray(uint32_t vao); const GpuFeatures* lovrGpuGetSupported();
void lovrGraphicsBindVertexBuffer(uint32_t vbo); const GpuLimits* lovrGpuGetLimits();
void lovrGraphicsBindIndexBuffer(uint32_t ibo); const GpuStats* lovrGpuGetStats();
void lovrGraphicsDrawArrays(GLenum mode, size_t start, size_t count, int instances);
void lovrGraphicsDrawElements(GLenum mode, size_t count, size_t indexSize, size_t offset, int instances);

View File

@ -1,33 +1,32 @@
#include "graphics/graphics.h"
#include "graphics/material.h" #include "graphics/material.h"
#include <math.h>
#include <stdlib.h>
Material* lovrMaterialCreate(bool isDefault) { Material* lovrMaterialCreate() {
Material* material = lovrAlloc(sizeof(Material), lovrMaterialDestroy); Material* material = lovrAlloc(Material, lovrMaterialDestroy);
if (!material) return NULL; if (!material) return NULL;
material->isDefault = isDefault;
for (int i = 0; i < MAX_MATERIAL_SCALARS; i++) { for (int i = 0; i < MAX_MATERIAL_SCALARS; i++) {
material->scalars[i] = 1.f; material->scalars[i] = 1.f;
} }
for (int i = 0; i < MAX_MATERIAL_COLORS; i++) { for (int i = 0; i < MAX_MATERIAL_COLORS; i++) {
material->colors[i] = (Color) { 1, 1, 1, 1 }; if (i == COLOR_EMISSIVE) {
material->colors[i] = (Color) { 0, 0, 0, 0 };
} else {
material->colors[i] = (Color) { 1, 1, 1, 1 };
}
} }
for (int i = 0; i < MAX_MATERIAL_TEXTURES; i++) { lovrMaterialSetTransform(material, 0, 0, 1, 1, 0);
material->textures[i] = NULL;
}
return material; return material;
} }
void lovrMaterialDestroy(const Ref* ref) { void lovrMaterialDestroy(void* ref) {
Material* material = containerof(ref, Material); Material* material = ref;
for (int i = 0; i < MAX_MATERIAL_TEXTURES; i++) { for (int i = 0; i < MAX_MATERIAL_TEXTURES; i++) {
if (material->textures[i]) { lovrRelease(material->textures[i]);
lovrRelease(&material->textures[i]->ref);
}
} }
free(material); free(material);
} }
@ -54,14 +53,30 @@ Texture* lovrMaterialGetTexture(Material* material, MaterialTexture textureType)
void lovrMaterialSetTexture(Material* material, MaterialTexture textureType, Texture* texture) { void lovrMaterialSetTexture(Material* material, MaterialTexture textureType, Texture* texture) {
if (texture != material->textures[textureType]) { if (texture != material->textures[textureType]) {
if (material->textures[textureType]) { lovrRetain(texture);
lovrRelease(&material->textures[textureType]->ref); lovrRelease(material->textures[textureType]);
}
material->textures[textureType] = texture; material->textures[textureType] = texture;
if (texture) {
lovrRetain(&texture->ref);
}
} }
} }
void lovrMaterialGetTransform(Material* material, float* ox, float* oy, float* sx, float* sy, float* angle) {
*ox = material->transform[6];
*oy = material->transform[7];
*sx = sqrt(material->transform[0] * material->transform[0] + material->transform[1] * material->transform[1]);
*sy = sqrt(material->transform[3] * material->transform[3] + material->transform[4] * material->transform[4]);
*angle = atan2(-material->transform[3], material->transform[0]);
}
void lovrMaterialSetTransform(Material* material, float ox, float oy, float sx, float sy, float angle) {
float c = cos(angle);
float s = sin(angle);
material->transform[0] = c * sx;
material->transform[1] = s * sx;
material->transform[2] = 0;
material->transform[3] = -s * sy;
material->transform[4] = c * sy;
material->transform[5] = 0;
material->transform[6] = ox;
material->transform[7] = oy;
material->transform[8] = 1;
}

View File

@ -32,14 +32,17 @@ typedef struct {
float scalars[MAX_MATERIAL_SCALARS]; float scalars[MAX_MATERIAL_SCALARS];
Color colors[MAX_MATERIAL_COLORS]; Color colors[MAX_MATERIAL_COLORS];
Texture* textures[MAX_MATERIAL_TEXTURES]; Texture* textures[MAX_MATERIAL_TEXTURES];
float transform[9];
bool isDefault; bool isDefault;
} Material; } Material;
Material* lovrMaterialCreate(bool isDefault); Material* lovrMaterialCreate();
void lovrMaterialDestroy(const Ref* ref); void lovrMaterialDestroy(void* ref);
float lovrMaterialGetScalar(Material* material, MaterialScalar scalarType); float lovrMaterialGetScalar(Material* material, MaterialScalar scalarType);
void lovrMaterialSetScalar(Material* material, MaterialScalar scalarType, float value); void lovrMaterialSetScalar(Material* material, MaterialScalar scalarType, float value);
Color lovrMaterialGetColor(Material* material, MaterialColor colorType); Color lovrMaterialGetColor(Material* material, MaterialColor colorType);
void lovrMaterialSetColor(Material* material, MaterialColor colorType, Color color); void lovrMaterialSetColor(Material* material, MaterialColor colorType, Color color);
Texture* lovrMaterialGetTexture(Material* material, MaterialTexture textureType); Texture* lovrMaterialGetTexture(Material* material, MaterialTexture textureType);
void lovrMaterialSetTexture(Material* material, MaterialTexture textureType, Texture* texture); void lovrMaterialSetTexture(Material* material, MaterialTexture textureType, Texture* texture);
void lovrMaterialGetTransform(Material* material, float* ox, float* oy, float* sx, float* sy, float* angle);
void lovrMaterialSetTransform(Material* material, float ox, float oy, float sx, float sy, float angle);

View File

@ -1,278 +0,0 @@
#include "graphics/mesh.h"
#include "graphics/graphics.h"
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
static void lovrMeshBindAttributes(Mesh* mesh) {
Shader* shader = lovrGraphicsGetActiveShader();
if (shader == mesh->lastShader && !mesh->attributesDirty) {
return;
}
lovrGraphicsBindVertexBuffer(mesh->vbo);
VertexFormat* format = &mesh->vertexData->format;
for (int i = 0; i < format->count; i++) {
Attribute attribute = format->attributes[i];
int location = lovrShaderGetAttributeId(shader, attribute.name);
if (location >= 0) {
if (mesh->enabledAttributes & (1 << i)) {
glEnableVertexAttribArray(location);
GLenum glType;
switch (attribute.type) {
case ATTR_FLOAT: glType = GL_FLOAT; break;
case ATTR_BYTE: glType = GL_UNSIGNED_BYTE; break;
case ATTR_INT: glType = GL_UNSIGNED_INT; break;
}
if (attribute.type == ATTR_INT) {
glVertexAttribIPointer(location, attribute.count, glType, format->stride, (void*) attribute.offset);
} else {
glVertexAttribPointer(location, attribute.count, glType, GL_TRUE, format->stride, (void*) attribute.offset);
}
} else {
glDisableVertexAttribArray(location);
}
}
}
mesh->lastShader = shader;
mesh->attributesDirty = false;
}
Mesh* lovrMeshCreate(uint32_t count, VertexFormat* format, MeshDrawMode drawMode, MeshUsage usage) {
Mesh* mesh = lovrAlloc(sizeof(Mesh), lovrMeshDestroy);
if (!mesh) return NULL;
#ifdef EMSCRIPTEN
mesh->vertexData = lovrVertexDataCreate(count, format, false);
#else
mesh->vertexData = lovrVertexDataCreate(count, format, true);
#endif
mesh->indices.raw = NULL;
mesh->indexCount = 0;
mesh->indexSize = count > USHRT_MAX ? sizeof(uint32_t) : sizeof(uint16_t);
mesh->enabledAttributes = ~0;
mesh->attributesDirty = true;
mesh->isMapped = false;
mesh->mapStart = 0;
mesh->mapCount = 0;
mesh->isRangeEnabled = false;
mesh->rangeStart = 0;
mesh->rangeCount = count;
mesh->drawMode = drawMode;
mesh->usage = usage;
mesh->vao = 0;
mesh->vbo = 0;
mesh->ibo = 0;
mesh->material = NULL;
mesh->lastShader = NULL;
glGenBuffers(1, &mesh->vbo);
glGenBuffers(1, &mesh->ibo);
lovrGraphicsBindVertexBuffer(mesh->vbo);
glBufferData(GL_ARRAY_BUFFER, count * mesh->vertexData->format.stride, NULL, mesh->usage);
glGenVertexArrays(1, &mesh->vao);
return mesh;
}
void lovrMeshDestroy(const Ref* ref) {
Mesh* mesh = containerof(ref, Mesh);
if (mesh->material) {
lovrRelease(&mesh->material->ref);
}
lovrRelease(&mesh->vertexData->ref);
glDeleteBuffers(1, &mesh->vbo);
glDeleteBuffers(1, &mesh->ibo);
glDeleteVertexArrays(1, &mesh->vao);
free(mesh->indices.raw);
free(mesh);
}
void lovrMeshDraw(Mesh* mesh, mat4 transform, float* pose, int instances) {
if (mesh->isMapped) {
lovrMeshUnmap(mesh);
}
if (transform) {
lovrGraphicsPush();
lovrGraphicsMatrixTransform(MATRIX_MODEL, transform);
}
lovrGraphicsSetDefaultShader(SHADER_DEFAULT);
lovrGraphicsPrepare(mesh->material, pose);
lovrGraphicsBindVertexArray(mesh->vao);
lovrMeshBindAttributes(mesh);
size_t start = mesh->rangeStart;
size_t count = mesh->rangeCount;
if (mesh->indexCount > 0) {
size_t offset = start * mesh->indexSize;
count = mesh->isRangeEnabled ? mesh->rangeCount : mesh->indexCount;
lovrGraphicsDrawElements(mesh->drawMode, count, mesh->indexSize, offset, instances);
} else {
lovrGraphicsDrawArrays(mesh->drawMode, start, count, instances);
}
if (transform) {
lovrGraphicsPop();
}
}
VertexFormat* lovrMeshGetVertexFormat(Mesh* mesh) {
return &mesh->vertexData->format;
}
MeshDrawMode lovrMeshGetDrawMode(Mesh* mesh) {
return mesh->drawMode;
}
void lovrMeshSetDrawMode(Mesh* mesh, MeshDrawMode drawMode) {
mesh->drawMode = drawMode;
}
int lovrMeshGetVertexCount(Mesh* mesh) {
return mesh->vertexData->count;
}
IndexPointer lovrMeshGetVertexMap(Mesh* mesh, size_t* count) {
if (count) {
*count = mesh->indexCount;
}
return mesh->indices;
}
void lovrMeshSetVertexMap(Mesh* mesh, void* data, size_t count) {
if (!data || count == 0) {
mesh->indexCount = 0;
return;
}
if (mesh->indexCount < count) {
mesh->indices.raw = realloc(mesh->indices.raw, count * mesh->indexSize);
}
mesh->indexCount = count;
memcpy(mesh->indices.raw, data, mesh->indexCount * mesh->indexSize);
lovrGraphicsBindVertexArray(mesh->vao);
lovrGraphicsBindIndexBuffer(mesh->ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh->indexCount * mesh->indexSize, mesh->indices.raw, GL_STATIC_DRAW);
}
bool lovrMeshIsAttributeEnabled(Mesh* mesh, const char* name) {
for (int i = 0; i < mesh->vertexData->format.count; i++) {
if (!strcmp(mesh->vertexData->format.attributes[i].name, name)) {
return mesh->enabledAttributes & (1 << i);
}
}
return false;
}
void lovrMeshSetAttributeEnabled(Mesh* mesh, const char* name, bool enable) {
for (int i = 0; i < mesh->vertexData->format.count; i++) {
if (!strcmp(mesh->vertexData->format.attributes[i].name, name)) {
int mask = 1 << i;
if (enable && !(mesh->enabledAttributes & mask)) {
mesh->enabledAttributes |= mask;
mesh->attributesDirty = true;
} else if (!enable && (mesh->enabledAttributes & mask)) {
mesh->enabledAttributes &= ~(1 << i);
mesh->attributesDirty = true;
}
}
}
}
bool lovrMeshIsRangeEnabled(Mesh* mesh) {
return mesh->isRangeEnabled;
}
void lovrMeshSetRangeEnabled(Mesh* mesh, char isEnabled) {
mesh->isRangeEnabled = isEnabled;
if (!isEnabled) {
mesh->rangeStart = 0;
mesh->rangeCount = mesh->vertexData->count;
}
}
void lovrMeshGetDrawRange(Mesh* mesh, int* start, int* count) {
*start = mesh->rangeStart;
*count = mesh->rangeCount;
}
void lovrMeshSetDrawRange(Mesh* mesh, int start, int count) {
size_t limit = mesh->indexCount > 0 ? mesh->indexCount : mesh->vertexData->count;
bool isValidRange = start >= 0 && count >= 0 && (size_t) start + count <= limit;
lovrAssert(isValidRange, "Invalid mesh draw range [%d, %d]", start + 1, start + count + 1);
mesh->rangeStart = start;
mesh->rangeCount = count;
}
Material* lovrMeshGetMaterial(Mesh* mesh) {
return mesh->material;
}
void lovrMeshSetMaterial(Mesh* mesh, Material* material) {
if (mesh->material != material) {
if (mesh->material) {
lovrRelease(&mesh->material->ref);
}
mesh->material = material;
if (material) {
lovrRetain(&material->ref);
}
}
}
VertexPointer lovrMeshMap(Mesh* mesh, int start, size_t count, bool read, bool write) {
#ifdef EMSCRIPTEN
mesh->isMapped = true;
mesh->mapStart = start;
mesh->mapCount = count;
void* p = mesh->vertexData->data.bytes + start * mesh->vertexData->format.stride;
return (VertexPointer) { .raw = p };
#else
if (mesh->isMapped) {
lovrMeshUnmap(mesh);
}
mesh->isMapped = true;
mesh->mapStart = start;
mesh->mapCount = count;
size_t stride = mesh->vertexData->format.stride;
GLbitfield access = 0;
access |= read ? GL_MAP_READ_BIT : 0;
access |= write ? GL_MAP_WRITE_BIT : 0;
access |= (write && start == 0 && count == mesh->vertexData->count) ? GL_MAP_INVALIDATE_BUFFER_BIT : 0;
lovrGraphicsBindVertexBuffer(mesh->vbo);
mesh->vertexData->data.raw = glMapBufferRange(GL_ARRAY_BUFFER, start * stride, count * stride, access);
return mesh->vertexData->data;
#endif
}
void lovrMeshUnmap(Mesh* mesh) {
if (!mesh->isMapped) {
return;
}
mesh->isMapped = false;
lovrGraphicsBindVertexBuffer(mesh->vbo);
#ifdef EMSCRIPTEN
size_t stride = mesh->vertexData->format.stride;
size_t start = mesh->mapStart * stride;
size_t count = mesh->mapCount * stride;
glBufferSubData(GL_ARRAY_BUFFER, start, count, mesh->vertexData->data.bytes + start);
#else
glUnmapBuffer(GL_ARRAY_BUFFER);
#endif
}

View File

@ -1,66 +1,45 @@
#include "graphics/shader.h"
#include "graphics/material.h" #include "graphics/material.h"
#include "graphics/shader.h"
#include "data/vertexData.h" #include "data/vertexData.h"
#include "math/math.h" #include <stdbool.h>
#include "lib/glfw.h"
#include "util.h"
#pragma once #pragma once
#define MAX_ATTACHMENTS 16
typedef enum { typedef enum {
MESH_POINTS = GL_POINTS, MESH_POINTS,
MESH_LINES = GL_LINES, MESH_LINES,
MESH_LINE_STRIP = GL_LINE_STRIP, MESH_LINE_STRIP,
MESH_TRIANGLE_STRIP = GL_TRIANGLE_STRIP, MESH_LINE_LOOP,
MESH_TRIANGLES = GL_TRIANGLES, MESH_TRIANGLE_STRIP,
MESH_TRIANGLE_FAN = GL_TRIANGLE_FAN MESH_TRIANGLES,
MESH_TRIANGLE_FAN
} MeshDrawMode; } MeshDrawMode;
typedef enum { typedef struct Mesh Mesh;
MESH_STATIC = GL_STATIC_DRAW,
MESH_DYNAMIC = GL_DYNAMIC_DRAW,
MESH_STREAM = GL_STREAM_DRAW
} MeshUsage;
typedef struct { Mesh* lovrMeshCreate(uint32_t count, VertexFormat format, MeshDrawMode drawMode, BufferUsage usage);
Ref ref; void lovrMeshDestroy(void* ref);
VertexData* vertexData; void lovrMeshAttachAttribute(Mesh* mesh, Mesh* other, const char* name, int divisor);
IndexPointer indices; void lovrMeshDetachAttribute(Mesh* mesh, const char* name);
size_t indexCount; void lovrMeshBind(Mesh* mesh, Shader* shader, int divisorMultiplier);
size_t indexSize; void lovrMeshDraw(Mesh* mesh, int instances);
int enabledAttributes;
bool attributesDirty;
bool isMapped;
int mapStart;
size_t mapCount;
bool isRangeEnabled;
int rangeStart;
int rangeCount;
MeshDrawMode drawMode;
MeshUsage usage;
GLuint vao;
GLuint vbo;
GLuint ibo;
Material* material;
Shader* lastShader;
} Mesh;
Mesh* lovrMeshCreate(uint32_t count, VertexFormat* format, MeshDrawMode drawMode, MeshUsage usage);
void lovrMeshDestroy(const Ref* ref);
void lovrMeshDraw(Mesh* mesh, mat4 transform, float* pose, int instances);
VertexFormat* lovrMeshGetVertexFormat(Mesh* mesh); VertexFormat* lovrMeshGetVertexFormat(Mesh* mesh);
MeshDrawMode lovrMeshGetDrawMode(Mesh* mesh); MeshDrawMode lovrMeshGetDrawMode(Mesh* mesh);
void lovrMeshSetDrawMode(Mesh* mesh, MeshDrawMode drawMode); void lovrMeshSetDrawMode(Mesh* mesh, MeshDrawMode drawMode);
int lovrMeshGetVertexCount(Mesh* mesh); int lovrMeshGetVertexCount(Mesh* mesh);
IndexPointer lovrMeshGetVertexMap(Mesh* mesh, size_t* count);
void lovrMeshSetVertexMap(Mesh* mesh, void* data, size_t count);
bool lovrMeshIsAttributeEnabled(Mesh* mesh, const char* name); bool lovrMeshIsAttributeEnabled(Mesh* mesh, const char* name);
void lovrMeshSetAttributeEnabled(Mesh* mesh, const char* name, bool enabled); void lovrMeshSetAttributeEnabled(Mesh* mesh, const char* name, bool enabled);
bool lovrMeshIsRangeEnabled(Mesh* mesh); void lovrMeshGetDrawRange(Mesh* mesh, uint32_t* start, uint32_t* count);
void lovrMeshSetRangeEnabled(Mesh* mesh, char isEnabled); void lovrMeshSetDrawRange(Mesh* mesh, uint32_t start, uint32_t count);
void lovrMeshGetDrawRange(Mesh* mesh, int* start, int* count);
void lovrMeshSetDrawRange(Mesh* mesh, int start, int count);
Material* lovrMeshGetMaterial(Mesh* mesh); Material* lovrMeshGetMaterial(Mesh* mesh);
void lovrMeshSetMaterial(Mesh* mesh, Material* material); void lovrMeshSetMaterial(Mesh* mesh, Material* material);
VertexPointer lovrMeshMap(Mesh* mesh, int start, size_t count, bool read, bool write); float* lovrMeshGetPose(Mesh* mesh);
void lovrMeshUnmap(Mesh* mesh); void lovrMeshSetPose(Mesh* mesh, float* pose);
VertexPointer lovrMeshMapVertices(Mesh* mesh, uint32_t start, uint32_t count, bool read, bool write);
void lovrMeshUnmapVertices(Mesh* mesh);
IndexPointer lovrMeshReadIndices(Mesh* mesh, uint32_t* count, size_t* size);
IndexPointer lovrMeshWriteIndices(Mesh* mesh, uint32_t count, size_t size);
void lovrMeshUnmapIndices(Mesh* mesh);
void lovrMeshResize(Mesh* mesh, uint32_t count);

View File

@ -1,16 +1,18 @@
#include "graphics/model.h" #include "graphics/model.h"
#include "graphics/graphics.h" #include "graphics/graphics.h"
#include "graphics/shader.h"
#include "data/blob.h"
#include "data/textureData.h"
#include "data/vertexData.h"
#include "math/mat4.h" #include "math/mat4.h"
#include "math/vec3.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <stdio.h>
static void renderNode(Model* model, int nodeIndex, int instances) { static void renderNode(Model* model, int nodeIndex, int instances) {
ModelNode* node = &model->modelData->nodes[nodeIndex]; ModelNode* node = &model->modelData->nodes[nodeIndex];
if (node->primitives.length > 0) { if (node->primitives.length > 0) {
lovrGraphicsPush();
lovrGraphicsMatrixTransform(MATRIX_MODEL, model->nodeTransforms[nodeIndex]);
float globalInverse[16]; float globalInverse[16];
if (model->animator) { if (model->animator) {
mat4_set(globalInverse, model->nodeTransforms[nodeIndex]); mat4_set(globalInverse, model->nodeTransforms[nodeIndex]);
@ -38,10 +40,14 @@ static void renderNode(Model* model, int nodeIndex, int instances) {
} }
lovrMeshSetDrawRange(model->mesh, primitive->drawStart, primitive->drawCount); lovrMeshSetDrawRange(model->mesh, primitive->drawStart, primitive->drawCount);
lovrMeshDraw(model->mesh, NULL, (float*) model->pose, instances); lovrMeshSetPose(model->mesh, (float*) model->pose);
lovrGraphicsDraw(&(DrawCommand) {
.transform = model->nodeTransforms[nodeIndex],
.mesh = model->mesh,
.material = lovrMeshGetMaterial(model->mesh),
.instances = instances
});
} }
lovrGraphicsPop();
} }
for (int i = 0; i < node->children.length; i++) { for (int i = 0; i < node->children.length; i++) {
@ -50,54 +56,59 @@ static void renderNode(Model* model, int nodeIndex, int instances) {
} }
Model* lovrModelCreate(ModelData* modelData) { Model* lovrModelCreate(ModelData* modelData) {
Model* model = lovrAlloc(sizeof(Model), lovrModelDestroy); Model* model = lovrAlloc(Model, lovrModelDestroy);
if (!model) return NULL; if (!model) return NULL;
lovrRetain(&modelData->ref); lovrRetain(modelData);
model->modelData = modelData; model->modelData = modelData;
model->aabbDirty = true; model->aabbDirty = true;
model->animator = NULL;
model->material = NULL;
model->mesh = lovrMeshCreate(modelData->vertexData->count, &modelData->vertexData->format, MESH_TRIANGLES, MESH_STATIC); model->mesh = lovrMeshCreate(modelData->vertexData->count, modelData->vertexData->format, MESH_TRIANGLES, USAGE_STATIC);
VertexPointer vertices = lovrMeshMap(model->mesh, 0, modelData->vertexData->count, false, true); VertexPointer vertices = lovrMeshMapVertices(model->mesh, 0, modelData->vertexData->count, false, true);
memcpy(vertices.raw, modelData->vertexData->data.raw, modelData->vertexData->count * modelData->vertexData->format.stride); memcpy(vertices.raw, modelData->vertexData->blob.data, modelData->vertexData->count * modelData->vertexData->format.stride);
lovrMeshUnmap(model->mesh);
lovrMeshSetVertexMap(model->mesh, modelData->indices.raw, modelData->indexCount); IndexPointer indices = lovrMeshWriteIndices(model->mesh, modelData->indexCount, modelData->indexSize);
lovrMeshSetRangeEnabled(model->mesh, true); memcpy(indices.raw, modelData->indices.raw, modelData->indexCount * modelData->indexSize);
if (modelData->textures.length > 0) { if (modelData->textures.length > 0) {
model->textures = malloc(modelData->textures.length * sizeof(Texture*)); model->textures = calloc(modelData->textures.length, sizeof(Texture*));
for (int i = 0; i < modelData->textures.length; i++) {
if (modelData->textures.data[i]) {
model->textures[i] = lovrTextureCreate(TEXTURE_2D, (TextureData**) &modelData->textures.data[i], 1, true);
} else {
model->textures[i] = NULL;
}
}
} else {
model->textures = NULL;
} }
if (modelData->materialCount > 0) { if (modelData->materialCount > 0) {
model->materials = malloc(modelData->materialCount * sizeof(Material*)); model->materials = calloc(modelData->materialCount, sizeof(Material*));
for (int i = 0; i < modelData->materialCount; i++) { for (int i = 0; i < modelData->materialCount; i++) {
ModelMaterial* materialData = &modelData->materials[i]; ModelMaterial* materialData = &modelData->materials[i];
Material* material = lovrMaterialCreate(false); Material* material = lovrMaterialCreate();
lovrMaterialSetScalar(material, SCALAR_METALNESS, materialData->metalness); lovrMaterialSetScalar(material, SCALAR_METALNESS, materialData->metalness);
lovrMaterialSetScalar(material, SCALAR_ROUGHNESS, materialData->roughness); lovrMaterialSetScalar(material, SCALAR_ROUGHNESS, materialData->roughness);
lovrMaterialSetColor(material, COLOR_DIFFUSE, materialData->diffuseColor); lovrMaterialSetColor(material, COLOR_DIFFUSE, materialData->diffuseColor);
lovrMaterialSetColor(material, COLOR_EMISSIVE, materialData->emissiveColor); lovrMaterialSetColor(material, COLOR_EMISSIVE, materialData->emissiveColor);
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, model->textures[materialData->diffuseTexture]); for (MaterialTexture textureType = 0; textureType < MAX_MATERIAL_TEXTURES; textureType++) {
lovrMaterialSetTexture(material, TEXTURE_EMISSIVE, model->textures[materialData->emissiveTexture]); int textureIndex = 0;
lovrMaterialSetTexture(material, TEXTURE_METALNESS, model->textures[materialData->metalnessTexture]);
lovrMaterialSetTexture(material, TEXTURE_ROUGHNESS, model->textures[materialData->roughnessTexture]); switch (textureType) {
lovrMaterialSetTexture(material, TEXTURE_OCCLUSION, model->textures[materialData->occlusionTexture]); case TEXTURE_DIFFUSE: textureIndex = materialData->diffuseTexture; break;
lovrMaterialSetTexture(material, TEXTURE_NORMAL, model->textures[materialData->normalTexture]); case TEXTURE_EMISSIVE: textureIndex = materialData->emissiveTexture; break;
case TEXTURE_METALNESS: textureIndex = materialData->metalnessTexture; break;
case TEXTURE_ROUGHNESS: textureIndex = materialData->roughnessTexture; break;
case TEXTURE_OCCLUSION: textureIndex = materialData->occlusionTexture; break;
case TEXTURE_NORMAL: textureIndex = materialData->normalTexture; break;
default: break;
}
if (textureIndex) {
if (!model->textures[textureIndex]) {
TextureData* textureData = modelData->textures.data[textureIndex];
bool srgb = textureType == TEXTURE_DIFFUSE || textureType == TEXTURE_EMISSIVE;
model->textures[textureIndex] = lovrTextureCreate(TEXTURE_2D, &textureData, 1, srgb, true, 0);
}
lovrMaterialSetTexture(material, textureType, model->textures[textureIndex]);
}
}
model->materials[i] = material; model->materials[i] = material;
} }
} else {
model->materials = NULL;
} }
for (int i = 0; i < MAX_BONES; i++) { for (int i = 0; i < MAX_BONES; i++) {
@ -120,26 +131,20 @@ Model* lovrModelCreate(ModelData* modelData) {
return model; return model;
} }
void lovrModelDestroy(const Ref* ref) { void lovrModelDestroy(void* ref) {
Model* model = containerof(ref, Model); Model* model = ref;
for (int i = 0; i < model->modelData->textures.length; i++) { for (int i = 0; i < model->modelData->textures.length; i++) {
if (model->textures[i]) { lovrRelease(model->textures[i]);
lovrRelease(&model->textures[i]->ref);
}
} }
for (int i = 0; i < model->modelData->materialCount; i++) { for (int i = 0; i < model->modelData->materialCount; i++) {
lovrRelease(&model->materials[i]->ref); lovrRelease(model->materials[i]);
}
if (model->animator) {
lovrRelease(&model->animator->ref);
}
if (model->material) {
lovrRelease(&model->material->ref);
} }
lovrRelease(model->animator);
lovrRelease(model->material);
free(model->textures); free(model->textures);
free(model->materials); free(model->materials);
lovrRelease(&model->modelData->ref); lovrRelease(model->modelData);
lovrRelease(&model->mesh->ref); lovrRelease(model->mesh);
free(model->nodeTransforms); free(model->nodeTransforms);
free(model); free(model);
} }
@ -153,8 +158,7 @@ void lovrModelDraw(Model* model, mat4 transform, int instances) {
for (int i = 0; i < model->modelData->nodeCount; i++) { for (int i = 0; i < model->modelData->nodeCount; i++) {
ModelNode* node = &model->modelData->nodes[i]; ModelNode* node = &model->modelData->nodes[i];
float localTransform[16]; float localTransform[16] = MAT4_IDENTITY;
mat4_identity(localTransform);
if (!lovrAnimatorEvaluate(model->animator, node->name, localTransform)) { if (!lovrAnimatorEvaluate(model->animator, node->name, localTransform)) {
mat4_set(localTransform, node->transform); mat4_set(localTransform, node->transform);
} }
@ -174,9 +178,10 @@ void lovrModelDraw(Model* model, mat4 transform, int instances) {
} }
lovrGraphicsPush(); lovrGraphicsPush();
lovrGraphicsMatrixTransform(MATRIX_MODEL, transform); lovrGraphicsMatrixTransform(transform);
renderNode(model, 0, instances); renderNode(model, 0, instances);
lovrGraphicsPop(); lovrGraphicsPop();
lovrMeshSetPose(model->mesh, NULL);
} }
Animator* lovrModelGetAnimator(Model* model) { Animator* lovrModelGetAnimator(Model* model) {
@ -185,15 +190,9 @@ Animator* lovrModelGetAnimator(Model* model) {
void lovrModelSetAnimator(Model* model, Animator* animator) { void lovrModelSetAnimator(Model* model, Animator* animator) {
if (model->animator != animator) { if (model->animator != animator) {
if (model->animator) { lovrRetain(animator);
lovrRelease(&model->animator->ref); lovrRelease(model->animator);
}
model->animator = animator; model->animator = animator;
if (animator) {
lovrRetain(&animator->ref);
}
} }
} }
@ -207,15 +206,9 @@ Material* lovrModelGetMaterial(Model* model) {
void lovrModelSetMaterial(Model* model, Material* material) { void lovrModelSetMaterial(Model* model, Material* material) {
if (model->material != material) { if (model->material != material) {
if (model->material) { lovrRetain(material);
lovrRelease(&model->material->ref); lovrRelease(model->material);
}
model->material = material; model->material = material;
if (material) {
lovrRetain(&material->ref);
}
} }
} }

View File

@ -3,8 +3,7 @@
#include "graphics/material.h" #include "graphics/material.h"
#include "graphics/mesh.h" #include "graphics/mesh.h"
#include "graphics/texture.h" #include "graphics/texture.h"
#include "math/math.h" #include "math/mat4.h"
#include "lib/glfw.h"
#include "util.h" #include "util.h"
#include <stdbool.h> #include <stdbool.h>
@ -25,7 +24,7 @@ typedef struct {
} Model; } Model;
Model* lovrModelCreate(ModelData* modelData); Model* lovrModelCreate(ModelData* modelData);
void lovrModelDestroy(const Ref* ref); void lovrModelDestroy(void* ref);
void lovrModelDraw(Model* model, mat4 transform, int instances); void lovrModelDraw(Model* model, mat4 transform, int instances);
Animator* lovrModelGetAnimator(Model* model); Animator* lovrModelGetAnimator(Model* model);
void lovrModelSetAnimator(Model* model, Animator* animator); void lovrModelSetAnimator(Model* model, Animator* animator);

2440
src/graphics/opengl.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,358 +0,0 @@
#include "graphics/shader.h"
#include "graphics/graphics.h"
#include "resources/shaders.h"
#include "math/mat4.h"
#include <stdio.h>
#include <stdlib.h>
static UniformType getUniformType(GLenum type, const char* debug) {
switch (type) {
case GL_FLOAT:
case GL_FLOAT_VEC2:
case GL_FLOAT_VEC3:
case GL_FLOAT_VEC4:
return UNIFORM_FLOAT;
case GL_INT:
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4:
return UNIFORM_INT;
case GL_FLOAT_MAT2:
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT4:
return UNIFORM_MATRIX;
case GL_SAMPLER_2D:
case GL_SAMPLER_CUBE:
return UNIFORM_SAMPLER;
default:
lovrThrow("Unsupported type for uniform '%s'", debug);
return UNIFORM_FLOAT;
}
}
static int getUniformComponents(GLenum type) {
switch (type) {
case GL_FLOAT:
case GL_INT:
case GL_SAMPLER_2D:
case GL_SAMPLER_CUBE:
return 1;
case GL_FLOAT_VEC2:
case GL_INT_VEC2:
case GL_FLOAT_MAT2:
return 2;
case GL_FLOAT_VEC3:
case GL_INT_VEC3:
case GL_FLOAT_MAT3:
return 3;
case GL_FLOAT_VEC4:
case GL_INT_VEC4:
case GL_FLOAT_MAT4:
return 4;
default:
return 1;
}
}
static GLuint compileShader(GLenum type, const char* source) {
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, (const GLchar**) &source, NULL);
glCompileShader(shader);
int isShaderCompiled;
glGetShaderiv(shader, GL_COMPILE_STATUS, &isShaderCompiled);
if (!isShaderCompiled) {
int logLength;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
char* log = malloc(logLength);
glGetShaderInfoLog(shader, logLength, &logLength, log);
lovrThrow("Could not compile shader %s", log);
}
return shader;
}
static GLuint linkShaders(GLuint vertexShader, GLuint fragmentShader) {
GLuint program = glCreateProgram();
if (vertexShader) {
glAttachShader(program, vertexShader);
}
if (fragmentShader) {
glAttachShader(program, fragmentShader);
}
glBindAttribLocation(program, LOVR_SHADER_POSITION, "lovrPosition");
glBindAttribLocation(program, LOVR_SHADER_NORMAL, "lovrNormal");
glBindAttribLocation(program, LOVR_SHADER_TEX_COORD, "lovrTexCoord");
glBindAttribLocation(program, LOVR_SHADER_VERTEX_COLOR, "lovrVertexColor");
glBindAttribLocation(program, LOVR_SHADER_BONES, "lovrBones");
glBindAttribLocation(program, LOVR_SHADER_BONE_WEIGHTS, "lovrBoneWeights");
glLinkProgram(program);
int isLinked;
glGetProgramiv(program, GL_LINK_STATUS, &isLinked);
if (!isLinked) {
int logLength;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
char* log = malloc(logLength);
glGetProgramInfoLog(program, logLength, &logLength, log);
lovrThrow("Could not link shader %s", log);
}
glDetachShader(program, vertexShader);
glDeleteShader(vertexShader);
glDetachShader(program, fragmentShader);
glDeleteShader(fragmentShader);
return program;
}
Shader* lovrShaderCreate(const char* vertexSource, const char* fragmentSource) {
Shader* shader = lovrAlloc(sizeof(Shader), lovrShaderDestroy);
if (!shader) return NULL;
char source[8192];
// Vertex
vertexSource = vertexSource == NULL ? lovrDefaultVertexShader : vertexSource;
snprintf(source, sizeof(source), "%s%s\n%s", lovrShaderVertexPrefix, vertexSource, lovrShaderVertexSuffix);
GLuint vertexShader = compileShader(GL_VERTEX_SHADER, source);
// Fragment
fragmentSource = fragmentSource == NULL ? lovrDefaultFragmentShader : fragmentSource;
snprintf(source, sizeof(source), "%s%s\n%s", lovrShaderFragmentPrefix, fragmentSource, lovrShaderFragmentSuffix);
GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, source);
// Link
uint32_t program = linkShaders(vertexShader, fragmentShader);
shader->program = program;
lovrGraphicsUseProgram(program);
// Set default vertex color
float defaultVertexColor[4] = { 1., 1., 1., 1. };
glVertexAttrib4fv(LOVR_SHADER_VERTEX_COLOR, defaultVertexColor);
// Set default bone ids
int defaultBones[4] = { 0., 0., 0., 0. };
glVertexAttribI4iv(LOVR_SHADER_BONES, defaultBones);
// Set default bone weights
float defaultBoneWeights[4] = { 1., 0., 0., 0. };
glVertexAttrib4fv(LOVR_SHADER_BONE_WEIGHTS, defaultBoneWeights);
// Uniform introspection
GLint uniformCount;
int textureSlot = 0;
GLsizei bufferSize = LOVR_MAX_UNIFORM_LENGTH / sizeof(GLchar);
map_init(&shader->uniforms);
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniformCount);
for (int i = 0; i < uniformCount; i++) {
Uniform uniform;
glGetActiveUniform(program, i, bufferSize, NULL, &uniform.count, &uniform.glType, uniform.name);
char* subscript = strchr(uniform.name, '[');
if (subscript) {
*subscript = '\0';
}
uniform.index = i;
uniform.location = glGetUniformLocation(program, uniform.name);
uniform.type = getUniformType(uniform.glType, uniform.name);
uniform.components = getUniformComponents(uniform.glType);
uniform.baseTextureSlot = (uniform.type == UNIFORM_SAMPLER) ? textureSlot : -1;
switch (uniform.type) {
case UNIFORM_FLOAT:
uniform.size = uniform.components * uniform.count * sizeof(float);
uniform.value.data = malloc(uniform.size);
break;
case UNIFORM_INT:
uniform.size = uniform.components * uniform.count * sizeof(int);
uniform.value.data = malloc(uniform.size);
break;
case UNIFORM_MATRIX:
uniform.size = uniform.components * uniform.components * uniform.count * sizeof(int);
uniform.value.data = malloc(uniform.size);
break;
case UNIFORM_SAMPLER:
uniform.size = uniform.components * uniform.count * MAX(sizeof(Texture*), sizeof(int));
uniform.value.data = malloc(uniform.size);
// Use the value for ints to bind texture slots, but use the value for textures afterwards.
for (int i = 0; i < uniform.count; i++) {
uniform.value.ints[i] = uniform.baseTextureSlot + i;
}
glUniform1iv(uniform.location, uniform.count, uniform.value.ints);
break;
}
memset(uniform.value.data, 0, uniform.size);
size_t offset = 0;
for (int j = 0; j < uniform.count; j++) {
int location = uniform.location;
if (uniform.count > 1) {
char name[LOVR_MAX_UNIFORM_LENGTH];
snprintf(name, LOVR_MAX_UNIFORM_LENGTH, "%s[%d]", uniform.name, j);
location = glGetUniformLocation(program, name);
}
switch (uniform.type) {
case UNIFORM_FLOAT:
glGetUniformfv(program, location, &uniform.value.floats[offset]);
offset += uniform.components;
break;
case UNIFORM_INT:
glGetUniformiv(program, location, &uniform.value.ints[offset]);
offset += uniform.components;
break;
case UNIFORM_MATRIX:
glGetUniformfv(program, location, &uniform.value.floats[offset]);
offset += uniform.components * uniform.components;
break;
default:
break;
}
}
map_set(&shader->uniforms, uniform.name, uniform);
textureSlot += (uniform.type == UNIFORM_SAMPLER) ? uniform.count : 0;
}
return shader;
}
Shader* lovrShaderCreateDefault(DefaultShader type) {
switch (type) {
case SHADER_DEFAULT: return lovrShaderCreate(NULL, NULL);
case SHADER_SKYBOX: return lovrShaderCreate(lovrSkyboxVertexShader, lovrSkyboxFragmentShader); break;
case SHADER_FONT: return lovrShaderCreate(NULL, lovrFontFragmentShader);
case SHADER_FULLSCREEN: return lovrShaderCreate(lovrNoopVertexShader, NULL);
default: lovrThrow("Unknown default shader type");
}
}
void lovrShaderDestroy(const Ref* ref) {
Shader* shader = containerof(ref, Shader);
glDeleteProgram(shader->program);
map_deinit(&shader->uniforms);
free(shader);
}
void lovrShaderBind(Shader* shader) {
map_iter_t iter = map_iter(&shader->uniforms);
const char* key;
while ((key = map_next(&shader->uniforms, &iter)) != NULL) {
Uniform* uniform = map_get(&shader->uniforms, key);
if (uniform->type != UNIFORM_SAMPLER && !uniform->dirty) {
continue;
}
uniform->dirty = false;
int count = uniform->count;
void* data = uniform->value.data;
switch (uniform->type) {
case UNIFORM_FLOAT:
switch (uniform->components) {
case 1: glUniform1fv(uniform->location, count, data); break;
case 2: glUniform2fv(uniform->location, count, data); break;
case 3: glUniform3fv(uniform->location, count, data); break;
case 4: glUniform4fv(uniform->location, count, data); break;
}
break;
case UNIFORM_INT:
switch (uniform->components) {
case 1: glUniform1iv(uniform->location, count, data); break;
case 2: glUniform2iv(uniform->location, count, data); break;
case 3: glUniform3iv(uniform->location, count, data); break;
case 4: glUniform4iv(uniform->location, count, data); break;
}
break;
case UNIFORM_MATRIX:
switch (uniform->components) {
case 2: glUniformMatrix2fv(uniform->location, count, GL_FALSE, data); break;
case 3: glUniformMatrix3fv(uniform->location, count, GL_FALSE, data); break;
case 4: glUniformMatrix4fv(uniform->location, count, GL_FALSE, data); break;
}
break;
case UNIFORM_SAMPLER:
for (int i = 0; i < count; i++) {
TextureType type = uniform->glType == GL_SAMPLER_2D ? TEXTURE_2D : TEXTURE_CUBE;
lovrGraphicsBindTexture(uniform->value.textures[i], type, uniform->baseTextureSlot + i);
}
break;
}
}
}
int lovrShaderGetAttributeId(Shader* shader, const char* name) {
if (!shader) {
return -1;
}
return glGetAttribLocation(shader->program, name);
}
Uniform* lovrShaderGetUniform(Shader* shader, const char* name) {
return map_get(&shader->uniforms, name);
}
static void lovrShaderSetUniform(Shader* shader, const char* name, UniformType type, void* data, int count, size_t size, const char* debug) {
Uniform* uniform = map_get(&shader->uniforms, name);
if (!uniform) {
return;
}
const char* plural = (uniform->size / size) > 1 ? "s" : "";
lovrAssert(uniform->type == type, "Unable to send %ss to uniform %s", debug, uniform->name);
lovrAssert(count * size <= uniform->size, "Expected at most %d %s%s for uniform %s, got %d", uniform->size / size, debug, plural, uniform->name, count);
if (!uniform->dirty && !memcmp(uniform->value.data, data, count * size)) {
return;
}
memcpy(uniform->value.data, data, count * size);
uniform->dirty = true;
}
void lovrShaderSetFloat(Shader* shader, const char* name, float* data, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_FLOAT, data, count, sizeof(float), "float");
}
void lovrShaderSetInt(Shader* shader, const char* name, int* data, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_INT, data, count, sizeof(int), "int");
}
void lovrShaderSetMatrix(Shader* shader, const char* name, float* data, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_MATRIX, data, count, sizeof(float), "float");
}
void lovrShaderSetTexture(Shader* shader, const char* name, Texture** data, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_SAMPLER, data, count, sizeof(Texture*), "texture");
}

View File

@ -1,74 +1,117 @@
#include "graphics/texture.h" #include "graphics/texture.h"
#include "math/math.h"
#include "lib/map/map.h" #include "lib/map/map.h"
#include "lib/glfw.h" #include "lib/vec/vec.h"
#include "util.h"
#include <stdbool.h> #include <stdbool.h>
#pragma once #pragma once
#define LOVR_SHADER_POSITION 0 #define LOVR_MAX_UNIFORM_LENGTH 64
#define LOVR_SHADER_NORMAL 1 #define LOVR_MAX_ATTRIBUTE_LENGTH 64
#define LOVR_SHADER_TEX_COORD 2
#define LOVR_SHADER_VERTEX_COLOR 3 typedef enum {
#define LOVR_SHADER_BONES 4 USAGE_STATIC,
#define LOVR_SHADER_BONE_WEIGHTS 5 USAGE_DYNAMIC,
#define LOVR_MAX_UNIFORM_LENGTH 256 USAGE_STREAM
} BufferUsage;
typedef enum {
ACCESS_READ,
ACCESS_WRITE,
ACCESS_READ_WRITE
} UniformAccess;
typedef enum {
BLOCK_UNIFORM,
BLOCK_STORAGE
} BlockType;
typedef enum { typedef enum {
UNIFORM_FLOAT, UNIFORM_FLOAT,
UNIFORM_MATRIX, UNIFORM_MATRIX,
UNIFORM_INT, UNIFORM_INT,
UNIFORM_SAMPLER UNIFORM_SAMPLER,
UNIFORM_IMAGE
} UniformType; } UniformType;
typedef union { typedef enum {
void* data; SHADER_GRAPHICS,
int* ints; SHADER_COMPUTE
float* floats; } ShaderType;
Texture** textures;
} UniformValue;
typedef enum { typedef enum {
SHADER_DEFAULT, SHADER_DEFAULT,
SHADER_SKYBOX, SHADER_CUBE,
SHADER_PANO,
SHADER_FONT, SHADER_FONT,
SHADER_FULLSCREEN SHADER_FILL,
MAX_DEFAULT_SHADERS
} DefaultShader; } DefaultShader;
typedef struct { typedef struct {
GLchar name[LOVR_MAX_UNIFORM_LENGTH]; Texture* texture;
GLenum glType; int slice;
int index; int mipmap;
int location; UniformAccess access;
int count; } Image;
int components;
size_t size; typedef struct {
char name[LOVR_MAX_UNIFORM_LENGTH];
UniformType type; UniformType type;
UniformValue value; int components;
int baseTextureSlot; int count;
int location;
int offset;
int size;
union {
void* data;
char* bytes;
int* ints;
float* floats;
Texture** textures;
Image* images;
} value;
TextureType textureType;
int baseSlot;
bool image;
bool dirty; bool dirty;
} Uniform; } Uniform;
typedef map_t(Uniform) map_uniform_t; typedef vec_t(Uniform) vec_uniform_t;
typedef struct Shader Shader;
typedef struct ShaderBlock ShaderBlock;
typedef struct { typedef struct {
Ref ref; vec_uniform_t uniforms;
uint32_t program; int slot;
map_uniform_t uniforms; ShaderBlock* source;
float model[16]; UniformAccess access;
float view[16]; } UniformBlock;
float projection[16];
Color color;
} Shader;
Shader* lovrShaderCreate(const char* vertexSource, const char* fragmentSource); typedef vec_t(UniformBlock) vec_block_t;
Shader* lovrShaderCreateGraphics(const char* vertexSource, const char* fragmentSource);
Shader* lovrShaderCreateCompute(const char* source);
Shader* lovrShaderCreateDefault(DefaultShader type); Shader* lovrShaderCreateDefault(DefaultShader type);
void lovrShaderDestroy(const Ref* ref); void lovrShaderDestroy(void* ref);
ShaderType lovrShaderGetType(Shader* shader);
void lovrShaderBind(Shader* shader); void lovrShaderBind(Shader* shader);
int lovrShaderGetAttributeId(Shader* shader, const char* name); int lovrShaderGetAttributeId(Shader* shader, const char* name);
Uniform* lovrShaderGetUniform(Shader* shader, const char* name); bool lovrShaderHasUniform(Shader* shader, const char* name);
void lovrShaderSetFloat(Shader* shader, const char* name, float* data, int count); const Uniform* lovrShaderGetUniform(Shader* shader, const char* name);
void lovrShaderSetInt(Shader* shader, const char* name, int* data, int count); void lovrShaderSetFloats(Shader* shader, const char* name, float* data, int start, int count);
void lovrShaderSetMatrix(Shader* shader, const char* name, float* data, int count); void lovrShaderSetInts(Shader* shader, const char* name, int* data, int start, int count);
void lovrShaderSetTexture(Shader* shader, const char* name, Texture** data, int count); void lovrShaderSetMatrices(Shader* shader, const char* name, float* data, int start, int count);
void lovrShaderSetTextures(Shader* shader, const char* name, Texture** data, int start, int count);
void lovrShaderSetImages(Shader* shader, const char* name, Image* data, int start, int count);
void lovrShaderSetColor(Shader* shader, const char* name, Color color);
void lovrShaderSetBlock(Shader* shader, const char* name, ShaderBlock* block, UniformAccess access);
ShaderBlock* lovrShaderBlockCreate(vec_uniform_t* uniforms, BlockType type, BufferUsage usage);
void lovrShaderBlockDestroy(void* ref);
size_t lovrShaderBlockGetSize(ShaderBlock* block);
BlockType lovrShaderBlockGetType(ShaderBlock* block);
char* lovrShaderBlockGetShaderCode(ShaderBlock* block, const char* blockName, size_t* length);
const Uniform* lovrShaderBlockGetUniform(ShaderBlock* block, const char* name);
void* lovrShaderBlockMap(ShaderBlock* block);
void lovrShaderBlockUnmap(ShaderBlock* block);

View File

@ -1,215 +0,0 @@
#include "graphics/texture.h"
#include "graphics/graphics.h"
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
GLenum lovrTextureFormatGetGLFormat(TextureFormat format) {
switch (format) {
case FORMAT_RGB: return GL_RGB;
case FORMAT_RGBA: return GL_RGBA;
case FORMAT_RGBA16F: return GL_RGBA;
case FORMAT_RGBA32F: return GL_RGBA;
case FORMAT_RG11B10F: return GL_RGB;
case FORMAT_DXT1: return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
case FORMAT_DXT3: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
case FORMAT_DXT5: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
}
}
GLenum lovrTextureFormatGetGLInternalFormat(TextureFormat format, bool srgb) {
switch (format) {
case FORMAT_RGB: return srgb ? GL_SRGB8 : GL_RGB8;
case FORMAT_RGBA: return srgb ? GL_SRGB8_ALPHA8 : GL_RGBA8;
case FORMAT_RGBA16F: return GL_RGBA16F;
case FORMAT_RGBA32F: return GL_RGBA32F;
case FORMAT_RG11B10F: return GL_R11F_G11F_B10F;
case FORMAT_DXT1: return srgb ? GL_COMPRESSED_SRGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
case FORMAT_DXT3: return srgb ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
case FORMAT_DXT5: return srgb ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
}
}
bool lovrTextureFormatIsCompressed(TextureFormat format) {
switch (format) {
case FORMAT_DXT1:
case FORMAT_DXT3:
case FORMAT_DXT5:
return true;
default:
return false;
}
}
static void lovrTextureUpload(Texture* texture) {
TextureData* textureData = texture->slices[0];
texture->width = textureData->width;
texture->height = textureData->height;
bool srgb = lovrGraphicsIsGammaCorrect() && texture->srgb;
// Allocate storage
if (!lovrTextureFormatIsCompressed(textureData->format) && texture->type != TEXTURE_CUBE) {
int w = textureData->width;
int h = textureData->height;
int mipmapCount = log2(MAX(w, h)) + 1;
GLenum glFormat = lovrTextureFormatGetGLFormat(textureData->format);
GLenum internalFormat = lovrTextureFormatGetGLInternalFormat(textureData->format, srgb);
#ifndef EMSCRIPTEN
if (GLAD_GL_ARB_texture_storage) {
#endif
glTexStorage2D(GL_TEXTURE_2D, mipmapCount, internalFormat, w, h);
#ifndef EMSCRIPTEN
} else {
for (int i = 0; i < mipmapCount; i++) {
glTexImage2D(GL_TEXTURE_2D, i, internalFormat, w, h, 0, glFormat, GL_UNSIGNED_BYTE, NULL);
w = MAX(w >> 1, 1);
h = MAX(h >> 1, 1);
}
}
#endif
}
// Upload data
for (int i = 0; i < texture->sliceCount; i++) {
TextureData* textureData = texture->slices[i];
GLenum glFormat = lovrTextureFormatGetGLFormat(textureData->format);
GLenum glInternalFormat = lovrTextureFormatGetGLInternalFormat(textureData->format, srgb);
GLenum binding = (texture->type == TEXTURE_CUBE) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + i : GL_TEXTURE_2D;
if (lovrTextureFormatIsCompressed(textureData->format)) {
Mipmap m; int i;
vec_foreach(&textureData->mipmaps, m, i) {
glCompressedTexImage2D(binding, i, glInternalFormat, m.width, m.height, 0, m.size, m.data);
}
} else {
int w = textureData->width;
int h = textureData->height;
if (texture->type == TEXTURE_CUBE) {
glTexImage2D(binding, 0, glInternalFormat, w, h, 0, glFormat, GL_UNSIGNED_BYTE, textureData->data);
} else if (textureData->data) {
glTexSubImage2D(binding, 0, 0, 0, w, h, glFormat, GL_UNSIGNED_BYTE, textureData->data);
}
if (textureData->generateMipmaps) {
glGenerateMipmap(texture->type);
}
}
}
}
static void validateSlices(TextureType type, TextureData* slices[6], int sliceCount) {
lovrAssert(sliceCount > 0, "At least one layer must be provided to create a texture");
int maxSize = lovrGraphicsGetLimits().textureSize;
int width = slices[0]->width;
int height = slices[0]->height;
bool oversized = width > maxSize || height > maxSize;
lovrAssert(!oversized, "Texture is too big, max size is %d x %d", maxSize, maxSize);
if (type == TEXTURE_CUBE) {
lovrAssert(sliceCount == 6, "Cube textures must have 6 images");
lovrAssert(width == height, "Cube textures must be square");
for (int i = 1; i < sliceCount; i++) {
int hasSameDimensions = slices[i]->width == width && slices[i]->height == height;
lovrAssert(hasSameDimensions, "All textures in a cube texture must have the same dimensions");
}
} else if (type == TEXTURE_2D) {
lovrAssert(sliceCount == 1, "2D textures can only contain a single image");
} else {
lovrThrow("Unknown texture type");
}
}
Texture* lovrTextureCreate(TextureType type, TextureData* slices[6], int sliceCount, bool srgb) {
Texture* texture = lovrAlloc(sizeof(Texture), lovrTextureDestroy);
if (!texture) return NULL;
texture->type = type;
validateSlices(type, slices, sliceCount);
texture->sliceCount = sliceCount;
memcpy(texture->slices, slices, sliceCount * sizeof(TextureData*));
texture->srgb = srgb;
glGenTextures(1, &texture->id);
lovrGraphicsBindTexture(texture, type, 0);
lovrTextureUpload(texture);
lovrTextureSetFilter(texture, lovrGraphicsGetDefaultFilter());
WrapMode wrapMode = (type == TEXTURE_CUBE) ? WRAP_CLAMP : WRAP_REPEAT;
lovrTextureSetWrap(texture, (TextureWrap) { .s = wrapMode, .t = wrapMode, .r = wrapMode });
for (int i = 0; i < sliceCount; i++) {
lovrRetain(&slices[i]->ref);
}
return texture;
}
void lovrTextureDestroy(const Ref* ref) {
Texture* texture = containerof(ref, Texture);
for (int i = 0; i < texture->sliceCount; i++) {
lovrRelease(&texture->slices[i]->ref);
}
glDeleteTextures(1, &texture->id);
free(texture);
}
void lovrTextureReplacePixels(Texture* texture, TextureData* textureData, int slice) {
lovrAssert(slice >= 0 && slice < texture->sliceCount, "Invalid texture slice to replace: %d", slice);
lovrAssert(textureData->width == texture->width && textureData->height == texture->height, "Texture dimensions must match");
lovrAssert(textureData->format == texture->slices[0]->format, "Texture formats must match");
lovrTextureUpload(texture);
}
TextureFilter lovrTextureGetFilter(Texture* texture) {
return texture->filter;
}
void lovrTextureSetFilter(Texture* texture, TextureFilter filter) {
bool hasMipmaps = lovrTextureFormatIsCompressed(texture->slices[0]->format) || texture->slices[0]->generateMipmaps;
float anisotropy = filter.mode == FILTER_ANISOTROPIC ? MAX(filter.anisotropy, 1.) : 1.;
lovrGraphicsBindTexture(texture, texture->type, 0);
texture->filter = filter;
switch (filter.mode) {
case FILTER_NEAREST:
glTexParameteri(texture->type, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(texture->type, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
break;
case FILTER_BILINEAR:
if (hasMipmaps) {
glTexParameteri(texture->type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(texture->type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else {
glTexParameteri(texture->type, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(texture->type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
break;
case FILTER_TRILINEAR:
case FILTER_ANISOTROPIC:
if (hasMipmaps) {
glTexParameteri(texture->type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(texture->type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else {
glTexParameteri(texture->type, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(texture->type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
break;
}
glTexParameteri(texture->type, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
}
TextureWrap lovrTextureGetWrap(Texture* texture) {
return texture->wrap;
}
void lovrTextureSetWrap(Texture* texture, TextureWrap wrap) {
texture->wrap = wrap;
lovrGraphicsBindTexture(texture, texture->type, 0);
glTexParameteri(texture->type, GL_TEXTURE_WRAP_S, wrap.s);
glTexParameteri(texture->type, GL_TEXTURE_WRAP_T, wrap.t);
if (texture->type == TEXTURE_CUBE) {
glTexParameteri(texture->type, GL_TEXTURE_WRAP_R, wrap.r);
}
}

View File

@ -1,12 +1,13 @@
#include "data/textureData.h" #include "data/textureData.h"
#include "lib/glfw.h" #include <stdbool.h>
#include "util.h"
#pragma once #pragma once
typedef enum { typedef enum {
TEXTURE_2D = GL_TEXTURE_2D, TEXTURE_2D,
TEXTURE_CUBE = GL_TEXTURE_CUBE_MAP TEXTURE_CUBE,
TEXTURE_ARRAY,
TEXTURE_VOLUME
} TextureType; } TextureType;
typedef enum { typedef enum {
@ -22,9 +23,9 @@ typedef struct {
} TextureFilter; } TextureFilter;
typedef enum { typedef enum {
WRAP_CLAMP = GL_CLAMP_TO_EDGE, WRAP_CLAMP,
WRAP_REPEAT = GL_REPEAT, WRAP_REPEAT,
WRAP_MIRRORED_REPEAT = GL_MIRRORED_REPEAT WRAP_MIRRORED_REPEAT
} WrapMode; } WrapMode;
typedef struct { typedef struct {
@ -33,26 +34,21 @@ typedef struct {
WrapMode r; WrapMode r;
} TextureWrap; } TextureWrap;
typedef struct { typedef struct Texture Texture;
Ref ref;
TextureType type;
TextureData* slices[6];
int sliceCount;
int width;
int height;
GLuint id;
TextureFilter filter;
TextureWrap wrap;
bool srgb;
} Texture;
GLenum lovrTextureFormatGetGLFormat(TextureFormat format); Texture* lovrTextureCreate(TextureType type, TextureData** slices, int sliceCount, bool srgb, bool mipmaps, int msaa);
GLenum lovrTextureFormatGetGLInternalFormat(TextureFormat format, bool srgb); Texture* lovrTextureCreateFromHandle(uint32_t handle, TextureType type);
bool lovrTextureFormatIsCompressed(TextureFormat format); void lovrTextureDestroy(void* ref);
void lovrTextureAllocate(Texture* texture, int width, int height, int depth, TextureFormat format);
Texture* lovrTextureCreate(TextureType type, TextureData* data[6], int count, bool srgb); void lovrTextureReplacePixels(Texture* texture, TextureData* data, int x, int y, int slice, int mipmap);
void lovrTextureDestroy(const Ref* ref); uint32_t lovrTextureGetId(Texture* texture);
void lovrTextureReplacePixels(Texture* texture, TextureData* data, int slice); int lovrTextureGetWidth(Texture* texture, int mipmap);
int lovrTextureGetHeight(Texture* texture, int mipmap);
int lovrTextureGetDepth(Texture* texture, int mipmap);
int lovrTextureGetMipmapCount(Texture* texture);
int lovrTextureGetMSAA(Texture* texture);
TextureType lovrTextureGetType(Texture* texture);
TextureFormat lovrTextureGetFormat(Texture* texture);
TextureFilter lovrTextureGetFilter(Texture* texture); TextureFilter lovrTextureGetFilter(Texture* texture);
void lovrTextureSetFilter(Texture* texture, TextureFilter filter); void lovrTextureSetFilter(Texture* texture, TextureFilter filter);
TextureWrap lovrTextureGetWrap(Texture* texture); TextureWrap lovrTextureGetWrap(Texture* texture);

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