mirror of https://github.com/bjornbytes/lovr.git
Untested merge of Oculus Mobile branch and Oct 2018 lovr master
This commit is contained in:
commit
56f8f1e26b
|
@ -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
|
|
@ -1,10 +1,10 @@
|
|||
.tup
|
||||
build
|
||||
/lovr
|
||||
/data
|
||||
/*.lua
|
||||
src/obj
|
||||
src/Tupfile
|
||||
**/Tupfile
|
||||
Tuprules.tup
|
||||
/watch.sh
|
||||
*.glsl
|
||||
.DS_Store
|
||||
bin
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
680
CMakeLists.txt
680
CMakeLists.txt
|
@ -1,202 +1,235 @@
|
|||
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()
|
||||
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()
|
||||
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)
|
||||
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 "")
|
||||
add_definitions(-D__APPLE__)
|
||||
add_subdirectory(deps/enet enet)
|
||||
include_directories(deps/enet/include)
|
||||
set(LOVR_ENET enet)
|
||||
remove_definitions(-D__APPLE__)
|
||||
else()
|
||||
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()
|
||||
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})
|
||||
if(EMSCRIPTEN)
|
||||
target_compile_definitions(enet PRIVATE __APPLE__)
|
||||
endif()
|
||||
endif()
|
||||
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))
|
||||
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()
|
||||
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()
|
||||
if(LOVR_ENABLE_PHYSICS)
|
||||
if(LOVR_SYSTEM_ODE)
|
||||
pkg_search_module(ODE REQUIRED ode)
|
||||
include_directories(${ODE_INCLUDE_DIRS})
|
||||
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()
|
||||
if(LOVR_ENABLE_AUDIO)
|
||||
if(LOVR_SYSTEM_OPENAL)
|
||||
pkg_search_module(OPENAL openal-soft)
|
||||
if (NOT OPENAL_FOUND)
|
||||
pkg_search_module(OPENAL openal)
|
||||
|
@ -208,27 +241,69 @@ else()
|
|||
string(REPLACE ";" " " OPENAL_LDFLAGS_STR "${OPENAL_LDFLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OPENAL_LDFLAGS_STR}")
|
||||
set(LOVR_OPENAL ${OPENAL_LIBRARIES})
|
||||
else()
|
||||
set(ALSOFT_UTILS OFF CACHE BOOL "")
|
||||
set(ALSOFT_EXAMPLES OFF CACHE BOOL "")
|
||||
set(ALSOFT_TESTS OFF CACHE BOOL "")
|
||||
if(NOT EMSCRIPTEN)
|
||||
add_subdirectory(deps/openal-soft openal)
|
||||
set(LOVR_OPENAL OpenAL)
|
||||
if(WIN32)
|
||||
set_target_properties(OpenAL PROPERTIES COMPILE_FLAGS "/wd4005 /wd4098")
|
||||
endif()
|
||||
endif()
|
||||
include_directories(deps/openal-soft/include)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# 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)
|
||||
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})
|
||||
|
|
159
COMPILING.md
159
COMPILING.md
|
@ -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.
|
|
@ -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
112
README.md
|
@ -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
|
||||
---
|
||||
|
|
17
appveyor.yml
17
appveyor.yml
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
*
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 8529e84fb4bb22abfc5c4d9cafbb06025a8b909d
|
|
@ -1 +1 @@
|
|||
Subproject commit 8577a0211b442805214eeeb264a8ca7cb8d9439e
|
||||
Subproject commit 9ba5b173f55df905cf765d5d5de99613b678ec1d
|
|
@ -1 +1 @@
|
|||
Subproject commit 18bb46163af68a5d7299733cd1e5f194be61d496
|
||||
Subproject commit 9c5307a48a58959a564be1999b119a87b7cdb8e0
|
123
src/api.h
123
src/api.h
|
@ -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[];
|
||||
|
|
170
src/api/audio.c
170
src/api/audio.c
|
@ -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;
|
||||
}
|
||||
|
|
158
src/api/data.c
158
src/api/data.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
204
src/api/event.c
204
src/api/event.c
|
@ -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);
|
||||
return 2;
|
||||
};
|
||||
|
||||
case EVENT_CONTROLLER_ADDED: {
|
||||
lua_pushstring(L, "controlleradded");
|
||||
luax_pushtype(L, Controller, event.data.controlleradded.controller);
|
||||
lovrRelease(&event.data.controlleradded.controller->ref);
|
||||
case EVENT_FOCUS:
|
||||
case EVENT_MOUNT:
|
||||
lua_pushboolean(L, event.data.boolean.value);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
||||
|
|
2350
src/api/graphics.c
2350
src/api/graphics.c
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
|
@ -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;
|
||||
}
|
||||
|
||||
int l_lovrHeadsetGetBoundsWidth(lua_State* L) {
|
||||
lua_pushnumber(L, lovrHeadsetGetBoundsWidth());
|
||||
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_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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
|
|
|
@ -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 }
|
||||
};
|
|
@ -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 },
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
|
|
|
@ -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,38 +147,67 @@ 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++) {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
uint32_t count = lua_objlen(L, 2);
|
||||
uint32_t vertexCount = lovrMeshGetVertexCount(mesh);
|
||||
size_t size = vertexCount > USHRT_MAX ? sizeof(uint32_t) : sizeof(uint16_t);
|
||||
IndexPointer indices = lovrMeshWriteIndices(mesh, count, size);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
lua_rawgeti(L, 2, i + 1);
|
||||
if (!lua_isnumber(L, -1)) {
|
||||
return luaL_error(L, "Mesh vertex map index #%d must be numeric", i);
|
||||
}
|
||||
|
||||
int index = lua_tointeger(L, -1);
|
||||
uint32_t 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)) {
|
||||
if (size == sizeof(uint16_t)) {
|
||||
indices.shorts[i] = index - 1;
|
||||
} else if (indexSize == sizeof(uint32_t)) {
|
||||
} else {
|
||||
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 },
|
||||
|
|
|
@ -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 }
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
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;
|
||||
}
|
||||
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;
|
||||
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 }
|
||||
};
|
||||
|
|
|
@ -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 }
|
||||
};
|
|
@ -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];
|
||||
|
|
|
@ -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 }
|
||||
};
|
|
@ -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 },
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "api.h"
|
||||
#include "data/textureData.h"
|
||||
|
||||
int l_lovrTextureDataEncode(lua_State* L) {
|
||||
TextureData* textureData = luax_checktype(L, 1, TextureData);
|
||||
|
|
|
@ -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 }
|
||||
};
|
|
@ -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 }
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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,6 +185,12 @@ void lovrSourceRewind(Source* source) {
|
|||
}
|
||||
|
||||
void lovrSourceSeek(Source* source, int sample) {
|
||||
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);
|
||||
|
@ -187,6 +198,9 @@ void lovrSourceSeek(Source* source, int sample) {
|
|||
if (wasPaused) {
|
||||
lovrSourcePause(source);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lovrSourceSetCone(Source* source, float innerAngle, float outerAngle, float outerGain) {
|
||||
|
@ -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,6 +258,13 @@ void lovrSourceStop(Source* source) {
|
|||
return;
|
||||
}
|
||||
|
||||
switch (source->type) {
|
||||
case SOURCE_STATIC:
|
||||
alSourceStop(source->id);
|
||||
break;
|
||||
|
||||
case SOURCE_STREAM: {
|
||||
|
||||
// Empty the buffers
|
||||
int count = 0;
|
||||
alGetSourcei(source->id, AL_BUFFERS_QUEUED, &count);
|
||||
|
@ -252,18 +276,25 @@ void lovrSourceStop(Source* source) {
|
|||
|
||||
// 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,6 +308,14 @@ void lovrSourceStream(Source* source, ALuint* buffers, int count) {
|
|||
}
|
||||
|
||||
int lovrSourceTell(Source* source) {
|
||||
switch (source->type) {
|
||||
case SOURCE_STATIC: {
|
||||
float offset;
|
||||
alGetSourcef(source->id, AL_SAMPLE_OFFSET, &offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
case SOURCE_STREAM: {
|
||||
int decoderOffset = lovrAudioStreamTell(source->stream);
|
||||
int samplesPerBuffer = source->stream->bufferSize / source->stream->channelCount / sizeof(ALshort);
|
||||
int queuedBuffers, sampleOffset;
|
||||
|
@ -290,4 +329,7 @@ int lovrSourceTell(Source* source) {
|
|||
} else {
|
||||
return offset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
|
@ -1,5 +0,0 @@
|
|||
#include "data/data.h"
|
||||
|
||||
void lovrDataInit() {
|
||||
//
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
void lovrDataInit();
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
|
@ -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);
|
||||
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->data = stbi_load_from_memory(blob->data, blob->size, &textureData->width, &textureData->height, NULL, 4);
|
||||
textureData->blob = NULL;
|
||||
textureData->generateMipmaps = true;
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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();
|
||||
|
|
|
@ -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++) {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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");
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue