Lysa  0.0
Lysa 3D Engine
Project layout

Source tree

lysa_tutorial_01/
├── CMakeLists.txt
├── .env.cmake (user-supplied)
├── shaders/ (populated at build time)
└── src/
├── Main.cpp
├── MainWindow.ixx
├── Camera.ixx / .cpp
├── SceneInstance.ixx / .cpp
├── SceneTree.ixx / .cpp
└── RotatingAssetScene.ixx / .cpp

Engine integration

The Lysa engine is integrated as a CMake subdirectory rather than a pre-built library. add_subdirectory compiles the engine in-tree and makes the lysa_engine and lysa_engine_shaders targets available to the project:

set(LYSA_ENGINE_TARGET "lysa_engine")
set(LYSA_ENGINE_TARGET_SHADERS "${LYSA_ENGINE_TARGET}_shaders")
set(LYSA_ENGINE_SHADERS_BUILD_DIR ${LYSA_ENGINE_PROJECT_DIR}/shaders)
add_subdirectory(${LYSA_ENGINE_PROJECT_DIR} external_lib_build/${LYSA_ENGINE_TARGET})

Several feature flags can be set before the add_subdirectory call to control which engine subsystems are compiled:

Variable Default Effect
LUA_BINDING OFF Lua scripting bindings
FORWARD_RENDERER ON Forward rendering pipeline
DEFERRED_RENDERER ON Deferred rendering pipeline
PHYSIC_ENGINE_JOLT OFF Jolt physics backend
PHYSIC_ENGINE_PHYSX OFF PhysX physics backend
DIRECTX_BACKEND ON (Windows only) DirectX 12 backend

Shader copy

Engine shaders are compiled into ${LYSA_ENGINE_PROJECT_DIR}/shaders as part of the lysa_engine_shaders target. A custom target shaders_copy copies that directory into the project's own shaders/ folder, which is where the app://shaders virtual path resolves at runtime:

add_custom_target(shaders_copy
COMMAND ${CMAKE_COMMAND} -E copy_directory
${LYSA_ENGINE_SHADERS_BUILD_DIR}
${SHADERS_BUILD_DIR}
COMMENT "Copying shaders"
VERBATIM
)
add_dependencies(shaders_copy ${LYSA_ENGINE_TARGET} ${LYSA_ENGINE_TARGET_SHADERS})

Application target

The build_target function encapsulates the boilerplate for creating an executable that uses C++ modules: it calls add_executable for plain .cpp sources, registers .ixx module interface files via FILE_SET CXX_MODULES, applies the engine's compile options, and links against lysa_engine:

function(build_target TARGET_NAME SRCS MODULES)
add_executable(${TARGET_NAME} ${SRCS})
target_sources(${TARGET_NAME}
PUBLIC FILE_SET CXX_MODULES FILES ${MODULES})
lysa_compile_options(${TARGET_NAME})
target_link_libraries(${TARGET_NAME} ${LYSA_ENGINE_TARGET})
add_dependencies(${TARGET_NAME} shaders_copy)
endfunction()

The sample application is built with:

set(MY_TARGET_SRC
${SRC_DIR}/Main.cpp
${SRC_DIR}/Camera.cpp
${SRC_DIR}/SceneInstance.cpp
${SRC_DIR}/SceneTree.cpp
${SRC_DIR}/RotatingAssetScene.cpp
)
set(MY_TARGET_MODULES
${SRC_DIR}/MainWindow.ixx
${SRC_DIR}/Camera.ixx
${SRC_DIR}/SceneInstance.ixx
${SRC_DIR}/SceneTree.ixx
${SRC_DIR}/RotatingAssetScene.ixx
)
build_target(myapp "${MY_TARGET_SRC}" "${MY_TARGET_MODULES}")

Note that .ixx module interface files and their .cpp implementation files are listed separately: interfaces go into MY_TARGET_MODULES (the CXX_MODULES file set), while plain translation units go into MY_TARGET_SRC.

Next : Engine context