v0.3.0: display abstraction, map-browser menu, SAGE terrain tiling/blends
- ra3.client::display: shared interactive loops; SDL and Vulkan backends implement only the primitives (init/present/poll_event/window_size/ key_down/present_terrain). ra3.display picks the backend. - Menu: maps by localized name (gamestrings.csf), red/gold theme, hover highlight, mouse + keyboard, wheel scroll, fullscreen and FPS/vsync options, loading progress bar. - Terrain: continuous tile sampling via a texture array (REPEAT, uv = cell/(2*cellSize)) removes per-cell grid seams; SAGE blend ramp for material transitions; FPS label + top-right minimap overlays. - Skip the skirmish sim for map views; reuse the Vulkan texture; no idle terrain redraw.
This commit is contained in:
+123
-5
@@ -25,7 +25,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
|
||||
|
||||
project(OpenRA3 VERSION 0.1.0 LANGUAGES CXX)
|
||||
project(OpenRA3 VERSION 0.3.0 LANGUAGES C CXX)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
|
||||
@@ -72,6 +72,46 @@ else()
|
||||
message(STATUS "OpenRA3: SDL3 not found - offscreen BMP output only")
|
||||
endif()
|
||||
|
||||
# --- Vulkan: vendored volk + headers, so no Vulkan SDK is needed ---------------
|
||||
# The loader is resolved at runtime (volk dlopen/LoadLibrary), which lets the
|
||||
# same source build the Vulkan backend on Linux and on the MinGW Windows target.
|
||||
option(OPENRA3_VULKAN "Build the Vulkan viewer" ON)
|
||||
set(OPENRA3_HAS_VULKAN OFF)
|
||||
if(OPENRA3_VULKAN AND OPENRA3_HAS_SDL3)
|
||||
set(OPENRA3_HAS_VULKAN ON)
|
||||
endif()
|
||||
if(OPENRA3_HAS_VULKAN)
|
||||
message(STATUS "OpenRA3: Vulkan viewer enabled (vendored volk)")
|
||||
else()
|
||||
message(STATUS "OpenRA3: Vulkan viewer disabled - offscreen image only")
|
||||
endif()
|
||||
|
||||
add_library(ra3_volk STATIC third_party/volk/volk.c)
|
||||
target_include_directories(ra3_volk PUBLIC third_party/volk third_party/vulkan/include)
|
||||
target_compile_definitions(ra3_volk PUBLIC VK_NO_PROTOTYPES)
|
||||
|
||||
# Embed the committed SPIR-V blobs into a generated header at configure time.
|
||||
# Regenerate with shaders/compile.sh after editing a .vert/.frag.
|
||||
function(openra3_embed_spirv out_header)
|
||||
set(content "// Generated by CMake from shaders/generated/*.spv.\n")
|
||||
string(APPEND content "#pragma once\n#include <cstddef>\n#include <cstdint>\n\nnamespace ra3_shaders {\n")
|
||||
foreach(spv IN LISTS ARGN)
|
||||
if(NOT EXISTS "${spv}")
|
||||
message(FATAL_ERROR "Missing precompiled shader ${spv}; run shaders/compile.sh")
|
||||
endif()
|
||||
get_filename_component(base "${spv}" NAME)
|
||||
string(MAKE_C_IDENTIFIER "${base}" ident)
|
||||
file(READ "${spv}" hex HEX)
|
||||
string(LENGTH "${hex}" hexlen)
|
||||
math(EXPR words "${hexlen} / 8")
|
||||
string(REGEX REPLACE "(..)(..)(..)(..)" "0x\\4\\3\\2\\1," words_list "${hex}")
|
||||
string(APPEND content "inline constexpr std::uint32_t ${ident}[] = {${words_list}};\n")
|
||||
string(APPEND content "inline constexpr std::size_t ${ident}_words = ${words};\n\n")
|
||||
endforeach()
|
||||
string(APPEND content "} // namespace ra3_shaders\n")
|
||||
file(WRITE "${out_header}" "${content}")
|
||||
endfunction()
|
||||
|
||||
# --- engine core -------------------------------------------------------------
|
||||
add_library(ra3_core STATIC)
|
||||
target_sources(ra3_core PUBLIC FILE_SET CXX_MODULES FILES src/core/ra3.core.cppm)
|
||||
@@ -83,6 +123,12 @@ target_sources(ra3_logic PUBLIC FILE_SET CXX_MODULES FILES src/logic/ra3.logic.c
|
||||
target_link_libraries(ra3_logic PUBLIC ra3_core)
|
||||
openra3_target_defaults(ra3_logic)
|
||||
|
||||
# --- RA3 gameplay data (real balance) ----------------------------------------
|
||||
add_library(ra3_data STATIC)
|
||||
target_sources(ra3_data PUBLIC FILE_SET CXX_MODULES FILES src/data/ra3.data.cppm)
|
||||
target_link_libraries(ra3_data PUBLIC ra3_core)
|
||||
openra3_target_defaults(ra3_data)
|
||||
|
||||
# --- client ------------------------------------------------------------------
|
||||
add_library(ra3_client STATIC)
|
||||
target_sources(ra3_client PUBLIC FILE_SET CXX_MODULES FILES src/client/ra3.client.cppm)
|
||||
@@ -110,7 +156,7 @@ openra3_target_defaults(ra3_map)
|
||||
# --- minimal skirmish simulation --------------------------------------------
|
||||
add_library(ra3_skirmish STATIC)
|
||||
target_sources(ra3_skirmish PUBLIC FILE_SET CXX_MODULES FILES src/skirmish/ra3.skirmish.cppm)
|
||||
target_link_libraries(ra3_skirmish PUBLIC ra3_core ra3_logic ra3_game ra3_map)
|
||||
target_link_libraries(ra3_skirmish PUBLIC ra3_core ra3_logic ra3_game ra3_data ra3_map)
|
||||
openra3_target_defaults(ra3_skirmish)
|
||||
|
||||
# --- software renderer -------------------------------------------------------
|
||||
@@ -119,21 +165,61 @@ target_sources(ra3_render PUBLIC FILE_SET CXX_MODULES FILES src/render/ra3.rende
|
||||
target_link_libraries(ra3_render PUBLIC ra3_core)
|
||||
openra3_target_defaults(ra3_render)
|
||||
|
||||
# --- real map terrain (HeightMapData / BlendTileData) ------------------------
|
||||
add_library(ra3_terrain STATIC)
|
||||
target_sources(ra3_terrain PUBLIC FILE_SET CXX_MODULES FILES src/terrain/ra3.terrain.cppm)
|
||||
target_link_libraries(ra3_terrain PUBLIC ra3_core ra3_fs ra3_render)
|
||||
openra3_target_defaults(ra3_terrain)
|
||||
|
||||
# The presentation facade imports render + terrain (for the terrain capability).
|
||||
target_link_libraries(ra3_client PUBLIC ra3_render ra3_terrain)
|
||||
|
||||
# --- windowed viewer (SDL3 or null) -----------------------------------------
|
||||
add_library(ra3_ui STATIC)
|
||||
if(OPENRA3_HAS_SDL3)
|
||||
target_sources(ra3_ui PUBLIC FILE_SET CXX_MODULES FILES src/ui/ra3.ui.sdl.cppm)
|
||||
target_link_libraries(ra3_ui PUBLIC ra3_core ra3_render openra3_sdl3)
|
||||
target_link_libraries(ra3_ui PUBLIC ra3_core ra3_render ra3_client openra3_sdl3)
|
||||
else()
|
||||
target_sources(ra3_ui PUBLIC FILE_SET CXX_MODULES FILES src/ui/ra3.ui.null.cppm)
|
||||
target_link_libraries(ra3_ui PUBLIC ra3_core ra3_render)
|
||||
target_link_libraries(ra3_ui PUBLIC ra3_core ra3_render ra3_client)
|
||||
endif()
|
||||
openra3_target_defaults(ra3_ui)
|
||||
|
||||
# --- Vulkan viewer (SDL3 surface + Vulkan, or null fallback) ------------------
|
||||
add_library(ra3_vulkan STATIC)
|
||||
if(OPENRA3_HAS_VULKAN AND OPENRA3_HAS_SDL3)
|
||||
openra3_embed_spirv(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated/shaders_embedded.hpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/generated/scene.vert.spv"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/generated/scene.frag.spv"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/generated/terrain.vert.spv"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/generated/terrain.frag.spv"
|
||||
)
|
||||
# The blobs are read at configure time, so re-run CMake when they change.
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/generated/scene.vert.spv"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/generated/scene.frag.spv"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/generated/terrain.vert.spv"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/generated/terrain.frag.spv")
|
||||
target_sources(ra3_vulkan PUBLIC FILE_SET CXX_MODULES FILES src/vulkan/ra3.vulkan.sdl.cppm)
|
||||
target_include_directories(ra3_vulkan PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||
target_link_libraries(ra3_vulkan PUBLIC ra3_core ra3_render ra3_terrain ra3_client openra3_sdl3 ra3_volk)
|
||||
else()
|
||||
target_sources(ra3_vulkan PUBLIC FILE_SET CXX_MODULES FILES src/vulkan/ra3.vulkan.null.cppm)
|
||||
target_link_libraries(ra3_vulkan PUBLIC ra3_core ra3_render ra3_client)
|
||||
endif()
|
||||
openra3_target_defaults(ra3_vulkan)
|
||||
|
||||
# --- display backend selection ------------------------------------------------
|
||||
add_library(ra3_display STATIC)
|
||||
target_sources(ra3_display PUBLIC FILE_SET CXX_MODULES FILES src/display/ra3.display.cppm)
|
||||
target_link_libraries(ra3_display PUBLIC ra3_core ra3_render ra3_terrain ra3_client ra3_ui ra3_vulkan)
|
||||
openra3_target_defaults(ra3_display)
|
||||
|
||||
# --- umbrella -----------------------------------------------------------------
|
||||
add_library(ra3 STATIC)
|
||||
target_sources(ra3 PUBLIC FILE_SET CXX_MODULES FILES src/ra3.cppm)
|
||||
target_link_libraries(ra3 PUBLIC ra3_core ra3_logic ra3_client ra3_game ra3_fs ra3_map ra3_skirmish ra3_render ra3_ui)
|
||||
target_link_libraries(ra3 PUBLIC ra3_core ra3_logic ra3_client ra3_data ra3_game ra3_fs ra3_map ra3_skirmish ra3_render ra3_terrain ra3_display ra3_ui ra3_vulkan)
|
||||
openra3_target_defaults(ra3)
|
||||
|
||||
# --- executable ---------------------------------------------------------------
|
||||
@@ -148,6 +234,38 @@ if(WIN32 AND OPENRA3_SDL3_ROOT)
|
||||
"$<TARGET_FILE_DIR:openra3>")
|
||||
endif()
|
||||
|
||||
# --- extracted assets ---------------------------------------------------------
|
||||
# At build time, extract the retail assets into <exe_dir>/assets so the
|
||||
# executable is self-contained (no --game-dir at run time). This drives the
|
||||
# ra3tools Python scripts (the game install + Python, no target executable), so
|
||||
# it also runs for a cross-build when the install is mounted into the container
|
||||
# and RA3_GAME_DIR points at it. The engine's own map/terrain reader is embedded
|
||||
# in the exe (run `openra3 extract` on a native host if you prefer).
|
||||
set(RA3_GAME_DIR "C:/Red Alert 3" CACHE PATH "Red Alert 3 installation used to extract assets")
|
||||
set(RA3TOOLS_DIR "C:/Users/Ender/CLionProjects/ra3-headless/ra3tools" CACHE PATH "ra3tools scripts (asset extraction)")
|
||||
find_program(OPENRA3_PYTHON NAMES python3 python)
|
||||
set(PYTHON_EXECUTABLE "${OPENRA3_PYTHON}" CACHE FILEPATH "Python interpreter used to run ra3tools")
|
||||
option(OPENRA3_EXTRACT_ALL "Extract every asset (models/textures/audio/movies) via ra3tools" ON)
|
||||
|
||||
if(EXISTS "${RA3_GAME_DIR}/Data" AND OPENRA3_PYTHON)
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_BINARY_DIR}/.assets_stamp"
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
"-DGAME_DIR=${RA3_GAME_DIR}"
|
||||
"-DOUT=$<TARGET_FILE_DIR:openra3>/assets"
|
||||
"-DPYTHON=${PYTHON_EXECUTABLE}"
|
||||
"-DRA3TOOLS=${RA3TOOLS_DIR}"
|
||||
"-DEXTRACT_ALL=$<IF:$<BOOL:${OPENRA3_EXTRACT_ALL}>,ON,OFF>"
|
||||
"-DSTAMP=${CMAKE_BINARY_DIR}/.assets_stamp"
|
||||
-P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/extract_assets.cmake"
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/extract_assets.cmake"
|
||||
COMMENT "Extracting Red Alert 3 assets to $<TARGET_FILE_DIR:openra3>/assets"
|
||||
VERBATIM)
|
||||
add_custom_target(openra3_assets ALL DEPENDS "${CMAKE_BINARY_DIR}/.assets_stamp")
|
||||
else()
|
||||
message(STATUS "OpenRA3: asset extraction skipped (RA3_GAME_DIR '${RA3_GAME_DIR}' or Python not found)")
|
||||
endif()
|
||||
|
||||
# --- tests --------------------------------------------------------------------
|
||||
enable_testing()
|
||||
add_executable(ra3_tests tests/ra3_tests.cpp)
|
||||
|
||||
Reference in New Issue
Block a user