cmake_minimum_required(VERSION 3.20) project(cromakga3d) set(SDK_ROOT_PATH "C:/workspace/sdk") # Single source of truth for SDK versions on the CMake side; sdk.props carries the same set for the # MSBuild build, and /check-sdk-updates diffs both against sdks.toml. # # Layout contract: inputs (official packages, source trees) are read-only and keep their vendor # folder names. Anything we built ourselves lives under _out/{platform}/{name}-{version} so the path # states which build produced it. set(SDL3_VERSION "3.4.14") set(SDL3_TTF_VERSION "3.2.2") set(BOX3D_VERSION "0.1.0") set(KTX_VERSION "4.4.2") set(MBEDTLS_VERSION "3.6.7") set(CURL_VERSION "8.21.0") set(LWS_VERSION "4.5-stable") if(EMSCRIPTEN) set(SDK_PLATFORM "wasm_mt") elseif(ANDROID) # the ABI set is a decision, not an omission — see the determinism contract in # CLAUDE.determinism.md. __build_sdk.py installs under these exact names, so deriving one from # ANDROID_ABI would point at a directory that does not exist. if(ANDROID_ABI STREQUAL "arm64-v8a") set(SDK_PLATFORM "android-arm64") elseif(ANDROID_ABI STREQUAL "x86_64") set(SDK_PLATFORM "android-x86_64") else() message(FATAL_ERROR "ANDROID_ABI=${ANDROID_ABI}: only arm64-v8a and x86_64 have dependencies built (python __build_sdk.py android-arm64 / android-x86_64)") endif() else() message(FATAL_ERROR "This CMakeLists targets Emscripten and Android; Windows builds use cromakga3d.vcxproj") endif() set(SDK_OUT_PATH "${SDK_ROOT_PATH}/_out/${SDK_PLATFORM}") set(APP_SOURCES src/main.cpp src/crApp.cpp src/crAudio.cpp src/crDevAudio.cpp src/crDevCamera.cpp src/crDevConsole.cpp src/crDevGizmos.cpp src/crDevGraphics.cpp src/crDevPostProcess.cpp src/crDevProfiler.cpp src/crDevText.cpp src/crUserData.cpp src/crDevApp.cpp src/crGraphics.cpp src/crCamera.cpp src/crSpriteAtlas.cpp src/crTextureLoader.cpp src/crBatchedBillboards.cpp src/crBatchedLines2D.cpp src/crBatchedLines3D.cpp src/crBatchedPrimitives.cpp src/crBatchedRibbonTrails.cpp src/crBatchedSprites.cpp src/crBatchedTexts.cpp src/crFontAtlas.cpp src/crGraphicsUniforms.cpp src/crMath.cpp src/crEcs.cpp src/crInputSystem.cpp src/crRenderSystem.cpp src/crRibbonTrailSystem.cpp src/crParticleSystem.cpp src/crUi.cpp src/game/Game.cpp src/crPhysics.cpp src/crPostProcess.cpp src/crPrimitiveGeometry.cpp src/crShadowMap.cpp src/crDevPhysics.cpp src/crNet.cpp src/crDevNet.cpp # crNet backend: native = curl + lws, WASM = emscripten fetch/websocket $<$>:src/crNet_native.cpp> $<$:src/crNet_wasm.cpp> $<$:src/crWasmBridge.cpp> # glad는 Windows(vcxproj) 전용 — Emscripten은 GL을 정적 제공하고 Android는 GLES3를 직접 링크한다 # imgui include/imgui/imgui.cpp include/imgui/imgui_draw.cpp include/imgui/imgui_tables.cpp include/imgui/imgui_widgets.cpp include/imgui/backends/imgui_impl_sdl3.cpp include/imgui/backends/imgui_impl_opengl3.cpp # stb_vorbis: crAudio.cpp가 구현을 단일 TU로 포함 — 별도 컴파일 금지 include/yyjson/yyjson.c include/enkiTS/TaskScheduler.cpp ) # Android has no process entry point of its own: SDLActivity loads lib.so and calls into it # over JNI, so the app is a shared library there. The target name decides the file name, and the # Activity's getLibraries() must list the same name. if(ANDROID) add_library(app SHARED ${APP_SOURCES}) else() add_executable(app ${APP_SOURCES}) endif() target_compile_definitions(app PRIVATE IMGUI_IMPL_OPENGL_ES3 ) # determinism: see the contract atop src/main.cpp. Harmless on wasm (no FMA instruction) but # mandatory on Android arm64, where clang fuses by default and physics would diverge. target_compile_options(app PRIVATE -ffp-contract=off) target_include_directories(app PRIVATE include include/auburn include/entt include/imgui include/imgui/backends include/yyjson src ${SDK_ROOT_PATH}/SDL3-${SDL3_VERSION}/include ${SDK_ROOT_PATH}/SDL3_ttf-${SDL3_TTF_VERSION}/include ${SDK_ROOT_PATH}/box3d-${BOX3D_VERSION}/include ${SDK_OUT_PATH}/KTX-Software-${KTX_VERSION}/include ) target_link_libraries(app PRIVATE # box3d's Release archive compiles B3_ASSERT out, but the macro lives in a public header, so a # build without NDEBUG still emits calls to b3InternalAssert. Debug links the 'd' archive that # carries it — the same pairing the vcxproj uses on Windows. $,${SDK_OUT_PATH}/box3d-${BOX3D_VERSION}/lib/libbox3dd.a,${SDK_OUT_PATH}/box3d-${BOX3D_VERSION}/lib/libbox3d.a> ${SDK_OUT_PATH}/KTX-Software-${KTX_VERSION}/lib/libktx_read.a ${SDK_OUT_PATH}/KTX-Software-${KTX_VERSION}/lib/libastcenc-none-static.a ) if(EMSCRIPTEN) target_link_libraries(app PRIVATE ${SDK_OUT_PATH}/SDL3-${SDL3_VERSION}/lib/libSDL3.a ${SDK_OUT_PATH}/SDL3_ttf-${SDL3_TTF_VERSION}/lib/libSDL3_ttf.a ) elseif(ANDROID) # SDL3/SDL3_ttf ship official Android .aar packages with prefab, so they resolve as shared # libraries through find_package rather than as a path into _out find_package(SDL3 REQUIRED CONFIG) find_package(SDL3_ttf REQUIRED CONFIG) target_link_libraries(app PRIVATE SDL3::SDL3 SDL3_ttf::SDL3_ttf EGL GLESv3 android log) target_include_directories(app PRIVATE ${SDK_OUT_PATH}/curl-${CURL_VERSION}/include ${SDK_OUT_PATH}/lws-${LWS_VERSION}/include ${SDK_OUT_PATH}/mbedtls-${MBEDTLS_VERSION}/include ) # static link order runs consumer -> provider; everest/p256m are not referenced by mbedcrypto target_link_libraries(app PRIVATE ${SDK_OUT_PATH}/curl-${CURL_VERSION}/lib/libcurl.a ${SDK_OUT_PATH}/lws-${LWS_VERSION}/lib/libwebsockets.a ${SDK_OUT_PATH}/mbedtls-${MBEDTLS_VERSION}/lib/libmbedtls.a ${SDK_OUT_PATH}/mbedtls-${MBEDTLS_VERSION}/lib/libmbedx509.a ${SDK_OUT_PATH}/mbedtls-${MBEDTLS_VERSION}/lib/libmbedcrypto.a ) # libapp.so is a shared library, so symbols pulled from a static archive are exported by # default — and --gc-sections cannot drop an exported symbol. Hiding them is what lets the # NDK's own -ffunction-sections/--gc-sections actually collect the unreachable code. # Our own objects are untouched, so SDL_main stays visible for SDL3's .so to call over JNI. target_link_options(app PRIVATE -Wl,--exclude-libs,ALL) endif() # WASM if(EMSCRIPTEN) # the artifact name lives in src/game/Game.h beside the game selector, so one file decides both # what is built and what it is called. __build_wasm.py parses the same line file(READ "${CMAKE_CURRENT_SOURCE_DIR}/src/game/Game.h" GAME_H_TEXT) string(REGEX MATCH "#define[ \t]+GAME_WASM_OUTPUT_NAME[ \t]+\"([^\"]+)\"" _ "${GAME_H_TEXT}") set(GAME_WASM_OUTPUT_NAME "${CMAKE_MATCH_1}") if(NOT GAME_WASM_OUTPUT_NAME) message(FATAL_ERROR "GAME_WASM_OUTPUT_NAME not found in src/game/Game.h") endif() # without this, editing Game.h rebuilds the code but never re-runs configure — the artifact would # keep the previous name and the change would look like it had not taken set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/game/Game.h") set_target_properties(app PROPERTIES OUTPUT_NAME "${GAME_WASM_OUTPUT_NAME}" SUFFIX ".html" ) target_compile_options(app PRIVATE -pthread) target_link_options(app PRIVATE -pthread -sPTHREAD_POOL_SIZE=4 -sALLOW_MEMORY_GROWTH=1 -sEXIT_RUNTIME=1 -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2 -sSTACK_SIZE=1048576 -sFETCH=1 -lidbfs.js -lwebsocket.js --shell-file ${CMAKE_SOURCE_DIR}/wasm_shell/__wasm_shell.html --preload-file ${CMAKE_SOURCE_DIR}/crassets@/crassets ) if(CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_options(app PRIVATE -g -O0 ) target_link_options(app PRIVATE -g -sASSERTIONS=2 -sSAFE_HEAP=1 -sSTACK_OVERFLOW_CHECK=2 -sGL_ASSERTIONS=1 ) endif() if(CMAKE_BUILD_TYPE STREQUAL "Release") target_compile_options(app PRIVATE -O3 -flto ) target_link_options(app PRIVATE -O3 -flto -g2 # TEMP: keep the wasm name section so crash stacks resolve to C++ symbols ) endif() endif()