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
build
/lovr
/data
/*.lua
src/obj
src/Tupfile
**/Tupfile
Tuprules.tup
/watch.sh
*.glsl
.DS_Store
bin

3
.gitmodules vendored
View File

@ -1,9 +1,6 @@
[submodule "deps/enet"]
path = deps/enet
url = https://github.com/lsalzman/enet
[submodule "deps/freetype"]
path = deps/freetype
url = https://github.com/aseprite/freetype2
[submodule "deps/glfw"]
path = deps/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)
# 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
if(EMSCRIPTEN)
string(CONCAT LOVR_EMSCRIPTEN_FLAGS
"-O3 "
"-s USE_WEBGL2=1 "
"-s FULL_ES3=1 "
"-s USE_FREETYPE=1 "
"-s WASM=1 "
"-s USE_GLFW=3 "
"-s USE_WEBGL2=1 "
"-s GL_PREINITIALIZED_CONTEXT=1 "
"-s USE_ZLIB=1 "
"-s FULL_ES3=1 "
"-s FORCE_FILESYSTEM=1 "
"-s ALLOW_MEMORY_GROWTH=1 "
"-s 'EXTRA_EXPORTED_RUNTIME_METHODS=[\"getValue\", \"setValue\"]' "
"-s WASM=1"
"-s \"EXPORTED_FUNCTIONS=[ "
"'_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_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)
find_package(PkgConfig REQUIRED)
if(APPLE)
set(CMAKE_MACOSX_RPATH 1)
endif()
find_package(PkgConfig)
endif()
# PhysicsFS
set(PHYSFS_BUILD_STATIC OFF CACHE BOOL "")
set(PHYSFS_ARCHIVE_7Z OFF CACHE BOOL "")
set(PHYSFS_ARCHIVE_GRP OFF CACHE BOOL "")
set(PHYSFS_ARCHIVE_WAD OFF CACHE BOOL "")
set(PHYSFS_ARCHIVE_HOG OFF CACHE BOOL "")
set(PHYSFS_ARCHIVE_MVL OFF CACHE BOOL "")
set(PHYSFS_ARCHIVE_QPAK OFF CACHE BOOL "")
set(PHYSFS_BUILD_TEST OFF CACHE BOOL "")
set(PHYSFS_BUILD_WX_TEST FALSE CACHE BOOL "")
if(WIN32 OR EMSCRIPTEN OR OCULUS_ANDROID_EMBED)
add_subdirectory(deps/physfs physfs)
include_directories(deps/physfs/src)
set(LOVR_PHYSFS physfs)
else()
find_package(PhysFS REQUIRED)
include_directories(${PHYSFS_INCLUDE_DIR})
set(LOVR_PHYSFS ${PHYSFS_LIBRARY})
if(LOVR_ENABLE_FILESYSTEM)
if(LOVR_SYSTEM_PHYSFS)
find_package(PhysFS REQUIRED)
include_directories(${PHYSFS_INCLUDE_DIR})
set(LOVR_PHYSFS ${PHYSFS_LIBRARY})
else()
set(PHYSFS_BUILD_STATIC OFF CACHE BOOL "")
set(PHYSFS_ARCHIVE_7Z OFF CACHE BOOL "")
set(PHYSFS_ARCHIVE_GRP OFF CACHE BOOL "")
set(PHYSFS_ARCHIVE_WAD OFF CACHE BOOL "")
set(PHYSFS_ARCHIVE_HOG OFF CACHE BOOL "")
set(PHYSFS_ARCHIVE_MVL OFF CACHE BOOL "")
set(PHYSFS_ARCHIVE_QPAK OFF CACHE BOOL "")
set(PHYSFS_ARCHIVE_SLB OFF CACHE BOOL "")
set(PHYSFS_ARCHIVE_ISO9660 OFF CACHE BOOL "")
set(PHYSFS_ARCHIVE_VDF OFF CACHE BOOL "")
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()
# Assimp
set(ASSIMP_BUILD_ASSIMP_TOOLS OFF CACHE BOOL "")
set(ASSIMP_BUILD_TESTS OFF CACHE BOOL "")
#set(ASSIMP_BUILD_ZLIB ON CACHE BOOL "")
set(ASSIMP_NO_EXPORT ON OFF CACHE BOOL "")
set(ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT OFF CACHE BOOL "")
set(ASSIMP_BUILD_COLLADA_IMPORTER ON CACHE BOOL "")
set(ASSIMP_BUILD_FBX_IMPORTER ON CACHE BOOL "")
set(ASSIMP_BUILD_GLTF_IMPORTER ON CACHE BOOL "")
set(ASSIMP_BUILD_OBJ_IMPORTER ON CACHE BOOL "")
if(EMSCRIPTEN)
set(ZLIB_FOUND 1)
set(ZLIB_LIBRARIES "-s USE_ZLIB=1")
set(ZLIB_INCLUDE_DIR "assimp/contrib/zlib")
add_subdirectory(deps/assimp assimp)
include_directories(deps/assimp/include ${CMAKE_BINARY_DIR}/assimp/include)
set(LOVR_ASSIMP assimp)
elseif (OCULUS_ANDROID_EMBED)
set(ASSIMP_BUILD_ZLIB 1)
add_subdirectory(deps/assimp assimp)
include_directories(deps/assimp/include ${CMAKE_BINARY_DIR}/assimp/include)
set(LOVR_ASSIMP assimp)
elseif(WIN32)
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})
if(LOVR_ENABLE_DATA AND LOVR_USE_ASSIMP)
if(LOVR_SYSTEM_ASSIMP)
pkg_search_module(ASSIMP REQUIRED assimp)
include_directories(${ASSIMP_INCLUDE_DIRS})
set(LOVR_ASSIMP ${ASSIMP_LIBRARIES})
else()
if(EMSCRIPTEN)
set(ZLIB_FOUND 1)
set(ZLIB_LIBRARIES "-s USE_ZLIB=1")
set(ZLIB_INCLUDE_DIR "assimp/contrib/zlib")
endif()
set(ASSIMP_BUILD_ASSIMP_TOOLS OFF CACHE BOOL "")
set(ASSIMP_BUILD_TESTS OFF CACHE BOOL "")
set(ASSIMP_NO_EXPORT ON CACHE BOOL "")
set(ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT OFF CACHE BOOL "")
set(ASSIMP_BUILD_FBX_IMPORTER ON CACHE BOOL "")
set(ASSIMP_BUILD_GLTF_IMPORTER ON CACHE BOOL "")
set(ASSIMP_BUILD_OBJ_IMPORTER ON CACHE BOOL "")
add_subdirectory(deps/assimp assimp)
include_directories(deps/assimp/include ${CMAKE_BINARY_DIR}/assimp/include)
set(LOVR_ASSIMP assimp)
endif()
endif()
# enet
if(EMSCRIPTEN)
set(HAVE_HAS_SOCKLEN_T TRUE CACHE BOOL "")
add_definitions(-D__APPLE__)
add_subdirectory(deps/enet enet)
include_directories(deps/enet/include)
set(LOVR_ENET enet)
remove_definitions(-D__APPLE__)
else()
add_subdirectory(deps/enet enet)
include_directories(deps/enet/include)
set(LOVR_ENET enet)
if(WIN32)
set(LOVR_ENET ${LOVR_ENET} ws2_32 winmm)
if(LOVR_ENABLE_ENET)
if(LOVR_SYSTEM_ENET)
pkg_search_module(ENET REQUIRED enet)
include_directories(${ENET_INCLUDE_DIRS})
set(LOVR_ENET ${ENET_LIBRARIES})
else()
if(EMSCRIPTEN)
set(HAVE_HAS_SOCKLEN_T TRUE CACHE BOOL "")
endif()
add_subdirectory(deps/enet enet)
include_directories(deps/enet/include)
set(LOVR_ENET enet)
if(WIN32)
set(LOVR_ENET ${LOVR_ENET} ws2_32 winmm)
endif()
if(EMSCRIPTEN)
target_compile_definitions(enet PRIVATE __APPLE__)
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
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "")
set(GLFW_BUILD_TESTS OFF CACHE BOOL "")
set(GLFW_BUILD_DOCS OFF CACHE BOOL "")
if(WIN32)
add_subdirectory(deps/glfw glfw)
include_directories(deps/glfw/include)
set(LOVR_GLFW glfw ${GLFW_LIBRARIES})
elseif(NOT (EMSCRIPTEN OR OCULUS_ANDROID_EMBED))
pkg_search_module(GLFW REQUIRED glfw3)
include_directories(${GLFW_INCLUDE_DIRS})
set(LOVR_GLFW ${GLFW_LIBRARIES})
if(NOT (EMSCRIPTEN OR OCULUS_ANDROID_EMBED))
if(LOVR_SYSTEM_GLFW)
pkg_search_module(GLFW REQUIRED glfw3)
include_directories(${GLFW_INCLUDE_DIRS})
set(LOVR_GLFW ${GLFW_LIBRARIES})
else()
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "")
set(GLFW_BUILD_TESTS OFF CACHE BOOL "")
set(GLFW_BUILD_DOCS OFF CACHE BOOL "")
add_subdirectory(deps/glfw glfw)
include_directories(deps/glfw/include)
set(LOVR_GLFW glfw ${GLFW_LIBRARIES})
endif()
unset(LIB_SUFFIX CACHE)
endif()
unset(LIB_SUFFIX CACHE)
# Lua
if(EMSCRIPTEN)
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(LOVR_USE_LUAJIT AND NOT (EMSCRIPTEN OR OCULUS_ANDROID_EMBED))
if (APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pagezero_size 10000 -image_base 100000000")
endif()
pkg_search_module(LUAJIT REQUIRED luajit)
include_directories(${LUAJIT_INCLUDE_DIRS})
set(LOVR_LUA ${LUAJIT_LIBRARIES})
if(LOVR_SYSTEM_LUA)
pkg_search_module(LUAJIT REQUIRED luajit)
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()
# MSDF
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(deps/msdfgen lib_msdfgen)
set(BUILD_SHARED_LIBS ON)
include_directories(deps/msdfgen)
set(LOVR_MSDF lib_msdfgen)
if(LOVR_ENABLE_DATA)
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(deps/msdfgen lib_msdfgen)
set(BUILD_SHARED_LIBS ON)
include_directories(deps/msdfgen)
set(LOVR_MSDF lib_msdfgen)
endif()
# ODE
if(EMSCRIPTEN OR OCULUS_ANDROID_EMBED)
set(ODE_BUILD_SHARED OFF CACHE BOOL "")
add_subdirectory(deps/ode ode)
include_directories(deps/ode/include "${CMAKE_CURRENT_BINARY_DIR}/ode/include")
set(LOVR_ODE ode)
elseif(WIN32)
set(ODE_BUILD_SHARED ON CACHE BOOL "")
add_subdirectory(deps/ode ode)
include_directories(deps/ode/include "${CMAKE_CURRENT_BINARY_DIR}/ode/include")
set(LOVR_ODE ode)
else()
pkg_search_module(ODE REQUIRED ode)
include_directories(${ODE_INCLUDE_DIRS})
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++")
set(LOVR_ODE ode)
if(LOVR_ENABLE_PHYSICS)
if(LOVR_SYSTEM_ODE)
pkg_search_module(ODE REQUIRED ode)
pkg_search_module(CCD REQUIRED ccd)
include_directories(${ODE_INCLUDE_DIRS} ${CCD_INCLUDE_DIRS})
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++")
set(LOVR_ODE ode ccd)
else()
if(EMSCRIPTEN)
set(ODE_BUILD_SHARED OFF CACHE BOOL "")
else()
set(ODE_BUILD_SHARED ON CACHE BOOL "")
endif()
add_subdirectory(deps/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()
# OpenAL
set(ALSOFT_UTILS OFF CACHE BOOL "")
set(ALSOFT_EXAMPLES OFF CACHE BOOL "")
set(ALSOFT_TESTS OFF CACHE BOOL "")
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(LOVR_ENABLE_AUDIO)
if(LOVR_SYSTEM_OPENAL)
pkg_search_module(OPENAL openal-soft)
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()
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()
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()
# OpenGL
if(NOT (WIN32 OR OCULUS_ANDROID_EMBED))
if(NOT (WIN32 OR EMSCRIPTEN OR OCULUS_ANDROID_EMBED))
find_package(OpenGL REQUIRED)
include_directories(${OPENGL_INCLUDE_DIRS})
set(LOVR_OPENGL ${OPENGL_LIBRARIES})
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_UNIVERSAL OFF CACHE BOOL "")
include_directories(deps/openvr/headers)
include_directories(deps/openvr/src)
include_directories(deps/openvr/src/vrcommon)
add_subdirectory(deps/openvr openvr_api)
set_target_properties(openvr_api PROPERTIES
if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
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"
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()
if (NOT OCULUS_ANDROID_EMBED)
@ -236,166 +311,225 @@ set(FAKE_SRC src/headset/fake.c)
endif()
# LÖVR
set(LOVR_SRC
src/api/audio.c
src/api/data.c
src/api/event.c
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
add_executable(lovr
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/luax.c
src/api/lovr.c
src/lib/map/map.c
src/lib/vec/vec.c
)
set(LOVR_USE_OCULUS OFF CACHE BOOL "")
if(EMSCRIPTEN)
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()
set_target_properties(lovr PROPERTIES C_STANDARD 99)
target_include_directories(lovr PRIVATE src)
target_link_libraries(lovr
${LOVR_ASSIMP}
${LOVR_ENET}
${LOVR_FREETYPE}
${LOVR_GLFW}
${LOVR_LUA}
${LOVR_MSDF}
${LOVR_ODE}
${LOVR_OPENAL}
${LOVR_OPENGL}
${LOVR_OPENVR}
${LOVR_OCULUS}
${LOVR_PHYSFS}
${LOVR_PTHREADS}
${LOVR_EMSCRIPTEN_FLAGS}
)
if (NOT (LOVR_USE_OCULUS OR OCULUS_ANDROID_EMBED))
target_link_libraries(lovr ${LOVR_OPENVR})
if(LOVR_ENABLE_AUDIO)
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()
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)
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_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)
add_custom_command(TARGET lovr POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:${ARG_TARGET}>
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>/$<TARGET_FILE_NAME:${ARG_TARGET}>
)
if(TARGET ${ARG_TARGET})
add_custom_command(TARGET lovr POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:${ARG_TARGET}>
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>/$<TARGET_FILE_NAME:${ARG_TARGET}>
)
endif()
endfunction()
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
[lovr.org repo](https://github.com/bjornbytes/lovr.org) would be greatly appreciated! There, each
documentation page is a markdown file in the `docs` folder, and examples can be found in the
`examples` folder.
[lovr-docs repo](https://github.com/bjornbytes/lovr-docs) would be greatly appreciated! The `api`
folder has Lua files for each function, the `guides` folder contains tutorials in markdown format,
and the `examples` folder has source code for sample projects and other demos.
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).
[**Homepage**](https://lovr.org) | [**Documentation**](https://lovr.org/docs) | [**Slack**](https://join.slack.com/ifyouwannabemylovr/shared_invite/MTc5ODk2MjE0NDM3LTE0OTQxMTIyMDEtMzdhOGVlODFhYg)
Inspired by [LÖVE](https://love2d.org), a 2D game framework.
[![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)
[![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
- 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">
<p align="left">
<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/planets.jpg" width="32%"/></span>
</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
---
You can download precompiled binaries from the [website](https://lovr.org). There, you
can also find documentation and a set of tutorials and examples. Here is the hello world example
for LÖVR:
It's really easy to get started making things with LÖVR! Grab a copy of the executable from <https://lovr.org/download>,
then write a `main.lua` script and drag its parent folder onto the executable. Here are some example projects to try:
#### Hello World
```lua
function lovr.draw()
@ -42,10 +44,6 @@ function lovr.draw()
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
```lua
@ -54,6 +52,19 @@ function lovr.draw()
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
```lua
@ -62,42 +73,35 @@ function lovr.load()
end
function lovr.draw()
model:draw()
local x, y, z = 0, 0, 0
model:draw(x, y, z)
end
```
#### Audio
You can also find lots of other WebVR examples on the [docs page](https://lovr.org/docs/Hello_World).
```lua
function lovr.load()
local sound = lovr.audio.newSource('darudeSandstorm.ogg')
sound:play()
end
Building
---
Here's how to compile LÖVR using CMake:
```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).
Community
---
> If you wanna be my LÖVR, you gotta get with my friends
> *- 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.
- [**Documentation**](https://lovr.org/docs): Guides, tutorials, examples, and API documentation.
- [**FAQ**](https://lovr.org/docs/FAQ): Frequently Asked Questions.
- [**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.
- [**Contributing**](CONTRIBUTING.md): Guide for helping out with development :heart:
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 "filesystem/blob.h"
#include "graphics/mesh.h"
#include "math/math.h"
#include "math/randomGenerator.h"
#include "physics/physics.h"
#include "lib/map/map.h"
#ifdef _WIN32
#define LOVR_API __declspec(dllexport)
#else
#define LOVR_API
#endif
// Module loaders
int l_lovrAudioInit(lua_State* L);
int l_lovrDataInit(lua_State* L);
int l_lovrEventInit(lua_State* L);
int l_lovrFilesystemInit(lua_State* L);
int l_lovrGraphicsInit(lua_State* L);
int l_lovrHeadsetInit(lua_State* L);
int l_lovrMathInit(lua_State* L);
int l_lovrPhysicsInit(lua_State* L);
int l_lovrTimerInit(lua_State* L);
// Modules
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[];
LOVR_API int luaopen_lovr(lua_State* L);
LOVR_API int luaopen_lovr_audio(lua_State* L);
LOVR_API int luaopen_lovr_data(lua_State* L);
LOVR_API int luaopen_lovr_event(lua_State* L);
LOVR_API int luaopen_lovr_filesystem(lua_State* L);
LOVR_API int luaopen_lovr_graphics(lua_State* L);
LOVR_API int luaopen_lovr_headset(lua_State* L);
LOVR_API int luaopen_lovr_math(lua_State* L);
LOVR_API int luaopen_lovr_physics(lua_State* L);
LOVR_API int luaopen_lovr_thread(lua_State* L);
LOVR_API int luaopen_lovr_timer(lua_State* L);
// Objects
extern const luaL_Reg lovrAnimator[];
@ -36,6 +27,7 @@ extern const luaL_Reg lovrBlob[];
extern const luaL_Reg lovrBoxShape[];
extern const luaL_Reg lovrCanvas[];
extern const luaL_Reg lovrCapsuleShape[];
extern const luaL_Reg lovrChannel[];
extern const luaL_Reg lovrController[];
extern const luaL_Reg lovrCylinderShape[];
extern const luaL_Reg lovrCollider[];
@ -45,61 +37,56 @@ extern const luaL_Reg lovrHingeJoint[];
extern const luaL_Reg lovrJoint[];
extern const luaL_Reg lovrMaterial[];
extern const luaL_Reg lovrMesh[];
extern const luaL_Reg lovrMicrophone[];
extern const luaL_Reg lovrModel[];
extern const luaL_Reg lovrModelData[];
extern const luaL_Reg lovrRandomGenerator[];
extern const luaL_Reg lovrRasterizer[];
extern const luaL_Reg lovrShader[];
extern const luaL_Reg lovrShaderBlock[];
extern const luaL_Reg lovrShape[];
extern const luaL_Reg lovrSliderJoint[];
extern const luaL_Reg lovrSoundData[];
extern const luaL_Reg lovrSource[];
extern const luaL_Reg lovrSphereShape[];
extern const luaL_Reg lovrTexture[];
extern const luaL_Reg lovrTextureData[];
extern const luaL_Reg lovrThread[];
extern const luaL_Reg lovrTransform[];
extern const luaL_Reg lovrVertexData[];
extern const luaL_Reg lovrWorld[];
// Enums
extern map_int_t ArcModes;
extern map_int_t AttributeTypes;
extern map_int_t BlendAlphaModes;
extern map_int_t BlendModes;
extern map_int_t CanvasTypes;
extern map_int_t CompareModes;
extern map_int_t ControllerAxes;
extern map_int_t ControllerButtons;
extern map_int_t ControllerHands;
extern map_int_t DrawModes;
extern map_int_t EventTypes;
extern map_int_t FilterModes;
extern map_int_t HeadsetEyes;
extern map_int_t HeadsetOrigins;
extern map_int_t HeadsetTypes;
extern map_int_t HorizontalAligns;
extern map_int_t JointTypes;
extern map_int_t MaterialColors;
extern map_int_t MaterialScalars;
extern map_int_t MaterialTextures;
extern map_int_t MatrixTypes;
extern map_int_t MeshDrawModes;
extern map_int_t MeshUsages;
extern map_int_t PolygonWindings;
extern map_int_t ShapeTypes;
extern map_int_t TextureFormats;
extern map_int_t TimeUnits;
extern map_int_t VerticalAligns;
extern map_int_t WrapModes;
// Shared helpers
void 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);
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);
extern const char* ArcModes[];
extern const char* AttributeTypes[];
extern const char* BlendAlphaModes[];
extern const char* BlendModes[];
extern const char* BufferUsages[];
extern const char* CompareModes[];
extern const char* ControllerAxes[];
extern const char* ControllerButtons[];
extern const char* ControllerHands[];
extern const char* DrawModes[];
extern const char* EventTypes[];
extern const char* FilterModes[];
extern const char* HeadsetDrivers[];
extern const char* HeadsetEyes[];
extern const char* HeadsetOrigins[];
extern const char* HeadsetTypes[];
extern const char* HorizontalAligns[];
extern const char* JointTypes[];
extern const char* MaterialColors[];
extern const char* MaterialScalars[];
extern const char* MaterialTextures[];
extern const char* MeshDrawModes[];
extern const char* ShaderTypes[];
extern const char* ShapeTypes[];
extern const char* SourceTypes[];
extern const char* StencilActions[];
extern const char* TextureFormats[];
extern const char* TextureTypes[];
extern const char* TimeUnits[];
extern const char* UniformAccesses[];
extern const char* VerticalAligns[];
extern const char* Windings[];
extern const char* WrapModes[];

View File

@ -1,29 +1,55 @@
#include "api.h"
#include "api/data.h"
#include "audio/audio.h"
#include "audio/source.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) {
lua_newtable(L);
luaL_register(L, NULL, lovrAudio);
luax_registertype(L, "Source", lovrSource);
const char* TimeUnits[] = {
[UNIT_SECONDS] = "seconds",
[UNIT_SAMPLES] = "samples",
NULL
};
map_init(&TimeUnits);
map_set(&TimeUnits, "seconds", UNIT_SECONDS);
map_set(&TimeUnits, "samples", UNIT_SAMPLES);
lovrAudioInit();
return 1;
}
int l_lovrAudioUpdate(lua_State* L) {
static int l_lovrAudioUpdate(lua_State* L) {
lovrAudioUpdate();
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;
lovrAudioGetOrientation(&angle, &ax, &ay, &az);
lua_pushnumber(L, angle);
@ -33,7 +59,7 @@ int l_lovrAudioGetOrientation(lua_State* L) {
return 4;
}
int l_lovrAudioGetPosition(lua_State* L) {
static int l_lovrAudioGetPosition(lua_State* L) {
float x, y, z;
lovrAudioGetPosition(&x, &y, &z);
lua_pushnumber(L, x);
@ -42,7 +68,7 @@ int l_lovrAudioGetPosition(lua_State* L) {
return 3;
}
int l_lovrAudioGetVelocity(lua_State* L) {
static int l_lovrAudioGetVelocity(lua_State* L) {
float x, y, z;
lovrAudioGetVelocity(&x, &y, &z);
lua_pushnumber(L, x);
@ -51,53 +77,91 @@ int l_lovrAudioGetVelocity(lua_State* L) {
return 3;
}
int l_lovrAudioGetVolume(lua_State* L) {
static int l_lovrAudioGetVolume(lua_State* L) {
lua_pushnumber(L, lovrAudioGetVolume());
return 1;
}
int l_lovrAudioIsSpatialized(lua_State* L) {
static int l_lovrAudioIsSpatialized(lua_State* L) {
lua_pushboolean(L, lovrAudioIsSpatialized());
return 1;
}
int l_lovrAudioNewSource(lua_State* L) {
void** type;
AudioStream* stream;
if ((type = luax_totype(L, 1, AudioStream)) != NULL) {
stream = *type;
} else {
Blob* blob = luax_readblob(L, 1, "Source");
stream = lovrAudioStreamCreate(blob, 4096);
lovrRelease(&blob->ref);
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);
static int l_lovrAudioNewMicrophone(lua_State* L) {
const char* name = luaL_optstring(L, 1, NULL);
int samples = luaL_optinteger(L, 2, 1024);
int sampleRate = luaL_optinteger(L, 3, 8000);
int bitDepth = luaL_optinteger(L, 4, 16);
int channelCount = luaL_optinteger(L, 5, 1);
Microphone* microphone = lovrMicrophoneCreate(name, samples, sampleRate, bitDepth, channelCount);
luax_pushobject(L, microphone);
lovrRelease(microphone);
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();
return 0;
}
int l_lovrAudioResume(lua_State* L) {
static int l_lovrAudioResume(lua_State* L) {
lovrAudioResume();
return 0;
}
int l_lovrAudioRewind(lua_State* L) {
static int l_lovrAudioRewind(lua_State* L) {
lovrAudioRewind();
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 ax = luaL_checknumber(L, 2);
float ay = luaL_checknumber(L, 3);
@ -106,7 +170,7 @@ int l_lovrAudioSetOrientation(lua_State* L) {
return 0;
}
int l_lovrAudioSetPosition(lua_State* L) {
static int l_lovrAudioSetPosition(lua_State* L) {
float x = luaL_checknumber(L, 1);
float y = luaL_checknumber(L, 2);
float z = luaL_checknumber(L, 3);
@ -114,7 +178,7 @@ int l_lovrAudioSetPosition(lua_State* L) {
return 0;
}
int l_lovrAudioSetVelocity(lua_State* L) {
static int l_lovrAudioSetVelocity(lua_State* L) {
float x = luaL_checknumber(L, 1);
float y = luaL_checknumber(L, 2);
float z = luaL_checknumber(L, 3);
@ -122,28 +186,32 @@ int l_lovrAudioSetVelocity(lua_State* L) {
return 0;
}
int l_lovrAudioSetVolume(lua_State* L) {
static int l_lovrAudioSetVolume(lua_State* L) {
float volume = luaL_checknumber(L, 1);
lovrAudioSetVolume(volume);
return 0;
}
int l_lovrAudioStop(lua_State* L) {
static int l_lovrAudioStop(lua_State* L) {
lovrAudioStop();
return 0;
}
const luaL_Reg lovrAudio[] = {
static const luaL_Reg lovrAudio[] = {
{ "update", l_lovrAudioUpdate },
{ "getDopplerEffect", l_lovrAudioGetDopplerEffect },
{ "getMicrophoneNames", l_lovrAudioGetMicrophoneNames },
{ "getOrientation", l_lovrAudioGetOrientation },
{ "getPosition", l_lovrAudioGetPosition },
{ "getVelocity", l_lovrAudioGetVelocity },
{ "getVolume", l_lovrAudioGetVolume },
{ "isSpatialized", l_lovrAudioIsSpatialized },
{ "newMicrophone", l_lovrAudioNewMicrophone },
{ "newSource", l_lovrAudioNewSource },
{ "pause", l_lovrAudioPause },
{ "resume", l_lovrAudioResume },
{ "rewind", l_lovrAudioRewind },
{ "setDopplerEffect", l_lovrAudioSetDopplerEffect },
{ "setOrientation", l_lovrAudioSetOrientation },
{ "setPosition", l_lovrAudioSetPosition },
{ "setVelocity", l_lovrAudioSetVelocity },
@ -151,3 +219,13 @@ const luaL_Reg lovrAudio[] = {
{ "stop", l_lovrAudioStop },
{ 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 "data/data.h"
#include "api/data.h"
#include "data/audioStream.h"
#include "data/modelData.h"
#include "data/rasterizer.h"
#include "data/soundData.h"
#include "data/textureData.h"
#include "data/vertexData.h"
int l_lovrDataInit(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrData);
luax_registertype(L, "AudioStream", lovrAudioStream);
luax_registertype(L, "ModelData", lovrModelData);
luax_registertype(L, "Rasterizer", lovrRasterizer);
luax_registertype(L, "TextureData", lovrTextureData);
luax_registertype(L, "VertexData", lovrVertexData);
static int l_lovrDataNewBlob(lua_State* L) {
size_t size;
uint8_t* data = NULL;
int type = lua_type(L, 1);
if (type == LUA_TNUMBER) {
size = lua_tonumber(L, 1);
data = calloc(1, size);
} 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;
}
int l_lovrDataNewAudioStream(lua_State* L) {
Blob* blob = luax_readblob(L, 1, "Sound");
static int l_lovrDataNewAudioStream(lua_State* L) {
Blob* blob = luax_readblob(L, 1, "AudioStream");
size_t bufferSize = luaL_optinteger(L, 2, 4096);
AudioStream* stream = lovrAudioStreamCreate(blob, bufferSize);
luax_pushtype(L, AudioStream, stream);
lovrRelease(&blob->ref);
lovrRelease(&stream->ref);
luax_pushobject(L, stream);
lovrRelease(blob);
lovrRelease(stream);
return 1;
}
int l_lovrDataNewModelData(lua_State* L) {
static int l_lovrDataNewModelData(lua_State* L) {
Blob* blob = luax_readblob(L, 1, "Model");
ModelData* modelData = lovrModelDataCreate(blob);
luax_pushtype(L, ModelData, modelData);
lovrRelease(&blob->ref);
lovrRelease(&modelData->ref);
luax_pushobject(L, modelData);
lovrRelease(blob);
lovrRelease(modelData);
return 1;
}
int l_lovrDataNewRasterizer(lua_State* L) {
static int l_lovrDataNewRasterizer(lua_State* L) {
Blob* blob = NULL;
float size;
@ -47,49 +62,114 @@ int l_lovrDataNewRasterizer(lua_State* L) {
}
Rasterizer* rasterizer = lovrRasterizerCreate(blob, size);
luax_pushtype(L, Rasterizer, rasterizer);
if (blob) {
lovrRelease(&blob->ref);
}
lovrRelease(&rasterizer->ref);
luax_pushobject(L, rasterizer);
lovrRelease(blob);
lovrRelease(rasterizer);
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;
if (lua_type(L, 1) == LUA_TNUMBER) {
int width = luaL_checknumber(L, 1);
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 {
Blob* blob = luax_readblob(L, 1, "Texture");
textureData = lovrTextureDataFromBlob(blob);
lovrRelease(&blob->ref);
textureData = lovrTextureDataCreateFromBlob(blob, true);
lovrRelease(blob);
}
luax_pushtype(L, TextureData, textureData);
lovrRelease(&textureData->ref);
luax_pushobject(L, textureData);
lovrRelease(textureData);
return 1;
}
int l_lovrDataNewVertexData(lua_State* L) {
uint32_t count = luaL_checkinteger(L, 1);
static int l_lovrDataNewVertexData(lua_State* L) {
uint32_t count;
int dataIndex = 0;
bool hasFormat = false;
VertexFormat format;
vertexFormatInit(&format);
luax_checkvertexformat(L, 2, &format);
VertexData* vertexData = lovrVertexDataCreate(count, format.count > 0 ? &format : NULL, true);
luax_pushtype(L, VertexData, vertexData);
lovrRelease(&vertexData->ref);
if (lua_isnumber(L, 1)) {
count = lua_tointeger(L, 1);
} 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;
}
const luaL_Reg lovrData[] = {
static const luaL_Reg lovrData[] = {
{ "newBlob", l_lovrDataNewBlob },
{ "newAudioStream", l_lovrDataNewAudioStream },
{ "newModelData", l_lovrDataNewModelData },
{ "newRasterizer", l_lovrDataNewRasterizer },
{ "newSoundData", l_lovrDataNewSoundData },
{ "newTextureData", l_lovrDataNewTextureData },
{ "newVertexData", l_lovrDataNewVertexData },
{ 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/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) {
Event event;
@ -12,122 +69,84 @@ static int nextEvent(lua_State* L) {
return 0;
}
if (event.type == EVENT_CUSTOM) {
lua_pushstring(L, event.data.custom.name);
} else {
lua_pushstring(L, EventTypes[event.type]);
}
switch (event.type) {
case EVENT_QUIT: {
lua_pushstring(L, "quit");
if (event.data.quit.restart)
lua_pushliteral(L, "restart");
else
case EVENT_QUIT:
if (event.data.quit.restart) {
lua_pushstring(L, "restart");
} else {
lua_pushnumber(L, event.data.quit.exitCode);
}
return 2;
}
case EVENT_FOCUS: {
lua_pushstring(L, "focus");
lua_pushboolean(L, event.data.focus.isFocused);
case EVENT_FOCUS:
case EVENT_MOUNT:
lua_pushboolean(L, event.data.boolean.value);
return 2;
};
case EVENT_CONTROLLER_ADDED: {
lua_pushstring(L, "controlleradded");
luax_pushtype(L, Controller, event.data.controlleradded.controller);
lovrRelease(&event.data.controlleradded.controller->ref);
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);
case EVENT_THREAD_ERROR:
luax_pushobject(L, event.data.thread.thread);
lua_pushstring(L, event.data.thread.error);
free((void*) event.data.thread.error);
return 3;
}
case EVENT_CONTROLLER_RELEASED: {
lua_pushstring(L, "controllerreleased");
luax_pushtype(L, Controller, event.data.controllerreleased.controller);
luax_pushenum(L, &ControllerButtons, event.data.controllerreleased.button);
case EVENT_CONTROLLER_ADDED:
case EVENT_CONTROLLER_REMOVED:
luax_pushobject(L, event.data.controller.controller);
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;
}
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:
return 0;
return 1;
}
}
int l_lovrEventInit(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) {
static int l_lovrEventClear(lua_State* L) {
lovrEventClear();
return 0;
}
int l_lovrEventPoll(lua_State* L) {
static int l_lovrEventPoll(lua_State* L) {
lua_rawgeti(L, LUA_REGISTRYINDEX, pollRef);
return 1;
}
int l_lovrEventPump(lua_State* L) {
static int l_lovrEventPump(lua_State* L) {
lovrEventPump();
return 0;
}
int l_lovrEventPush(lua_State* L) {
EventType type = *(EventType*) luax_checkenum(L, 1, &EventTypes, "event type");
EventData data;
switch (type) {
case EVENT_QUIT:
data.quit.exitCode = luaL_optint(L, 2, 0);
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;
static int l_lovrEventPush(lua_State* L) {
CustomEvent eventData;
const char* name = luaL_checkstring(L, 1);
strncpy(eventData.name, name, MAX_EVENT_NAME_LENGTH - 1);
eventData.count = MIN(lua_gettop(L) - 1, 4);
for (int i = 0; i < eventData.count; i++) {
luax_checkvariant(L, 2 + i, &eventData.data[i]);
}
Event event = { .type = type, .data = data };
lovrEventPush(event);
lovrEventPush((Event) { .type = EVENT_CUSTOM, .data.custom = eventData });
return 0;
}
int l_lovrEventQuit(lua_State* L) {
static int l_lovrEventQuit(lua_State* L) {
EventData data;
int argType = lua_type(L, 1);
@ -147,7 +166,7 @@ int l_lovrEventQuit(lua_State* L) {
return 0;
}
const luaL_Reg lovrEvent[] = {
static const luaL_Reg lovrEvent[] = {
{ "clear", l_lovrEventClear },
{ "poll", l_lovrEventPoll },
{ "pump", l_lovrEventPump },
@ -155,3 +174,16 @@ const luaL_Reg lovrEvent[] = {
{ "quit", l_lovrEventQuit },
{ 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 "filesystem/filesystem.h"
#include "filesystem/blob.h"
#include "data/blob.h"
#include <stdlib.h>
#include <string.h>
// Returns a Blob, leaving stack unchanged. The Blob must be released when finished.
Blob* luax_readblob(lua_State* L, int index, const char* debug) {
if (lua_type(L, index) == LUA_TUSERDATA) {
Blob* blob = luax_checktype(L, index, Blob);
lovrRetain(&blob->ref);
lovrRetain(blob);
return blob;
} else {
const char* path = luaL_checkstring(L, index);
@ -31,41 +30,56 @@ static int pushDirectoryItem(void* userdata, const char* path, const char* filen
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 filesystemLoader(lua_State* L) {
const char* module = luaL_checkstring(L, -1);
char* dot;
static int moduleLoader(lua_State* L) {
const char* module = luaL_gsub(L, lua_tostring(L, -1), ".", "/");
lua_pop(L, 2);
char path[LOVR_PATH_MAX];
strncpy(path, module, LOVR_PATH_MAX);
while ((dot = strchr(path, '.')) != NULL) {
*dot = '/';
char* path; int i;
vec_foreach(lovrFilesystemGetRequirePath(), path, i) {
const char* filename = luaL_gsub(L, path, "?", module);
if (lovrFilesystemIsFile(filename)) {
return l_lovrFilesystemLoad(L);
}
lua_pop(L, 1);
}
const char* requirePath[] = {
"?.lua",
"?/init.lua"
};
return 0;
}
for (size_t i = 0; i < sizeof(requirePath) / sizeof(char*); i++) {
char filename[LOVR_PATH_MAX];
char* sub = strchr(requirePath[i], '?');
static const char* libraryExtensions[] = {
#ifdef _WIN32
".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) {
int index = (int) (sub - requirePath[i]);
strncat(filename, requirePath[i], index);
strncat(filename, path, strlen(path));
strncat(filename, requirePath[i] + index + 1, strlen(requirePath[i]) - index);
char* path; int i;
vec_foreach(lovrFilesystemGetCRequirePath(), path, i) {
for (const char** extension = libraryExtensions; *extension != NULL; extension++) {
char buffer[64];
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)) {
lua_pop(L, 1);
lua_pushstring(L, filename);
return l_lovrFilesystemLoad(L);
char fullPath[LOVR_PATH_MAX];
const char* realPath = lovrFilesystemGetRealDirectory(filename);
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;
}
int l_lovrFilesystemInit(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) {
static int l_lovrFilesystemAppend(lua_State* L) {
size_t size;
const char* path = luaL_checkstring(L, 1);
const char* content = luaL_checklstring(L, 2, &size);
@ -111,14 +95,14 @@ int l_lovrFilesystemAppend(lua_State* L) {
return 1;
}
int l_lovrFilesystemCreateDirectory(lua_State* L) {
static int l_lovrFilesystemCreateDirectory(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
lua_pushboolean(L, !lovrFilesystemCreateDirectory(path));
return 1;
}
int l_lovrFilesystemGetAppdataDirectory(lua_State* L) {
char buffer[1024];
static int l_lovrFilesystemGetAppdataDirectory(lua_State* L) {
char buffer[LOVR_PATH_MAX];
if (lovrFilesystemGetAppdataDirectory(buffer, sizeof(buffer))) {
lua_pushnil(L);
@ -129,15 +113,15 @@ int l_lovrFilesystemGetAppdataDirectory(lua_State* L) {
return 1;
}
int l_lovrFilesystemGetDirectoryItems(lua_State* L) {
static int l_lovrFilesystemGetDirectoryItems(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
lua_newtable(L);
lovrFilesystemGetDirectoryItems(path, pushDirectoryItem, L);
return 1;
}
int l_lovrFilesystemGetExecutablePath(lua_State* L) {
char buffer[1024];
static int l_lovrFilesystemGetExecutablePath(lua_State* L) {
char buffer[LOVR_PATH_MAX];
if (lovrFilesystemGetExecutablePath(buffer, sizeof(buffer))) {
lua_pushnil(L);
@ -148,7 +132,7 @@ int l_lovrFilesystemGetExecutablePath(lua_State* L) {
return 1;
}
int l_lovrFilesystemGetIdentity(lua_State* L) {
static int l_lovrFilesystemGetIdentity(lua_State* L) {
const char* identity = lovrFilesystemGetIdentity();
if (identity) {
lua_pushstring(L, identity);
@ -158,7 +142,7 @@ int l_lovrFilesystemGetIdentity(lua_State* L) {
return 1;
}
int l_lovrFilesystemGetLastModified(lua_State* L) {
static int l_lovrFilesystemGetLastModified(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
int lastModified = lovrFilesystemGetLastModified(path);
@ -171,18 +155,34 @@ int l_lovrFilesystemGetLastModified(lua_State* L) {
return 1;
}
int l_lovrFilesystemGetRealDirectory(lua_State* L) {
static int l_lovrFilesystemGetRealDirectory(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
lua_pushstring(L, lovrFilesystemGetRealDirectory(path));
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());
return 1;
}
int l_lovrFilesystemGetSize(lua_State* L) {
static int l_lovrFilesystemGetSize(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
size_t size = lovrFilesystemGetSize(path);
if ((int) size == -1) {
@ -192,7 +192,7 @@ int l_lovrFilesystemGetSize(lua_State* L) {
return 1;
}
int l_lovrFilesystemGetSource(lua_State* L) {
static int l_lovrFilesystemGetSource(lua_State* L) {
const char* source = lovrFilesystemGetSource();
if (source) {
@ -204,29 +204,41 @@ int l_lovrFilesystemGetSource(lua_State* L) {
return 1;
}
int l_lovrFilesystemGetUserDirectory(lua_State* L) {
static int l_lovrFilesystemGetUserDirectory(lua_State* L) {
lua_pushstring(L, lovrFilesystemGetUserDirectory());
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);
lua_pushboolean(L, lovrFilesystemIsDirectory(path));
return 1;
}
int l_lovrFilesystemIsFile(lua_State* L) {
static int l_lovrFilesystemIsFile(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
lua_pushboolean(L, lovrFilesystemIsFile(path));
return 1;
}
int l_lovrFilesystemIsFused(lua_State* L) {
static int l_lovrFilesystemIsFused(lua_State* L) {
lua_pushboolean(L, lovrFilesystemIsFused());
return 1;
}
int l_lovrFilesystemLoad(lua_State* L) {
static int l_lovrFilesystemLoad(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
size_t 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);
}
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);
switch (status) {
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* mountpoint = luaL_optstring(L, 2, NULL);
bool append = lua_isnoneornil(L, 3) ? 0 : lua_toboolean(L, 3);
@ -252,48 +267,34 @@ int l_lovrFilesystemMount(lua_State* L) {
return 1;
}
int l_lovrFilesystemNewBlob(lua_State* L) {
const char* path;
char* data;
static int l_lovrFilesystemNewBlob(lua_State* L) {
size_t size;
if (lua_gettop(L) == 1) {
path = luaL_checkstring(L, 1);
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);
}
const char* path = luaL_checkstring(L, 1);
uint8_t* data = lovrFilesystemRead(path, &size);
lovrAssert(data, "Could not load file '%s'", path);
Blob* blob = lovrBlobCreate((void*) data, size, path);
luax_pushtype(L, Blob, blob);
lovrRelease(&blob->ref);
luax_pushobject(L, blob);
lovrRelease(blob);
return 1;
}
int l_lovrFilesystemRead(lua_State* L) {
static int l_lovrFilesystemRead(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
size_t size;
char* content = lovrFilesystemRead(path, &size);
if (!content) {
return luaL_error(L, "Could not read file '%s'", path);
}
lovrAssert(content, "Could not read file '%s'", path);
lua_pushlstring(L, content, size);
free(content);
return 1;
}
int l_lovrFilesystemRemove(lua_State* L) {
static int l_lovrFilesystemRemove(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
lua_pushboolean(L, !lovrFilesystemRemove(path));
return 1;
}
int l_lovrFilesystemSetIdentity(lua_State* L) {
static int l_lovrFilesystemSetIdentity(lua_State* L) {
if (lua_isnoneornil(L, 1)) {
lovrFilesystemSetIdentity(NULL);
} else {
@ -303,13 +304,19 @@ int l_lovrFilesystemSetIdentity(lua_State* L) {
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);
lua_pushboolean(L, !lovrFilesystemUnmount(path));
return 1;
}
int l_lovrFilesystemWrite(lua_State* L) {
static int l_lovrFilesystemWrite(lua_State* L) {
size_t size;
const char* path = luaL_checkstring(L, 1);
const char* content = luaL_checklstring(L, 2, &size);
@ -317,7 +324,7 @@ int l_lovrFilesystemWrite(lua_State* L) {
return 1;
}
const luaL_Reg lovrFilesystem[] = {
static const luaL_Reg lovrFilesystem[] = {
{ "append", l_lovrFilesystemAppend },
{ "createDirectory", l_lovrFilesystemCreateDirectory },
{ "getAppdataDirectory", l_lovrFilesystemGetAppdataDirectory },
@ -326,10 +333,12 @@ const luaL_Reg lovrFilesystem[] = {
{ "getIdentity", l_lovrFilesystemGetIdentity },
{ "getLastModified", l_lovrFilesystemGetLastModified },
{ "getRealDirectory", l_lovrFilesystemGetRealDirectory },
{ "getRequirePath", l_lovrFilesystemGetRequirePath },
{ "getSaveDirectory", l_lovrFilesystemGetSaveDirectory },
{ "getSize", l_lovrFilesystemGetSize },
{ "getSource", l_lovrFilesystemGetSource },
{ "getUserDirectory", l_lovrFilesystemGetUserDirectory },
{ "getWorkingDirectory", l_lovrFilesystemGetWorkingDirectory },
{ "isDirectory", l_lovrFilesystemIsDirectory },
{ "isFile", l_lovrFilesystemIsFile },
{ "isFused", l_lovrFilesystemIsFused },
@ -338,7 +347,27 @@ const luaL_Reg lovrFilesystem[] = {
{ "newBlob", l_lovrFilesystemNewBlob },
{ "read", l_lovrFilesystemRead },
{ "remove", l_lovrFilesystemRemove },
{ "setRequirePath", l_lovrFilesystemSetRequirePath },
{ "setIdentity", l_lovrFilesystemSetIdentity },
{ "unmount", l_lovrFilesystemUnmount },
{ "write", l_lovrFilesystemWrite },
{ 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 "headset/headset.h"
map_int_t ControllerAxes;
map_int_t ControllerButtons;
map_int_t ControllerHands;
map_int_t HeadsetDrivers;
map_int_t HeadsetEyes;
map_int_t HeadsetOrigins;
map_int_t HeadsetTypes;
const char* ControllerAxes[] = {
[CONTROLLER_AXIS_TRIGGER] = "trigger",
[CONTROLLER_AXIS_GRIP] = "grip",
[CONTROLLER_AXIS_TOUCHPAD_X] = "touchx",
[CONTROLLER_AXIS_TOUCHPAD_Y] = "touchy",
NULL
};
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 {
lua_State* L;
@ -16,200 +64,149 @@ typedef struct {
static HeadsetRenderData headsetRenderData;
static void renderHelper(HeadsetEye eye, void* userdata) {
static void renderHelper(void* userdata) {
HeadsetRenderData* renderData = userdata;
lua_State* L = renderData->L;
#ifdef EMSCRIPTEN
lua_rawgeti(L, LUA_REGISTRYINDEX, renderData->ref);
luax_pushenum(L, &HeadsetEyes, eye);
lua_call(L, 1, 0);
#else
lua_pushvalue(L, -1);
#endif
lua_call(L, 0, 0);
}
int l_lovrHeadsetInit(lua_State* L) {
lua_newtable(L);
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;
static int l_lovrHeadsetGetDriver(lua_State* L) {
lua_pushstring(L, HeadsetDrivers[lovrHeadsetDriver->driverType]);
return 1;
}
int l_lovrHeadsetIsPresent(lua_State* L) {
lua_pushboolean(L, lovrHeadsetIsPresent());
static int l_lovrHeadsetGetType(lua_State* L) {
lua_pushstring(L, HeadsetTypes[lovrHeadsetDriver->getType()]);
return 1;
}
int l_lovrHeadsetGetDriver(lua_State* L) {
const HeadsetDriver* driver = lovrHeadsetGetDriver();
if (driver) {
luax_pushenum(L, &HeadsetDrivers, *driver);
static int l_lovrHeadsetGetOriginType(lua_State* L) {
lua_pushstring(L, HeadsetOrigins[lovrHeadsetDriver->getOriginType()]);
return 1;
}
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 {
lua_pushnil(L);
lua_pushboolean(L, mirrored);
}
return 1;
}
int l_lovrHeadsetGetType(lua_State* L) {
luax_pushenum(L, &HeadsetTypes, lovrHeadsetGetType());
return 1;
}
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);
static int l_lovrHeadsetSetMirrored(lua_State* L) {
bool mirror = lua_toboolean(L, 1);
HeadsetEye eye = lua_type(L, 2) == LUA_TSTRING ? luaL_checkoption(L, 2, NULL, HeadsetEyes) : EYE_BOTH;
lovrHeadsetDriver->setMirrored(mirror, eye);
return 0;
}
int l_lovrHeadsetGetDisplayWidth(lua_State* L) {
int width;
lovrHeadsetGetDisplayDimensions(&width, NULL);
static int l_lovrHeadsetGetDisplayWidth(lua_State* L) {
uint32_t width, height;
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);
return 1;
}
int l_lovrHeadsetGetDisplayHeight(lua_State* L) {
int height;
lovrHeadsetGetDisplayDimensions(NULL, &height);
lua_pushnumber(L, height);
static int l_lovrHeadsetGetBoundsDepth(lua_State* L) {
float width, depth;
lovrHeadsetDriver->getBoundsDimensions(&width, &depth);
lua_pushnumber(L, depth);
return 1;
}
int l_lovrHeadsetGetDisplayDimensions(lua_State* L) {
int width, height;
lovrHeadsetGetDisplayDimensions(&width, &height);
static int l_lovrHeadsetGetBoundsDimensions(lua_State* L) {
float width, depth;
lovrHeadsetDriver->getBoundsDimensions(&width, &depth);
lua_pushnumber(L, width);
lua_pushnumber(L, height);
lua_pushnumber(L, depth);
return 2;
}
int l_lovrHeadsetGetClipDistance(lua_State* L) {
float near, far;
lovrHeadsetGetClipDistance(&near, &far);
lua_pushnumber(L, near);
lua_pushnumber(L, far);
return 2;
}
static int l_lovrHeadsetGetBoundsGeometry(lua_State* L) {
int count;
const float* points = lovrHeadsetDriver->getBoundsGeometry(&count);
int l_lovrHeadsetSetClipDistance(lua_State* L) {
float near = luaL_checknumber(L, 1);
float far = luaL_checknumber(L, 2);
lovrHeadsetSetClipDistance(near, far);
return 0;
}
if (!points) {
lua_pushnil(L);
return 1;
}
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;
}
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) {
if (lua_type(L, 1) == LUA_TSTRING) {
HeadsetEye eye = *(HeadsetEye*) luax_checkenum(L, 1, &HeadsetEyes, "eye");
lovrHeadsetGetEyePose(eye, x, y, z, angle, ax, ay, az);
HeadsetEye eye = luaL_checkoption(L, 1, NULL, HeadsetEyes);
lovrHeadsetDriver->getEyePose(eye, x, y, z, angle, ax, ay, az);
} 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;
luax_getPose(L, &x, &y, &z, &angle, &ax, &ay, &az);
lua_pushnumber(L, x);
@ -222,7 +219,7 @@ int l_lovrHeadsetGetPose(lua_State* L) {
return 7;
}
int l_lovrHeadsetGetPosition(lua_State* L) {
static int l_lovrHeadsetGetPosition(lua_State* L) {
float x, y, z, angle, ax, ay, az;
luax_getPose(L, &x, &y, &z, &angle, &ax, &ay, &az);
lua_pushnumber(L, x);
@ -231,7 +228,7 @@ int l_lovrHeadsetGetPosition(lua_State* L) {
return 3;
}
int l_lovrHeadsetGetOrientation(lua_State* L) {
static int l_lovrHeadsetGetOrientation(lua_State* L) {
float x, y, z, angle, ax, ay, az;
luax_getPose(L, &x, &y, &z, &angle, &ax, &ay, &az);
lua_pushnumber(L, angle);
@ -241,75 +238,71 @@ int l_lovrHeadsetGetOrientation(lua_State* L) {
return 4;
}
int l_lovrHeadsetGetVelocity(lua_State* L) {
static int l_lovrHeadsetGetVelocity(lua_State* L) {
float x, y, z;
lovrHeadsetGetVelocity(&x, &y, &z);
lovrHeadsetDriver->getVelocity(&x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
return 3;
}
int l_lovrHeadsetGetAngularVelocity(lua_State* L) {
static int l_lovrHeadsetGetAngularVelocity(lua_State* L) {
float x, y, z;
lovrHeadsetGetAngularVelocity(&x, &y, &z);
lovrHeadsetDriver->getAngularVelocity(&x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
return 3;
}
int l_lovrHeadsetGetControllers(lua_State* L) {
vec_controller_t* controllers = lovrHeadsetGetControllers();
if (!controllers) {
lua_newtable(L);
return 1;
}
lua_newtable(L);
Controller* controller; int i;
vec_foreach(controllers, controller, i) {
luax_pushtype(L, Controller, controller);
static int l_lovrHeadsetGetControllers(lua_State* L) {
uint8_t count;
Controller** controllers = lovrHeadsetDriver->getControllers(&count);
lua_createtable(L, count, 0);
for (uint8_t i = 0; i < count; i++) {
luax_pushobject(L, controllers[i]);
lua_rawseti(L, -2, i + 1);
}
return 1;
}
int l_lovrHeadsetGetControllerCount(lua_State* L) {
vec_controller_t* controllers = lovrHeadsetGetControllers();
if (controllers) {
lua_pushnumber(L, controllers->length);
} else {
lua_pushnumber(L, 0);
}
static int l_lovrHeadsetGetControllerCount(lua_State* L) {
uint8_t count;
lovrHeadsetDriver->getControllers(&count);
lua_pushnumber(L, count);
return 1;
}
int l_lovrHeadsetRenderTo(lua_State* L) {
static int l_lovrHeadsetRenderTo(lua_State* L) {
lua_settop(L, 1);
luaL_checktype(L, 1, LUA_TFUNCTION);
#ifdef EMSCRIPTEN
if (headsetRenderData.ref != LUA_NOREF) {
luaL_unref(L, LUA_REGISTRYINDEX, headsetRenderData.ref);
}
headsetRenderData.ref = luaL_ref(L, LUA_REGISTRYINDEX);
#endif
headsetRenderData.L = L;
lovrHeadsetRenderTo(renderHelper, &headsetRenderData);
lovrHeadsetDriver->renderTo(renderHelper, &headsetRenderData);
return 0;
}
int l_lovrHeadsetUpdate(lua_State* L) {
float dt = luaL_checknumber(L, 1);
lovrHeadsetUpdate(dt);
static int l_lovrHeadsetUpdate(lua_State* L) {
if (lovrHeadsetDriver->update) {
lovrHeadsetDriver->update(luaL_checknumber(L, 1));
}
return 0;
}
const luaL_Reg lovrHeadset[] = {
{ "isPresent", l_lovrHeadsetIsPresent },
static const luaL_Reg lovrHeadset[] = {
{ "getDriver", l_lovrHeadsetGetDriver },
{ "getType", l_lovrHeadsetGetType },
{ "getOriginType", l_lovrHeadsetGetOriginType },
{ "isMounted", l_lovrHeadsetIsMounted },
{ "isMirrored", l_lovrHeadsetIsMirrored },
{ "setMirrored", l_lovrHeadsetSetMirrored },
{ "getDisplayWidth", l_lovrHeadsetGetDisplayWidth },
@ -320,6 +313,7 @@ const luaL_Reg lovrHeadset[] = {
{ "getBoundsWidth", l_lovrHeadsetGetBoundsWidth },
{ "getBoundsDepth", l_lovrHeadsetGetBoundsDepth },
{ "getBoundsDimensions", l_lovrHeadsetGetBoundsDimensions },
{ "getBoundsGeometry", l_lovrHeadsetGetBoundsGeometry },
{ "getPose", l_lovrHeadsetGetPose },
{ "getPosition", l_lovrHeadsetGetPosition },
{ "getOrientation", l_lovrHeadsetGetOrientation },
@ -331,3 +325,59 @@ const luaL_Reg lovrHeadset[] = {
{ "update", l_lovrHeadsetUpdate },
{ 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/math.h"
#include "math/math.h"
#include "math/mat4.h"
#include "math/quat.h"
#include "math/randomGenerator.h"
#include "math/transform.h"
extern int l_lovrRandomGeneratorRandom(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) {
static int l_lovrMathNewRandomGenerator(lua_State* L) {
RandomGenerator* generator = lovrRandomGeneratorCreate();
if (lua_gettop(L) > 0){
Seed seed = luax_checkrandomseed(L, 1);
lovrRandomGeneratorSetSeed(generator, seed);
}
luax_pushtype(L, RandomGenerator, generator);
lovrRelease(&generator->ref);
luax_pushobject(L, generator);
lovrRelease(generator);
return 1;
}
int l_lovrMathNewTransform(lua_State* L) {
static int l_lovrMathNewTransform(lua_State* L) {
float matrix[16];
luax_readtransform(L, 1, matrix, 0);
luax_readtransform(L, 1, matrix, 3);
Transform* transform = lovrTransformCreate(matrix);
luax_pushtype(L, Transform, transform);
lovrRelease(&transform->ref);
luax_pushobject(L, transform);
lovrRelease(transform);
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 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) };
@ -54,7 +41,7 @@ int l_lovrMathLookAt(lua_State* L) {
return 4;
}
int l_lovrMathOrientationToDirection(lua_State* L) {
static int l_lovrMathOrientationToDirection(lua_State* L) {
float angle = luaL_checknumber(L, 1);
float ax = luaL_optnumber(L, 2, 0);
float ay = luaL_optnumber(L, 3, 1);
@ -67,31 +54,44 @@ int l_lovrMathOrientationToDirection(lua_State* L) {
return 3;
}
int l_lovrMathRandom(lua_State* L) {
luax_pushtype(L, RandomGenerator, lovrMathGetRandomGenerator());
static int l_lovrMathNoise(lua_State* L) {
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);
return l_lovrRandomGeneratorRandom(L);
}
int l_lovrMathRandomNormal(lua_State* L) {
luax_pushtype(L, RandomGenerator, lovrMathGetRandomGenerator());
static int l_lovrMathRandomNormal(lua_State* L) {
luax_pushobject(L, lovrMathGetRandomGenerator());
lua_insert(L, 1);
return l_lovrRandomGeneratorRandomNormal(L);
}
int l_lovrMathGetRandomSeed(lua_State* L) {
luax_pushtype(L, RandomGenerator, lovrMathGetRandomGenerator());
static int l_lovrMathGetRandomSeed(lua_State* L) {
luax_pushobject(L, lovrMathGetRandomGenerator());
lua_insert(L, 1);
return l_lovrRandomGeneratorGetSeed(L);
}
int l_lovrMathSetRandomSeed(lua_State* L) {
luax_pushtype(L, RandomGenerator, lovrMathGetRandomGenerator());
static int l_lovrMathSetRandomSeed(lua_State* L) {
luax_pushobject(L, lovrMathGetRandomGenerator());
lua_insert(L, 1);
return l_lovrRandomGeneratorSetSeed(L);
}
int l_lovrMathGammaToLinear(lua_State* L) {
static int l_lovrMathGammaToLinear(lua_State* L) {
if (lua_istable(L, 1)) {
for (int i = 0; i < 3; i++) {
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)) {
for (int i = 0; i < 3; i++) {
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 },
{ "newTransform", l_lovrMathNewTransform },
{ "orientationToDirection", l_lovrMathOrientationToDirection },
{ "lookAt", l_lovrMathLookAt },
{ "noise", l_lovrMathNoise },
{ "random", l_lovrMathRandom },
{ "randomNormal", l_lovrMathRandomNormal },
{ "getRandomSeed", l_lovrMathGetRandomSeed },
@ -139,3 +140,12 @@ const luaL_Reg lovrMath[] = {
{ 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 "physics/physics.h"
map_int_t ShapeTypes;
map_int_t JointTypes;
const char* ShapeTypes[] = {
[SHAPE_SPHERE] = "sphere",
[SHAPE_BOX] = "box",
[SHAPE_CAPSULE] = "capsule",
[SHAPE_CYLINDER] = "cylinder",
NULL
};
int l_lovrPhysicsInit(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrPhysics);
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);
const char* JointTypes[] = {
[JOINT_BALL] = "ball",
[JOINT_DISTANCE] = "distance",
[JOINT_HINGE] = "hinge",
[JOINT_SLIDER] = "slider",
NULL
};
map_init(&JointTypes);
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) {
static int l_lovrPhysicsNewWorld(lua_State* L) {
float xg = luaL_optnumber(L, 1, 0.f);
float yg = luaL_optnumber(L, 2, -9.81);
float zg = luaL_optnumber(L, 3, 0.f);
@ -56,47 +39,52 @@ int l_lovrPhysicsNewWorld(lua_State* L) {
tagCount = 0;
}
World* world = lovrWorldCreate(xg, yg, zg, allowSleep, tags, tagCount);
luax_pushtype(L, World, world);
luax_pushobject(L, world);
lovrRelease(world);
return 1;
}
int l_lovrPhysicsNewBallJoint(lua_State* L) {
static int l_lovrPhysicsNewBallJoint(lua_State* L) {
Collider* a = luax_checktype(L, 1, Collider);
Collider* b = luax_checktype(L, 2, Collider);
float x = luaL_checknumber(L, 3);
float y = luaL_checknumber(L, 4);
float z = luaL_checknumber(L, 5);
BallJoint* joint = lovrBallJointCreate(a, b, x, y, z);
luax_pushtype(L, BallJoint, joint);
luax_pushobject(L, joint);
lovrRelease(joint);
return 1;
}
int l_lovrPhysicsNewBoxShape(lua_State* L) {
static int l_lovrPhysicsNewBoxShape(lua_State* L) {
float x = luaL_optnumber(L, 1, 1.f);
float y = luaL_optnumber(L, 2, x);
float z = luaL_optnumber(L, 3, x);
BoxShape* box = lovrBoxShapeCreate(x, y, z);
luax_pushtype(L, BoxShape, box);
luax_pushobject(L, box);
lovrRelease(box);
return 1;
}
int l_lovrPhysicsNewCapsuleShape(lua_State* L) {
static int l_lovrPhysicsNewCapsuleShape(lua_State* L) {
float radius = luaL_optnumber(L, 1, 1.f);
float length = luaL_optnumber(L, 2, 1.f);
CapsuleShape* capsule = lovrCapsuleShapeCreate(radius, length);
luax_pushtype(L, CapsuleShape, capsule);
luax_pushobject(L, capsule);
lovrRelease(capsule);
return 1;
}
int l_lovrPhysicsNewCylinderShape(lua_State* L) {
static int l_lovrPhysicsNewCylinderShape(lua_State* L) {
float radius = luaL_optnumber(L, 1, 1.f);
float length = luaL_optnumber(L, 2, 1.f);
CylinderShape* cylinder = lovrCylinderShapeCreate(radius, length);
luax_pushtype(L, CylinderShape, cylinder);
luax_pushobject(L, cylinder);
lovrRelease(cylinder);
return 1;
}
int l_lovrPhysicsNewDistanceJoint(lua_State* L) {
static int l_lovrPhysicsNewDistanceJoint(lua_State* L) {
Collider* a = luax_checktype(L, 1, Collider);
Collider* b = luax_checktype(L, 2, Collider);
float x1 = luaL_checknumber(L, 3);
@ -106,11 +94,12 @@ int l_lovrPhysicsNewDistanceJoint(lua_State* L) {
float y2 = luaL_checknumber(L, 7);
float z2 = luaL_checknumber(L, 8);
DistanceJoint* joint = lovrDistanceJointCreate(a, b, x1, y1, z1, x2, y2, z2);
luax_pushtype(L, DistanceJoint, joint);
luax_pushobject(L, joint);
lovrRelease(joint);
return 1;
}
int l_lovrPhysicsNewHingeJoint(lua_State* L) {
static int l_lovrPhysicsNewHingeJoint(lua_State* L) {
Collider* a = luax_checktype(L, 1, Collider);
Collider* b = luax_checktype(L, 2, Collider);
float x = luaL_checknumber(L, 3);
@ -120,29 +109,32 @@ int l_lovrPhysicsNewHingeJoint(lua_State* L) {
float ay = luaL_checknumber(L, 7);
float az = luaL_checknumber(L, 8);
HingeJoint* joint = lovrHingeJointCreate(a, b, x, y, z, ax, ay, az);
luax_pushtype(L, HingeJoint, joint);
luax_pushobject(L, joint);
lovrRelease(joint);
return 1;
}
int l_lovrPhysicsNewSliderJoint(lua_State* L) {
static int l_lovrPhysicsNewSliderJoint(lua_State* L) {
Collider* a = luax_checktype(L, 1, Collider);
Collider* b = luax_checktype(L, 2, Collider);
float ax = luaL_checknumber(L, 3);
float ay = luaL_checknumber(L, 4);
float az = luaL_checknumber(L, 5);
SliderJoint* joint = lovrSliderJointCreate(a, b, ax, ay, az);
luax_pushtype(L, SliderJoint, joint);
luax_pushobject(L, joint);
lovrRelease(joint);
return 1;
}
int l_lovrPhysicsNewSphereShape(lua_State* L) {
static int l_lovrPhysicsNewSphereShape(lua_State* L) {
float radius = luaL_optnumber(L, 1, 1.f);
SphereShape* sphere = lovrSphereShapeCreate(radius);
luax_pushtype(L, SphereShape, sphere);
luax_pushobject(L, sphere);
lovrRelease(sphere);
return 1;
}
const luaL_Reg lovrPhysics[] = {
static const luaL_Reg lovrPhysics[] = {
{ "newWorld", l_lovrPhysicsNewWorld },
{ "newBallJoint", l_lovrPhysicsNewBallJoint },
{ "newBoxShape", l_lovrPhysicsNewBoxShape },
@ -154,3 +146,21 @@ const luaL_Reg lovrPhysics[] = {
{ "newSphereShape", l_lovrPhysicsNewSphereShape },
{ 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 "timer/timer.h"
int l_lovrTimerInit(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrTimer);
lovrTimerInit();
return 1;
}
int l_lovrTimerGetDelta(lua_State* L) {
static int l_lovrTimerGetDelta(lua_State* L) {
lua_pushnumber(L, lovrTimerGetDelta());
return 1;
}
int l_lovrTimerGetAverageDelta(lua_State* L) {
static int l_lovrTimerGetAverageDelta(lua_State* L) {
lua_pushnumber(L, lovrTimerGetAverageDelta());
return 1;
}
int l_lovrTimerGetFPS(lua_State* L) {
static int l_lovrTimerGetFPS(lua_State* L) {
lua_pushnumber(L, lovrTimerGetFPS());
return 1;
}
int l_lovrTimerGetTime(lua_State* L) {
static int l_lovrTimerGetTime(lua_State* L) {
lua_pushnumber(L, lovrTimerGetTime());
return 1;
}
int l_lovrTimerStep(lua_State* L) {
static int l_lovrTimerStep(lua_State* L) {
lua_pushnumber(L, lovrTimerStep());
return 1;
}
int l_lovrTimerSleep(lua_State* L) {
static int l_lovrTimerSleep(lua_State* L) {
double duration = luaL_checknumber(L, 1);
lovrTimerSleep(duration);
return 0;
}
const luaL_Reg lovrTimer[] = {
static const luaL_Reg lovrTimer[] = {
{ "getDelta", l_lovrTimerGetDelta },
{ "getAverageDelta", l_lovrTimerGetAverageDelta },
{ "getFPS", l_lovrTimerGetFPS },
@ -48,3 +41,11 @@ const luaL_Reg lovrTimer[] = {
{ "sleep", l_lovrTimerSleep },
{ 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 "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) {
AudioStream* stream = luax_checktype(L, 1, AudioStream);
@ -25,18 +40,11 @@ int l_lovrAudioStreamGetSampleRate(lua_State* L) {
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[] = {
{ "decode", l_lovrAudioStreamDecode },
{ "getBitDepth", l_lovrAudioStreamGetBitDepth },
{ "getChannelCount", l_lovrAudioStreamGetChannelCount },
{ "getDuration", l_lovrAudioStreamGetDuration },
{ "getSampleRate", l_lovrAudioStreamGetSampleRate },
{ "seek", l_lovrAudioStreamSeek },
{ NULL, NULL }
};

View File

@ -1,7 +1,7 @@
#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);
lua_pushstring(L, blob->name);
return 1;
@ -26,7 +26,7 @@ int l_lovrBlobGetString(lua_State* L) {
}
const luaL_Reg lovrBlob[] = {
{ "getFilename", l_lovrBlobGetFilename },
{ "getName", l_lovrBlobGetName },
{ "getPointer", l_lovrBlobGetPointer },
{ "getSize", l_lovrBlobGetSize },
{ "getString", l_lovrBlobGetString },

View File

@ -1,35 +1,158 @@
#include "api.h"
#include "graphics/graphics.h"
#include "api/graphics.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) {
Canvas* canvas = luax_checktype(L, 1, Canvas);
luaL_checktype(L, 2, LUA_TFUNCTION);
int nargs = lua_gettop(L) - 2;
lovrGraphicsPushView();
lovrCanvasBind(canvas);
lua_call(L, nargs, 0);
lovrCanvasResolveMSAA(canvas);
lovrGraphicsPopView();
int argumentCount = lua_gettop(L) - 2;
Canvas* old = lovrGraphicsGetCanvas();
lovrGraphicsSetCanvas(canvas);
lua_call(L, argumentCount, 0);
lovrGraphicsSetCanvas(old);
return 0;
}
int l_lovrCanvasGetFormat(lua_State* L) {
int l_lovrCanvasGetTexture(lua_State* L) {
Canvas* canvas = luax_checktype(L, 1, Canvas);
TextureFormat format = lovrCanvasGetFormat(canvas);
luax_pushenum(L, &TextureFormats, format);
int count;
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;
}
int l_lovrCanvasGetMSAA(lua_State* L) {
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;
}
const luaL_Reg lovrCanvas[] = {
{ "newTextureData", l_lovrCanvasNewTextureData },
{ "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 },
{ "isStereo", l_lovrCanvasIsStereo },
{ 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) {
Collider* collider = luax_checktype(L, 1, Collider);
World* world = lovrColliderGetWorld(collider);
luax_pushtype(L, World, world);
luax_pushobject(L, world);
return 1;
}
int l_lovrColliderAddShape(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
Shape* shape = luax_checktypeof(L, 2, Shape);
Shape* shape = luax_checktype(L, 2, Shape);
lovrColliderAddShape(collider, shape);
return 0;
}
int l_lovrColliderRemoveShape(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
Shape* shape = luax_checktypeof(L, 2, Shape);
Shape* shape = luax_checktype(L, 2, Shape);
lovrColliderRemoveShape(collider, shape);
return 0;
}
int l_lovrColliderGetShapeList(lua_State* L) {
int l_lovrColliderGetShapes(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
lua_newtable(L);
vec_void_t* shapes = lovrColliderGetShapes(collider);
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);
}
return 1;
}
int l_lovrColliderGetJointList(lua_State* L) {
int l_lovrColliderGetJoints(lua_State* L) {
Collider* collider = luax_checktype(L, 1, Collider);
lua_newtable(L);
vec_void_t* joints = lovrColliderGetJoints(collider);
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);
}
return 1;
@ -476,7 +476,8 @@ const luaL_Reg lovrCollider[] = {
{ "getWorld", l_lovrColliderGetWorld },
{ "addShape", l_lovrColliderAddShape },
{ "removeShape", l_lovrColliderRemoveShape },
{ "getShapeList", l_lovrColliderGetShapeList },
{ "getShapes", l_lovrColliderGetShapes },
{ "getJoints", l_lovrColliderGetJoints },
{ "getUserData", l_lovrColliderGetUserData },
{ "setUserData", l_lovrColliderSetUserData },
{ "isKinematic", l_lovrColliderIsKinematic },

View File

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

View File

@ -1,25 +1,15 @@
#include "api.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) {
Joint* joint = luax_checktypeof(L, 1, Joint);
Joint* joint = luax_checktype(L, 1, Joint);
lovrJointDestroyData(joint);
return 0;
}
int l_lovrJointGetType(lua_State* L) {
Joint* joint = luax_checktypeof(L, 1, Joint);
luax_pushenum(L, &JointTypes, lovrJointGetType(joint));
Joint* joint = luax_checktype(L, 1, Joint);
lua_pushstring(L, JointTypes[lovrJointGetType(joint)]);
return 1;
}
@ -28,20 +18,20 @@ int l_lovrJointGetColliders(lua_State* L) {
Collider* a;
Collider* b;
lovrJointGetColliders(joint, &a, &b);
luax_pushtype(L, Collider, a);
luax_pushtype(L, Collider, b);
luax_pushobject(L, a);
luax_pushobject(L, b);
return 2;
}
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);
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
return 1;
}
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);
if (ref) {
luaL_unref(L, LUA_REGISTRYINDEX, ref);

View File

@ -1,9 +1,10 @@
#include "api.h"
#include "api/graphics.h"
#include "graphics/material.h"
int l_lovrMaterialGetColor(lua_State* L) {
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);
lua_pushnumber(L, color.r);
lua_pushnumber(L, color.g);
@ -17,7 +18,7 @@ int l_lovrMaterialSetColor(lua_State* L) {
MaterialColor colorType = COLOR_DIFFUSE;
int index = 2;
if (lua_type(L, index) == LUA_TSTRING) {
colorType = *(MaterialColor*) luax_checkenum(L, index, &MaterialColors, "color");
colorType = luaL_checkoption(L, index, NULL, MaterialColors);
index++;
}
Color color = luax_checkcolor(L, index);
@ -27,7 +28,7 @@ int l_lovrMaterialSetColor(lua_State* L) {
int l_lovrMaterialGetScalar(lua_State* L) {
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);
lua_pushnumber(L, value);
return 1;
@ -35,7 +36,7 @@ int l_lovrMaterialGetScalar(lua_State* L) {
int l_lovrMaterialSetScalar(lua_State* L) {
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);
lovrMaterialSetScalar(material, scalarType, value);
return 0;
@ -43,9 +44,9 @@ int l_lovrMaterialSetScalar(lua_State* L) {
int l_lovrMaterialGetTexture(lua_State* L) {
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);
luax_pushtype(L, Texture, texture);
luax_pushobject(L, texture);
return 1;
}
@ -54,14 +55,37 @@ int l_lovrMaterialSetTexture(lua_State* L) {
MaterialTexture textureType = TEXTURE_DIFFUSE;
int index = 2;
if (lua_type(L, index) == LUA_TSTRING) {
textureType = *(MaterialTexture*) luax_checkenum(L, index, &MaterialTextures, "texture");
textureType = luaL_checkoption(L, index, NULL, MaterialTextures);
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);
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[] = {
{ "getColor", l_lovrMaterialGetColor },
{ "setColor", l_lovrMaterialSetColor },
@ -69,5 +93,7 @@ const luaL_Reg lovrMaterial[] = {
{ "setScalar", l_lovrMaterialSetScalar },
{ "getTexture", l_lovrMaterialGetTexture },
{ "setTexture", l_lovrMaterialSetTexture },
{ "getTransform", l_lovrMaterialGetTransform },
{ "setTransform", l_lovrMaterialSetTransform },
{ NULL, NULL }
};

View File

@ -1,11 +1,70 @@
#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) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
int instances = luaL_checkinteger(L, 2);
float transform[16];
luax_readtransform(L, 3, transform, 1);
lovrMeshDraw(mesh, transform, NULL, instances);
lovrGraphicsDraw(&(DrawCommand) {
.transform = transform,
.mesh = mesh,
.material = lovrMeshGetMaterial(mesh),
.instances = instances
});
return 0;
}
@ -17,14 +76,14 @@ int l_lovrMeshDraw(lua_State* L) {
int l_lovrMeshGetDrawMode(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
luax_pushenum(L, &MeshDrawModes, lovrMeshGetDrawMode(mesh));
lua_pushstring(L, MeshDrawModes[lovrMeshGetDrawMode(mesh)]);
return 1;
}
int l_lovrMeshSetDrawMode(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
MeshDrawMode* drawMode = (MeshDrawMode*) luax_checkenum(L, 2, &MeshDrawModes, "mesh draw mode");
lovrMeshSetDrawMode(mesh, *drawMode);
MeshDrawMode drawMode = luaL_checkoption(L, 2, NULL, MeshDrawModes);
lovrMeshSetDrawMode(mesh, drawMode);
return 0;
}
@ -43,7 +102,7 @@ int l_lovrMeshGetVertexCount(lua_State* L) {
int l_lovrMeshGetVertex(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
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);
return luax_pushvertex(L, &vertex, format);
}
@ -53,7 +112,7 @@ int l_lovrMeshSetVertex(lua_State* L) {
int index = luaL_checkint(L, 2) - 1;
lovrAssert(index >= 0 && index < lovrMeshGetVertexCount(mesh), "Invalid mesh vertex index: %d", index + 1);
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);
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(attributeIndex >= 0 && attributeIndex < format->count, "Invalid mesh attribute: %d", attributeIndex + 1);
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;
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(attributeIndex >= 0 && attributeIndex < format->count, "Invalid mesh attribute: %d", attributeIndex + 1);
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;
luax_setvertexattribute(L, 4, &vertex, attribute);
return 0;
@ -88,18 +147,35 @@ int l_lovrMeshSetVertexAttribute(lua_State* L) {
int l_lovrMeshSetVertices(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
VertexFormat* format = lovrMeshGetVertexFormat(mesh);
luaL_checktype(L, 2, LUA_TTABLE);
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);
uint32_t capacity = lovrMeshGetVertexCount(mesh);
for (int i = 0; i < vertexCount; i++) {
lua_rawgeti(L, 2, i + 1);
luaL_checktype(L, -1, LUA_TTABLE);
luax_setvertex(L, -1, &vertices, format);
lua_pop(L, 1);
VertexData* vertexData = NULL;
uint32_t sourceSize;
if (lua_istable(L, 2)) {
sourceSize = lua_objlen(L, 2);
} 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;
@ -107,19 +183,31 @@ int l_lovrMeshSetVertices(lua_State* L) {
int l_lovrMeshGetVertexMap(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
size_t count;
IndexPointer indices = lovrMeshGetVertexMap(mesh, &count);
uint32_t count;
size_t size;
IndexPointer indices = lovrMeshReadIndices(mesh, &count, &size);
if (count == 0) {
if (count == 0 || !indices.raw) {
lua_pushnil(L);
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++) {
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_rawseti(L, -2, i + 1);
lua_rawseti(L, 2, i + 1);
}
return 1;
@ -129,38 +217,45 @@ int l_lovrMeshSetVertexMap(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
if (lua_isnoneornil(L, 2)) {
lovrMeshSetVertexMap(mesh, NULL, 0);
lovrMeshWriteIndices(mesh, 0, 0);
return 0;
}
luaL_checktype(L, 2, LUA_TTABLE);
int count = lua_objlen(L, 2);
int vertexCount = lovrMeshGetVertexCount(mesh);
int indexSize = mesh->indexSize;
IndexPointer indices = lovrMeshGetVertexMap(mesh, NULL);
indices.raw = realloc(indices.raw, indexSize * count);
if (lua_type(L, 2) == LUA_TUSERDATA) {
Blob* blob = luax_checktype(L, 2, Blob);
size_t size = luaL_optinteger(L, 3, 4);
lovrAssert(size == 2 || size == 4, "Size of Mesh indices should be 2 bytes or 4 bytes");
uint32_t count = blob->size / size;
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++) {
lua_rawgeti(L, 2, i + 1);
if (!lua_isnumber(L, -1)) {
return luaL_error(L, "Mesh vertex map index #%d must be numeric", i);
for (uint32_t i = 0; i < count; i++) {
lua_rawgeti(L, 2, i + 1);
if (!lua_isnumber(L, -1)) {
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;
}
@ -181,13 +276,14 @@ int l_lovrMeshSetAttributeEnabled(lua_State* L) {
int l_lovrMeshGetDrawRange(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
if (!lovrMeshIsRangeEnabled(mesh)) {
uint32_t start, count;
lovrMeshGetDrawRange(mesh, &start, &count);
if (count == 0) {
lua_pushnil(L);
return 1;
}
int start, count;
lovrMeshGetDrawRange(mesh, &start, &count);
lua_pushinteger(L, start + 1);
lua_pushinteger(L, count);
return 2;
@ -196,11 +292,10 @@ int l_lovrMeshGetDrawRange(lua_State* L) {
int l_lovrMeshSetDrawRange(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
if (lua_isnoneornil(L, 2)) {
lovrMeshSetRangeEnabled(mesh, 0);
lovrMeshSetDrawRange(mesh, 0, 0);
return 0;
}
lovrMeshSetRangeEnabled(mesh, 1);
int rangeStart = luaL_checkinteger(L, 2) - 1;
int rangeCount = luaL_checkinteger(L, 3);
lovrMeshSetDrawRange(mesh, rangeStart, rangeCount);
@ -210,11 +305,7 @@ int l_lovrMeshSetDrawRange(lua_State* L) {
int l_lovrMeshGetMaterial(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
Material* material = lovrMeshGetMaterial(mesh);
if (material) {
luax_pushtype(L, Material, material);
} else {
lua_pushnil(L);
}
luax_pushobject(L, material);
return 1;
}
@ -230,6 +321,8 @@ int l_lovrMeshSetMaterial(lua_State* L) {
}
const luaL_Reg lovrMesh[] = {
{ "attachAttributes", l_lovrMeshAttachAttributes },
{ "detachAttributes", l_lovrMeshDetachAttributes },
{ "drawInstanced", l_lovrMeshDrawInstanced },
{ "draw", l_lovrMeshDraw },
{ "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/math.h"
#include "graphics/model.h"
int l_lovrModelDrawInstanced(lua_State* L) {
@ -28,7 +29,7 @@ int l_lovrModelGetAABB(lua_State* L) {
int l_lovrModelGetAnimator(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
Animator* animator = lovrModelGetAnimator(model);
luax_pushtype(L, Animator, animator);
luax_pushobject(L, animator);
return 1;
}
@ -53,7 +54,7 @@ int l_lovrModelGetMaterial(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
Material* material = lovrModelGetMaterial(model);
if (material) {
luax_pushtype(L, Material, material);
luax_pushobject(L, material);
} else {
lua_pushnil(L);
}
@ -74,7 +75,7 @@ int l_lovrModelSetMaterial(lua_State* L) {
int l_lovrModelGetMesh(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
Mesh* mesh = lovrModelGetMesh(model);
luax_pushtype(L, Mesh, mesh);
luax_pushobject(L, mesh);
return 1;
}

View File

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

View File

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

View File

@ -1,147 +1,258 @@
#include "api.h"
#include "graphics/graphics.h"
#include "api/graphics.h"
#include "graphics/shader.h"
#include "math/transform.h"
struct TempData {
void* data;
size_t size;
int size;
};
// Not thread safe
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) {
Shader* shader = luax_checktype(L, 1, Shader);
const char* name = luaL_checkstring(L, 2);
lua_pushboolean(L, lovrShaderGetUniform(shader, name) != NULL);
lua_pushboolean(L, lovrShaderHasUniform(shader, name));
return 1;
}
int l_lovrShaderSend(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader);
const char* name = luaL_checkstring(L, 2);
lua_settop(L, 3);
Uniform* uniform = lovrShaderGetUniform(shader, name);
if (!uniform) {
return luaL_error(L, "Unknown shader variable '%s'", name);
}
const Uniform* uniform = lovrShaderGetUniform(shader, name);
lovrAssert(uniform, "Unknown shader variable '%s'", name);
if (tempData.size < uniform->size) {
tempData.size = uniform->size;
tempData.data = realloc(tempData.data, tempData.size);
}
int* ints = (int*) tempData.data;
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);
}
}
luax_checkuniform(L, 3, uniform, tempData.data, name);
switch (uniform->type) {
case UNIFORM_FLOAT:
if (components == 1) {
floats[0] = luaL_checknumber(L, 3);
} else {
luaL_checktype(L, 3, LUA_TTABLE);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
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_FLOAT: lovrShaderSetFloats(shader, uniform->name, tempData.data, 0, uniform->count * uniform->components); break;
case UNIFORM_INT: lovrShaderSetInts(shader, uniform->name, tempData.data, 0, uniform->count * uniform->components); break;
case UNIFORM_MATRIX: lovrShaderSetMatrices(shader, uniform->name, tempData.data, 0, uniform->count * uniform->components * uniform->components); break;
case UNIFORM_SAMPLER: lovrShaderSetTextures(shader, uniform->name, tempData.data, 0, uniform->count); break;
case UNIFORM_IMAGE: lovrShaderSetImages(shader, uniform->name, tempData.data, 0, uniform->count); break;
}
return 0;
}
case UNIFORM_INT:
if (components == 1) {
ints[0] = luaL_checkinteger(L, 3);
} else {
luaL_checktype(L, 3, LUA_TTABLE);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
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;
int l_lovrShaderSendBlock(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader);
const char* name = luaL_checkstring(L, 2);
ShaderBlock* block = luax_checktype(L, 3, ShaderBlock);
UniformAccess access = luaL_checkoption(L, 4, "readwrite", UniformAccesses);
lovrShaderSetBlock(shader, name, block, access);
return 0;
}
case UNIFORM_MATRIX:
if (components == 4 && lua_isuserdata(L, 3)) {
Transform* transform = luax_checktype(L, 3, Transform);
memcpy(floats, transform->matrix, 16 * sizeof(float));
} 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;
int l_lovrShaderSendImage(lua_State* L) {
int index = 1;
Shader* shader = luax_checktype(L, index++, Shader);
const char* name = luaL_checkstring(L, index++);
case UNIFORM_SAMPLER:
if (components == 1) {
textures[0] = luax_checktypeof(L, 3, Texture);
} 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;
int start = 0;
if (lua_type(L, index) == LUA_TNUMBER) {
start = lua_tointeger(L, index++);
}
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;
}
const luaL_Reg lovrShader[] = {
{ "getType", l_lovrShaderGetType },
{ "hasUniform", l_lovrShaderHasUniform },
{ "send", l_lovrShaderSend },
{ "sendBlock", l_lovrShaderSendBlock },
{ "sendImage", l_lovrShaderSendImage },
{ 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 "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) {
Shape* shape = luax_checktypeof(L, 1, Shape);
Shape* shape = luax_checktype(L, 1, Shape);
lovrShapeDestroyData(shape);
return 0;
}
int l_lovrShapeGetType(lua_State* L) {
Shape* shape = luax_checktypeof(L, 1, Shape);
luax_pushenum(L, &ShapeTypes, lovrShapeGetType(shape));
Shape* shape = luax_checktype(L, 1, Shape);
lua_pushstring(L, ShapeTypes[lovrShapeGetType(shape)]);
return 1;
}
int l_lovrShapeGetCollider(lua_State* L) {
Shape* shape = luax_checktypeof(L, 1, Shape);
luax_pushtype(L, Collider, lovrShapeGetCollider(shape));
Shape* shape = luax_checktype(L, 1, Shape);
luax_pushobject(L, lovrShapeGetCollider(shape));
return 1;
}
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));
return 1;
}
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);
lovrShapeSetEnabled(shape, enabled);
return 0;
}
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);
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
return 1;
}
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);
if (ref) {
luaL_unref(L, LUA_REGISTRYINDEX, ref);
@ -67,7 +57,7 @@ int l_lovrShapeSetUserData(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;
lovrShapeGetPosition(shape, &x, &y, &z);
lua_pushnumber(L, x);
@ -77,7 +67,7 @@ int l_lovrShapeGetPosition(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 y = luaL_checknumber(L, 3);
float z = luaL_checknumber(L, 4);
@ -86,7 +76,7 @@ int l_lovrShapeSetPosition(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;
lovrShapeGetOrientation(shape, &angle, &x, &y, &z);
lua_pushnumber(L, angle);
@ -97,7 +87,7 @@ int l_lovrShapeGetOrientation(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 x = luaL_checknumber(L, 3);
float y = luaL_checknumber(L, 4);
@ -107,7 +97,7 @@ int l_lovrShapeSetOrientation(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 cx, cy, cz, mass;
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) {
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);
if (*unit == UNIT_SECONDS) {
if (unit == UNIT_SECONDS) {
lua_pushnumber(L, (float) duration / lovrSourceGetSampleRate(source));
} else {
lua_pushinteger(L, duration);
@ -78,6 +78,12 @@ int l_lovrSourceGetSampleRate(lua_State* L) {
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) {
float 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) {
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);
int sampleRate = lovrSourceGetSampleRate(source);
lovrSourceSeek(source, (int) (seconds * sampleRate + .5f));
@ -243,10 +249,10 @@ int l_lovrSourceStop(lua_State* L) {
int l_lovrSourceTell(lua_State* L) {
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);
if (*unit == UNIT_SECONDS) {
if (unit == UNIT_SECONDS) {
lua_pushnumber(L, (float) offset / lovrSourceGetSampleRate(source));
} else {
lua_pushinteger(L, offset);
@ -265,6 +271,7 @@ const luaL_Reg lovrSource[] = {
{ "getPitch", l_lovrSourceGetPitch },
{ "getPosition", l_lovrSourceGetPosition },
{ "getSampleRate", l_lovrSourceGetSampleRate },
{ "getType", l_lovrSourceGetType },
{ "getVelocity", l_lovrSourceGetVelocity },
{ "getVolume", l_lovrSourceGetVolume },
{ "getVolumeLimits", l_lovrSourceGetVolumeLimits },

View File

@ -1,17 +1,34 @@
#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) {
Texture* texture = luax_checktypeof(L, 1, Texture);
lua_pushnumber(L, texture->width);
lua_pushnumber(L, texture->height);
Texture* texture = luax_checktype(L, 1, Texture);
int mipmap = luax_optmipmap(L, 2, texture);
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;
}
int l_lovrTextureGetFilter(lua_State* L) {
Texture* texture = luax_checktypeof(L, 1, Texture);
Texture* texture = luax_checktype(L, 1, Texture);
TextureFilter filter = lovrTextureGetFilter(texture);
luax_pushenum(L, &FilterModes, filter.mode);
lua_pushstring(L, FilterModes[filter.mode]);
if (filter.mode == FILTER_ANISOTROPIC) {
lua_pushnumber(L, filter.anisotropy);
return 2;
@ -19,25 +36,43 @@ int l_lovrTextureGetFilter(lua_State* L) {
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) {
Texture* texture = luax_checktypeof(L, 1, Texture);
lua_pushnumber(L, texture->height);
Texture* texture = luax_checktype(L, 1, Texture);
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;
}
int l_lovrTextureGetWidth(lua_State* L) {
Texture* texture = luax_checktypeof(L, 1, Texture);
lua_pushnumber(L, texture->width);
Texture* texture = luax_checktype(L, 1, Texture);
lua_pushnumber(L, lovrTextureGetWidth(texture, luax_optmipmap(L, 2, texture)));
return 1;
}
int l_lovrTextureGetWrap(lua_State* L) {
Texture* texture = luax_checktypeof(L, 1, Texture);
Texture* texture = luax_checktype(L, 1, Texture);
TextureWrap wrap = lovrTextureGetWrap(texture);
luax_pushenum(L, &WrapModes, wrap.s);
luax_pushenum(L, &WrapModes, wrap.t);
if (texture->type == TEXTURE_CUBE) {
luax_pushenum(L, &WrapModes, wrap.r);
lua_pushstring(L, WrapModes[wrap.s]);
lua_pushstring(L, WrapModes[wrap.t]);
if (lovrTextureGetType(texture) == TEXTURE_CUBE) {
lua_pushstring(L, WrapModes[wrap.r]);
return 3;
}
return 2;
@ -46,14 +81,17 @@ int l_lovrTextureGetWrap(lua_State* L) {
int l_lovrTextureReplacePixels(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
TextureData* textureData = luax_checktype(L, 2, TextureData);
int slice = luaL_optinteger(L, 3, 1);
lovrTextureReplacePixels(texture, textureData, slice - 1);
int x = luaL_optinteger(L, 3, 0);
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;
}
int l_lovrTextureSetFilter(lua_State* L) {
Texture* texture = luax_checktypeof(L, 1, Texture);
FilterMode mode = *(FilterMode*) luax_checkenum(L, 2, &FilterModes, "filter mode");
Texture* texture = luax_checktype(L, 1, Texture);
FilterMode mode = luaL_checkoption(L, 2, NULL, FilterModes);
float anisotropy = luaL_optnumber(L, 3, 1.);
TextureFilter filter = { .mode = mode, .anisotropy = anisotropy };
lovrTextureSetFilter(texture, filter);
@ -61,19 +99,23 @@ int l_lovrTextureSetFilter(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;
wrap.s = *(WrapMode*) luax_checkenum(L, 2, &WrapModes, "wrap mode");
wrap.t = *(WrapMode*) luax_optenum(L, 3, luaL_checkstring(L, 2), &WrapModes, "wrap mode");
wrap.r = *(WrapMode*) luax_optenum(L, 4, luaL_checkstring(L, 2), &WrapModes, "wrap mode");
wrap.s = luaL_checkoption(L, 2, NULL, WrapModes);
wrap.t = luaL_checkoption(L, 3, luaL_checkstring(L, 2), WrapModes);
wrap.r = luaL_checkoption(L, 4, luaL_checkstring(L, 2), WrapModes);
lovrTextureSetWrap(texture, wrap);
return 0;
}
const luaL_Reg lovrTexture[] = {
{ "getDepth", l_lovrTextureGetDepth },
{ "getDimensions", l_lovrTextureGetDimensions },
{ "getFilter", l_lovrTextureGetFilter },
{ "getFormat", l_lovrTextureGetFormat },
{ "getHeight", l_lovrTextureGetHeight },
{ "getMipmapCount", l_lovrTextureGetMipmapCount },
{ "getType", l_lovrTextureGetType },
{ "getWidth", l_lovrTextureGetWidth },
{ "getWrap", l_lovrTextureGetWrap },
{ "replacePixels", l_lovrTextureReplacePixels },

View File

@ -1,4 +1,5 @@
#include "api.h"
#include "data/textureData.h"
int l_lovrTextureDataEncode(lua_State* L) {
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/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)) {
float x = luaL_optnumber(L, index++, 0);
float y = luaL_optnumber(L, index++, 0);
float z = luaL_optnumber(L, index++, 0);
float sx, sy, sz;
if (uniformScale) {
sx = sy = sz = luaL_optnumber(L, index++, 1);
float scale[3] = { 1., 1., 1. };
if (scaleComponents == 1) {
scale[0] = scale[1] = scale[2] = luaL_optnumber(L, index++, 1);
} else {
sx = luaL_optnumber(L, index++, 1);
sy = luaL_optnumber(L, index++, 1);
sz = luaL_optnumber(L, index++, 1);
for (int i = 0; i < scaleComponents; i++) {
scale[i] = luaL_optnumber(L, index++, 1);
}
}
float angle = luaL_optnumber(L, index++, 0);
float ax = luaL_optnumber(L, index++, 0);
float ay = luaL_optnumber(L, index++, 1);
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;
} else if (lua_isnoneornil(L, index)) {
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) {
Transform* transform = luax_checktype(L, 1, Transform);
bool table = lua_istable(L, 2);
lua_settop(L, 2);
float matrix[16];
lovrTransformGetMatrix(transform, matrix);
for (int i = 0; i < 16; 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) {
@ -67,16 +72,16 @@ int l_lovrTransformSetMatrix(lua_State* L) {
int l_lovrTransformClone(lua_State* L) {
Transform* transform = luax_checktype(L, 1, Transform);
Transform* clone = lovrTransformCreate(transform->matrix);
luax_pushtype(L, Transform, clone);
lovrRelease(&clone->ref);
luax_pushobject(L, clone);
lovrRelease(clone);
return 1;
}
int l_lovrTransformInverse(lua_State* L) {
Transform* transform = luax_checktype(L, 1, Transform);
Transform* inverse = lovrTransformCreate(lovrTransformInverse(transform));
luax_pushtype(L, Transform, inverse);
lovrRelease(&inverse->ref);
luax_pushobject(L, inverse);
lovrRelease(inverse);
return 1;
}
@ -125,39 +130,59 @@ int l_lovrTransformScale(lua_State* L) {
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) {
Transform* transform = luax_checktype(L, 1, Transform);
lovrTransformOrigin(transform); // Dirty the Transform
luax_readtransform(L, 2, transform->matrix, 0);
luax_readtransform(L, 2, transform->matrix, 3);
lua_pushvalue(L, 1);
return 1;
}
int l_lovrTransformTransformPoint(lua_State* L) {
Transform* transform = luax_checktype(L, 1, Transform);
float point[3] = {
luaL_checknumber(L, 2),
luaL_checknumber(L, 3),
luaL_checknumber(L, 4)
};
lovrTransformTransformPoint(transform, point);
lua_pushnumber(L, point[0]);
lua_pushnumber(L, point[1]);
lua_pushnumber(L, point[2]);
float x = luaL_checknumber(L, 2);
float y = luaL_checknumber(L, 3);
float z = luaL_checknumber(L, 4);
lovrTransformTransformPoint(transform, &x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
return 3;
}
int l_lovrTransformInverseTransformPoint(lua_State* L) {
Transform* transform = luax_checktype(L, 1, Transform);
float point[3] = {
luaL_checknumber(L, 2),
luaL_checknumber(L, 3),
luaL_checknumber(L, 4)
};
lovrTransformInverseTransformPoint(transform, point);
lua_pushnumber(L, point[0]);
lua_pushnumber(L, point[1]);
lua_pushnumber(L, point[2]);
float x = luaL_checknumber(L, 2);
float y = luaL_checknumber(L, 3);
float z = luaL_checknumber(L, 4);
lovrTransformInverseTransformPoint(transform, &x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
return 3;
}
@ -171,7 +196,8 @@ const luaL_Reg lovrTransform[] = {
{ "translate", l_lovrTransformTranslate },
{ "rotate", l_lovrTransformRotate },
{ "scale", l_lovrTransformScale },
{ "setTransformation", l_lovrTransformSetTransformation },
{ "setOrthographic", l_lovrTransformSetOrthographic },
{ "setPerspective", l_lovrTransformSetPerspective },
{ "transformPoint", l_lovrTransformTransformPoint },
{ "inverseTransformPoint", l_lovrTransformInverseTransformPoint },
{ NULL, NULL }

View File

@ -1,18 +1,48 @@
#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)) {
return;
return false;
}
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++) {
lua_rawgeti(L, index, i + 1);
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");
return;
return false;
}
lua_rawgeti(L, -1, 1);
@ -20,11 +50,14 @@ void luax_checkvertexformat(lua_State* L, int index, VertexFormat* format) {
lua_rawgeti(L, -3, 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);
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);
}
return true;
}
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);
// Type
luax_pushenum(L, &AttributeTypes, attribute.type);
lua_pushstring(L, AttributeTypes[attribute.type]);
lua_rawseti(L, -2, 2);
// Count
@ -55,7 +88,7 @@ int luax_pushvertexattribute(lua_State* L, VertexPointer* vertex, Attribute attr
switch (attribute.type) {
case ATTR_FLOAT: lua_pushnumber(L, *vertex->floats++); 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;
@ -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) {
VertexData* vertexData = luax_checktype(L, 1, VertexData);
uint32_t count = vertexData->count;
@ -137,7 +152,7 @@ int l_lovrVertexDataGetFormat(lua_State* L) {
int l_lovrVertexDataGetVertex(lua_State* L) {
VertexData* vertexData = luax_checktype(L, 1, VertexData);
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);
}
@ -146,9 +161,9 @@ int l_lovrVertexDataSetVertex(lua_State* L) {
uint32_t index = (uint32_t) luaL_checkint(L, 2) - 1;
lovrAssert(index < vertexData->count, "Invalid vertex index: %d", index + 1);
VertexFormat* format = &vertexData->format;
VertexPointer* vertex = &vertexData->data;
vertex->bytes += index * format->stride;
luax_setvertex(L, 3, vertex, format);
VertexPointer vertex = { .raw = vertexData->blob.data };
vertex.bytes += index * format->stride;
luax_setvertex(L, 3, &vertex, format);
return 0;
}
@ -160,7 +175,7 @@ int l_lovrVertexDataGetVertexAttribute(lua_State* L) {
lovrAssert(vertexIndex < vertexData->count, "Invalid vertex index: %d", vertexIndex + 1);
lovrAssert(attributeIndex >= 0 && attributeIndex < format->count, "Invalid attribute index: %d", attributeIndex + 1);
Attribute attribute = format->attributes[attributeIndex];
VertexPointer vertex = vertexData->data;
VertexPointer vertex = { .raw = vertexData->blob.data };
vertex.bytes += vertexIndex * format->stride + attribute.offset;
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(attributeIndex >= 0 && attributeIndex < format->count, "Invalid attribute index: %d", attributeIndex + 1);
Attribute attribute = format->attributes[attributeIndex];
VertexPointer vertex = vertexData->data;
VertexPointer vertex = { .raw = vertexData->blob.data };
vertex.bytes += vertexIndex * format->stride + attribute.offset;
luax_setvertexattribute(L, 4, &vertex, attribute);
return 0;
@ -186,7 +201,7 @@ int l_lovrVertexDataSetVertices(lua_State* L) {
uint32_t vertexCount = lua_objlen(L, 2);
int start = luaL_optnumber(L, 3, 1) - 1;
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;
for (uint32_t i = 0; i < vertexCount; i++) {
@ -200,9 +215,6 @@ int l_lovrVertexDataSetVertices(lua_State* L) {
}
const luaL_Reg lovrVertexData[] = {
{ "getPointer", l_lovrVertexDataGetPointer },
{ "getSize", l_lovrVertexDataGetSize },
{ "getString", l_lovrVertexDataGetString },
{ "getCount", l_lovrVertexDataGetCount },
{ "getFormat", l_lovrVertexDataGetFormat },
{ "getVertex", l_lovrVertexDataGetVertex },

View File

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

View File

@ -3,15 +3,25 @@
#include "math/quat.h"
#include "util.h"
#include <stdlib.h>
#include "lovr.h"
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() {
if (audioAlreadyInit) { // During a reload, bring down the audio device then recreate it
lovrAudioDestroy();
}
if (state.initialized) return;
ALCdevice* device = alcOpenDevice(NULL);
lovrAssert(device, "Unable to open default audio device");
@ -26,8 +36,7 @@ void lovrAudioInit() {
state.isSpatialized = alcIsExtensionPresent(device, "ALC_SOFT_HRTF");
if (state.isSpatialized) {
ALCint attrs[3] = { ALC_HRTF_SOFT, ALC_TRUE, 0 };
alcResetDeviceSOFT(device, attrs);
alcResetDeviceSOFT(device, (ALCint[]) { ALC_HRTF_SOFT, ALC_TRUE, 0 });
}
state.device = device;
@ -36,23 +45,28 @@ void lovrAudioInit() {
quat_set(state.orientation, 0, 0, 0, -1);
vec3_set(state.position, 0, 0, 0);
vec3_set(state.velocity, 0, 0, 0);
if (!audioAlreadyInit) {
atexit(lovrAudioDestroy);
audioAlreadyInit = true;
}
state.initialized = true;
}
void lovrAudioDestroy() {
if (!state.initialized) return;
alcMakeContextCurrent(NULL);
alcDestroyContext(state.context);
alcCloseDevice(state.device);
for (int i = 0; i < state.sources.length; i++) {
lovrRelease(state.sources.data[i]);
}
vec_deinit(&state.sources);
memset(&state, 0, sizeof(AudioState));
}
void lovrAudioUpdate() {
int i; Source* source;
vec_foreach_rev(&state.sources, source, i) {
if (source->type == SOURCE_STATIC) {
continue;
}
bool isStopped = lovrSourceIsStopped(source);
ALint processed;
alGetSourcei(source->id, AL_BUFFERS_PROCESSED, &processed);
@ -67,18 +81,32 @@ void lovrAudioUpdate() {
} else if (isStopped) {
lovrAudioStreamRewind(source->stream);
vec_splice(&state.sources, i, 1);
lovrRelease(&source->ref);
lovrRelease(source);
}
}
}
void lovrAudioAdd(Source* source) {
if (!lovrAudioHas(source)) {
lovrRetain(&source->ref);
lovrRetain(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) {
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) {
// Rotate the unit forward/up vectors by the quaternion derived from the specified angle/axis
float f[3] = { 0, 0, -1 };
float u[3] = { 0, 1, 0 };
float axis[3] = { ax, ay, az };
quat_fromAngleAxis(state.orientation, angle, axis);
quat_fromAngleAxis(state.orientation, angle, (float[3]) { ax, ay, az });
quat_rotate(state.orientation, f);
quat_rotate(state.orientation, u);

View File

@ -1,4 +1,5 @@
#include "audio/source.h"
#include "audio/microphone.h"
#include "lib/vec/vec.h"
#include <AL/al.h>
#include <AL/alc.h>
@ -7,20 +8,27 @@
#pragma once
#define MAX_MICROPHONES 8
typedef struct {
bool initialized;
ALCdevice* device;
ALCcontext* context;
vec_void_t sources;
bool isSpatialized;
float orientation[4];
float position[3];
float velocity[4];
float velocity[3];
} AudioState;
ALenum lovrAudioConvertFormat(int bitDepth, int channelCount);
void lovrAudioInit();
void lovrAudioDestroy();
void lovrAudioUpdate();
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 lovrAudioGetPosition(float* x, float* y, float* z);
void lovrAudioGetVelocity(float* x, float* y, float* z);
@ -30,6 +38,7 @@ bool lovrAudioIsSpatialized();
void lovrAudioPause();
void lovrAudioResume();
void lovrAudioRewind();
void lovrAudioSetDopplerEffect(float factor, float speedOfSound);
void lovrAudioSetOrientation(float angle, float ax, float ay, float az);
void lovrAudioSetPosition(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/audio.h"
#include "data/audioStream.h"
#define _USE_MATH_DEFINES
#include <math.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) {
ALenum state;
alGetSourcei(source->id, AL_SOURCE_STATE, &state);
return state;
}
Source* lovrSourceCreate(AudioStream* stream) {
Source* source = lovrAlloc(sizeof(Source), lovrSourceDestroy);
Source* lovrSourceCreateStatic(SoundData* soundData) {
Source* source = lovrAlloc(Source, lovrSourceDestroy);
if (!source) return NULL;
source->stream = stream;
source->isLooping = false;
ALenum format = lovrAudioConvertFormat(soundData->bitDepth, soundData->channelCount);
source->type = SOURCE_STATIC;
source->soundData = soundData;
alGenSources(1, &source->id);
alGenBuffers(SOURCE_BUFFERS, source->buffers);
lovrRetain(&stream->ref);
alGenBuffers(1, source->buffers);
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;
}
void lovrSourceDestroy(const Ref* ref) {
Source* source = containerof(ref, Source);
Source* lovrSourceCreateStream(AudioStream* stream) {
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);
alDeleteBuffers(SOURCE_BUFFERS, source->buffers);
lovrRelease(&source->stream->ref);
alDeleteBuffers(source->type == SOURCE_STATIC ? 1 : SOURCE_BUFFERS, source->buffers);
lovrRelease(source->soundData);
lovrRelease(source->stream);
free(source);
}
SourceType lovrSourceGetType(Source* source) {
return source->type;
}
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) {
@ -61,7 +66,7 @@ void lovrSourceGetCone(Source* source, float* innerAngle, float* outerAngle, flo
}
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) {
@ -73,7 +78,7 @@ void lovrSourceGetDirection(Source* source, float* x, float* y, float* z) {
}
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) {
@ -97,7 +102,7 @@ void lovrSourceGetPosition(Source* source, float* x, float* y, float* z) {
}
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) {
@ -180,12 +185,21 @@ void lovrSourceRewind(Source* source) {
}
void lovrSourceSeek(Source* source, int sample) {
bool wasPaused = lovrSourceIsPaused(source);
lovrSourceStop(source);
lovrAudioStreamSeek(source->stream, sample);
lovrSourcePlay(source);
if (wasPaused) {
lovrSourcePause(source);
switch (source->type) {
case SOURCE_STATIC:
alSourcef(source->id, AL_SAMPLE_OFFSET, sample);
break;
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) {
source->isLooping = isLooping;
if (source->type == SOURCE_STATIC) {
alSourcei(source->id, AL_LOOPING, isLooping ? AL_TRUE : AL_FALSE);
}
}
void lovrSourceSetPitch(Source* source, float pitch) {
@ -241,29 +258,43 @@ void lovrSourceStop(Source* source) {
return;
}
// Empty the buffers
int count = 0;
alGetSourcei(source->id, AL_BUFFERS_QUEUED, &count);
alSourceUnqueueBuffers(source->id, count, NULL);
switch (source->type) {
case SOURCE_STATIC:
alSourceStop(source->id);
break;
// Stop the source
alSourceStop(source->id);
alSourcei(source->id, AL_BUFFER, AL_NONE);
case SOURCE_STREAM: {
// Rewind the decoder
lovrAudioStreamRewind(source->stream);
// Empty the buffers
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
void lovrSourceStream(Source* source, ALuint* buffers, int count) {
if (source->type == SOURCE_STATIC) {
return;
}
AudioStream* stream = source->stream;
ALenum format = lovrSourceGetFormat(source);
ALenum format = lovrAudioConvertFormat(stream->bitDepth, stream->channelCount);
int frequency = stream->sampleRate;
int samples = 0;
int n = 0;
// 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);
}
@ -277,17 +308,28 @@ void lovrSourceStream(Source* source, ALuint* buffers, int count) {
}
int lovrSourceTell(Source* source) {
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);
switch (source->type) {
case SOURCE_STATIC: {
float offset;
alGetSourcef(source->id, AL_SAMPLE_OFFSET, &offset);
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) {
return offset + source->stream->samples;
} else {
return offset;
int offset = decoderOffset - queuedBuffers * samplesPerBuffer + sampleOffset;
if (offset < 0) {
return offset + source->stream->samples;
} else {
return offset;
}
break;
}
}
}

View File

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

View File

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

View File

@ -1,4 +1,4 @@
#include "filesystem/blob.h"
#include "data/blob.h"
#include "util.h"
#pragma once
@ -16,8 +16,8 @@ typedef struct {
} AudioStream;
AudioStream* lovrAudioStreamCreate(Blob* blob, size_t bufferSize);
void lovrAudioStreamDestroy(const Ref* ref);
int lovrAudioStreamDecode(AudioStream* stream);
void lovrAudioStreamDestroy(void* ref);
int lovrAudioStreamDecode(AudioStream* stream, short* destination, size_t size);
void lovrAudioStreamRewind(AudioStream* stream);
void lovrAudioStreamSeek(AudioStream* stream, int sample);
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* blob = lovrAlloc(sizeof(Blob), lovrBlobDestroy);
Blob* blob = lovrAlloc(Blob, lovrBlobDestroy);
if (!blob) return NULL;
blob->data = data;
blob->size = size;
blob->name = name;
blob->seek = 0;
return blob;
}
void lovrBlobDestroy(const Ref* ref) {
Blob* blob = containerof(ref, Blob);
void lovrBlobDestroy(void* ref) {
Blob* blob = ref;
free(blob->data);
free(blob);
}

View File

@ -1,4 +1,5 @@
#include "util.h"
#include <stdlib.h>
#pragma once
@ -11,4 +12,4 @@ typedef struct {
} Blob;
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 "filesystem/filesystem.h"
#include "filesystem/file.h"
#include "math/math.h"
#include "math/mat4.h"
#include "math/quat.h"
#include "math/vec3.h"
@ -9,6 +8,9 @@
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef LOVR_USE_ASSIMP
#include <assimp/cfileio.h>
#include <assimp/cimport.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) {
float scalar;
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;
if (aiGetMaterialColor(assimpMaterial, key, type, index, &assimpColor) == aiReturn_SUCCESS) {
Color color;
color.r = assimpColor.r;
color.g = assimpColor.g;
color.b = assimpColor.b;
color.a = assimpColor.a;
return color;
return (Color) { .r = assimpColor.r, .g = assimpColor.g, .b = assimpColor.b, .a = assimpColor.a };
} 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;
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;
int* cachedTexture = (TextureData**) map_get(textureCache, path);
int* cachedTexture = map_get(textureCache, path);
if (cachedTexture) {
return *cachedTexture;
}
@ -135,7 +161,8 @@ static TextureData* readMaterialTexture(struct aiMaterial* assimpMaterial, enum
strncpy(fullPath, dirname, LOVR_PATH_MAX);
char* lastSlash = strrchr(fullPath, '/');
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);
size_t size;
@ -145,7 +172,8 @@ static TextureData* readMaterialTexture(struct aiMaterial* assimpMaterial, enum
}
Blob* blob = lovrBlobCreate(data, size, path);
TextureData* textureData = lovrTextureDataFromBlob(blob);
TextureData* textureData = lovrTextureDataCreateFromBlob(blob, true);
lovrRelease(blob);
int textureIndex = modelData->textures.length;
vec_push(&modelData->textures, textureData);
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;
}
static unsigned long assimpFileTell(struct aiFile* assimpFile) {
static size_t assimpFileTell(struct aiFile* assimpFile) {
File* file = (File*) assimpFile->UserData;
return lovrFileTell(file);
}
@ -223,6 +251,7 @@ static struct aiFile* assimpFileOpen(struct aiFileIO* io, const char* path, cons
File* file = lovrFileCreate(normalizedPath);
if (lovrFileOpen(file, OPEN_READ)) {
lovrRelease(file);
return NULL;
}
@ -241,13 +270,13 @@ static void assimpFileClose(struct aiFileIO* io, struct aiFile* assimpFile) {
if (assimpFile->UserData != blob) {
File* file = (File*) assimpFile->UserData;
lovrFileClose(file);
lovrRelease(&file->ref);
lovrRelease(file);
}
free(assimpFile);
}
ModelData* lovrModelDataCreate(Blob* blob) {
ModelData* modelData = lovrAlloc(sizeof(ModelData), lovrModelDataDestroy);
ModelData* modelData = lovrAlloc(ModelData, lovrModelDataDestroy);
if (!modelData) return NULL;
struct aiFileIO assimpIO;
@ -266,13 +295,11 @@ ModelData* lovrModelDataCreate(Blob* blob) {
lovrThrow("Unable to load model from '%s': %s\n", blob->name, aiGetErrorString());
}
modelData->nodeCount = 0;
modelData->indexCount = 0;
uint32_t vertexCount = 0;
bool hasNormals = false;
bool hasUVs = false;
bool hasVertexColors = false;
bool hasTangents = false;
bool isSkinned = false;
for (unsigned int m = 0; m < scene->mNumMeshes; m++) {
@ -282,6 +309,7 @@ ModelData* lovrModelDataCreate(Blob* blob) {
hasNormals |= assimpMesh->mNormals != NULL;
hasUVs |= assimpMesh->mTextureCoords[0] != NULL;
hasVertexColors |= assimpMesh->mColors[0] != NULL;
hasTangents |= assimpMesh->mTangents != NULL;
isSkinned |= assimpMesh->mNumBones > 0;
}
@ -292,12 +320,13 @@ ModelData* lovrModelDataCreate(Blob* blob) {
if (hasNormals) vertexFormatAppend(&format, "lovrNormal", ATTR_FLOAT, 3);
if (hasUVs) vertexFormatAppend(&format, "lovrTexCoord", ATTR_FLOAT, 2);
if (hasVertexColors) vertexFormatAppend(&format, "lovrVertexColor", ATTR_BYTE, 4);
if (hasTangents) vertexFormatAppend(&format, "lovrTangent", ATTR_FLOAT, 3);
size_t boneByteOffset = format.stride;
if (isSkinned) vertexFormatAppend(&format, "lovrBones", ATTR_INT, 4);
if (isSkinned) vertexFormatAppend(&format, "lovrBoneWeights", ATTR_FLOAT, 4);
// Allocate
modelData->vertexData = lovrVertexDataCreate(vertexCount, &format, true);
modelData->vertexData = lovrVertexDataCreate(vertexCount, &format);
modelData->indexSize = vertexCount > USHRT_MAX ? sizeof(uint32_t) : sizeof(uint16_t);
modelData->indices.raw = malloc(modelData->indexCount * modelData->indexSize);
modelData->primitiveCount = scene->mNumMeshes;
@ -335,7 +364,7 @@ ModelData* lovrModelDataCreate(Blob* blob) {
// Vertices
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.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++;
}
@ -396,7 +437,7 @@ ModelData* lovrModelDataCreate(Blob* blob) {
for (unsigned int w = 0; w < assimpBone->mNumWeights; w++) {
uint32_t vertexIndex = baseVertex + assimpBone->mWeights[w].mVertexId;
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;
uint32_t* bones = (uint32_t*) (vertices.bytes + boneByteOffset);
float* weights = (float*) (bones + MAX_BONES_PER_VERTEX);
@ -424,8 +465,8 @@ ModelData* lovrModelDataCreate(Blob* blob) {
ModelMaterial* material = &modelData->materials[m];
struct aiMaterial* assimpMaterial = scene->mMaterials[m];
material->diffuseColor = readMaterialColor(assimpMaterial, AI_MATKEY_COLOR_DIFFUSE);
material->emissiveColor = readMaterialColor(assimpMaterial, AI_MATKEY_COLOR_EMISSIVE);
material->diffuseColor = readMaterialColor(assimpMaterial, AI_MATKEY_COLOR_DIFFUSE, (Color) { 1., 1., 1., 1. });
material->emissiveColor = readMaterialColor(assimpMaterial, AI_MATKEY_COLOR_EMISSIVE, (Color) { 0., 0., 0., 0. });
material->diffuseTexture = readMaterialTexture(assimpMaterial, aiTextureType_DIFFUSE, modelData, &textureCache, blob->name);
material->emissiveTexture = readMaterialTexture(assimpMaterial, aiTextureType_EMISSIVE, 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++) {
struct aiVectorKey assimpKeyframe = assimpChannel->mPositionKeys[k];
struct aiVector3D position = assimpKeyframe.mValue;
Keyframe keyframe;
keyframe.time = assimpKeyframe.mTime / ticksPerSecond;
vec3_set(keyframe.data, position.x, position.y, position.z);
vec_push(&channel.positionKeyframes, keyframe);
vec_push(&channel.positionKeyframes, ((Keyframe) {
.time = assimpKeyframe.mTime / ticksPerSecond,
.data = { position.x, position.y, position.z }
}));
}
for (unsigned int k = 0; k < assimpChannel->mNumRotationKeys; k++) {
struct aiQuatKey assimpKeyframe = assimpChannel->mRotationKeys[k];
struct aiQuaternion quaternion = assimpKeyframe.mValue;
Keyframe keyframe;
keyframe.time = assimpKeyframe.mTime / ticksPerSecond;
quat_set(keyframe.data, quaternion.x, quaternion.y, quaternion.z, quaternion.w);
vec_push(&channel.rotationKeyframes, keyframe);
vec_push(&channel.rotationKeyframes, ((Keyframe) {
.time = assimpKeyframe.mTime / ticksPerSecond,
.data = { quaternion.x, quaternion.y, quaternion.z, quaternion.w }
}));
}
for (unsigned int k = 0; k < assimpChannel->mNumScalingKeys; k++) {
struct aiVectorKey assimpKeyframe = assimpChannel->mScalingKeys[k];
struct aiVector3D scale = assimpKeyframe.mValue;
Keyframe keyframe;
keyframe.time = assimpKeyframe.mTime / ticksPerSecond;
vec3_set(keyframe.data, scale.x, scale.y, scale.z);
vec_push(&channel.scaleKeyframes, keyframe);
vec_push(&channel.scaleKeyframes, ((Keyframe) {
.time = assimpKeyframe.mTime / ticksPerSecond,
.data = { scale.x, scale.y, scale.z }
}));
}
map_set(&animation->channels, channel.node, channel);
@ -502,17 +543,32 @@ ModelData* lovrModelDataCreate(Blob* blob) {
aiReleaseImport(scene);
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* modelData = containerof(ref, ModelData);
ModelData* lovrModelDataCreateEmpty() {
return lovrAlloc(ModelData, lovrModelDataDestroy);
}
void lovrModelDataDestroy(void* ref) {
ModelData* modelData = ref;
for (int i = 0; i < modelData->nodeCount; i++) {
vec_deinit(&modelData->nodes[i].children);
vec_deinit(&modelData->nodes[i].primitives);
free((char*) modelData->nodes[i].name);
}
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++) {
@ -526,19 +582,17 @@ void lovrModelDataDestroy(const Ref* ref) {
vec_deinit(&channel->scaleKeyframes);
}
map_deinit(&animation->channels);
free((char*) animation->name);
}
for (int i = 0; i < modelData->textures.length; i++) {
TextureData* textureData = modelData->textures.data[i];
if (textureData) {
lovrRelease(&textureData->ref);
}
lovrRelease(modelData->textures.data[i]);
}
vec_deinit(&modelData->textures);
map_deinit(&modelData->nodeMap);
lovrRelease(&modelData->vertexData->ref);
lovrRelease(modelData->vertexData);
free(modelData->nodes);
free(modelData->primitives);
@ -548,44 +602,12 @@ void lovrModelDataDestroy(const Ref* ref) {
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]) {
float transform[16];
mat4_identity(transform);
aabb[0] = FLT_MAX;
aabb[1] = -FLT_MAX;
aabb[2] = FLT_MAX;
aabb[3] = -FLT_MAX;
aabb[4] = 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/vertexData.h"
#include "util.h"
@ -24,6 +24,8 @@ typedef struct {
int boneCount;
} ModelPrimitive;
typedef vec_t(unsigned int) vec_uint_t;
typedef struct ModelNode {
const char* name;
float transform[16];
@ -88,5 +90,6 @@ typedef struct {
} ModelData;
ModelData* lovrModelDataCreate(Blob* blob);
void lovrModelDataDestroy(const Ref* ref);
ModelData* lovrModelDataCreateEmpty();
void lovrModelDataDestroy(void* ref);
void lovrModelDataGetAABB(ModelData* modelData, float aabb[6]);

View File

@ -1,109 +1,47 @@
#include "data/rasterizer.h"
#include "resources/Cabin.ttf.h"
#include "util.h"
#include "lib/stb/stb_truetype.h"
#include "msdfgen-c.h"
#include <ft2build.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;
}
#include <math.h>
Rasterizer* lovrRasterizerCreate(Blob* blob, int size) {
if (!ft && FT_Init_FreeType(&ft)) {
lovrThrow("Error initializing FreeType");
Rasterizer* rasterizer = lovrAlloc(Rasterizer, lovrRasterizerDestroy);
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;
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;
lovrRetain(blob);
rasterizer->blob = blob;
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;
rasterizer->height = metrics.height >> 6;
rasterizer->advance = metrics.max_advance >> 6;
rasterizer->ascent = metrics.ascender >> 6;
rasterizer->descent = metrics.descender >> 6;
int ascent, descent, linegap;
stbtt_GetFontVMetrics(font, &ascent, &descent, &linegap);
rasterizer->ascent = roundf(ascent * rasterizer->scale);
rasterizer->descent = roundf(descent * rasterizer->scale);
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;
}
void lovrRasterizerDestroy(const Ref* ref) {
Rasterizer* rasterizer = containerof(ref, Rasterizer);
FT_Done_Face(rasterizer->ftHandle);
if (rasterizer->blob) {
lovrRelease(&rasterizer->blob->ref);
}
void lovrRasterizerDestroy(void* ref) {
Rasterizer* rasterizer = ref;
lovrRelease(rasterizer->blob);
free(rasterizer);
}
bool lovrRasterizerHasGlyph(Rasterizer* rasterizer, uint32_t character) {
FT_Face face = rasterizer->ftHandle;
return FT_Get_Char_Index(face, character) != 0;
return stbtt_FindGlyphIndex(&rasterizer->font, character) != 0;
}
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) {
FT_Face face = rasterizer->ftHandle;
FT_Error err = FT_Err_Ok;
FT_Glyph_Metrics* metrics;
FT_Outline_Funcs callbacks = {
.move_to = ftMoveTo,
.line_to = ftLineTo,
.conic_to = ftConicTo,
.cubic_to = ftCubicTo
};
int glyphIndex = stbtt_FindGlyphIndex(&rasterizer->font, character);
lovrAssert(glyphIndex, "Error loading glyph");
// Trace glyph outline
stbtt_vertex* vertices;
int vertexCount = stbtt_GetGlyphShape(&rasterizer->font, glyphIndex, &vertices);
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);
err = err || FT_Outline_Decompose(&face->glyph->outline, &callbacks, &context);
lovrAssert(!err, "Error loading glyph");
for (int i = 0; i < vertexCount; i++) {
stbtt_vertex vertex = vertices[i];
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->y = 0;
glyph->w = metrics->width >> 6;
glyph->h = metrics->height >> 6;
glyph->w = empty ? 0 : ceilf((x1 - x0) * rasterizer->scale);
glyph->h = empty ? 0 : ceilf((y1 - y0) * rasterizer->scale);
glyph->tw = glyph->w + 2 * GLYPH_PADDING;
glyph->th = glyph->h + 2 * GLYPH_PADDING;
glyph->dx = metrics->horiBearingX >> 6;
glyph->dy = metrics->horiBearingY >> 6;
glyph->advance = metrics->horiAdvance >> 6;
glyph->data = malloc(glyph->tw * glyph->th * 3 * sizeof(uint8_t));
glyph->dx = empty ? 0 : roundf(bearing * rasterizer->scale);
glyph->dy = empty ? 0 : roundf(y1 * rasterizer->scale);
glyph->advance = roundf(advance * rasterizer->scale);
glyph->data = lovrTextureDataCreate(glyph->tw, glyph->th, 0, FORMAT_RGB);
// Render SDF
float tx = GLYPH_PADDING + -glyph->dx;
float ty = GLYPH_PADDING + glyph->h - glyph->dy;
msShapeNormalize(shape);
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);
}
int lovrRasterizerGetKerning(Rasterizer* rasterizer, uint32_t left, uint32_t right) {
FT_Face face = rasterizer->ftHandle;
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;
return stbtt_GetCodepointKernAdvance(&rasterizer->font, left, right) * rasterizer->scale;
}

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/stb/stb_truetype.h"
#include "util.h"
#include <stdint.h>
#include <stdbool.h>
@ -10,9 +12,10 @@
typedef struct {
Ref ref;
void* ftHandle;
stbtt_fontinfo font;
Blob* blob;
int size;
float scale;
int glyphCount;
int height;
int advance;
@ -30,13 +33,13 @@ typedef struct {
int dx;
int dy;
int advance;
uint8_t* data;
TextureData* data;
} Glyph;
typedef map_t(Glyph) map_glyph_t;
Rasterizer* lovrRasterizerCreate(Blob* blob, int size);
void lovrRasterizerDestroy(const Ref* ref);
void lovrRasterizerDestroy(void* ref);
bool lovrRasterizerHasGlyph(Rasterizer* fontData, uint32_t character);
bool lovrRasterizerHasGlyphs(Rasterizer* fontData, const char* str);
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 "filesystem/file.h"
#include "math/math.h"
#include "lib/dds.h"
#include "lib/stb/stb_image.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);
}
textureData->data = NULL;
textureData->blob.data = NULL;
return 0;
}
TextureData* lovrTextureDataGetBlank(int width, int height, uint8_t value, TextureFormat format) {
TextureData* textureData = lovrAlloc(sizeof(TextureData), lovrTextureDataDestroy);
TextureData* lovrTextureDataCreate(int width, int height, uint8_t value, TextureFormat format) {
TextureData* textureData = lovrAlloc(TextureData, lovrTextureDataDestroy);
if (!textureData) return NULL;
size_t pixelSize = 0;
switch (format) {
case FORMAT_RGB: pixelSize = 3; break;
case FORMAT_RGBA: pixelSize = 4; break;
case FORMAT_RGBA4: pixelSize = 2; break;
case FORMAT_RGBA16F: pixelSize = 8; 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;
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");
@ -133,47 +147,34 @@ TextureData* lovrTextureDataGetBlank(int width, int height, uint8_t value, Textu
textureData->width = width;
textureData->height = height;
textureData->format = format;
textureData->data = memset(malloc(size), value, size);
textureData->blob = NULL;
textureData->blob.size = size;
textureData->blob.data = memset(malloc(size), value, size);
vec_init(&textureData->mipmaps);
textureData->generateMipmaps = false;
return textureData;
}
TextureData* lovrTextureDataGetEmpty(int width, int height, TextureFormat format) {
TextureData* textureData = lovrAlloc(sizeof(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);
TextureData* lovrTextureDataCreateFromBlob(Blob* blob, bool flip) {
TextureData* textureData = lovrAlloc(TextureData, lovrTextureDataDestroy);
if (!textureData) return NULL;
vec_init(&textureData->mipmaps);
if (!parseDDS(blob->data, blob->size, textureData)) {
textureData->blob = blob;
lovrRetain(&blob->ref);
textureData->source = blob;
lovrRetain(blob);
return textureData;
}
stbi_set_flip_vertically_on_load(1);
textureData->format = FORMAT_RGBA;
textureData->data = stbi_load_from_memory(blob->data, blob->size, &textureData->width, &textureData->height, NULL, 4);
textureData->blob = NULL;
textureData->generateMipmaps = true;
stbi_set_flip_vertically_on_load(flip);
if (stbi_is_hdr_from_memory(blob->data, blob->size)) {
textureData->format = FORMAT_RGBA32F;
textureData->blob.data = stbi_loadf_from_memory(blob->data, blob->size, &textureData->width, &textureData->height, NULL, 4);
} 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);
free(textureData);
return NULL;
@ -183,26 +184,26 @@ TextureData* lovrTextureDataFromBlob(Blob* blob) {
}
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 };
}
bool inside = x >= 0 && y >= 0 && x <= (textureData->width - 1) && y <= (textureData->height - 1);
lovrAssert(inside, "getPixel coordinates must be in TextureData bounds");
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 };
}
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;
}
bool inside = x >= 0 && y >= 0 && x <= (textureData->width - 1) && y <= (textureData->height - 1);
lovrAssert(inside, "setPixel coordinates must be in TextureData bounds");
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[1] = (uint8_t) (color.g * 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) {
File* file = NULL;
if ((file = lovrFileCreate(filename)) == NULL || lovrFileOpen(file, OPEN_WRITE)) {
lovrRelease(file);
return false;
}
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 width = textureData->width;
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;
bool success = stbi_write_png_to_func(writeCallback, file, width, height, components, data, stride);
lovrFileClose(file);
lovrRelease(file);
return success;
}
void lovrTextureDataDestroy(const Ref* ref) {
TextureData* textureData = containerof(ref, TextureData);
if (textureData->blob) {
lovrRelease(&textureData->blob->ref);
}
void lovrTextureDataDestroy(void* ref) {
TextureData* textureData = ref;
lovrRelease(textureData->source);
vec_deinit(&textureData->mipmaps);
free(textureData->data);
free(textureData);
lovrBlobDestroy(ref);
}

View File

@ -1,24 +1,27 @@
#include "filesystem/blob.h"
#include "data/blob.h"
#include "util.h"
#include "lib/vec/vec.h"
#include <stdint.h>
#include <stdbool.h>
#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 {
FORMAT_RGB,
FORMAT_RGBA,
FORMAT_RGBA4,
FORMAT_RGBA16F,
FORMAT_RGBA32F,
FORMAT_R16F,
FORMAT_R32F,
FORMAT_RG16F,
FORMAT_RG32F,
FORMAT_RGB5A1,
FORMAT_RGB10A2,
FORMAT_RG11B10F,
FORMAT_D16,
FORMAT_D32F,
FORMAT_D24S8,
FORMAT_DXT1,
FORMAT_DXT3,
FORMAT_DXT5
@ -34,20 +37,17 @@ typedef struct {
typedef vec_t(Mipmap) vec_mipmap_t;
typedef struct {
Ref ref;
Blob blob;
int width;
int height;
void* data;
Blob* blob;
Blob* source;
TextureFormat format;
bool generateMipmaps;
vec_mipmap_t mipmaps;
} TextureData;
TextureData* lovrTextureDataGetBlank(int width, int height, uint8_t value, TextureFormat format);
TextureData* lovrTextureDataGetEmpty(int width, int height, TextureFormat format);
TextureData* lovrTextureDataFromBlob(Blob* blob);
TextureData* lovrTextureDataCreate(int width, int height, uint8_t value, TextureFormat format);
TextureData* lovrTextureDataCreateFromBlob(Blob* blob, bool flip);
Color lovrTextureDataGetPixel(TextureData* textureData, int x, int y);
void lovrTextureDataSetPixel(TextureData* textureData, int x, int y, Color color);
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 <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) {
memset(format, 0, sizeof(*format));
@ -15,8 +14,8 @@ void vertexFormatAppend(VertexFormat* format, const char* name, AttributeType ty
format->stride += size * count;
}
VertexData* lovrVertexDataCreate(uint32_t count, VertexFormat* format, bool allocate) {
VertexData* vertexData = lovrAlloc(sizeof(VertexData), lovrVertexDataDestroy);
VertexData* lovrVertexDataCreate(uint32_t count, VertexFormat* format) {
VertexData* vertexData = lovrAlloc(VertexData, lovrBlobDestroy);
if (!vertexData) return NULL;
if (format) {
@ -30,21 +29,10 @@ VertexData* lovrVertexDataCreate(uint32_t count, VertexFormat* format, bool allo
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->data.raw = NULL;
if (allocate) {
vertexData->data.raw = malloc(format->stride * count);
memset(vertexData->data.raw, 0, format->stride * count);
}
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 <stdint.h>
#include <stdbool.h>
#pragma once
@ -39,14 +38,12 @@ typedef union {
} IndexPointer;
typedef struct {
Ref ref;
Blob blob;
VertexFormat format;
VertexPointer data;
uint32_t count;
} VertexData;
void vertexFormatInit(VertexFormat* format);
void vertexFormatAppend(VertexFormat* format, const char* name, AttributeType type, int count);
VertexData* lovrVertexDataCreate(uint32_t count, VertexFormat* format, bool allocate);
void lovrVertexDataDestroy(const Ref* ref);
VertexData* lovrVertexDataCreate(uint32_t count, VertexFormat* format);

View File

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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@
#include <physfs.h>
#include <stdio.h>
#include <stdlib.h>
#include "lovr.h"
#include <string.h>
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
@ -22,14 +22,17 @@
#include "BridgeLovr.h"
#endif
#ifdef _WIN32
const char lovrDirSep = '\\';
#else
const char lovrDirSep = '/';
#endif
static FilesystemState state;
bool filesystemAlreadyInit = false;
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
return;
filesystemAlreadyInit = true;
if (state.initialized) return;
state.initialized = true;
if (!PHYSFS_init(arg0)) {
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.identity = NULL;
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
lovrFilesystemGetExecutablePath(state.source, LOVR_PATH_MAX);
@ -47,23 +54,26 @@ void lovrFilesystemInit(const char* arg0, const char* arg1) {
if (arg1) {
strncpy(state.source, arg1, LOVR_PATH_MAX);
if (!lovrFilesystemMount(state.source, NULL, 1)) {
goto mounted;
return;
}
}
free(state.source);
state.source = NULL;
}
mounted:
atexit(lovrFilesystemDestroy);
}
void lovrFilesystemDestroy() {
if (!state.initialized) return;
free(state.source);
free(state.savePathFull);
free(state.savePathRelative);
for (int i = 0; i < 2; i++) {
free(state.requirePath[i]);
vec_deinit(&state.requirePattern[i]);
}
PHYSFS_deinit();
memset(&state, 0, sizeof(FilesystemState));
}
int lovrFilesystemCreateDirectory(const char* path) {
@ -108,27 +118,6 @@ void lovrFilesystemGetDirectoryItems(const char* path, getDirectoryItemsCallback
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() {
return state.identity;
}
@ -142,6 +131,14 @@ const char* lovrFilesystemGetRealDirectory(const char* path) {
return PHYSFS_getRealDir(path);
}
vec_str_t* lovrFilesystemGetRequirePath() {
return &state.requirePattern[0];
}
vec_str_t* lovrFilesystemGetCRequirePath() {
return &state.requirePattern[1];
}
const char* lovrFilesystemGetSaveDirectory() {
return state.savePathFull;
}
@ -171,6 +168,20 @@ const char* lovrFilesystemGetUserDirectory() {
#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) {
PHYSFS_Stat stat;
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
File* file = lovrFileCreate(path);
if (!file) {
lovrRelease(file);
return NULL;
}
// Open it
if (lovrFileOpen(file, OPEN_READ)) {
lovrRelease(file);
return NULL;
}
// Get file size
size_t size = lovrFileGetSize(file);
if (size == (unsigned int) -1) {
lovrRelease(file);
return NULL;
}
// Allocate buffer
void* data = malloc(size);
if (!data) {
lovrRelease(file);
return NULL;
}
// Perform read
*bytesRead = lovrFileRead(file, data, size);
lovrFileClose(file);
lovrRelease(file);
// Make sure we got everything
if (*bytesRead != (size_t) size) {
@ -266,6 +282,32 @@ int lovrFilesystemSetIdentity(const char* identity) {
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) {
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);
size_t bytesWritten = lovrFileWrite(file, (void*) content, size);
lovrFileClose(file);
lovrRelease(&file->ref);
lovrRelease(file);
return bytesWritten;
}

View File

@ -1,3 +1,4 @@
#include "lib/vec/vec.h"
#include <stdio.h>
#include <stdbool.h>
@ -5,14 +6,19 @@
#define LOVR_PATH_MAX 1024
extern const char lovrDirSep;
typedef int getDirectoryItemsCallback(void* userdata, const char* dir, const char* file);
typedef struct {
bool initialized;
char* source;
const char* identity;
char* savePathRelative;
char* savePathFull;
bool isFused;
char* requirePath[2];
vec_str_t requirePattern[2];
} FilesystemState;
void lovrFilesystemInit(const char* arg0, const char* arg1);
@ -20,14 +26,17 @@ void lovrFilesystemDestroy();
int lovrFilesystemCreateDirectory(const char* path);
int lovrFilesystemGetAppdataDirectory(char* dest, unsigned int size);
void lovrFilesystemGetDirectoryItems(const char* path, getDirectoryItemsCallback callback, void* userdata);
int lovrFilesystemGetExecutablePath(char* dest, unsigned int size);
#define lovrFilesystemGetExecutablePath lovrGetExecutablePath
const char* lovrFilesystemGetIdentity();
long lovrFilesystemGetLastModified(const char* path);
const char* lovrFilesystemGetRealDirectory(const char* path);
vec_str_t* lovrFilesystemGetRequirePath();
vec_str_t* lovrFilesystemGetCRequirePath();
const char* lovrFilesystemGetSaveDirectory();
size_t lovrFilesystemGetSize(const char* path);
const char* lovrFilesystemGetSource();
const char* lovrFilesystemGetUserDirectory();
int lovrFilesystemGetWorkingDirectory(char* dest, unsigned int size);
bool lovrFilesystemIsDirectory(const char* path);
bool lovrFilesystemIsFile(const char* path);
bool lovrFilesystemIsFused();
@ -35,6 +44,7 @@ int lovrFilesystemMount(const char* path, const char* mountpoint, bool append);
void* lovrFilesystemRead(const char* path, size_t* bytesRead);
int lovrFilesystemRemove(const char* path);
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);
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 "math/mat4.h"
#include "math/vec3.h"
#include "math/quat.h"
#include <math.h>
#include <stdlib.h>
static Track* lovrAnimatorEnsureTrack(Animator* animator, const char* 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* animator = lovrAlloc(sizeof(Animator), lovrAnimatorDestroy);
Animator* animator = lovrAlloc(Animator, lovrAnimatorDestroy);
if (!animator) return NULL;
lovrRetain(&modelData->ref);
lovrRetain(modelData);
animator->modelData = modelData;
map_init(&animator->trackMap);
vec_init(&animator->trackList);
@ -43,9 +45,9 @@ Animator* lovrAnimatorCreate(ModelData* modelData) {
return animator;
}
void lovrAnimatorDestroy(const Ref* ref) {
Animator* animator = containerof(ref, Animator);
lovrRelease(&animator->modelData->ref);
void lovrAnimatorDestroy(void* ref) {
Animator* animator = ref;
lovrRelease(animator->modelData);
map_deinit(&animator->trackMap);
vec_deinit(&animator->trackList);
free(animator);

View File

@ -2,6 +2,7 @@
#include "math/mat4.h"
#include "util.h"
#include "lib/map/map.h"
#include "lib/vec/vec.h"
#include <stdbool.h>
#pragma once
@ -27,7 +28,7 @@ typedef struct {
} Animator;
Animator* lovrAnimatorCreate(ModelData* modelData);
void lovrAnimatorDestroy(const Ref* ref);
void lovrAnimatorDestroy(void* ref);
void lovrAnimatorReset(Animator* animator);
void lovrAnimatorUpdate(Animator* animator, float dt);
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 "util.h"
typedef enum {
CANVAS_3D,
CANVAS_2D
} CanvasType;
#pragma once
#define MAX_CANVAS_ATTACHMENTS 4
typedef struct {
Texture texture;
CanvasType type;
GLuint framebuffer;
GLuint resolveFramebuffer;
GLuint depthStencilBuffer;
GLuint msaaTexture;
Texture* texture;
int slice;
int level;
} Attachment;
typedef struct {
struct {
bool enabled;
bool readable;
TextureFormat format;
} depth;
bool stereo;
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);
void lovrCanvasDestroy(const Ref* ref);
void lovrCanvasBind(Canvas* canvas);
void lovrCanvasResolveMSAA(Canvas* canvas);
TextureFormat lovrCanvasGetFormat(Canvas* canvas);
Canvas* lovrCanvasCreate(int width, int height, CanvasFlags flags);
void lovrCanvasDestroy(void* ref);
const Attachment* lovrCanvasGetAttachments(Canvas* canvas, int* count);
void lovrCanvasSetAttachments(Canvas* canvas, Attachment* attachments, int count);
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);
Texture* lovrCanvasGetDepthTexture(Canvas* canvas);
TextureData* lovrCanvasNewTextureData(Canvas* canvas, int index);

View File

@ -5,30 +5,30 @@
#include "data/textureData.h"
#include "util.h"
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
static int lovrFontAlignLine(vec_float_t* vertices, int index, float width, HorizontalAlign halign) {
while (index < vertices->length) {
static float* lovrFontAlignLine(float* x, float* lineEnd, float width, HorizontalAlign halign) {
while(x < lineEnd) {
if (halign == ALIGN_CENTER) {
vertices->data[index] -= width / 2.f;
*x -= width / 2.f;
} else if (halign == ALIGN_RIGHT) {
vertices->data[index] -= width;
*x -= width;
}
index += 5;
x += 8;
}
return index;
return x;
}
Font* lovrFontCreate(Rasterizer* rasterizer) {
Font* font = lovrAlloc(sizeof(Font), lovrFontDestroy);
Font* font = lovrAlloc(Font, lovrFontDestroy);
if (!font) return NULL;
lovrRetain(&rasterizer->ref);
lovrRetain(rasterizer);
font->rasterizer = rasterizer;
font->texture = NULL;
font->lineHeight = 1.f;
font->pixelDensity = (float) font->rasterizer->height;
map_init(&font->kerning);
@ -53,16 +53,22 @@ Font* lovrFontCreate(Rasterizer* rasterizer) {
return font;
}
void lovrFontDestroy(const Ref* ref) {
Font* font = containerof(ref, Font);
lovrRelease(&font->rasterizer->ref);
lovrRelease(&font->texture->ref);
void lovrFontDestroy(void* ref) {
Font* font = ref;
lovrRelease(font->rasterizer);
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->kerning);
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;
float cx = 0;
@ -78,17 +84,16 @@ void lovrFontRender(Font* font, const char* str, float wrap, HorizontalAlign hal
unsigned int codepoint;
size_t bytes;
int linePtr = 0;
float* cursor = vertices.floats;
float* lineStart = vertices.floats;
int lineCount = 1;
vec_reserve(vertices, len * 30);
vec_clear(vertices);
*vertexCount = 0;
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
// Newlines
if (codepoint == '\n' || (wrap && cx * scale > wrap && codepoint == ' ')) {
linePtr = lovrFontAlignLine(vertices, linePtr, cx, halign);
lineStart = lovrFontAlignLine(lineStart, cursor, cx, halign);
lineCount++;
cx = 0;
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
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;
}
@ -121,16 +126,18 @@ void lovrFontRender(Font* font, const char* str, float wrap, HorizontalAlign hal
float s2 = (glyph->x + glyph->tw) / u;
float t2 = glyph->y / v;
float quad[30] = {
x1, y1, 0, s1, t1,
x1, y2, 0, s1, t2,
x2, y1, 0, s2, t1,
x2, y1, 0, s2, t1,
x1, y2, 0, s1, t2,
x2, y2, 0, s2, t2
float quad[48] = {
x1, y1, 0, 0, 0, 0, s1, t1,
x1, y2, 0, 0, 0, 0, s1, t2,
x2, y1, 0, 0, 0, 0, s2, t1,
x2, y1, 0, 0, 0, 0, s2, t1,
x1, y2, 0, 0, 0, 0, s1, 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
@ -139,7 +146,7 @@ void lovrFontRender(Font* font, const char* str, float wrap, HorizontalAlign hal
}
// Align the last line
lovrFontAlignLine(vertices, linePtr, cx, halign);
lovrFontAlignLine(lineStart, cursor, cx, halign);
// Calculate vertical offset
if (valign == ALIGN_MIDDLE) {
@ -274,8 +281,7 @@ void lovrFontAddGlyph(Font* font, Glyph* glyph) {
glyph->y = atlas->y;
// Paste glyph into texture
lovrGraphicsBindTexture(font->texture, TEXTURE_2D, 0);
glTexSubImage2D(GL_TEXTURE_2D, 0, atlas->x, atlas->y, glyph->tw, glyph->th, GL_RGB, GL_UNSIGNED_BYTE, glyph->data);
lovrTextureReplacePixels(font->texture, glyph->data, atlas->x, atlas->y, 0, 0);
// Advance atlas cursor
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) {
if (font->texture) {
lovrRelease(&font->texture->ref);
}
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);
lovrRelease(font->texture);
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 });
lovrTextureSetWrap(font->texture, (TextureWrap) { .s = WRAP_CLAMP, .t = WRAP_CLAMP });
lovrRelease(textureData);
}

View File

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

View File

@ -1,33 +1,32 @@
#include "graphics/graphics.h"
#include "graphics/material.h"
#include <math.h>
#include <stdlib.h>
Material* lovrMaterialCreate(bool isDefault) {
Material* material = lovrAlloc(sizeof(Material), lovrMaterialDestroy);
Material* lovrMaterialCreate() {
Material* material = lovrAlloc(Material, lovrMaterialDestroy);
if (!material) return NULL;
material->isDefault = isDefault;
for (int i = 0; i < MAX_MATERIAL_SCALARS; i++) {
material->scalars[i] = 1.f;
}
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++) {
material->textures[i] = NULL;
}
lovrMaterialSetTransform(material, 0, 0, 1, 1, 0);
return material;
}
void lovrMaterialDestroy(const Ref* ref) {
Material* material = containerof(ref, Material);
void lovrMaterialDestroy(void* ref) {
Material* material = ref;
for (int i = 0; i < MAX_MATERIAL_TEXTURES; i++) {
if (material->textures[i]) {
lovrRelease(&material->textures[i]->ref);
}
lovrRelease(material->textures[i]);
}
free(material);
}
@ -54,14 +53,30 @@ Texture* lovrMaterialGetTexture(Material* material, MaterialTexture textureType)
void lovrMaterialSetTexture(Material* material, MaterialTexture textureType, Texture* texture) {
if (texture != material->textures[textureType]) {
if (material->textures[textureType]) {
lovrRelease(&material->textures[textureType]->ref);
}
lovrRetain(texture);
lovrRelease(material->textures[textureType]);
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];
Color colors[MAX_MATERIAL_COLORS];
Texture* textures[MAX_MATERIAL_TEXTURES];
float transform[9];
bool isDefault;
} Material;
Material* lovrMaterialCreate(bool isDefault);
void lovrMaterialDestroy(const Ref* ref);
Material* lovrMaterialCreate();
void lovrMaterialDestroy(void* ref);
float lovrMaterialGetScalar(Material* material, MaterialScalar scalarType);
void lovrMaterialSetScalar(Material* material, MaterialScalar scalarType, float value);
Color lovrMaterialGetColor(Material* material, MaterialColor colorType);
void lovrMaterialSetColor(Material* material, MaterialColor colorType, Color color);
Texture* lovrMaterialGetTexture(Material* material, MaterialTexture textureType);
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/shader.h"
#include "data/vertexData.h"
#include "math/math.h"
#include "lib/glfw.h"
#include "util.h"
#include <stdbool.h>
#pragma once
#define MAX_ATTACHMENTS 16
typedef enum {
MESH_POINTS = GL_POINTS,
MESH_LINES = GL_LINES,
MESH_LINE_STRIP = GL_LINE_STRIP,
MESH_TRIANGLE_STRIP = GL_TRIANGLE_STRIP,
MESH_TRIANGLES = GL_TRIANGLES,
MESH_TRIANGLE_FAN = GL_TRIANGLE_FAN
MESH_POINTS,
MESH_LINES,
MESH_LINE_STRIP,
MESH_LINE_LOOP,
MESH_TRIANGLE_STRIP,
MESH_TRIANGLES,
MESH_TRIANGLE_FAN
} MeshDrawMode;
typedef enum {
MESH_STATIC = GL_STATIC_DRAW,
MESH_DYNAMIC = GL_DYNAMIC_DRAW,
MESH_STREAM = GL_STREAM_DRAW
} MeshUsage;
typedef struct Mesh Mesh;
typedef struct {
Ref ref;
VertexData* vertexData;
IndexPointer indices;
size_t indexCount;
size_t indexSize;
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);
Mesh* lovrMeshCreate(uint32_t count, VertexFormat format, MeshDrawMode drawMode, BufferUsage usage);
void lovrMeshDestroy(void* ref);
void lovrMeshAttachAttribute(Mesh* mesh, Mesh* other, const char* name, int divisor);
void lovrMeshDetachAttribute(Mesh* mesh, const char* name);
void lovrMeshBind(Mesh* mesh, Shader* shader, int divisorMultiplier);
void lovrMeshDraw(Mesh* mesh, int instances);
VertexFormat* lovrMeshGetVertexFormat(Mesh* mesh);
MeshDrawMode lovrMeshGetDrawMode(Mesh* mesh);
void lovrMeshSetDrawMode(Mesh* mesh, MeshDrawMode drawMode);
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);
void lovrMeshSetAttributeEnabled(Mesh* mesh, const char* name, bool enabled);
bool lovrMeshIsRangeEnabled(Mesh* mesh);
void lovrMeshSetRangeEnabled(Mesh* mesh, char isEnabled);
void lovrMeshGetDrawRange(Mesh* mesh, int* start, int* count);
void lovrMeshSetDrawRange(Mesh* mesh, int start, int count);
void lovrMeshGetDrawRange(Mesh* mesh, uint32_t* start, uint32_t* count);
void lovrMeshSetDrawRange(Mesh* mesh, uint32_t start, uint32_t count);
Material* lovrMeshGetMaterial(Mesh* mesh);
void lovrMeshSetMaterial(Mesh* mesh, Material* material);
VertexPointer lovrMeshMap(Mesh* mesh, int start, size_t count, bool read, bool write);
void lovrMeshUnmap(Mesh* mesh);
float* lovrMeshGetPose(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/graphics.h"
#include "graphics/shader.h"
#include "data/blob.h"
#include "data/textureData.h"
#include "data/vertexData.h"
#include "math/mat4.h"
#include "math/vec3.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
static void renderNode(Model* model, int nodeIndex, int instances) {
ModelNode* node = &model->modelData->nodes[nodeIndex];
if (node->primitives.length > 0) {
lovrGraphicsPush();
lovrGraphicsMatrixTransform(MATRIX_MODEL, model->nodeTransforms[nodeIndex]);
float globalInverse[16];
if (model->animator) {
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);
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++) {
@ -50,54 +56,59 @@ static void renderNode(Model* model, int nodeIndex, int instances) {
}
Model* lovrModelCreate(ModelData* modelData) {
Model* model = lovrAlloc(sizeof(Model), lovrModelDestroy);
Model* model = lovrAlloc(Model, lovrModelDestroy);
if (!model) return NULL;
lovrRetain(&modelData->ref);
lovrRetain(modelData);
model->modelData = modelData;
model->aabbDirty = true;
model->animator = NULL;
model->material = NULL;
model->mesh = lovrMeshCreate(modelData->vertexData->count, &modelData->vertexData->format, MESH_TRIANGLES, MESH_STATIC);
VertexPointer vertices = lovrMeshMap(model->mesh, 0, modelData->vertexData->count, false, true);
memcpy(vertices.raw, modelData->vertexData->data.raw, modelData->vertexData->count * modelData->vertexData->format.stride);
lovrMeshUnmap(model->mesh);
lovrMeshSetVertexMap(model->mesh, modelData->indices.raw, modelData->indexCount);
lovrMeshSetRangeEnabled(model->mesh, true);
model->mesh = lovrMeshCreate(modelData->vertexData->count, modelData->vertexData->format, MESH_TRIANGLES, USAGE_STATIC);
VertexPointer vertices = lovrMeshMapVertices(model->mesh, 0, modelData->vertexData->count, false, true);
memcpy(vertices.raw, modelData->vertexData->blob.data, modelData->vertexData->count * modelData->vertexData->format.stride);
IndexPointer indices = lovrMeshWriteIndices(model->mesh, modelData->indexCount, modelData->indexSize);
memcpy(indices.raw, modelData->indices.raw, modelData->indexCount * modelData->indexSize);
if (modelData->textures.length > 0) {
model->textures = malloc(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;
model->textures = calloc(modelData->textures.length, sizeof(Texture*));
}
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++) {
ModelMaterial* materialData = &modelData->materials[i];
Material* material = lovrMaterialCreate(false);
Material* material = lovrMaterialCreate();
lovrMaterialSetScalar(material, SCALAR_METALNESS, materialData->metalness);
lovrMaterialSetScalar(material, SCALAR_ROUGHNESS, materialData->roughness);
lovrMaterialSetColor(material, COLOR_DIFFUSE, materialData->diffuseColor);
lovrMaterialSetColor(material, COLOR_EMISSIVE, materialData->emissiveColor);
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, model->textures[materialData->diffuseTexture]);
lovrMaterialSetTexture(material, TEXTURE_EMISSIVE, model->textures[materialData->emissiveTexture]);
lovrMaterialSetTexture(material, TEXTURE_METALNESS, model->textures[materialData->metalnessTexture]);
lovrMaterialSetTexture(material, TEXTURE_ROUGHNESS, model->textures[materialData->roughnessTexture]);
lovrMaterialSetTexture(material, TEXTURE_OCCLUSION, model->textures[materialData->occlusionTexture]);
lovrMaterialSetTexture(material, TEXTURE_NORMAL, model->textures[materialData->normalTexture]);
for (MaterialTexture textureType = 0; textureType < MAX_MATERIAL_TEXTURES; textureType++) {
int textureIndex = 0;
switch (textureType) {
case TEXTURE_DIFFUSE: textureIndex = materialData->diffuseTexture; break;
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;
}
} else {
model->materials = NULL;
}
for (int i = 0; i < MAX_BONES; i++) {
@ -120,26 +131,20 @@ Model* lovrModelCreate(ModelData* modelData) {
return model;
}
void lovrModelDestroy(const Ref* ref) {
Model* model = containerof(ref, Model);
void lovrModelDestroy(void* ref) {
Model* model = ref;
for (int i = 0; i < model->modelData->textures.length; i++) {
if (model->textures[i]) {
lovrRelease(&model->textures[i]->ref);
}
lovrRelease(model->textures[i]);
}
for (int i = 0; i < model->modelData->materialCount; i++) {
lovrRelease(&model->materials[i]->ref);
}
if (model->animator) {
lovrRelease(&model->animator->ref);
}
if (model->material) {
lovrRelease(&model->material->ref);
lovrRelease(model->materials[i]);
}
lovrRelease(model->animator);
lovrRelease(model->material);
free(model->textures);
free(model->materials);
lovrRelease(&model->modelData->ref);
lovrRelease(&model->mesh->ref);
lovrRelease(model->modelData);
lovrRelease(model->mesh);
free(model->nodeTransforms);
free(model);
}
@ -153,8 +158,7 @@ void lovrModelDraw(Model* model, mat4 transform, int instances) {
for (int i = 0; i < model->modelData->nodeCount; i++) {
ModelNode* node = &model->modelData->nodes[i];
float localTransform[16];
mat4_identity(localTransform);
float localTransform[16] = MAT4_IDENTITY;
if (!lovrAnimatorEvaluate(model->animator, node->name, localTransform)) {
mat4_set(localTransform, node->transform);
}
@ -174,9 +178,10 @@ void lovrModelDraw(Model* model, mat4 transform, int instances) {
}
lovrGraphicsPush();
lovrGraphicsMatrixTransform(MATRIX_MODEL, transform);
lovrGraphicsMatrixTransform(transform);
renderNode(model, 0, instances);
lovrGraphicsPop();
lovrMeshSetPose(model->mesh, NULL);
}
Animator* lovrModelGetAnimator(Model* model) {
@ -185,15 +190,9 @@ Animator* lovrModelGetAnimator(Model* model) {
void lovrModelSetAnimator(Model* model, Animator* animator) {
if (model->animator != animator) {
if (model->animator) {
lovrRelease(&model->animator->ref);
}
lovrRetain(animator);
lovrRelease(model->animator);
model->animator = animator;
if (animator) {
lovrRetain(&animator->ref);
}
}
}
@ -207,15 +206,9 @@ Material* lovrModelGetMaterial(Model* model) {
void lovrModelSetMaterial(Model* model, Material* material) {
if (model->material != material) {
if (model->material) {
lovrRelease(&model->material->ref);
}
lovrRetain(material);
lovrRelease(model->material);
model->material = material;
if (material) {
lovrRetain(&material->ref);
}
}
}

View File

@ -3,8 +3,7 @@
#include "graphics/material.h"
#include "graphics/mesh.h"
#include "graphics/texture.h"
#include "math/math.h"
#include "lib/glfw.h"
#include "math/mat4.h"
#include "util.h"
#include <stdbool.h>
@ -25,7 +24,7 @@ typedef struct {
} Model;
Model* lovrModelCreate(ModelData* modelData);
void lovrModelDestroy(const Ref* ref);
void lovrModelDestroy(void* ref);
void lovrModelDraw(Model* model, mat4 transform, int instances);
Animator* lovrModelGetAnimator(Model* model);
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 "math/math.h"
#include "lib/map/map.h"
#include "lib/glfw.h"
#include "util.h"
#include "lib/vec/vec.h"
#include <stdbool.h>
#pragma once
#define LOVR_SHADER_POSITION 0
#define LOVR_SHADER_NORMAL 1
#define LOVR_SHADER_TEX_COORD 2
#define LOVR_SHADER_VERTEX_COLOR 3
#define LOVR_SHADER_BONES 4
#define LOVR_SHADER_BONE_WEIGHTS 5
#define LOVR_MAX_UNIFORM_LENGTH 256
#define LOVR_MAX_UNIFORM_LENGTH 64
#define LOVR_MAX_ATTRIBUTE_LENGTH 64
typedef enum {
USAGE_STATIC,
USAGE_DYNAMIC,
USAGE_STREAM
} BufferUsage;
typedef enum {
ACCESS_READ,
ACCESS_WRITE,
ACCESS_READ_WRITE
} UniformAccess;
typedef enum {
BLOCK_UNIFORM,
BLOCK_STORAGE
} BlockType;
typedef enum {
UNIFORM_FLOAT,
UNIFORM_MATRIX,
UNIFORM_INT,
UNIFORM_SAMPLER
UNIFORM_SAMPLER,
UNIFORM_IMAGE
} UniformType;
typedef union {
void* data;
int* ints;
float* floats;
Texture** textures;
} UniformValue;
typedef enum {
SHADER_GRAPHICS,
SHADER_COMPUTE
} ShaderType;
typedef enum {
SHADER_DEFAULT,
SHADER_SKYBOX,
SHADER_CUBE,
SHADER_PANO,
SHADER_FONT,
SHADER_FULLSCREEN
SHADER_FILL,
MAX_DEFAULT_SHADERS
} DefaultShader;
typedef struct {
GLchar name[LOVR_MAX_UNIFORM_LENGTH];
GLenum glType;
int index;
int location;
int count;
int components;
size_t size;
Texture* texture;
int slice;
int mipmap;
UniformAccess access;
} Image;
typedef struct {
char name[LOVR_MAX_UNIFORM_LENGTH];
UniformType type;
UniformValue value;
int baseTextureSlot;
int components;
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;
} 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 {
Ref ref;
uint32_t program;
map_uniform_t uniforms;
float model[16];
float view[16];
float projection[16];
Color color;
} Shader;
vec_uniform_t uniforms;
int slot;
ShaderBlock* source;
UniformAccess access;
} UniformBlock;
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);
void lovrShaderDestroy(const Ref* ref);
void lovrShaderDestroy(void* ref);
ShaderType lovrShaderGetType(Shader* shader);
void lovrShaderBind(Shader* shader);
int lovrShaderGetAttributeId(Shader* shader, const char* name);
Uniform* lovrShaderGetUniform(Shader* shader, const char* name);
void lovrShaderSetFloat(Shader* shader, const char* name, float* data, int count);
void lovrShaderSetInt(Shader* shader, const char* name, int* data, int count);
void lovrShaderSetMatrix(Shader* shader, const char* name, float* data, int count);
void lovrShaderSetTexture(Shader* shader, const char* name, Texture** data, int count);
bool lovrShaderHasUniform(Shader* shader, const char* name);
const Uniform* lovrShaderGetUniform(Shader* shader, const char* name);
void lovrShaderSetFloats(Shader* shader, const char* name, float* data, int start, int count);
void lovrShaderSetInts(Shader* shader, const char* name, int* data, int start, 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 "lib/glfw.h"
#include "util.h"
#include <stdbool.h>
#pragma once
typedef enum {
TEXTURE_2D = GL_TEXTURE_2D,
TEXTURE_CUBE = GL_TEXTURE_CUBE_MAP
TEXTURE_2D,
TEXTURE_CUBE,
TEXTURE_ARRAY,
TEXTURE_VOLUME
} TextureType;
typedef enum {
@ -22,9 +23,9 @@ typedef struct {
} TextureFilter;
typedef enum {
WRAP_CLAMP = GL_CLAMP_TO_EDGE,
WRAP_REPEAT = GL_REPEAT,
WRAP_MIRRORED_REPEAT = GL_MIRRORED_REPEAT
WRAP_CLAMP,
WRAP_REPEAT,
WRAP_MIRRORED_REPEAT
} WrapMode;
typedef struct {
@ -33,26 +34,21 @@ typedef struct {
WrapMode r;
} TextureWrap;
typedef struct {
Ref ref;
TextureType type;
TextureData* slices[6];
int sliceCount;
int width;
int height;
GLuint id;
TextureFilter filter;
TextureWrap wrap;
bool srgb;
} Texture;
typedef struct Texture Texture;
GLenum lovrTextureFormatGetGLFormat(TextureFormat format);
GLenum lovrTextureFormatGetGLInternalFormat(TextureFormat format, bool srgb);
bool lovrTextureFormatIsCompressed(TextureFormat format);
Texture* lovrTextureCreate(TextureType type, TextureData* data[6], int count, bool srgb);
void lovrTextureDestroy(const Ref* ref);
void lovrTextureReplacePixels(Texture* texture, TextureData* data, int slice);
Texture* lovrTextureCreate(TextureType type, TextureData** slices, int sliceCount, bool srgb, bool mipmaps, int msaa);
Texture* lovrTextureCreateFromHandle(uint32_t handle, TextureType type);
void lovrTextureDestroy(void* ref);
void lovrTextureAllocate(Texture* texture, int width, int height, int depth, TextureFormat format);
void lovrTextureReplacePixels(Texture* texture, TextureData* data, int x, int y, int slice, int mipmap);
uint32_t lovrTextureGetId(Texture* texture);
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);
void lovrTextureSetFilter(Texture* texture, TextureFilter filter);
TextureWrap lovrTextureGetWrap(Texture* texture);

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