From 13af296bf8b9023a8d13df9445a3e29d4783fb99 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Thu, 19 Oct 2023 08:08:50 +0100 Subject: [PATCH 001/122] initial toolchain -test of an initial toolchain setup for macosx builds --- CMakeLists.txt | 1 + Engine/lib/CMakeLists.txt | 7 +- Tools/CMake/toolchain/apple.toolchain.cmake | 329 ++++++++++++++++++++ Tools/CMake/torque_macros.cmake | 24 +- 4 files changed, 359 insertions(+), 2 deletions(-) create mode 100644 Tools/CMake/toolchain/apple.toolchain.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index a94931c74..6a79ed0fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ set(TORQUE_APP_GAME_DIRECTORY "${TORQUE_APP_ROOT_DIRECTORY}/game") #library handling set(TORQUE_LIB_ROOT_DIRECTORY "${CMAKE_SOURCE_DIR}/Engine/lib") set(TORQUE_LIB_TARG_DIRECTORY "${CMAKE_BINARY_DIR}/Engine/lib") +set(TORQUE_SOURCE_DIRECTROY "${CMAKE_SOURCE_DIR}/Engine/source") # Ensure all possible configurations end up in the project directory set(CMAKE_INSTALL_PREFIX "${TORQUE_APP_ROOT_DIRECTORY}") diff --git a/Engine/lib/CMakeLists.txt b/Engine/lib/CMakeLists.txt index 1944b6446..c73efbd25 100644 --- a/Engine/lib/CMakeLists.txt +++ b/Engine/lib/CMakeLists.txt @@ -153,8 +153,11 @@ mark_as_advanced(ASSIMP_PACKAGE_VERSION) mark_as_advanced(ASSIMP_RUNTIME_OUTPUT_DIRECTORY) add_subdirectory(assimp ${TORQUE_LIB_TARG_DIRECTORY}/assimp EXCLUDE_FROM_ALL) target_compile_definitions(assimp PUBLIC ASSIMP_BUILD_NO_OWN_ZLIB) + if (TORQUE_CPU_ARM32 OR TORQUE_CPU_ARM64) - set(PNG_ARM_NEON on CACHE BOOL "" FORCE) + if(NOT APPLE) + set(PNG_ARM_NEON on CACHE BOOL "" FORCE) + endif(NOT APPLE) endif (TORQUE_CPU_ARM32 OR TORQUE_CPU_ARM64) #PNG @@ -168,10 +171,12 @@ set(PNG_TESTS off CACHE BOOL "" FORCE) mark_as_advanced(PNG_TESTS) set(PNG_HARDWARE_OPTIMIZATIONS on CACHE BOOL "" FORCE) mark_as_advanced(PNG_HARDWARE_OPTIMIZATIONS) + if(APPLE) set(PNG_FRAMEWORK on CACHE BOOL "" FORCE) addDef(PNG_DEBUG Debug) endif() + mark_as_advanced(PNG_DEBUG) mark_as_advanced(PNG_FRAMEWORK) mark_as_advanced(PNG_PREFIX) diff --git a/Tools/CMake/toolchain/apple.toolchain.cmake b/Tools/CMake/toolchain/apple.toolchain.cmake new file mode 100644 index 000000000..0b06ee365 --- /dev/null +++ b/Tools/CMake/toolchain/apple.toolchain.cmake @@ -0,0 +1,329 @@ +cmake_minimum_required (VERSION 3.21.0) + +project(${TORQUE_APP_NAME}) + +enable_language(OBJC) +enable_language(OBJCXX) + +set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE INTERNAL "") +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE INTERNAL "") + +set(CMAKE_THREAD_LIBS_INIT "-lpthread") +set(CMAKE_HAVE_THREADS_LIBRARY 1) +set(CMAKE_USE_WIN32_THREADS_INIT 0) +set(CMAKE_USE_PTHREADS_INIT 1) +set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO") +set(CMAKE_SHARED_LIBRARY_PREFIX "lib") +set(CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") +set(CMAKE_SHARED_MODULE_PREFIX "lib") +set(CMAKE_SHARED_MODULE_SUFFIX ".so") +set(CMAKE_C_COMPILER_ABI ELF) +set(CMAKE_CXX_COMPILER_ABI ELF) +set(CMAKE_C_HAS_ISYSROOT 1) +set(CMAKE_CXX_HAS_ISYSROOT 1) +set(CMAKE_MODULE_EXISTS 1) +set(CMAKE_DL_LIBS "") +set(CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") +set(CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") +set(CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") +set(CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") +set(CMAKE_MACOSX_BUNDLE YES) +set(OBJC_VARS "-fobjc-abi-version=2 -DOBJC_OLD_DISPATCH_PROTOTYPES=0") + +set(CMAKE_OBJC_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${BITCODE} ${VISIBILITY} ${FOBJC_ARC} ${OBJC_VARS} ${CMAKE_OBJC_FLAGS}") +set(CMAKE_OBJC_FLAGS_DEBUG "-O0 -g ${CMAKE_OBJC_FLAGS_DEBUG}") +set(CMAKE_OBJC_FLAGS_MINSIZEREL "-DNDEBUG -Os ${CMAKE_OBJC_FLAGS_MINSIZEREL}") +set(CMAKE_OBJC_FLAGS_RELWITHDEBINFO "-DNDEBUG -O2 -g ${CMAKE_OBJC_FLAGS_RELWITHDEBINFO}") +set(CMAKE_OBJC_FLAGS_RELEASE "-DNDEBUG -O3 ${CMAKE_OBJC_FLAGS_RELEASE}") +set(CMAKE_OBJCXX_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${BITCODE} ${VISIBILITY} ${FOBJC_ARC} ${OBJC_VARS} ${CMAKE_OBJCXX_FLAGS}") +set(CMAKE_OBJCXX_FLAGS_DEBUG "-O0 -g ${CMAKE_OBJCXX_FLAGS_DEBUG}") +set(CMAKE_OBJCXX_FLAGS_MINSIZEREL "-DNDEBUG -Os ${CMAKE_OBJCXX_FLAGS_MINSIZEREL}") +set(CMAKE_OBJCXX_FLAGS_RELWITHDEBINFO "-DNDEBUG -O2 -g ${CMAKE_OBJCXX_FLAGS_RELWITHDEBINFO}") +set(CMAKE_OBJCXX_FLAGS_RELEASE "-DNDEBUG -O3 ${CMAKE_OBJCXX_FLAGS_RELEASE}") +set(CMAKE_OBJC_LINK_FLAGS "${C_TARGET_FLAGS} ${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_OBJC_LINK_FLAGS}") +set(CMAKE_OBJCXX_LINK_FLAGS "${C_TARGET_FLAGS} ${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_OBJCXX_LINK_FLAGS}") + +set(CMAKE_PLATFORM_HAS_INSTALLNAME 1) +set(CMAKE_SHARED_LINKER_FLAGS "-rpath @executable_path/Frameworks -rpath @loader_path/Frameworks") +set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -Wl,-headerpad_max_install_names") +set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -Wl,-headerpad_max_install_names") +set(CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") +set(CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") +set(CMAKE_FIND_LIBRARY_SUFFIXES ".tbd" ".dylib" ".so" ".a") +set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-install_name") + +# Add our libs. +add_subdirectory(${TORQUE_LIB_ROOT_DIRECTORY}) + +################# Helper Function Calls ################### +forwardDef(TORQUE_OPENGL) +forwardDef(TORQUE_D3D11) +forwardDef(TORQUE_ADVANCED_LIGHTING) +forwardDef(TORQUE_BASIC_LIGHTING) +set(TORQUE_SDL ON) # we need sdl to do our platform interop +forwardDef(TORQUE_SDL) + +if(TORQUE_TESTING) +set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} TORQUE_TESTS_ENABLED) +set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} "_VARIADIC_MAX=10") +endif() + +set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} __MACOSX__) + +################# Collect Source Files ################### + +# Handle app +torqueToolchainSourceDirectories("app" "app/net") + +# Handle console +torqueToolchainSourceDirectories("console") +torqueToolchainSourceDirectories("console/torquescript") + +# Handle Platform +torqueToolchainSourceDirectories("platform" "platform/threads" "platform/async" + "platform/input" "platform/output") + +torqueToolchainSourceDirectories("platform/nativeDialogs") + +# Handle T3D +torqueToolchainSourceDirectories("T3D/fps" "T3D/fx" "T3D/vehicles" "T3D/physics" + "T3D/decal" "T3D/sfx" "T3D/gameBase" "T3D/turret" + "T3D/lighting" "T3D/gameOBjects" "T3D/components" + "T3D/systems" "T3D/assets" "T3D" "T3D/gameBase/std") + +# Handle TS +torqueToolchainSourceDirectories("ts" "ts/collada" "ts/assimp" "ts/loader" "ts/arch") + +# Handle SFX - OpenAL is handled as a module later on +torqueToolchainSourceDirectories("sfx" "sfx/media" "sfx/null") +if(TORQUE_SFX_OPENAL AND NOT TORQUE_DEDICATED) + torqueToolchainSourceDirectories("sfx/openal") + torqueToolchainSourceDirectories("sfx/openal/mac") +endif() +# Handle GFX +torqueToolchainSourceDirectories("gfx" "gfx/Null" "gfx/test" "gfx/bitmap" "gfx/bitmap/loaders" + "gfx/util" "gfx/video" "gfx/sim" ) + +if (TORQUE_OPENGL) + torqueToolchainSourceDirectories("gfx/gl" "gfx/gl/sdl" "gfx/gl/tGL") +endif (TORQUE_OPENGL) + +# Handle core +torqueToolchainSourceDirectories("core" "core/stream" "core/strings" "core/util" + "core/util/journal" "core/util/zip" "core/util/compressors") +# Handle GUI +torqueToolchainSourceDirectories("gui" "gui/buttons" "gui/containers" "gui/controls" "gui/core" + "gui/game" "gui/shiny" "gui/utility" "gui/3d") + +# Handle postFX +torqueToolchainSourceDirectories("postFx") + +# Handle Windowmanager +torqueToolchainSourceDirectories("windowManager" "windowManager/torque" "windowManager/sdl") + +# Handle scene +torqueToolchainSourceDirectories("scene" "scene/culling" "scene/zones" "scene/mixin") + +# Handle math +torqueToolchainSourceDirectories("math" "math/util") + +# Handle persistence +torqueToolchainSourceDirectories("persistence/taml" "persistence/taml/binary" "persistence/taml/xml") + +# Handle Cinterface +torqueToolchainSourceDirectories("cinterface") + +# Handle util +torqueToolchainSourceDirectories("util" "util/messaging") + +# Handle assets +torqueToolchainSourceDirectories("assets") + +# Handle Sim +torqueToolchainSourceDirectories("sim") + +# Handle module +torqueToolchainSourceDirectories("module") + +# Handle forest +torqueToolchainSourceDirectories("forest" "forest/ts") +if(TORQUE_OPENGL) + torqueToolchainSourceDirectories("forest" "forest/glsl") +endif(TORQUE_OPENGL) + +# Handle shadergen +torqueToolchainSourceDirectories("shaderGen") + +if (TORQUE_OPENGL) +torqueToolchainSourceDirectories("shaderGen/GLSL") +endif (TORQUE_OPENGL) + +# Handle terrain +torqueToolchainSourceDirectories("terrain") + +if (TORQUE_OPENGL) +torqueToolchainSourceDirectories("terrain/glsl") +endif (TORQUE_OPENGL) + +# Handle Materials +torqueToolchainSourceDirectories("materials") + +# Handle collision +torqueToolchainSourceDirectories("collision") + +# Handle lighting +torqueToolchainSourceDirectories("lighting" "lighting/common" + "lighting/shadowMap") +if (TORQUE_ADVANCED_LIGHTING) +torqueToolchainSourceDirectories("lighting/advanced") + if (TORQUE_OPENGL) + torqueToolchainSourceDirectories("lighting/advanced/glsl") + endif (TORQUE_OPENGL) +endif (TORQUE_ADVANCED_LIGHTING) + +if (TORQUE_BASIC_LIGHTING) +torqueToolchainSourceDirectories("lighting/basic" "lighting/basic/shadowMap") +endif (TORQUE_BASIC_LIGHTING) + +# Begin handling platform specific stuff +# Handle Platform POSIX +if (UNIX) +torqueToolchainSourceDirectories("platformPOSIX") + + if (TORQUE_CPU_X32 OR TORQUE_CPU_X64) + torqueToolchainSourceDirectories("platformX86UNIX") + endif (TORQUE_CPU_X32 OR TORQUE_CPU_X64) +endif (UNIX) + +# Mac platform +torqueToolchainSourceDirectories("platformMac") + +# Handle platformSDL +torqueToolchainSourceDirectories("platformSDL" "platformSDL/threads") + +if(TORQUE_TESTING) +torqueToolchainSourceDirectories("testing") + set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} TORQUE_SHARED SDL_MAIN_HANDLED) +endif(TORQUE_TESTING) + +# Add the collected files to our engine group +source_group(TREE "${CMAKE_SOURCE_DIR}/Engine/source" PREFIX "Engine" FILES ${TORQUE_SOURCE_FILES}) +file(GLOB_RECURSE TORQUE_APP_GAME_SOURCES "${TORQUE_APP_ROOT_DIRECTORY}/source/*.cpp" "${TORQUE_APP_ROOT_DIRECTORY}/source/*.h") +source_group(TREE "${TORQUE_APP_ROOT_DIRECTORY}/source" PREFIX "Source Files" FILES ${TORQUE_APP_GAME_SOURCES}) +set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} "${TORQUE_APP_ROOT_DIRECTORY}/source") + +################# Engine Module Handling ################### + +set(TORQUE_MODULE_PATHS "${CMAKE_SOURCE_DIR}/Tools/CMake/modules" "${TORQUE_APP_GAME_DIRECTORY}/data") +if (NOT "${TORQUE_MODULE_USER_PATH}" STREQUAL "") + list(APPEND TORQUE_MODULE_PATHS "${TORQUE_MODULE_USER_PATH}") +endif() + +# Before doing module scanning, store away the engine sources - we do this so that modules +# can be placed into the proper filters +set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES} ${TORQUE_APP_GAME_SOURCES}) +set(TORQUE_SOURCE_FILES "") + +foreach (TORQUE_MODULE_PATH ${TORQUE_MODULE_PATHS}) + # First find simple cmake scripts, mostly used for in-engine modules + file(GLOB MODULE_SCRIPTS "${TORQUE_MODULE_PATH}/*.cmake") + foreach (TORQUE_MODULE_SCRIPT ${MODULE_SCRIPTS}) + include(${TORQUE_MODULE_SCRIPT}) + + # Add this script's collected files to our Engine group + source_group(TREE "${CMAKE_SOURCE_DIR}/Engine" PREFIX "Engine" FILES ${TORQUE_SOURCE_FILES}) + + set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${TORQUE_SOURCE_FILES}) + set(TORQUE_SOURCE_FILES "") + endforeach() + + # Next find sub projects, these can introduce new source files + SUBDIRLIST(POSSIBLE_PROJECTS "${TORQUE_MODULE_PATH}") + foreach (POSSIBLE_PROJECT ${POSSIBLE_PROJECTS}) + # Retrieve the absolute path of this possible project + get_filename_component(POSSIBLE_PROJECT_ABSOLUTEPATH "${POSSIBLE_PROJECT}" + REALPATH BASE_DIR "${TORQUE_MODULE_PATH}") + + if (EXISTS "${POSSIBLE_PROJECT_ABSOLUTEPATH}/CMakeLists.txt") + add_subdirectory("${POSSIBLE_PROJECT_ABSOLUTEPATH}" ${CMAKE_BINARY_DIR}/temp/${POSSIBLE_PROJECT} EXCLUDE_FROM_ALL) + source_group(TREE "${POSSIBLE_PROJECT_ABSOLUTEPATH}" PREFIX "Modules/${POSSIBLE_PROJECT}" FILES ${TORQUE_SOURCE_FILES}) + + set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${TORQUE_SOURCE_FILES}) + set(TORQUE_SOURCE_FILES "") + elseif(NOT EXISTS "${POSSIBLE_PROJECT_ABSOLUTEPATH}/*.cmake") + file(GLOB_RECURSE MODULE_SOURCE "${POSSIBLE_PROJECT_ABSOLUTEPATH}/source/*.h" "${POSSIBLE_PROJECT_ABSOLUTEPATH}/source/*.cpp") + #message(STATUS "MODULE_SOURCE:${MODULE_SOURCE}") + source_group(TREE "${POSSIBLE_PROJECT_ABSOLUTEPATH}" PREFIX "Modules/${POSSIBLE_PROJECT}/" FILES ${MODULE_SOURCE}) + set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${MODULE_SOURCE}) + endif() + endforeach() +endforeach() + +set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES_TEMPORARY}) + +################# Library Post-build Handling ################### +set(TORQUE_LIBRARY_PATHS "${CMAKE_SOURCE_DIR}/Engine/lib" "${TORQUE_APP_GAME_DIRECTORY}/data") +if (NOT "${TORQUE_LIBRARY_USER_PATH}" STREQUAL "") + list(APPEND TORQUE_LIBRARY_PATHS "${TORQUE_LIBRARY_USER_PATH}") +endif() + +foreach (TORQUE_LIBRARY_PATH ${TORQUE_LIBRARY_PATHS}) + # First find simple cmake scripts, mostly used for in-engine libraries + file(GLOB_RECURSE LIBRARY_SCRIPTS "${TORQUE_LIBRARY_PATH}/*Torque_postBuild.cmake") + #message(STATUS "LIBRARY_SCRIPTS:${LIBRARY_SCRIPTS}") + foreach (TORQUE_LIBRARY_SCRIPT ${LIBRARY_SCRIPTS}) + include(${TORQUE_LIBRARY_SCRIPT}) + endforeach() +endforeach() + +set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_MAC_SOURCES} "${CMAKE_SOURCE_DIR}/Tools/CMake/torque.icns") +set_source_files_properties("${CMAKE_SOURCE_DIR}/Tools/CMake/torque.icns" PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") + +set(EXECUTABLE_NAME "${TORQUE_APP_NAME}") +configure_file("${CMAKE_SOURCE_DIR}/Tools/CMake/Info.plist.in" "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" COPYONLY) + +addDef(TORQUE_DEBUG Debug) +addDef(TORQUE_RELEASE "RelWithDebInfo;Release") +addDef(TORQUE_ENABLE_ASSERTS "Debug;RelWithDebInfo") +addDef(TORQUE_DEBUG_GFX_MODE "RelWithDebInfo") +addDef(TORQUE_OGGVORBIS) + +if(NOT TORQUE_SDL) + filterOut("platform/nativeDialogs/fileDialog.cpp" ) +endif() +if(NOT TORQUE_OPENGL) + filterOut("platformSDL/sdlPlatformGL.cpp") +endif() +if (NOT TORQUE_NET_CURL) + filterOut("app/net/httpObject.h" "app/net/httpObject.cpp") +endif() + + +add_executable(${TORQUE_APP_NAME} MACOSX_BUNDLE ${TORQUE_SOURCE_FILES}) +set_target_properties(${TORQUE_APP_NAME} PROPERTIES + BUNDLE true + MACOSX_BUNDLE_INFO_PLIST "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist") + + +# Ensure the shared libraries are actually referenced at the correct path +set(CMAKE_XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks") + +install(TARGETS ${APP_NAME} + BUNDLE DESTINATION . COMPONENT Runtime + RUNTIME DESTINATION bin COMPONENT Runtime + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib/static) + + # Note Mac specific extension .app + set(APPS "\${CMAKE_BINARY_DIR}/game/${APP_NAME}.app") + + # Directories to look for dependencies + set(DIRS ${CMAKE_BINARY_DIR}) + + install(CODE "include(BundleUtilities) + fixup_bundle(\"${APPS}\" \"\" \"${DIRS}\")") + + set(CPACK_GENERATOR "DRAGNDROP") + include(CPack) + + diff --git a/Tools/CMake/torque_macros.cmake b/Tools/CMake/torque_macros.cmake index 5aee66f32..6783c7671 100644 --- a/Tools/CMake/torque_macros.cmake +++ b/Tools/CMake/torque_macros.cmake @@ -65,6 +65,23 @@ macro (torqueAddSourceDirectories) endforeach() endmacro (torqueAddSourceDirectories) +# Helper function to add a directory to the TORQUE_SOURCE_FILES variable. It automatically searches for .cpp and .h files in the +# specified directory then adds them to the TORQUE_SOURCE_FILES variable. +macro (torqueToolchainSourceDirectories) + foreach(ARGUMENT ${ARGV}) + file(GLOB SCANNED_SOURCE_FILES "${TORQUE_SOURCE_DIRECTROY}/${ARGUMENT}/*.cpp") + file(GLOB SCANNED_INCLUDE_FILES "${TORQUE_SOURCE_DIRECTROY}/${ARGUMENT}/*.h") + + if (APPLE) + file(GLOB SCANNED_MAC_FILES "${TORQUE_SOURCE_DIRECTROY}/${ARGUMENT}/*.mm") + endif (APPLE) + + # Set in both current and parent scope so this macro can be used from loaded modules + set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${SCANNED_SOURCE_FILES} ${SCANNED_INCLUDE_FILES} ${SCANNED_MAC_FILES}) + set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} PARENT_SCOPE) + endforeach() +endmacro (torqueToolchainSourceDirectories) + ################# Set Conditional Engine Defines ################### macro (forwardDef flag) if (${flag}) @@ -128,6 +145,11 @@ endmacro (filterOut) ################# apple frameworks ################### macro(addFramework framework) if (APPLE) - set(TORQUE_LINK_LIBRARIES ${TORQUE_LINK_LIBRARIES} "-framework ${framework}") + find_library(${FRAMEWORK_LIB} ${framework}) + if(NOT ${FRAMEWORK_LIB}) + message(STATUS "${framework} not found.") + else() + set(TORQUE_LINK_LIBRARIES ${TORQUE_LINK_LIBRARIES} "${framework}") + endif() endif() endmacro() \ No newline at end of file From e6c387c67ae62d5eeaad44f291cc6ee8e94b13fb Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Thu, 19 Oct 2023 08:29:27 +0100 Subject: [PATCH 002/122] Update apple.toolchain.cmake --- Tools/CMake/toolchain/apple.toolchain.cmake | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Tools/CMake/toolchain/apple.toolchain.cmake b/Tools/CMake/toolchain/apple.toolchain.cmake index 0b06ee365..fdd99f3a6 100644 --- a/Tools/CMake/toolchain/apple.toolchain.cmake +++ b/Tools/CMake/toolchain/apple.toolchain.cmake @@ -304,6 +304,20 @@ set_target_properties(${TORQUE_APP_NAME} PROPERTIES BUNDLE true MACOSX_BUNDLE_INFO_PLIST "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist") +target_compile_definitions(${TORQUE_APP_NAME} PUBLIC ${TORQUE_COMPILE_DEFINITIONS}) +target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_LIBRARIES}) +target_link_options(${TORQUE_APP_NAME} PUBLIC ${TORQUE_LINK_OPTIONS}) +if (TORQUE_TARGET_PROPERTIES) + set_target_properties(${TORQUE_APP_NAME} PROPERTIES ${TORQUE_TARGET_PROPERTIES}) +endif (TORQUE_TARGET_PROPERTIES) +target_include_directories(${TORQUE_APP_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_BINARY_DIR}/temp" ${TORQUE_INCLUDE_DIRECTORIES}) + +append_defs() + +foreach (LIBRARY_BINARY ${TORQUE_ADDITIONAL_LIBRARY_BINARIES}) + # For OSX, we want these binaries to be copied to the Frameworks directory + add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}/${TORQUE_APP_NAME}.app/Contents/Frameworks") +endforeach() # Ensure the shared libraries are actually referenced at the correct path set(CMAKE_XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks") @@ -315,7 +329,7 @@ install(TARGETS ${APP_NAME} ARCHIVE DESTINATION lib/static) # Note Mac specific extension .app - set(APPS "\${CMAKE_BINARY_DIR}/game/${APP_NAME}.app") + set(APPS "\${TORQUE_APP_GAME_DIRECTORY}/${APP_NAME}.app") # Directories to look for dependencies set(DIRS ${CMAKE_BINARY_DIR}) From 1b49f28d19acbac4c3bb82e1bb4454ba363b2205 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Thu, 19 Oct 2023 08:57:24 +0100 Subject: [PATCH 003/122] move all setup to toolchain -All apple config in toolchain --- Tools/CMake/toolchain/apple.toolchain.cmake | 2 +- Tools/CMake/torqueMacOSconfigs.cmake | 22 --------------------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/Tools/CMake/toolchain/apple.toolchain.cmake b/Tools/CMake/toolchain/apple.toolchain.cmake index fdd99f3a6..41b24880f 100644 --- a/Tools/CMake/toolchain/apple.toolchain.cmake +++ b/Tools/CMake/toolchain/apple.toolchain.cmake @@ -7,7 +7,7 @@ enable_language(OBJCXX) set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE INTERNAL "") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE INTERNAL "") - +set(CMAKE_SYSTEM_NAME Darwin) set(CMAKE_THREAD_LIBS_INIT "-lpthread") set(CMAKE_HAVE_THREADS_LIBRARY 1) set(CMAKE_USE_WIN32_THREADS_INIT 0) diff --git a/Tools/CMake/torqueMacOSconfigs.cmake b/Tools/CMake/torqueMacOSconfigs.cmake index 99f918ea8..5f0c870d4 100644 --- a/Tools/CMake/torqueMacOSconfigs.cmake +++ b/Tools/CMake/torqueMacOSconfigs.cmake @@ -1,23 +1 @@ #detect Architecture -if (APPLE AND NOT IOS) - option(TORQUE_MACOS_UNIVERSAL_BINARY OFF) - - # Detect architecture if not using universal - if (TORQUE_MACOS_UNIVERSAL_BINARY) - set(ARCHITECTURE_STRING_APPLE "x86_64;arm64") - set(DEPLOYMENT_TARGET_APPLE "10.13") - else() - if (CMAKE_SYSTEM_PROCESSOR MATCHES "arm64") - set(ARCHITECTURE_STRING_APPLE "arm64") - set(DEPLOYMENT_TARGET_APPLE "11.0") - else() - set(ARCHITECTURE_STRING_APPLE "x86_64") - set(DEPLOYMENT_TARGET_APPLE "10.13") - endif() - endif() - - set(CMAKE_OSX_ARCHITECTURES ${ARCHITECTURE_STRING_APPLE} CACHE STRING "OSX Architecture" FORCE) - set(CMAKE_OSX_DEPLOYMENT_TARGET ${DEPLOYMENT_TARGET_APPLE} CACHE STRING "OSX Deployment target" FORCE) - mark_as_advanced(CMAKE_OSX_ARCHITECTURES) - mark_as_advanced(CMAKE_OSX_DEPLOYMENT_TARGET) -endif() \ No newline at end of file From ac60ce8da7a3a27bb7bd1c745555235680bfc828 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Thu, 19 Oct 2023 09:58:55 +0100 Subject: [PATCH 004/122] Update apple.toolchain.cmake --- Tools/CMake/toolchain/apple.toolchain.cmake | 58 +++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/Tools/CMake/toolchain/apple.toolchain.cmake b/Tools/CMake/toolchain/apple.toolchain.cmake index 41b24880f..3bd52aa27 100644 --- a/Tools/CMake/toolchain/apple.toolchain.cmake +++ b/Tools/CMake/toolchain/apple.toolchain.cmake @@ -5,8 +5,66 @@ project(${TORQUE_APP_NAME}) enable_language(OBJC) enable_language(OBJCXX) +find_program(XCODEBUILD_EXECUTABLE xcodebuild) +execute_process(COMMAND ${XCODEBUILD_EXECUTABLE} -version -sdk macosx Path + OUTPUT_VARIABLE XCODE_SDK_ROOT_DIR + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + +set(XCODE_SDK_ROOT_DIR "${XCODE_SDK_ROOT_DIR}" CACHE INTERNAL "") +# Specify the location or name of the platform SDK to be used in CMAKE_OSX_SYSROOT. +set(CMAKE_OSX_SYSROOT "${XCODE_SDK_ROOT_DIR}" CACHE INTERNAL "") + +if (NOT DEFINED CMAKE_DEVELOPER_ROOT AND NOT CMAKE_GENERATOR MATCHES "Xcode") + get_filename_component(PLATFORM_SDK_DIR ${XCODE_SDK_ROOT_DIR} PATH) + get_filename_component(CMAKE_DEVELOPER_ROOT ${PLATFORM_SDK_DIR} PATH) + if (NOT EXISTS "${CMAKE_DEVELOPER_ROOT}") + message(FATAL_ERROR "Invalid CMAKE_DEVELOPER_ROOT: ${CMAKE_DEVELOPER_ROOT} does not exist.") + endif() +endif() + +# Find (Apple's) libtool. +if(DEFINED BUILD_LIBTOOL) + # Environment variables are always preserved. + set(ENV{_BUILD_LIBTOOL} "${BUILD_LIBTOOL}") +elseif(DEFINED ENV{_BUILD_LIBTOOL}) + set(BUILD_LIBTOOL "$ENV{_BUILD_LIBTOOL}") +elseif(NOT DEFINED BUILD_LIBTOOL) + execute_process(COMMAND xcrun -sdk ${XCODE_SDK_ROOT_DIR} -find libtool + OUTPUT_VARIABLE BUILD_LIBTOOL + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() + +# Find the toolchain's provided install_name_tool if none is found on the host +if(DEFINED CMAKE_INSTALL_NAME_TOOL) + # Environment variables are always preserved. + set(ENV{_CMAKE_INSTALL_NAME_TOOL} "${CMAKE_INSTALL_NAME_TOOL}") +elseif(DEFINED ENV{_CMAKE_INSTALL_NAME_TOOL}) + set(CMAKE_INSTALL_NAME_TOOL "$ENV{_CMAKE_INSTALL_NAME_TOOL}") +elseif(NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + execute_process(COMMAND xcrun -sdk ${XCODE_SDK_ROOT_DIR} -find install_name_tool + OUTPUT_VARIABLE CMAKE_INSTALL_NAME_TOOL_INT + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(CMAKE_INSTALL_NAME_TOOL ${CMAKE_INSTALL_NAME_TOOL_INT} CACHE INTERNAL "") +endif() + +get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) +foreach(lang ${languages}) + set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "${BUILD_LIBTOOL} -static -o " CACHE INTERNAL "") +endforeach() + +set(CMAKE_FRAMEWORK_PATH + ${CMAKE_DEVELOPER_ROOT}/Library/PrivateFrameworks + ${XCODE_SDK_ROOT_DIR}/System/Library/Frameworks + ${CMAKE_FRAMEWORK_PATH} CACHE INTERNAL "") + +set(CMAKE_FIND_FRAMEWORK FIRST) set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE INTERNAL "") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE INTERNAL "") +set(CMAKE_XCODE_ATTRIBUTE_SDKROOT macosx) +set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "NO") set(CMAKE_SYSTEM_NAME Darwin) set(CMAKE_THREAD_LIBS_INIT "-lpthread") set(CMAKE_HAVE_THREADS_LIBRARY 1) From c11587cad381bf096ec10260ddf65ddb1a808709 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Thu, 19 Oct 2023 11:04:16 +0100 Subject: [PATCH 005/122] Attempt 2 -Missing includes may be due to no parent_scope --- Engine/source/CMakeLists.txt | 24 +- Tools/CMake/toolchain/apple.toolchain.cmake | 401 -------------------- Tools/CMake/torqueMacOSconfigs.cmake | 107 ++++++ 3 files changed, 130 insertions(+), 402 deletions(-) delete mode 100644 Tools/CMake/toolchain/apple.toolchain.cmake diff --git a/Engine/source/CMakeLists.txt b/Engine/source/CMakeLists.txt index 3ec15127a..77d0a56f7 100644 --- a/Engine/source/CMakeLists.txt +++ b/Engine/source/CMakeLists.txt @@ -351,7 +351,9 @@ endif (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) if (APPLE) add_executable(${TORQUE_APP_NAME} MACOSX_BUNDLE ${TORQUE_SOURCE_FILES}) - set_target_properties(${TORQUE_APP_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist") + set_target_properties(${TORQUE_APP_NAME} PROPERTIES + BUNDLE true + MACOSX_BUNDLE_INFO_PLIST "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist") # Ensure the shared libraries are actually referenced at the correct path set(CMAKE_XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks") @@ -454,3 +456,23 @@ if (UNIX) endif() endforeach() endif (UNIX) + +if(APPLE) +install(TARGETS ${APP_NAME} + BUNDLE DESTINATION . COMPONENT Runtime + RUNTIME DESTINATION bin COMPONENT Runtime + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib/static) + +# Note Mac specific extension .app +set(APPS "\${TORQUE_APP_GAME_DIRECTORY}/${APP_NAME}.app") + +# Directories to look for dependencies +set(DIRS ${CMAKE_BINARY_DIR}) + +install(CODE "include(BundleUtilities) + fixup_bundle(\"${APPS}\" \"\" \"${DIRS}\")") + +set(CPACK_GENERATOR "DRAGNDROP") +include(CPack) +endif() diff --git a/Tools/CMake/toolchain/apple.toolchain.cmake b/Tools/CMake/toolchain/apple.toolchain.cmake deleted file mode 100644 index 3bd52aa27..000000000 --- a/Tools/CMake/toolchain/apple.toolchain.cmake +++ /dev/null @@ -1,401 +0,0 @@ -cmake_minimum_required (VERSION 3.21.0) - -project(${TORQUE_APP_NAME}) - -enable_language(OBJC) -enable_language(OBJCXX) - -find_program(XCODEBUILD_EXECUTABLE xcodebuild) -execute_process(COMMAND ${XCODEBUILD_EXECUTABLE} -version -sdk macosx Path - OUTPUT_VARIABLE XCODE_SDK_ROOT_DIR - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - -set(XCODE_SDK_ROOT_DIR "${XCODE_SDK_ROOT_DIR}" CACHE INTERNAL "") -# Specify the location or name of the platform SDK to be used in CMAKE_OSX_SYSROOT. -set(CMAKE_OSX_SYSROOT "${XCODE_SDK_ROOT_DIR}" CACHE INTERNAL "") - -if (NOT DEFINED CMAKE_DEVELOPER_ROOT AND NOT CMAKE_GENERATOR MATCHES "Xcode") - get_filename_component(PLATFORM_SDK_DIR ${XCODE_SDK_ROOT_DIR} PATH) - get_filename_component(CMAKE_DEVELOPER_ROOT ${PLATFORM_SDK_DIR} PATH) - if (NOT EXISTS "${CMAKE_DEVELOPER_ROOT}") - message(FATAL_ERROR "Invalid CMAKE_DEVELOPER_ROOT: ${CMAKE_DEVELOPER_ROOT} does not exist.") - endif() -endif() - -# Find (Apple's) libtool. -if(DEFINED BUILD_LIBTOOL) - # Environment variables are always preserved. - set(ENV{_BUILD_LIBTOOL} "${BUILD_LIBTOOL}") -elseif(DEFINED ENV{_BUILD_LIBTOOL}) - set(BUILD_LIBTOOL "$ENV{_BUILD_LIBTOOL}") -elseif(NOT DEFINED BUILD_LIBTOOL) - execute_process(COMMAND xcrun -sdk ${XCODE_SDK_ROOT_DIR} -find libtool - OUTPUT_VARIABLE BUILD_LIBTOOL - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) -endif() - -# Find the toolchain's provided install_name_tool if none is found on the host -if(DEFINED CMAKE_INSTALL_NAME_TOOL) - # Environment variables are always preserved. - set(ENV{_CMAKE_INSTALL_NAME_TOOL} "${CMAKE_INSTALL_NAME_TOOL}") -elseif(DEFINED ENV{_CMAKE_INSTALL_NAME_TOOL}) - set(CMAKE_INSTALL_NAME_TOOL "$ENV{_CMAKE_INSTALL_NAME_TOOL}") -elseif(NOT DEFINED CMAKE_INSTALL_NAME_TOOL) - execute_process(COMMAND xcrun -sdk ${XCODE_SDK_ROOT_DIR} -find install_name_tool - OUTPUT_VARIABLE CMAKE_INSTALL_NAME_TOOL_INT - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - set(CMAKE_INSTALL_NAME_TOOL ${CMAKE_INSTALL_NAME_TOOL_INT} CACHE INTERNAL "") -endif() - -get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) -foreach(lang ${languages}) - set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "${BUILD_LIBTOOL} -static -o " CACHE INTERNAL "") -endforeach() - -set(CMAKE_FRAMEWORK_PATH - ${CMAKE_DEVELOPER_ROOT}/Library/PrivateFrameworks - ${XCODE_SDK_ROOT_DIR}/System/Library/Frameworks - ${CMAKE_FRAMEWORK_PATH} CACHE INTERNAL "") - -set(CMAKE_FIND_FRAMEWORK FIRST) -set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE INTERNAL "") -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE INTERNAL "") -set(CMAKE_XCODE_ATTRIBUTE_SDKROOT macosx) -set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "NO") -set(CMAKE_SYSTEM_NAME Darwin) -set(CMAKE_THREAD_LIBS_INIT "-lpthread") -set(CMAKE_HAVE_THREADS_LIBRARY 1) -set(CMAKE_USE_WIN32_THREADS_INIT 0) -set(CMAKE_USE_PTHREADS_INIT 1) -set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO") -set(CMAKE_SHARED_LIBRARY_PREFIX "lib") -set(CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") -set(CMAKE_SHARED_MODULE_PREFIX "lib") -set(CMAKE_SHARED_MODULE_SUFFIX ".so") -set(CMAKE_C_COMPILER_ABI ELF) -set(CMAKE_CXX_COMPILER_ABI ELF) -set(CMAKE_C_HAS_ISYSROOT 1) -set(CMAKE_CXX_HAS_ISYSROOT 1) -set(CMAKE_MODULE_EXISTS 1) -set(CMAKE_DL_LIBS "") -set(CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") -set(CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") -set(CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") -set(CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") -set(CMAKE_MACOSX_BUNDLE YES) -set(OBJC_VARS "-fobjc-abi-version=2 -DOBJC_OLD_DISPATCH_PROTOTYPES=0") - -set(CMAKE_OBJC_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${BITCODE} ${VISIBILITY} ${FOBJC_ARC} ${OBJC_VARS} ${CMAKE_OBJC_FLAGS}") -set(CMAKE_OBJC_FLAGS_DEBUG "-O0 -g ${CMAKE_OBJC_FLAGS_DEBUG}") -set(CMAKE_OBJC_FLAGS_MINSIZEREL "-DNDEBUG -Os ${CMAKE_OBJC_FLAGS_MINSIZEREL}") -set(CMAKE_OBJC_FLAGS_RELWITHDEBINFO "-DNDEBUG -O2 -g ${CMAKE_OBJC_FLAGS_RELWITHDEBINFO}") -set(CMAKE_OBJC_FLAGS_RELEASE "-DNDEBUG -O3 ${CMAKE_OBJC_FLAGS_RELEASE}") -set(CMAKE_OBJCXX_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${BITCODE} ${VISIBILITY} ${FOBJC_ARC} ${OBJC_VARS} ${CMAKE_OBJCXX_FLAGS}") -set(CMAKE_OBJCXX_FLAGS_DEBUG "-O0 -g ${CMAKE_OBJCXX_FLAGS_DEBUG}") -set(CMAKE_OBJCXX_FLAGS_MINSIZEREL "-DNDEBUG -Os ${CMAKE_OBJCXX_FLAGS_MINSIZEREL}") -set(CMAKE_OBJCXX_FLAGS_RELWITHDEBINFO "-DNDEBUG -O2 -g ${CMAKE_OBJCXX_FLAGS_RELWITHDEBINFO}") -set(CMAKE_OBJCXX_FLAGS_RELEASE "-DNDEBUG -O3 ${CMAKE_OBJCXX_FLAGS_RELEASE}") -set(CMAKE_OBJC_LINK_FLAGS "${C_TARGET_FLAGS} ${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_OBJC_LINK_FLAGS}") -set(CMAKE_OBJCXX_LINK_FLAGS "${C_TARGET_FLAGS} ${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_OBJCXX_LINK_FLAGS}") - -set(CMAKE_PLATFORM_HAS_INSTALLNAME 1) -set(CMAKE_SHARED_LINKER_FLAGS "-rpath @executable_path/Frameworks -rpath @loader_path/Frameworks") -set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -Wl,-headerpad_max_install_names") -set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -Wl,-headerpad_max_install_names") -set(CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") -set(CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") -set(CMAKE_FIND_LIBRARY_SUFFIXES ".tbd" ".dylib" ".so" ".a") -set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-install_name") - -# Add our libs. -add_subdirectory(${TORQUE_LIB_ROOT_DIRECTORY}) - -################# Helper Function Calls ################### -forwardDef(TORQUE_OPENGL) -forwardDef(TORQUE_D3D11) -forwardDef(TORQUE_ADVANCED_LIGHTING) -forwardDef(TORQUE_BASIC_LIGHTING) -set(TORQUE_SDL ON) # we need sdl to do our platform interop -forwardDef(TORQUE_SDL) - -if(TORQUE_TESTING) -set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} TORQUE_TESTS_ENABLED) -set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} "_VARIADIC_MAX=10") -endif() - -set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} __MACOSX__) - -################# Collect Source Files ################### - -# Handle app -torqueToolchainSourceDirectories("app" "app/net") - -# Handle console -torqueToolchainSourceDirectories("console") -torqueToolchainSourceDirectories("console/torquescript") - -# Handle Platform -torqueToolchainSourceDirectories("platform" "platform/threads" "platform/async" - "platform/input" "platform/output") - -torqueToolchainSourceDirectories("platform/nativeDialogs") - -# Handle T3D -torqueToolchainSourceDirectories("T3D/fps" "T3D/fx" "T3D/vehicles" "T3D/physics" - "T3D/decal" "T3D/sfx" "T3D/gameBase" "T3D/turret" - "T3D/lighting" "T3D/gameOBjects" "T3D/components" - "T3D/systems" "T3D/assets" "T3D" "T3D/gameBase/std") - -# Handle TS -torqueToolchainSourceDirectories("ts" "ts/collada" "ts/assimp" "ts/loader" "ts/arch") - -# Handle SFX - OpenAL is handled as a module later on -torqueToolchainSourceDirectories("sfx" "sfx/media" "sfx/null") -if(TORQUE_SFX_OPENAL AND NOT TORQUE_DEDICATED) - torqueToolchainSourceDirectories("sfx/openal") - torqueToolchainSourceDirectories("sfx/openal/mac") -endif() -# Handle GFX -torqueToolchainSourceDirectories("gfx" "gfx/Null" "gfx/test" "gfx/bitmap" "gfx/bitmap/loaders" - "gfx/util" "gfx/video" "gfx/sim" ) - -if (TORQUE_OPENGL) - torqueToolchainSourceDirectories("gfx/gl" "gfx/gl/sdl" "gfx/gl/tGL") -endif (TORQUE_OPENGL) - -# Handle core -torqueToolchainSourceDirectories("core" "core/stream" "core/strings" "core/util" - "core/util/journal" "core/util/zip" "core/util/compressors") -# Handle GUI -torqueToolchainSourceDirectories("gui" "gui/buttons" "gui/containers" "gui/controls" "gui/core" - "gui/game" "gui/shiny" "gui/utility" "gui/3d") - -# Handle postFX -torqueToolchainSourceDirectories("postFx") - -# Handle Windowmanager -torqueToolchainSourceDirectories("windowManager" "windowManager/torque" "windowManager/sdl") - -# Handle scene -torqueToolchainSourceDirectories("scene" "scene/culling" "scene/zones" "scene/mixin") - -# Handle math -torqueToolchainSourceDirectories("math" "math/util") - -# Handle persistence -torqueToolchainSourceDirectories("persistence/taml" "persistence/taml/binary" "persistence/taml/xml") - -# Handle Cinterface -torqueToolchainSourceDirectories("cinterface") - -# Handle util -torqueToolchainSourceDirectories("util" "util/messaging") - -# Handle assets -torqueToolchainSourceDirectories("assets") - -# Handle Sim -torqueToolchainSourceDirectories("sim") - -# Handle module -torqueToolchainSourceDirectories("module") - -# Handle forest -torqueToolchainSourceDirectories("forest" "forest/ts") -if(TORQUE_OPENGL) - torqueToolchainSourceDirectories("forest" "forest/glsl") -endif(TORQUE_OPENGL) - -# Handle shadergen -torqueToolchainSourceDirectories("shaderGen") - -if (TORQUE_OPENGL) -torqueToolchainSourceDirectories("shaderGen/GLSL") -endif (TORQUE_OPENGL) - -# Handle terrain -torqueToolchainSourceDirectories("terrain") - -if (TORQUE_OPENGL) -torqueToolchainSourceDirectories("terrain/glsl") -endif (TORQUE_OPENGL) - -# Handle Materials -torqueToolchainSourceDirectories("materials") - -# Handle collision -torqueToolchainSourceDirectories("collision") - -# Handle lighting -torqueToolchainSourceDirectories("lighting" "lighting/common" - "lighting/shadowMap") -if (TORQUE_ADVANCED_LIGHTING) -torqueToolchainSourceDirectories("lighting/advanced") - if (TORQUE_OPENGL) - torqueToolchainSourceDirectories("lighting/advanced/glsl") - endif (TORQUE_OPENGL) -endif (TORQUE_ADVANCED_LIGHTING) - -if (TORQUE_BASIC_LIGHTING) -torqueToolchainSourceDirectories("lighting/basic" "lighting/basic/shadowMap") -endif (TORQUE_BASIC_LIGHTING) - -# Begin handling platform specific stuff -# Handle Platform POSIX -if (UNIX) -torqueToolchainSourceDirectories("platformPOSIX") - - if (TORQUE_CPU_X32 OR TORQUE_CPU_X64) - torqueToolchainSourceDirectories("platformX86UNIX") - endif (TORQUE_CPU_X32 OR TORQUE_CPU_X64) -endif (UNIX) - -# Mac platform -torqueToolchainSourceDirectories("platformMac") - -# Handle platformSDL -torqueToolchainSourceDirectories("platformSDL" "platformSDL/threads") - -if(TORQUE_TESTING) -torqueToolchainSourceDirectories("testing") - set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} TORQUE_SHARED SDL_MAIN_HANDLED) -endif(TORQUE_TESTING) - -# Add the collected files to our engine group -source_group(TREE "${CMAKE_SOURCE_DIR}/Engine/source" PREFIX "Engine" FILES ${TORQUE_SOURCE_FILES}) -file(GLOB_RECURSE TORQUE_APP_GAME_SOURCES "${TORQUE_APP_ROOT_DIRECTORY}/source/*.cpp" "${TORQUE_APP_ROOT_DIRECTORY}/source/*.h") -source_group(TREE "${TORQUE_APP_ROOT_DIRECTORY}/source" PREFIX "Source Files" FILES ${TORQUE_APP_GAME_SOURCES}) -set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} "${TORQUE_APP_ROOT_DIRECTORY}/source") - -################# Engine Module Handling ################### - -set(TORQUE_MODULE_PATHS "${CMAKE_SOURCE_DIR}/Tools/CMake/modules" "${TORQUE_APP_GAME_DIRECTORY}/data") -if (NOT "${TORQUE_MODULE_USER_PATH}" STREQUAL "") - list(APPEND TORQUE_MODULE_PATHS "${TORQUE_MODULE_USER_PATH}") -endif() - -# Before doing module scanning, store away the engine sources - we do this so that modules -# can be placed into the proper filters -set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES} ${TORQUE_APP_GAME_SOURCES}) -set(TORQUE_SOURCE_FILES "") - -foreach (TORQUE_MODULE_PATH ${TORQUE_MODULE_PATHS}) - # First find simple cmake scripts, mostly used for in-engine modules - file(GLOB MODULE_SCRIPTS "${TORQUE_MODULE_PATH}/*.cmake") - foreach (TORQUE_MODULE_SCRIPT ${MODULE_SCRIPTS}) - include(${TORQUE_MODULE_SCRIPT}) - - # Add this script's collected files to our Engine group - source_group(TREE "${CMAKE_SOURCE_DIR}/Engine" PREFIX "Engine" FILES ${TORQUE_SOURCE_FILES}) - - set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${TORQUE_SOURCE_FILES}) - set(TORQUE_SOURCE_FILES "") - endforeach() - - # Next find sub projects, these can introduce new source files - SUBDIRLIST(POSSIBLE_PROJECTS "${TORQUE_MODULE_PATH}") - foreach (POSSIBLE_PROJECT ${POSSIBLE_PROJECTS}) - # Retrieve the absolute path of this possible project - get_filename_component(POSSIBLE_PROJECT_ABSOLUTEPATH "${POSSIBLE_PROJECT}" - REALPATH BASE_DIR "${TORQUE_MODULE_PATH}") - - if (EXISTS "${POSSIBLE_PROJECT_ABSOLUTEPATH}/CMakeLists.txt") - add_subdirectory("${POSSIBLE_PROJECT_ABSOLUTEPATH}" ${CMAKE_BINARY_DIR}/temp/${POSSIBLE_PROJECT} EXCLUDE_FROM_ALL) - source_group(TREE "${POSSIBLE_PROJECT_ABSOLUTEPATH}" PREFIX "Modules/${POSSIBLE_PROJECT}" FILES ${TORQUE_SOURCE_FILES}) - - set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${TORQUE_SOURCE_FILES}) - set(TORQUE_SOURCE_FILES "") - elseif(NOT EXISTS "${POSSIBLE_PROJECT_ABSOLUTEPATH}/*.cmake") - file(GLOB_RECURSE MODULE_SOURCE "${POSSIBLE_PROJECT_ABSOLUTEPATH}/source/*.h" "${POSSIBLE_PROJECT_ABSOLUTEPATH}/source/*.cpp") - #message(STATUS "MODULE_SOURCE:${MODULE_SOURCE}") - source_group(TREE "${POSSIBLE_PROJECT_ABSOLUTEPATH}" PREFIX "Modules/${POSSIBLE_PROJECT}/" FILES ${MODULE_SOURCE}) - set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${MODULE_SOURCE}) - endif() - endforeach() -endforeach() - -set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES_TEMPORARY}) - -################# Library Post-build Handling ################### -set(TORQUE_LIBRARY_PATHS "${CMAKE_SOURCE_DIR}/Engine/lib" "${TORQUE_APP_GAME_DIRECTORY}/data") -if (NOT "${TORQUE_LIBRARY_USER_PATH}" STREQUAL "") - list(APPEND TORQUE_LIBRARY_PATHS "${TORQUE_LIBRARY_USER_PATH}") -endif() - -foreach (TORQUE_LIBRARY_PATH ${TORQUE_LIBRARY_PATHS}) - # First find simple cmake scripts, mostly used for in-engine libraries - file(GLOB_RECURSE LIBRARY_SCRIPTS "${TORQUE_LIBRARY_PATH}/*Torque_postBuild.cmake") - #message(STATUS "LIBRARY_SCRIPTS:${LIBRARY_SCRIPTS}") - foreach (TORQUE_LIBRARY_SCRIPT ${LIBRARY_SCRIPTS}) - include(${TORQUE_LIBRARY_SCRIPT}) - endforeach() -endforeach() - -set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_MAC_SOURCES} "${CMAKE_SOURCE_DIR}/Tools/CMake/torque.icns") -set_source_files_properties("${CMAKE_SOURCE_DIR}/Tools/CMake/torque.icns" PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") - -set(EXECUTABLE_NAME "${TORQUE_APP_NAME}") -configure_file("${CMAKE_SOURCE_DIR}/Tools/CMake/Info.plist.in" "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" COPYONLY) - -addDef(TORQUE_DEBUG Debug) -addDef(TORQUE_RELEASE "RelWithDebInfo;Release") -addDef(TORQUE_ENABLE_ASSERTS "Debug;RelWithDebInfo") -addDef(TORQUE_DEBUG_GFX_MODE "RelWithDebInfo") -addDef(TORQUE_OGGVORBIS) - -if(NOT TORQUE_SDL) - filterOut("platform/nativeDialogs/fileDialog.cpp" ) -endif() -if(NOT TORQUE_OPENGL) - filterOut("platformSDL/sdlPlatformGL.cpp") -endif() -if (NOT TORQUE_NET_CURL) - filterOut("app/net/httpObject.h" "app/net/httpObject.cpp") -endif() - - -add_executable(${TORQUE_APP_NAME} MACOSX_BUNDLE ${TORQUE_SOURCE_FILES}) -set_target_properties(${TORQUE_APP_NAME} PROPERTIES - BUNDLE true - MACOSX_BUNDLE_INFO_PLIST "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist") - -target_compile_definitions(${TORQUE_APP_NAME} PUBLIC ${TORQUE_COMPILE_DEFINITIONS}) -target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_LIBRARIES}) -target_link_options(${TORQUE_APP_NAME} PUBLIC ${TORQUE_LINK_OPTIONS}) -if (TORQUE_TARGET_PROPERTIES) - set_target_properties(${TORQUE_APP_NAME} PROPERTIES ${TORQUE_TARGET_PROPERTIES}) -endif (TORQUE_TARGET_PROPERTIES) -target_include_directories(${TORQUE_APP_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_BINARY_DIR}/temp" ${TORQUE_INCLUDE_DIRECTORIES}) - -append_defs() - -foreach (LIBRARY_BINARY ${TORQUE_ADDITIONAL_LIBRARY_BINARIES}) - # For OSX, we want these binaries to be copied to the Frameworks directory - add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}/${TORQUE_APP_NAME}.app/Contents/Frameworks") -endforeach() - -# Ensure the shared libraries are actually referenced at the correct path -set(CMAKE_XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks") - -install(TARGETS ${APP_NAME} - BUNDLE DESTINATION . COMPONENT Runtime - RUNTIME DESTINATION bin COMPONENT Runtime - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib/static) - - # Note Mac specific extension .app - set(APPS "\${TORQUE_APP_GAME_DIRECTORY}/${APP_NAME}.app") - - # Directories to look for dependencies - set(DIRS ${CMAKE_BINARY_DIR}) - - install(CODE "include(BundleUtilities) - fixup_bundle(\"${APPS}\" \"\" \"${DIRS}\")") - - set(CPACK_GENERATOR "DRAGNDROP") - include(CPack) - - diff --git a/Tools/CMake/torqueMacOSconfigs.cmake b/Tools/CMake/torqueMacOSconfigs.cmake index 5f0c870d4..3d19ffc76 100644 --- a/Tools/CMake/torqueMacOSconfigs.cmake +++ b/Tools/CMake/torqueMacOSconfigs.cmake @@ -1 +1,108 @@ #detect Architecture +enable_language(OBJC) +enable_language(OBJCXX) + +find_program(XCODEBUILD_EXECUTABLE xcodebuild) +execute_process(COMMAND ${XCODEBUILD_EXECUTABLE} -version -sdk macosx Path + OUTPUT_VARIABLE XCODE_SDK_ROOT_DIR + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + +set(XCODE_SDK_ROOT_DIR "${XCODE_SDK_ROOT_DIR}" CACHE INTERNAL "") +# Specify the location or name of the platform SDK to be used in CMAKE_OSX_SYSROOT. +set(CMAKE_OSX_SYSROOT "${XCODE_SDK_ROOT_DIR}" CACHE INTERNAL "") + +if (NOT DEFINED CMAKE_DEVELOPER_ROOT AND NOT CMAKE_GENERATOR MATCHES "Xcode") + get_filename_component(PLATFORM_SDK_DIR ${XCODE_SDK_ROOT_DIR} PATH) + get_filename_component(CMAKE_DEVELOPER_ROOT ${PLATFORM_SDK_DIR} PATH) + if (NOT EXISTS "${CMAKE_DEVELOPER_ROOT}") + message(FATAL_ERROR "Invalid CMAKE_DEVELOPER_ROOT: ${CMAKE_DEVELOPER_ROOT} does not exist.") + endif() +endif() + +# Find (Apple's) libtool. +if(DEFINED BUILD_LIBTOOL) + # Environment variables are always preserved. + set(ENV{_BUILD_LIBTOOL} "${BUILD_LIBTOOL}") +elseif(DEFINED ENV{_BUILD_LIBTOOL}) + set(BUILD_LIBTOOL "$ENV{_BUILD_LIBTOOL}") +elseif(NOT DEFINED BUILD_LIBTOOL) + execute_process(COMMAND xcrun -sdk ${XCODE_SDK_ROOT_DIR} -find libtool + OUTPUT_VARIABLE BUILD_LIBTOOL + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() + +# Find the toolchain's provided install_name_tool if none is found on the host +if(DEFINED CMAKE_INSTALL_NAME_TOOL) + # Environment variables are always preserved. + set(ENV{_CMAKE_INSTALL_NAME_TOOL} "${CMAKE_INSTALL_NAME_TOOL}") +elseif(DEFINED ENV{_CMAKE_INSTALL_NAME_TOOL}) + set(CMAKE_INSTALL_NAME_TOOL "$ENV{_CMAKE_INSTALL_NAME_TOOL}") +elseif(NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + execute_process(COMMAND xcrun -sdk ${XCODE_SDK_ROOT_DIR} -find install_name_tool + OUTPUT_VARIABLE CMAKE_INSTALL_NAME_TOOL_INT + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(CMAKE_INSTALL_NAME_TOOL ${CMAKE_INSTALL_NAME_TOOL_INT} CACHE INTERNAL "") +endif() + +get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) +foreach(lang ${languages}) + set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "${BUILD_LIBTOOL} -static -o " CACHE INTERNAL "") +endforeach() + +set(CMAKE_FRAMEWORK_PATH + ${CMAKE_DEVELOPER_ROOT}/Library/PrivateFrameworks + ${XCODE_SDK_ROOT_DIR}/System/Library/Frameworks + ${CMAKE_FRAMEWORK_PATH} CACHE INTERNAL "") + +set(CMAKE_FIND_FRAMEWORK FIRST) +set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE INTERNAL "") +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE INTERNAL "") +set(CMAKE_XCODE_ATTRIBUTE_SDKROOT macosx) +set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "NO") +set(CMAKE_SYSTEM_NAME Darwin) +set(CMAKE_THREAD_LIBS_INIT "-lpthread") +set(CMAKE_HAVE_THREADS_LIBRARY 1) +set(CMAKE_USE_WIN32_THREADS_INIT 0) +set(CMAKE_USE_PTHREADS_INIT 1) +set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO") +set(CMAKE_SHARED_LIBRARY_PREFIX "lib") +set(CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") +set(CMAKE_SHARED_MODULE_PREFIX "lib") +set(CMAKE_SHARED_MODULE_SUFFIX ".so") +set(CMAKE_C_COMPILER_ABI ELF) +set(CMAKE_CXX_COMPILER_ABI ELF) +set(CMAKE_C_HAS_ISYSROOT 1) +set(CMAKE_CXX_HAS_ISYSROOT 1) +set(CMAKE_MODULE_EXISTS 1) +set(CMAKE_DL_LIBS "") +set(CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") +set(CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") +set(CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") +set(CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") +set(CMAKE_MACOSX_BUNDLE YES) +set(OBJC_VARS "-fobjc-abi-version=2 -DOBJC_OLD_DISPATCH_PROTOTYPES=0") + +set(CMAKE_OBJC_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${BITCODE} ${VISIBILITY} ${FOBJC_ARC} ${OBJC_VARS} ${CMAKE_OBJC_FLAGS}") +set(CMAKE_OBJC_FLAGS_DEBUG "-O0 -g ${CMAKE_OBJC_FLAGS_DEBUG}") +set(CMAKE_OBJC_FLAGS_MINSIZEREL "-DNDEBUG -Os ${CMAKE_OBJC_FLAGS_MINSIZEREL}") +set(CMAKE_OBJC_FLAGS_RELWITHDEBINFO "-DNDEBUG -O2 -g ${CMAKE_OBJC_FLAGS_RELWITHDEBINFO}") +set(CMAKE_OBJC_FLAGS_RELEASE "-DNDEBUG -O3 ${CMAKE_OBJC_FLAGS_RELEASE}") +set(CMAKE_OBJCXX_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${BITCODE} ${VISIBILITY} ${FOBJC_ARC} ${OBJC_VARS} ${CMAKE_OBJCXX_FLAGS}") +set(CMAKE_OBJCXX_FLAGS_DEBUG "-O0 -g ${CMAKE_OBJCXX_FLAGS_DEBUG}") +set(CMAKE_OBJCXX_FLAGS_MINSIZEREL "-DNDEBUG -Os ${CMAKE_OBJCXX_FLAGS_MINSIZEREL}") +set(CMAKE_OBJCXX_FLAGS_RELWITHDEBINFO "-DNDEBUG -O2 -g ${CMAKE_OBJCXX_FLAGS_RELWITHDEBINFO}") +set(CMAKE_OBJCXX_FLAGS_RELEASE "-DNDEBUG -O3 ${CMAKE_OBJCXX_FLAGS_RELEASE}") +set(CMAKE_OBJC_LINK_FLAGS "${C_TARGET_FLAGS} ${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_OBJC_LINK_FLAGS}") +set(CMAKE_OBJCXX_LINK_FLAGS "${C_TARGET_FLAGS} ${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_OBJCXX_LINK_FLAGS}") + +set(CMAKE_PLATFORM_HAS_INSTALLNAME 1) +set(CMAKE_SHARED_LINKER_FLAGS "-rpath @executable_path/Frameworks -rpath @loader_path/Frameworks") +set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -Wl,-headerpad_max_install_names") +set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -Wl,-headerpad_max_install_names") +set(CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") +set(CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") +set(CMAKE_FIND_LIBRARY_SUFFIXES ".tbd" ".dylib" ".so" ".a") +set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-install_name") \ No newline at end of file From a20055449630f02c73a2dc150ff4fd17110d763e Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Thu, 19 Oct 2023 11:45:25 +0100 Subject: [PATCH 006/122] more changes -Apple = more trouble than its worth --- Tools/CMake/torqueMacOSconfigs.cmake | 16 +++++++++++++++- Tools/CMake/torque_macros.cmake | 17 ----------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/Tools/CMake/torqueMacOSconfigs.cmake b/Tools/CMake/torqueMacOSconfigs.cmake index 3d19ffc76..fca739680 100644 --- a/Tools/CMake/torqueMacOSconfigs.cmake +++ b/Tools/CMake/torqueMacOSconfigs.cmake @@ -105,4 +105,18 @@ set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -Wl,-headerpad_max_install_names set(CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") set(CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") set(CMAKE_FIND_LIBRARY_SUFFIXES ".tbd" ".dylib" ".so" ".a") -set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-install_name") \ No newline at end of file +set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-install_name") + +if(CMAKE_OSX_ARCHITECTURES MATCHES "((^|;|, )(arm64|arm64e|x86_64))+") + set(CMAKE_C_SIZEOF_DATA_PTR 8) + set(CMAKE_CXX_SIZEOF_DATA_PTR 8) + if(CMAKE_OSX_ARCHITECTURES MATCHES "((^|;|, )(arm64|arm64e))+") + set(CMAKE_SYSTEM_PROCESSOR "aarch64") + else() + set(CMAKE_SYSTEM_PROCESSOR "x86_64") + endif() +else() + set(CMAKE_C_SIZEOF_DATA_PTR 4) + set(CMAKE_CXX_SIZEOF_DATA_PTR 4) + set(CMAKE_SYSTEM_PROCESSOR "arm") +endif() \ No newline at end of file diff --git a/Tools/CMake/torque_macros.cmake b/Tools/CMake/torque_macros.cmake index 6783c7671..04371ca6c 100644 --- a/Tools/CMake/torque_macros.cmake +++ b/Tools/CMake/torque_macros.cmake @@ -65,23 +65,6 @@ macro (torqueAddSourceDirectories) endforeach() endmacro (torqueAddSourceDirectories) -# Helper function to add a directory to the TORQUE_SOURCE_FILES variable. It automatically searches for .cpp and .h files in the -# specified directory then adds them to the TORQUE_SOURCE_FILES variable. -macro (torqueToolchainSourceDirectories) - foreach(ARGUMENT ${ARGV}) - file(GLOB SCANNED_SOURCE_FILES "${TORQUE_SOURCE_DIRECTROY}/${ARGUMENT}/*.cpp") - file(GLOB SCANNED_INCLUDE_FILES "${TORQUE_SOURCE_DIRECTROY}/${ARGUMENT}/*.h") - - if (APPLE) - file(GLOB SCANNED_MAC_FILES "${TORQUE_SOURCE_DIRECTROY}/${ARGUMENT}/*.mm") - endif (APPLE) - - # Set in both current and parent scope so this macro can be used from loaded modules - set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${SCANNED_SOURCE_FILES} ${SCANNED_INCLUDE_FILES} ${SCANNED_MAC_FILES}) - set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} PARENT_SCOPE) - endforeach() -endmacro (torqueToolchainSourceDirectories) - ################# Set Conditional Engine Defines ################### macro (forwardDef flag) if (${flag}) From 0af0b5a24ab1f6806bb61949778b92e5651536a9 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Thu, 19 Oct 2023 14:39:22 +0100 Subject: [PATCH 007/122] MACOS working Mac working in this stage --- Engine/lib/CMakeLists.txt | 5 +++++ Engine/lib/nativeFileDialogs/nfd_cocoa.m | 10 +++++----- Engine/source/CMakeLists.txt | 9 +++++---- Tools/CMake/torqueMacOSconfigs.cmake | 9 +++------ Tools/CMake/torque_macros.cmake | 7 +------ 5 files changed, 19 insertions(+), 21 deletions(-) diff --git a/Engine/lib/CMakeLists.txt b/Engine/lib/CMakeLists.txt index c73efbd25..b8545dffa 100644 --- a/Engine/lib/CMakeLists.txt +++ b/Engine/lib/CMakeLists.txt @@ -110,6 +110,7 @@ mark_as_advanced(SDL_X11) mark_as_advanced(SDL_XINPUT) add_subdirectory(sdl ${TORQUE_LIB_TARG_DIRECTORY}/sdl2 EXCLUDE_FROM_ALL) + add_subdirectory(nativeFileDialogs ${TORQUE_LIB_TARG_DIRECTORY}/nfd EXCLUDE_FROM_ALL) # Assimp @@ -160,6 +161,10 @@ if (TORQUE_CPU_ARM32 OR TORQUE_CPU_ARM64) endif(NOT APPLE) endif (TORQUE_CPU_ARM32 OR TORQUE_CPU_ARM64) +if(APPLE) + set(PNG_ARM_NEON off CACHE BOOL "" FORCE) +endif() + #PNG set(PNG_STATIC on CACHE BOOL "" FORCE) mark_as_advanced(PNG_STATIC) diff --git a/Engine/lib/nativeFileDialogs/nfd_cocoa.m b/Engine/lib/nativeFileDialogs/nfd_cocoa.m index 776152d41..7874c3b11 100644 --- a/Engine/lib/nativeFileDialogs/nfd_cocoa.m +++ b/Engine/lib/nativeFileDialogs/nfd_cocoa.m @@ -75,7 +75,7 @@ static nfdresult_t AllocPathSet( NSArray *urls, nfdpathset_t *pathset ) assert([urls count]); pathset->count = (size_t)[urls count]; - pathset->indices = NFDi_Malloc( sizeof(size_t)*pathset->count ); + pathset->indices = (unsigned long*)NFDi_Malloc( sizeof(size_t)*pathset->count ); if ( !pathset->indices ) { return NFD_ERROR; @@ -89,7 +89,7 @@ static nfdresult_t AllocPathSet( NSArray *urls, nfdpathset_t *pathset ) bufsize += [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; } - pathset->buf = NFDi_Malloc( sizeof(nfdchar_t) * bufsize ); + pathset->buf = (char *)NFDi_Malloc( sizeof(nfdchar_t) * bufsize ); if ( !pathset->buf ) { return NFD_ERROR; @@ -144,7 +144,7 @@ nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList, // byte count, not char count size_t len = strlen(utf8Path);//NFDi_UTF8_Strlen(utf8Path); - *outPath = NFDi_Malloc( len+1 ); + *outPath = (nfdchar_t*)NFDi_Malloc(len+1); if ( !*outPath ) { [pool release]; @@ -229,7 +229,7 @@ nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, size_t byteLen = [url.path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; - *outPath = NFDi_Malloc( byteLen ); + *outPath = (char *)NFDi_Malloc( byteLen ); if ( !*outPath ) { [pool release]; @@ -269,7 +269,7 @@ nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath, // byte count, not char count size_t len = strlen(utf8Path);//NFDi_UTF8_Strlen(utf8Path); - *outPath = NFDi_Malloc( len+1 ); + *outPath = (char *)NFDi_Malloc( len+1 ); if ( !*outPath ) { [pool release]; diff --git a/Engine/source/CMakeLists.txt b/Engine/source/CMakeLists.txt index 77d0a56f7..2f5564f02 100644 --- a/Engine/source/CMakeLists.txt +++ b/Engine/source/CMakeLists.txt @@ -57,7 +57,6 @@ torqueAddSourceDirectories("platform" "platform/threads" "platform/async" "platform/input" "platform/output") torqueAddSourceDirectories("platform/nativeDialogs") - # Handle T3D torqueAddSourceDirectories("T3D/fps" "T3D/fx" "T3D/vehicles" "T3D/physics" "T3D/decal" "T3D/sfx" "T3D/gameBase" "T3D/turret" @@ -199,9 +198,11 @@ torqueAddSourceDirectories("i18n") if (UNIX) torqueAddSourceDirectories("platformPOSIX") - if (TORQUE_CPU_X32 OR TORQUE_CPU_X64) - torqueAddSourceDirectories("platformX86UNIX") - endif (TORQUE_CPU_X32 OR TORQUE_CPU_X64) + if(NOT APPLE) + if (TORQUE_CPU_X32 OR TORQUE_CPU_X64) + torqueAddSourceDirectories("platformX86UNIX") + endif (TORQUE_CPU_X32 OR TORQUE_CPU_X64) + endif(NOT APPLE) endif (UNIX) # Handle platformMac diff --git a/Tools/CMake/torqueMacOSconfigs.cmake b/Tools/CMake/torqueMacOSconfigs.cmake index fca739680..37ba4d2de 100644 --- a/Tools/CMake/torqueMacOSconfigs.cmake +++ b/Tools/CMake/torqueMacOSconfigs.cmake @@ -52,14 +52,11 @@ foreach(lang ${languages}) set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "${BUILD_LIBTOOL} -static -o " CACHE INTERNAL "") endforeach() -set(CMAKE_FRAMEWORK_PATH - ${CMAKE_DEVELOPER_ROOT}/Library/PrivateFrameworks - ${XCODE_SDK_ROOT_DIR}/System/Library/Frameworks - ${CMAKE_FRAMEWORK_PATH} CACHE INTERNAL "") +set(CMAKE_FRAMEWORK_PATH "/Applications/XCode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks") set(CMAKE_FIND_FRAMEWORK FIRST) -set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE INTERNAL "") -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE INTERNAL "") +set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "" FORCE) +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "" FORCE) set(CMAKE_XCODE_ATTRIBUTE_SDKROOT macosx) set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "NO") set(CMAKE_SYSTEM_NAME Darwin) diff --git a/Tools/CMake/torque_macros.cmake b/Tools/CMake/torque_macros.cmake index 04371ca6c..7f9fdf8bc 100644 --- a/Tools/CMake/torque_macros.cmake +++ b/Tools/CMake/torque_macros.cmake @@ -128,11 +128,6 @@ endmacro (filterOut) ################# apple frameworks ################### macro(addFramework framework) if (APPLE) - find_library(${FRAMEWORK_LIB} ${framework}) - if(NOT ${FRAMEWORK_LIB}) - message(STATUS "${framework} not found.") - else() - set(TORQUE_LINK_LIBRARIES ${TORQUE_LINK_LIBRARIES} "${framework}") - endif() + set(TORQUE_LINK_LIBRARIES ${TORQUE_LINK_LIBRARIES} "${CMAKE_FRAMEWORK_PATH}/${framework}.framework") endif() endmacro() \ No newline at end of file From 8c4650d5dce4ec748243d0fd0db2ea22e97556da Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Thu, 19 Oct 2023 15:47:16 +0100 Subject: [PATCH 008/122] NativeFileDialogs -Needed to change nfd_cocoa to an objc++ file with mm extension --- Engine/lib/nativeFileDialogs/CMakeLists.txt | 2 +- Engine/lib/nativeFileDialogs/{nfd_cocoa.m => nfd_cocoa.mm} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename Engine/lib/nativeFileDialogs/{nfd_cocoa.m => nfd_cocoa.mm} (100%) diff --git a/Engine/lib/nativeFileDialogs/CMakeLists.txt b/Engine/lib/nativeFileDialogs/CMakeLists.txt index 1d0454bc6..f40f44dde 100644 --- a/Engine/lib/nativeFileDialogs/CMakeLists.txt +++ b/Engine/lib/nativeFileDialogs/CMakeLists.txt @@ -5,7 +5,7 @@ set(TORQUE_NFD_COMMON_SOURCES "${TORQUE_NFD_ROOT}/nfd_common.c") set(TORQUE_NFD_INCLUDE_DIRECTORIES "${TORQUE_NFD_ROOT}" "${TORQUE_NFD_ROOT}/include") if (APPLE) - set(TORQUE_NFD_SOURCES ${TORQUE_NFD_COMMON_SOURCES} "${TORQUE_NFD_ROOT}/nfd_cocoa.m") + set(TORQUE_NFD_SOURCES ${TORQUE_NFD_COMMON_SOURCES} "${TORQUE_NFD_ROOT}/nfd_cocoa.mm") elseif (UNIX) if (TORQUE_USE_ZENITY) set(TORQUE_NFD_SOURCES ${TORQUE_NFD_COMMON_SOURCES} "${TORQUE_NFD_ROOT}/nfd_zenity.c") diff --git a/Engine/lib/nativeFileDialogs/nfd_cocoa.m b/Engine/lib/nativeFileDialogs/nfd_cocoa.mm similarity index 100% rename from Engine/lib/nativeFileDialogs/nfd_cocoa.m rename to Engine/lib/nativeFileDialogs/nfd_cocoa.mm From b6c9bef48fdd24e7098e8679262ed34952411969 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Thu, 19 Oct 2023 16:29:04 +0100 Subject: [PATCH 009/122] revert nativeFile -NativeFileDialog m file was not being set to obj-c, now we force it withe set_source_files_properties. --- Engine/lib/nativeFileDialogs/CMakeLists.txt | 4 ++- .../{nfd_cocoa.mm => nfd_cocoa.m} | 28 +++++++++---------- 2 files changed, 17 insertions(+), 15 deletions(-) rename Engine/lib/nativeFileDialogs/{nfd_cocoa.mm => nfd_cocoa.m} (90%) diff --git a/Engine/lib/nativeFileDialogs/CMakeLists.txt b/Engine/lib/nativeFileDialogs/CMakeLists.txt index f40f44dde..606be00fc 100644 --- a/Engine/lib/nativeFileDialogs/CMakeLists.txt +++ b/Engine/lib/nativeFileDialogs/CMakeLists.txt @@ -5,7 +5,9 @@ set(TORQUE_NFD_COMMON_SOURCES "${TORQUE_NFD_ROOT}/nfd_common.c") set(TORQUE_NFD_INCLUDE_DIRECTORIES "${TORQUE_NFD_ROOT}" "${TORQUE_NFD_ROOT}/include") if (APPLE) - set(TORQUE_NFD_SOURCES ${TORQUE_NFD_COMMON_SOURCES} "${TORQUE_NFD_ROOT}/nfd_cocoa.mm") + enable_language(OBJC) + set(TORQUE_NFD_SOURCES ${TORQUE_NFD_COMMON_SOURCES} "${TORQUE_NFD_ROOT}/nfd_cocoa.m") + set_source_files_properties(${TORQUE_NFD_SOURCES} PROPERTIES LANGUAGE OBJC) elseif (UNIX) if (TORQUE_USE_ZENITY) set(TORQUE_NFD_SOURCES ${TORQUE_NFD_COMMON_SOURCES} "${TORQUE_NFD_ROOT}/nfd_zenity.c") diff --git a/Engine/lib/nativeFileDialogs/nfd_cocoa.mm b/Engine/lib/nativeFileDialogs/nfd_cocoa.m similarity index 90% rename from Engine/lib/nativeFileDialogs/nfd_cocoa.mm rename to Engine/lib/nativeFileDialogs/nfd_cocoa.m index 7874c3b11..1e51d4efb 100644 --- a/Engine/lib/nativeFileDialogs/nfd_cocoa.mm +++ b/Engine/lib/nativeFileDialogs/nfd_cocoa.m @@ -64,7 +64,7 @@ static void SetDefaultPath( NSSavePanel *dialog, const nfdchar_t *defaultPath ) NSString *defaultPathString = [NSString stringWithUTF8String: defaultPath]; NSURL *url = [NSURL fileURLWithPath:defaultPathString isDirectory:YES]; - [dialog setDirectoryURL:url]; + [dialog setDirectoryURL:url]; } @@ -75,8 +75,8 @@ static nfdresult_t AllocPathSet( NSArray *urls, nfdpathset_t *pathset ) assert([urls count]); pathset->count = (size_t)[urls count]; - pathset->indices = (unsigned long*)NFDi_Malloc( sizeof(size_t)*pathset->count ); - if ( !pathset->indices ) + pathset->indices = NFDi_Malloc( sizeof(size_t)*pathset->count ); + if ( !pathset->indices ) { return NFD_ERROR; } @@ -89,7 +89,7 @@ static nfdresult_t AllocPathSet( NSArray *urls, nfdpathset_t *pathset ) bufsize += [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; } - pathset->buf = (char *)NFDi_Malloc( sizeof(nfdchar_t) * bufsize ); + pathset->buf = NFDi_Malloc( sizeof(nfdchar_t) * bufsize ); if ( !pathset->buf ) { return NFD_ERROR; @@ -125,7 +125,7 @@ nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList, { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow]; + NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow]; NSOpenPanel *dialog = [NSOpenPanel openPanel]; [dialog setAllowsMultipleSelection:NO]; @@ -144,11 +144,11 @@ nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList, // byte count, not char count size_t len = strlen(utf8Path);//NFDi_UTF8_Strlen(utf8Path); - *outPath = (nfdchar_t*)NFDi_Malloc(len+1); + *outPath = NFDi_Malloc( len+1 ); if ( !*outPath ) { [pool release]; - [keyWindow makeKeyAndOrderFront:nil]; + [keyWindow makeKeyAndOrderFront:nil]; return NFD_ERROR; } memcpy( *outPath, utf8Path, len+1 ); /* copy null term */ @@ -185,14 +185,14 @@ nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, if ( [urls count] == 0 ) { [pool release]; - [keyWindow makeKeyAndOrderFront:nil]; + [keyWindow makeKeyAndOrderFront:nil]; return NFD_CANCEL; } if ( AllocPathSet( urls, outPaths ) == NFD_ERROR ) { [pool release]; - [keyWindow makeKeyAndOrderFront:nil]; + [keyWindow makeKeyAndOrderFront:nil]; return NFD_ERROR; } @@ -200,7 +200,7 @@ nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, } [pool release]; - [keyWindow makeKeyAndOrderFront:nil]; + [keyWindow makeKeyAndOrderFront:nil]; return nfdResult; } @@ -229,11 +229,11 @@ nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, size_t byteLen = [url.path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; - *outPath = (char *)NFDi_Malloc( byteLen ); + *outPath = NFDi_Malloc( byteLen ); if ( !*outPath ) { [pool release]; - [keyWindow makeKeyAndOrderFront:nil]; + [keyWindow makeKeyAndOrderFront:nil]; return NFD_ERROR; } memcpy( *outPath, utf8Path, byteLen ); @@ -269,11 +269,11 @@ nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath, // byte count, not char count size_t len = strlen(utf8Path);//NFDi_UTF8_Strlen(utf8Path); - *outPath = (char *)NFDi_Malloc( len+1 ); + *outPath = NFDi_Malloc( len+1 ); if ( !*outPath ) { [pool release]; - [keyWindow makeKeyAndOrderFront:nil]; + [keyWindow makeKeyAndOrderFront:nil]; return NFD_ERROR; } memcpy( *outPath, utf8Path, len+1 ); /* copy null term */ From e381bf483828bd5512404f6617d3b76053fef051 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Fri, 20 Oct 2023 00:19:40 +0100 Subject: [PATCH 010/122] Working archive -Archive now working if we override the shared libs rpath with the correct installation path --- CMakeLists.txt | 2 +- Engine/source/CMakeLists.txt | 30 +++++++--------------------- Tools/CMake/torqueMacOSconfigs.cmake | 26 ++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a79ed0fd..5de867942 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ set(TORQUE_LIB_TARG_DIRECTORY "${CMAKE_BINARY_DIR}/Engine/lib") set(TORQUE_SOURCE_DIRECTROY "${CMAKE_SOURCE_DIR}/Engine/source") # Ensure all possible configurations end up in the project directory -set(CMAKE_INSTALL_PREFIX "${TORQUE_APP_ROOT_DIRECTORY}") +set(CMAKE_INSTALL_PREFIX "${TORQUE_APP_ROOT_DIRECTORY}" CACHE STRING "" FORCE) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${TORQUE_APP_GAME_DIRECTORY}") foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) diff --git a/Engine/source/CMakeLists.txt b/Engine/source/CMakeLists.txt index 2f5564f02..fe93f6145 100644 --- a/Engine/source/CMakeLists.txt +++ b/Engine/source/CMakeLists.txt @@ -354,7 +354,9 @@ if (APPLE) add_executable(${TORQUE_APP_NAME} MACOSX_BUNDLE ${TORQUE_SOURCE_FILES}) set_target_properties(${TORQUE_APP_NAME} PROPERTIES BUNDLE true - MACOSX_BUNDLE_INFO_PLIST "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist") + MACOSX_BUNDLE_INFO_PLIST "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" + XCODE_ATTRIBUTE_INSTALL_PATH "/Applications" + XCODE_ATTRIBUTE_SKIP_INSTALL "No") # Ensure the shared libraries are actually referenced at the correct path set(CMAKE_XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks") @@ -428,7 +430,7 @@ append_defs() foreach (LIBRARY_BINARY ${TORQUE_ADDITIONAL_LIBRARY_BINARIES}) if (APPLE) # For OSX, we want these binaries to be copied to the Frameworks directory - add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}/${TORQUE_APP_NAME}.app/Contents/Frameworks") + #add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}/${TORQUE_APP_NAME}.app/Contents/Frameworks") else() # All other platforms expect the file next to the executable add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}") @@ -449,7 +451,9 @@ if (UNIX) # Only pay attention to shared libraries and make them output to the app resources if ("${LINK_LIBRARY_TYPE}" STREQUAL "SHARED_LIBRARY") if (APPLE) - set_target_properties(${GAME_LINK_LIBRARY} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${TORQUE_APP_GAME_DIRECTORY}/${TORQUE_APP_NAME}.app/Contents/Frameworks") + set_target_properties(${GAME_LINK_LIBRARY} PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${TORQUE_APP_GAME_DIRECTORY}/${TORQUE_APP_NAME}.app/Contents/Frameworks" + XCODE_ATTRIBUTE_INSTALL_PATH "/Applications/${TORQUE_APP_NAME}.app/Contents/Frameworks/$(CONFIGURATION)") else() set_target_properties(${GAME_LINK_LIBRARY} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${TORQUE_APP_GAME_DIRECTORY}") endif(APPLE) @@ -457,23 +461,3 @@ if (UNIX) endif() endforeach() endif (UNIX) - -if(APPLE) -install(TARGETS ${APP_NAME} - BUNDLE DESTINATION . COMPONENT Runtime - RUNTIME DESTINATION bin COMPONENT Runtime - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib/static) - -# Note Mac specific extension .app -set(APPS "\${TORQUE_APP_GAME_DIRECTORY}/${APP_NAME}.app") - -# Directories to look for dependencies -set(DIRS ${CMAKE_BINARY_DIR}) - -install(CODE "include(BundleUtilities) - fixup_bundle(\"${APPS}\" \"\" \"${DIRS}\")") - -set(CPACK_GENERATOR "DRAGNDROP") -include(CPack) -endif() diff --git a/Tools/CMake/torqueMacOSconfigs.cmake b/Tools/CMake/torqueMacOSconfigs.cmake index 37ba4d2de..e8aea05c5 100644 --- a/Tools/CMake/torqueMacOSconfigs.cmake +++ b/Tools/CMake/torqueMacOSconfigs.cmake @@ -96,7 +96,6 @@ set(CMAKE_OBJC_LINK_FLAGS "${C_TARGET_FLAGS} ${SDK_NAME_VERSION_FLAGS} -Wl,-sear set(CMAKE_OBJCXX_LINK_FLAGS "${C_TARGET_FLAGS} ${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_OBJCXX_LINK_FLAGS}") set(CMAKE_PLATFORM_HAS_INSTALLNAME 1) -set(CMAKE_SHARED_LINKER_FLAGS "-rpath @executable_path/Frameworks -rpath @loader_path/Frameworks") set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -Wl,-headerpad_max_install_names") set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -Wl,-headerpad_max_install_names") set(CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") @@ -116,4 +115,27 @@ else() set(CMAKE_C_SIZEOF_DATA_PTR 4) set(CMAKE_CXX_SIZEOF_DATA_PTR 4) set(CMAKE_SYSTEM_PROCESSOR "arm") -endif() \ No newline at end of file +endif() + +# Only create a single Xcode project file +set(CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY TRUE) +# Add all libraries to project link phase (lets Xcode handle linking) +set(CMAKE_XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION) + +# Enable codesigning with secure timestamp when not in Debug configuration (required for Notarization) +set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS[variant=Release] "--timestamp") +set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS[variant=RelWithDebInfo] "--timestamp") + +# Enable codesigning with hardened runtime option when not in Debug configuration (required for Notarization) +set(CMAKE_XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME[variant=Release] YES) +set(CMAKE_XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME[variant=RelWithDebInfo] YES) + +# Disable injection of Xcode's base entitlements used for debugging when not in Debug configuration (required for +# Notarization) +set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS[variant=Release] NO) +set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS[variant=RelWithDebInfo] NO) + +set(_release_configs RelWithDebInfo Release) + if(CMAKE_BUILD_TYPE IN_LIST _release_configs) + add_link_options(LINKER:-dead_strip) + endif() \ No newline at end of file From b6617b1b0f1251cc2e28ebb2a591e6e28cf97586 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Fri, 20 Oct 2023 12:04:56 +0100 Subject: [PATCH 011/122] Working multi-arch archiving -Working multi arch compile -Required assets now copy to the app bundle TODO: Make the logic to wrap around whether its multi arch -The changes in torqumacosconfigs.cmake are not required if we are only building one architecture, also if it is x86_64 we can drop min deployment target to 10.13 --- Engine/lib/Torque_postBuild.cmake | 2 +- Engine/source/CMakeLists.txt | 27 +++++++++++++++++++++------ Tools/CMake/torqueMacOSconfigs.cmake | 10 +++++++++- Tools/CMake/torque_macros.cmake | 2 +- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/Engine/lib/Torque_postBuild.cmake b/Engine/lib/Torque_postBuild.cmake index d5863dd61..15e448701 100644 --- a/Engine/lib/Torque_postBuild.cmake +++ b/Engine/lib/Torque_postBuild.cmake @@ -19,7 +19,7 @@ if (APPLE) addFramework("IOKit") #grrr damn you sdl! addFramework("Carbon") - set(TORQUE_LINK_LIBRARIES ${TORQUE_LINK_LIBRARIES} iconv) + set(TORQUE_LINK_FRAMEWORKS ${TORQUE_LINK_FRAMEWORKS} iconv) if(NOT TORQUE_DEDICATED) addFramework("OpenGL") addFramework("CoreVideo") diff --git a/Engine/source/CMakeLists.txt b/Engine/source/CMakeLists.txt index fe93f6145..c444a1477 100644 --- a/Engine/source/CMakeLists.txt +++ b/Engine/source/CMakeLists.txt @@ -309,8 +309,17 @@ endif (WIN32) # Prepare OSX Plist if (APPLE) - set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_MAC_SOURCES} "${CMAKE_SOURCE_DIR}/Tools/CMake/torque.icns") - set_source_files_properties("${CMAKE_SOURCE_DIR}/Tools/CMake/torque.icns" PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") + set(MACOSX_RESOURCES "${CMAKE_SOURCE_DIR}/Tools/CMake/torque.icns" + "${TORQUE_APP_GAME_DIRECTORY}/data" + "${TORQUE_APP_GAME_DIRECTORY}/core" + "${TORQUE_APP_GAME_DIRECTORY}/tools" + "${TORQUE_APP_GAME_DIRECTORY}/main.${TORQUE_SCRIPT_EXTENSION}") + + set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_MAC_SOURCES} ${MACOSX_RESOURCES}) + + source_group("Resources" FILES ${MACOSX_RESOURCES}) + + set_source_files_properties(${MACOSX_RESOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") set(EXECUTABLE_NAME "${TORQUE_APP_NAME}") configure_file("${CMAKE_SOURCE_DIR}/Tools/CMake/Info.plist.in" "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" COPYONLY) @@ -358,8 +367,6 @@ if (APPLE) XCODE_ATTRIBUTE_INSTALL_PATH "/Applications" XCODE_ATTRIBUTE_SKIP_INSTALL "No") - # Ensure the shared libraries are actually referenced at the correct path - set(CMAKE_XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks") elseif (WIN32) add_executable(${TORQUE_APP_NAME} WIN32 ${TORQUE_SOURCE_FILES}) @@ -406,8 +413,17 @@ if(MSVC) set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${TORQUE_APP_NAME}) endif() +set_property(GLOBAL PROPERTY USE_FOLDERS ON) +foreach (TORQUE_LIBRARY ${TORQUE_LINK_LIBRARIES}) + set_target_properties(${TORQUE_LIBRARY} PROPERTIES + FOLDER "Libraries") +endforeach() + target_compile_definitions(${TORQUE_APP_NAME} PUBLIC ${TORQUE_COMPILE_DEFINITIONS}) target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_LIBRARIES}) +if(APPLE) +target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_FRAMEWORKS}) +endif(APPLE) target_link_options(${TORQUE_APP_NAME} PUBLIC ${TORQUE_LINK_OPTIONS}) if (TORQUE_TARGET_PROPERTIES) set_target_properties(${TORQUE_APP_NAME} PROPERTIES ${TORQUE_TARGET_PROPERTIES}) @@ -430,7 +446,7 @@ append_defs() foreach (LIBRARY_BINARY ${TORQUE_ADDITIONAL_LIBRARY_BINARIES}) if (APPLE) # For OSX, we want these binaries to be copied to the Frameworks directory - #add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}/${TORQUE_APP_NAME}.app/Contents/Frameworks") + add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}/${TORQUE_APP_NAME}.app/Contents/Frameworks/$(CONFIGURATION)") else() # All other platforms expect the file next to the executable add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}") @@ -447,7 +463,6 @@ if (UNIX) # For eg. OSX some links are not valid targets - for example frameworks provided by OS if (TARGET ${GAME_LINK_LIBRARY}) get_target_property(LINK_LIBRARY_TYPE ${GAME_LINK_LIBRARY} TYPE) - # Only pay attention to shared libraries and make them output to the app resources if ("${LINK_LIBRARY_TYPE}" STREQUAL "SHARED_LIBRARY") if (APPLE) diff --git a/Tools/CMake/torqueMacOSconfigs.cmake b/Tools/CMake/torqueMacOSconfigs.cmake index e8aea05c5..06de88552 100644 --- a/Tools/CMake/torqueMacOSconfigs.cmake +++ b/Tools/CMake/torqueMacOSconfigs.cmake @@ -55,8 +55,10 @@ endforeach() set(CMAKE_FRAMEWORK_PATH "/Applications/XCode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks") set(CMAKE_FIND_FRAMEWORK FIRST) +# minimum for multi arch build is 11. +set(CMAKE_OSX_DEPLOYMENT_TARGET "11" CACHE STRING "" FORCE) set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "" FORCE) -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "" FORCE) +set(CMAKE_XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET[arch=arm64] "11.0" CACHE STRING "arm 64 minimum deployment target" FORCE) set(CMAKE_XCODE_ATTRIBUTE_SDKROOT macosx) set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "NO") set(CMAKE_SYSTEM_NAME Darwin) @@ -69,6 +71,11 @@ set(CMAKE_SHARED_LIBRARY_PREFIX "lib") set(CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") set(CMAKE_SHARED_MODULE_PREFIX "lib") set(CMAKE_SHARED_MODULE_SUFFIX ".so") + +set(CMAKE_C_COMPILER_TARGET x86_64-arm64-apple-macosx11) +set(CMAKE_CXX_COMPILER_TARGET x86_64-arm64-apple-macosx11) +set(CMAKE_ASM_COMPILER_TARGET x86_64-arm64-apple-macosx11) + set(CMAKE_C_COMPILER_ABI ELF) set(CMAKE_CXX_COMPILER_ABI ELF) set(CMAKE_C_HAS_ISYSROOT 1) @@ -121,6 +128,7 @@ endif() set(CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY TRUE) # Add all libraries to project link phase (lets Xcode handle linking) set(CMAKE_XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION) +set(CMAKE_XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@loader_path/../Frameworks") # Enable codesigning with secure timestamp when not in Debug configuration (required for Notarization) set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS[variant=Release] "--timestamp") diff --git a/Tools/CMake/torque_macros.cmake b/Tools/CMake/torque_macros.cmake index 7f9fdf8bc..e98cb1f6c 100644 --- a/Tools/CMake/torque_macros.cmake +++ b/Tools/CMake/torque_macros.cmake @@ -128,6 +128,6 @@ endmacro (filterOut) ################# apple frameworks ################### macro(addFramework framework) if (APPLE) - set(TORQUE_LINK_LIBRARIES ${TORQUE_LINK_LIBRARIES} "${CMAKE_FRAMEWORK_PATH}/${framework}.framework") + set(TORQUE_LINK_FRAMEWORKS ${TORQUE_LINK_FRAMEWORKS} "${CMAKE_FRAMEWORK_PATH}/${framework}.framework") endif() endmacro() \ No newline at end of file From f10520e751a587b7e1683eb66399a10e6d4c02ef Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Fri, 20 Oct 2023 21:06:53 +0100 Subject: [PATCH 012/122] Macosx build with rpath Archiving builds no longer have linking errors when run --- Engine/source/CMakeLists.txt | 6 +-- Tools/CMake/torqueMacOSconfigs.cmake | 62 ++++++++++++++++------------ 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/Engine/source/CMakeLists.txt b/Engine/source/CMakeLists.txt index c444a1477..faadd39ed 100644 --- a/Engine/source/CMakeLists.txt +++ b/Engine/source/CMakeLists.txt @@ -365,7 +365,8 @@ if (APPLE) BUNDLE true MACOSX_BUNDLE_INFO_PLIST "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" XCODE_ATTRIBUTE_INSTALL_PATH "/Applications" - XCODE_ATTRIBUTE_SKIP_INSTALL "No") + XCODE_ATTRIBUTE_SKIP_INSTALL "No" + MACOSX_RPATH TRUE) elseif (WIN32) add_executable(${TORQUE_APP_NAME} WIN32 ${TORQUE_SOURCE_FILES}) @@ -467,8 +468,7 @@ if (UNIX) if ("${LINK_LIBRARY_TYPE}" STREQUAL "SHARED_LIBRARY") if (APPLE) set_target_properties(${GAME_LINK_LIBRARY} PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${TORQUE_APP_GAME_DIRECTORY}/${TORQUE_APP_NAME}.app/Contents/Frameworks" - XCODE_ATTRIBUTE_INSTALL_PATH "/Applications/${TORQUE_APP_NAME}.app/Contents/Frameworks/$(CONFIGURATION)") + XCODE_ATTRIBUTE_INSTALL_PATH "@rpath") else() set_target_properties(${GAME_LINK_LIBRARY} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${TORQUE_APP_GAME_DIRECTORY}") endif(APPLE) diff --git a/Tools/CMake/torqueMacOSconfigs.cmake b/Tools/CMake/torqueMacOSconfigs.cmake index 06de88552..9c3f5020a 100644 --- a/Tools/CMake/torqueMacOSconfigs.cmake +++ b/Tools/CMake/torqueMacOSconfigs.cmake @@ -1,6 +1,7 @@ #detect Architecture enable_language(OBJC) enable_language(OBJCXX) +enable_language(CXX) find_program(XCODEBUILD_EXECUTABLE xcodebuild) execute_process(COMMAND ${XCODEBUILD_EXECUTABLE} -version -sdk macosx Path @@ -33,27 +34,13 @@ elseif(NOT DEFINED BUILD_LIBTOOL) OUTPUT_STRIP_TRAILING_WHITESPACE) endif() -# Find the toolchain's provided install_name_tool if none is found on the host -if(DEFINED CMAKE_INSTALL_NAME_TOOL) - # Environment variables are always preserved. - set(ENV{_CMAKE_INSTALL_NAME_TOOL} "${CMAKE_INSTALL_NAME_TOOL}") -elseif(DEFINED ENV{_CMAKE_INSTALL_NAME_TOOL}) - set(CMAKE_INSTALL_NAME_TOOL "$ENV{_CMAKE_INSTALL_NAME_TOOL}") -elseif(NOT DEFINED CMAKE_INSTALL_NAME_TOOL) - execute_process(COMMAND xcrun -sdk ${XCODE_SDK_ROOT_DIR} -find install_name_tool - OUTPUT_VARIABLE CMAKE_INSTALL_NAME_TOOL_INT - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - set(CMAKE_INSTALL_NAME_TOOL ${CMAKE_INSTALL_NAME_TOOL_INT} CACHE INTERNAL "") -endif() - get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) foreach(lang ${languages}) set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "${BUILD_LIBTOOL} -static -o " CACHE INTERNAL "") endforeach() set(CMAKE_FRAMEWORK_PATH "/Applications/XCode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks") - +set(CMAKE_SHARED_LINKER_FLAGS "-rpath @executable_path/../Frameworks -rpath @loader_path/../Frameworks") set(CMAKE_FIND_FRAMEWORK FIRST) # minimum for multi arch build is 11. set(CMAKE_OSX_DEPLOYMENT_TARGET "11" CACHE STRING "" FORCE) @@ -62,10 +49,9 @@ set(CMAKE_XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET[arch=arm64] "11.0" CACHE STRI set(CMAKE_XCODE_ATTRIBUTE_SDKROOT macosx) set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "NO") set(CMAKE_SYSTEM_NAME Darwin) -set(CMAKE_THREAD_LIBS_INIT "-lpthread") -set(CMAKE_HAVE_THREADS_LIBRARY 1) -set(CMAKE_USE_WIN32_THREADS_INIT 0) -set(CMAKE_USE_PTHREADS_INIT 1) + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO") set(CMAKE_SHARED_LIBRARY_PREFIX "lib") set(CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") @@ -108,7 +94,6 @@ set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -Wl,-headerpad_max_install_names set(CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") set(CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") set(CMAKE_FIND_LIBRARY_SUFFIXES ".tbd" ".dylib" ".so" ".a") -set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-install_name") if(CMAKE_OSX_ARCHITECTURES MATCHES "((^|;|, )(arm64|arm64e|x86_64))+") set(CMAKE_C_SIZEOF_DATA_PTR 8) @@ -124,19 +109,32 @@ else() set(CMAKE_SYSTEM_PROCESSOR "arm") endif() +if(DEFINED CMAKE_INSTALL_NAME_TOOL) + # Environment variables are always preserved. + set(ENV{_CMAKE_INSTALL_NAME_TOOL} "${CMAKE_INSTALL_NAME_TOOL}") +elseif(DEFINED ENV{_CMAKE_INSTALL_NAME_TOOL}) + set(CMAKE_INSTALL_NAME_TOOL "$ENV{_CMAKE_INSTALL_NAME_TOOL}") +elseif(NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + execute_process(COMMAND xcrun -sdk ${XCODE_SDK_ROOT_DIR} -find install_name_tool + OUTPUT_VARIABLE CMAKE_INSTALL_NAME_TOOL_INT + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(CMAKE_INSTALL_NAME_TOOL ${CMAKE_INSTALL_NAME_TOOL_INT} CACHE INTERNAL "") +endif() + # Only create a single Xcode project file set(CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY TRUE) # Add all libraries to project link phase (lets Xcode handle linking) set(CMAKE_XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION) -set(CMAKE_XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@loader_path/../Frameworks") +set(CMAKE_XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks") # Enable codesigning with secure timestamp when not in Debug configuration (required for Notarization) set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS[variant=Release] "--timestamp") set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS[variant=RelWithDebInfo] "--timestamp") # Enable codesigning with hardened runtime option when not in Debug configuration (required for Notarization) -set(CMAKE_XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME[variant=Release] YES) -set(CMAKE_XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME[variant=RelWithDebInfo] YES) +#set(CMAKE_XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME[variant=Release] YES) +#set(CMAKE_XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME[variant=RelWithDebInfo] YES) # Disable injection of Xcode's base entitlements used for debugging when not in Debug configuration (required for # Notarization) @@ -144,6 +142,18 @@ set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS[variant=Release] NO set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS[variant=RelWithDebInfo] NO) set(_release_configs RelWithDebInfo Release) - if(CMAKE_BUILD_TYPE IN_LIST _release_configs) - add_link_options(LINKER:-dead_strip) - endif() \ No newline at end of file +if(CMAKE_BUILD_TYPE IN_LIST _release_configs) + add_link_options(LINKER:-dead_strip) +endif() + +if(APPLE) + set(CMAKE_THREAD_LIBS_INIT "-lpthread") + set(CMAKE_HAVE_THREADS_LIBRARY 1) + set(CMAKE_USE_WIN32_THREADS_INIT 0) + set(CMAKE_USE_PTHREADS_INIT 1) + set(THREADS_PREFER_PTHREAD_FLAG ON) +endif() + +set(CMAKE_MACOSX_RPATH 1) +set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") + From 9bc06f088763069d756d138391aac84037b4fe1f Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Fri, 20 Oct 2023 22:23:38 +0100 Subject: [PATCH 013/122] main dir SDL_BaseDir was returning the directory that contains the app, now it returns the resources directory --- Engine/source/platformMac/macFileIO.mm | 2 +- Tools/CMake/Info.plist.in | 2 +- Tools/CMake/torqueMacOSconfigs.cmake | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Engine/source/platformMac/macFileIO.mm b/Engine/source/platformMac/macFileIO.mm index dae844163..d0eb99c10 100644 --- a/Engine/source/platformMac/macFileIO.mm +++ b/Engine/source/platformMac/macFileIO.mm @@ -646,7 +646,7 @@ StringTableEntry Platform::getExecutablePath() return cwd; } - NSString* string = [[NSBundle mainBundle] pathForResource:@"main" ofType:@"cs"]; + NSString* string = [[NSBundle mainBundle] pathForResource:@"main" ofType:@"tscript"]; if(!string) string = [[NSBundle mainBundle] bundlePath]; diff --git a/Tools/CMake/Info.plist.in b/Tools/CMake/Info.plist.in index 8c39d8f4f..0c6657131 100644 --- a/Tools/CMake/Info.plist.in +++ b/Tools/CMake/Info.plist.in @@ -17,6 +17,6 @@ CFBundleVersion 1.0 SDL_FILESYSTEM_BASE_DIR_TYPE - parent + resource diff --git a/Tools/CMake/torqueMacOSconfigs.cmake b/Tools/CMake/torqueMacOSconfigs.cmake index 9c3f5020a..03fbbc198 100644 --- a/Tools/CMake/torqueMacOSconfigs.cmake +++ b/Tools/CMake/torqueMacOSconfigs.cmake @@ -157,3 +157,8 @@ endif() set(CMAKE_MACOSX_RPATH 1) set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") +# Debug configuration for quicker debug builds +set(CMAKE_XCODE_ATTRIBUTE_LINKER_DISPLAYS_MANGLED_NAMES[variant=Debug] YES) +set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH[variant=Debug] YES) +set(CMAKE_XCODE_ATTRIBUTE_ENABLE_TESTABILITY[variant=Debug] YES) + From 8b7e318fd574b4971b91c2c1dc7aa81d5dbcf9f4 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Fri, 20 Oct 2023 22:33:20 +0100 Subject: [PATCH 014/122] multiplatform fixes -Wrap macosconfig in if(apple) --- Engine/lib/Torque_postBuild.cmake | 8 ++++---- Engine/source/CMakeLists.txt | 12 +++++++++++- Tools/CMake/torqueMacOSconfigs.cmake | 14 +++++++------- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/Engine/lib/Torque_postBuild.cmake b/Engine/lib/Torque_postBuild.cmake index 15e448701..d8430e112 100644 --- a/Engine/lib/Torque_postBuild.cmake +++ b/Engine/lib/Torque_postBuild.cmake @@ -2,9 +2,9 @@ # When on Windows, we need to link against winsock and windows codecs if (WIN32) - set(TORQUE_LINK_LIBRARIES ${TORQUE_LINK_LIBRARIES} WS2_32.LIB windowscodecs.lib) + set(TORQUE_LINK_WINDOWS ${TORQUE_LINK_WINDOWS} WS2_32.LIB windowscodecs.lib) if (TORQUE_D3D11) - set(TORQUE_LINK_LIBRARIES ${TORQUE_LINK_LIBRARIES} dxguid.lib) + set(TORQUE_LINK_WINDOWS ${TORQUE_LINK_WINDOWS} dxguid.lib) endif (TORQUE_D3D11) endif (WIN32) @@ -36,8 +36,8 @@ set(TORQUE_LINK_LIBRARIES ${TORQUE_LINK_LIBRARIES} nativeFileDialogs) # Linux requires X11 & freetype if (UNIX AND NOT APPLE) - set(TORQUE_LINK_LIBRARIES ${TORQUE_LINK_LIBRARIES} "X11" "Xft" "dl" "pthread") + set(TORQUE_LINK_LINUX ${TORQUE_LINK_LINUX} "X11" "Xft" "dl" "pthread") find_package(Freetype REQUIRED) set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} ${FREETYPE_INCLUDE_DIRS}) - set(TORQUE_LINK_LIBRARIES ${TORQUE_LINK_LIBRARIES} ${FREETYPE_LIBRARIES}) + set(TORQUE_LINK_LINUX ${TORQUE_LINK_LINUX} ${FREETYPE_LIBRARIES}) endif (UNIX AND NOT APPLE) \ No newline at end of file diff --git a/Engine/source/CMakeLists.txt b/Engine/source/CMakeLists.txt index faadd39ed..aad4df349 100644 --- a/Engine/source/CMakeLists.txt +++ b/Engine/source/CMakeLists.txt @@ -422,9 +422,19 @@ endforeach() target_compile_definitions(${TORQUE_APP_NAME} PUBLIC ${TORQUE_COMPILE_DEFINITIONS}) target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_LIBRARIES}) + if(APPLE) -target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_FRAMEWORKS}) + target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_FRAMEWORKS}) endif(APPLE) + +if(WIN32) + target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_WINDOWS}) +endif(WIN32) + +if(UNIX AND NOT APPLE) + target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_LINUX}) +endif(UNIX AND NOT APPLE) + target_link_options(${TORQUE_APP_NAME} PUBLIC ${TORQUE_LINK_OPTIONS}) if (TORQUE_TARGET_PROPERTIES) set_target_properties(${TORQUE_APP_NAME} PROPERTIES ${TORQUE_TARGET_PROPERTIES}) diff --git a/Tools/CMake/torqueMacOSconfigs.cmake b/Tools/CMake/torqueMacOSconfigs.cmake index 03fbbc198..0e583d0b4 100644 --- a/Tools/CMake/torqueMacOSconfigs.cmake +++ b/Tools/CMake/torqueMacOSconfigs.cmake @@ -1,3 +1,4 @@ +if(APPLE) #detect Architecture enable_language(OBJC) enable_language(OBJCXX) @@ -146,13 +147,11 @@ if(CMAKE_BUILD_TYPE IN_LIST _release_configs) add_link_options(LINKER:-dead_strip) endif() -if(APPLE) - set(CMAKE_THREAD_LIBS_INIT "-lpthread") - set(CMAKE_HAVE_THREADS_LIBRARY 1) - set(CMAKE_USE_WIN32_THREADS_INIT 0) - set(CMAKE_USE_PTHREADS_INIT 1) - set(THREADS_PREFER_PTHREAD_FLAG ON) -endif() +set(CMAKE_THREAD_LIBS_INIT "-lpthread") +set(CMAKE_HAVE_THREADS_LIBRARY 1) +set(CMAKE_USE_WIN32_THREADS_INIT 0) +set(CMAKE_USE_PTHREADS_INIT 1) +set(THREADS_PREFER_PTHREAD_FLAG ON) set(CMAKE_MACOSX_RPATH 1) set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") @@ -162,3 +161,4 @@ set(CMAKE_XCODE_ATTRIBUTE_LINKER_DISPLAYS_MANGLED_NAMES[variant=Debug] YES) set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH[variant=Debug] YES) set(CMAKE_XCODE_ATTRIBUTE_ENABLE_TESTABILITY[variant=Debug] YES) +endif(APPLE) \ No newline at end of file From 233c6a80457bd3aaba3083e0f957444a973533e6 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Sat, 21 Oct 2023 01:50:31 +0100 Subject: [PATCH 015/122] example cmake workflow --- CMakeLists.txt | 15 +- Engine/source/CMakeLists.txt | 471 +----------------- Engine/source/torqueEveryEngine.cmake | 465 +++++++++++++++++ Engine/source/torqueMacosEngine.cmake | 283 +++++++++++ .../torqueMacosxconfig.cmake} | 3 +- 5 files changed, 763 insertions(+), 474 deletions(-) create mode 100644 Engine/source/torqueEveryEngine.cmake create mode 100644 Engine/source/torqueMacosEngine.cmake rename Tools/CMake/{torqueMacOSconfigs.cmake => platformconfigs/torqueMacosxconfig.cmake} (99%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5de867942..c5bec3818 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,10 +4,10 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;RelWithDebInfo;Release" CACHE STRING "" FOR include("${CMAKE_SOURCE_DIR}/Tools/CMake/torque_macros.cmake") include("${CMAKE_SOURCE_DIR}/Tools/CMake/torque_configs.cmake") -file(GLOB OS_SCRIPTS "${CMAKE_SOURCE_DIR}/Tools/CMake/torque*configs.cmake") -foreach (TORQUE_OS_SCRIPT ${OS_SCRIPTS}) - include(${TORQUE_OS_SCRIPT}) -endforeach() +#file(GLOB OS_SCRIPTS "${CMAKE_SOURCE_DIR}/Tools/CMake/torque*configs.cmake") +#foreach (TORQUE_OS_SCRIPT ${OS_SCRIPTS}) +# include(${TORQUE_OS_SCRIPT}) +#endforeach() # Ensure multi-core compilation is enabled for everything add_compile_options($<$:/MP>) @@ -28,6 +28,7 @@ set(TORQUE_APP_GAME_DIRECTORY "${TORQUE_APP_ROOT_DIRECTORY}/game") set(TORQUE_LIB_ROOT_DIRECTORY "${CMAKE_SOURCE_DIR}/Engine/lib") set(TORQUE_LIB_TARG_DIRECTORY "${CMAKE_BINARY_DIR}/Engine/lib") set(TORQUE_SOURCE_DIRECTROY "${CMAKE_SOURCE_DIR}/Engine/source") +set(TORQUE_ENGINE_DIRECTORY "${CMAKE_SOURCE_DIR}/Engine") # Ensure all possible configurations end up in the project directory set(CMAKE_INSTALL_PREFIX "${TORQUE_APP_ROOT_DIRECTORY}" CACHE STRING "" FORCE) @@ -74,4 +75,8 @@ endif(NOT TORQUE_INSTALLED_TEMPLATE) # Generate torqueConfig.h in our temp directory configure_file("${CMAKE_SOURCE_DIR}/Tools/CMake/torqueConfig.h.in" "${TORQUE_APP_ROOT_DIRECTORY}/source/torqueConfig.h") -add_subdirectory(Engine) +if(APPLE) + include("${CMAKE_SOURCE_DIR}/Tools/CMake/platformconfigs/torqueMacosxconfig.cmake") +else(APPLE) + add_subdirectory(Engine) +endif(APPLE) \ No newline at end of file diff --git a/Engine/source/CMakeLists.txt b/Engine/source/CMakeLists.txt index aad4df349..ee80caa46 100644 --- a/Engine/source/CMakeLists.txt +++ b/Engine/source/CMakeLists.txt @@ -18,471 +18,8 @@ set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} TORQUE_TESTS_ENABLE set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} "_VARIADIC_MAX=10") endif() -# On Windows we disable CRT Security warnings - this comes from recommendations to use non-portable functions. -if (WIN32) - set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} _CRT_SECURE_NO_WARNINGS WIN32) -endif (WIN32) - -if (APPLE) - set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} __MACOSX__) -elseif (UNIX) - set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} __linux__) -endif (APPLE) - -################# Set Engine Linkages ################### - -# When on Windows, we need to link against winsock and windows codecs -if (WIN32) - set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_WIN_SOURCES}) -endif (WIN32) - -# Linux requires X11 & freetype -if (UNIX AND NOT APPLE) - set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_X11_SOURCES}) - find_package(Freetype REQUIRED) - set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} ${FREETYPE_INCLUDE_DIRS}) -endif (UNIX AND NOT APPLE) - -################# Collect Source Files ################### - -# Handle app -torqueAddSourceDirectories("app" "app/net") - -# Handle console -torqueAddSourceDirectories("console") -torqueAddSourceDirectories("console/torquescript") - -# Handle Platform -torqueAddSourceDirectories("platform" "platform/threads" "platform/async" - "platform/input" "platform/output") - -torqueAddSourceDirectories("platform/nativeDialogs") -# Handle T3D -torqueAddSourceDirectories("T3D/fps" "T3D/fx" "T3D/vehicles" "T3D/physics" - "T3D/decal" "T3D/sfx" "T3D/gameBase" "T3D/turret" - "T3D/lighting" "T3D/gameOBjects" "T3D/components" - "T3D/systems" "T3D/assets" "T3D" "T3D/gameBase/std") - -# Handle TS -torqueAddSourceDirectories("ts" "ts/collada" "ts/assimp" "ts/loader" "ts/arch") - -# Handle SFX - OpenAL is handled as a module later on -torqueAddSourceDirectories("sfx" "sfx/media" "sfx/null") -if(TORQUE_SFX_OPENAL AND NOT TORQUE_DEDICATED) - torqueAddSourceDirectories("sfx/openal") - if(WIN32) - torqueAddSourceDirectories("sfx/openal/win32") - elseif(UNIX AND NOT APPLE) - torqueAddSourceDirectories("sfx/openal/linux") - elseif(APPLE) - torqueAddSourceDirectories("sfx/openal/mac") - endif() -endif() -# Handle GFX -torqueAddSourceDirectories("gfx" "gfx/Null" "gfx/test" "gfx/bitmap" "gfx/bitmap/loaders" - "gfx/util" "gfx/video" "gfx/sim" ) - -if (TORQUE_OPENGL) - torqueAddSourceDirectories("gfx/gl" "gfx/gl/sdl" "gfx/gl/tGL") -endif (TORQUE_OPENGL) - -if (WIN32 AND TORQUE_D3D11) - torqueAddSourceDirectories("gfx/D3D11") -endif (WIN32 AND TORQUE_D3D11) - -# Handle core -torqueAddSourceDirectories("core" "core/stream" "core/strings" "core/util" - "core/util/journal" "core/util/zip" "core/util/compressors") -# Handle GUI -torqueAddSourceDirectories("gui" "gui/buttons" "gui/containers" "gui/controls" "gui/core" - "gui/game" "gui/shiny" "gui/utility" "gui/3d") - -# Handle postFX -torqueAddSourceDirectories("postFx") - -# Handle Windowmanager -torqueAddSourceDirectories("windowManager" "windowManager/torque" "windowManager/sdl") - -# Handle scene -torqueAddSourceDirectories("scene" "scene/culling" "scene/zones" "scene/mixin") - -# Handle math -torqueAddSourceDirectories("math" "math/util") - -# Handle persistence -torqueAddSourceDirectories("persistence/taml" "persistence/taml/binary" "persistence/taml/xml") - -# Handle Cinterface -torqueAddSourceDirectories("cinterface") - -# Handle util -torqueAddSourceDirectories("util" "util/messaging") - -# Handle assets -torqueAddSourceDirectories("assets") - -# Handle Sim -torqueAddSourceDirectories("sim") - -# Handle module -torqueAddSourceDirectories("module") - -# Handle forest -torqueAddSourceDirectories("forest" "forest/ts") -if(TORQUE_OPENGL) - torqueAddSourceDirectories("forest" "forest/glsl") -endif(TORQUE_OPENGL) -if(TORQUE_D3D11) - torqueAddSourceDirectories("forest" "forest/hlsl") -endif(TORQUE_D3D11) - -# Handle shadergen -torqueAddSourceDirectories("shaderGen") - -if (WIN32 AND TORQUE_D3D11) - torqueAddSourceDirectories("shaderGen/HLSL") -endif (WIN32 AND TORQUE_D3D11) - -if (TORQUE_OPENGL) - torqueAddSourceDirectories("shaderGen/GLSL") -endif (TORQUE_OPENGL) - -# Handle terrain -torqueAddSourceDirectories("terrain") - -if (WIN32 AND TORQUE_D3D11) - torqueAddSourceDirectories("terrain/hlsl") -endif (WIN32 AND TORQUE_D3D11) - -if (TORQUE_OPENGL) - torqueAddSourceDirectories("terrain/glsl") -endif (TORQUE_OPENGL) - -# Handle Materials -torqueAddSourceDirectories("materials") - -# Handle collision -torqueAddSourceDirectories("collision") - -# Handle lighting -torqueAddSourceDirectories("lighting" "lighting/common" - "lighting/shadowMap") - -if (TORQUE_ADVANCED_LIGHTING) - torqueAddSourceDirectories("lighting/advanced") - - if (WIN32 AND TORQUE_D3D11) - torqueAddSourceDirectories("lighting/advanced/hlsl") - endif (WIN32 AND TORQUE_D3D11) - - if (TORQUE_OPENGL) - torqueAddSourceDirectories("lighting/advanced/glsl") - endif (TORQUE_OPENGL) -endif (TORQUE_ADVANCED_LIGHTING) - -if (TORQUE_BASIC_LIGHTING) - torqueAddSourceDirectories("lighting/basic" "lighting/basic/shadowMap") -endif (TORQUE_BASIC_LIGHTING) - -# Handle environment -torqueAddSourceDirectories("environment") - -# Handle renderInstance -torqueAddSourceDirectories("renderInstance") - -# Handle i18n -torqueAddSourceDirectories("i18n") - -# Begin handling platform specific stuff -# Handle Platform POSIX -if (UNIX) - torqueAddSourceDirectories("platformPOSIX") - - if(NOT APPLE) - if (TORQUE_CPU_X32 OR TORQUE_CPU_X64) - torqueAddSourceDirectories("platformX86UNIX") - endif (TORQUE_CPU_X32 OR TORQUE_CPU_X64) - endif(NOT APPLE) -endif (UNIX) - -# Handle platformMac -if (APPLE) - torqueAddSourceDirectories("platformMac") -endif (APPLE) - -# Handle platformWin32 -if (WIN32) - torqueAddSourceDirectories("platformWin32" "platformWin32/videoInfo") -endif (WIN32) - -# Handle platformSDL -torqueAddSourceDirectories("platformSDL" "platformSDL/threads") - -# Handle platformX11 -if (UNIX AND NOT APPLE) - torqueAddSourceDirectories("platformX11") -endif (UNIX AND NOT APPLE) - -if(TORQUE_TESTING) - torqueAddSourceDirectories("testing") - set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} TORQUE_SHARED SDL_MAIN_HANDLED) -endif(TORQUE_TESTING) - -# Add the collected files to our engine group -source_group(TREE "${CMAKE_SOURCE_DIR}/Engine/source" PREFIX "Engine" FILES ${TORQUE_SOURCE_FILES}) -file(GLOB_RECURSE TORQUE_APP_GAME_SOURCES "${TORQUE_APP_ROOT_DIRECTORY}/source/*.cpp" "${TORQUE_APP_ROOT_DIRECTORY}/source/*.h") -source_group(TREE "${TORQUE_APP_ROOT_DIRECTORY}/source" PREFIX "Source Files" FILES ${TORQUE_APP_GAME_SOURCES}) -set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} "${TORQUE_APP_ROOT_DIRECTORY}/source") - -################# Engine Module Handling ################### - -set(TORQUE_MODULE_PATHS "${CMAKE_SOURCE_DIR}/Tools/CMake/modules" "${TORQUE_APP_GAME_DIRECTORY}/data") -if (NOT "${TORQUE_MODULE_USER_PATH}" STREQUAL "") - list(APPEND TORQUE_MODULE_PATHS "${TORQUE_MODULE_USER_PATH}") -endif() - -# Before doing module scanning, store away the engine sources - we do this so that modules -# can be placed into the proper filters -set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES} ${TORQUE_APP_GAME_SOURCES}) -set(TORQUE_SOURCE_FILES "") - -foreach (TORQUE_MODULE_PATH ${TORQUE_MODULE_PATHS}) - # First find simple cmake scripts, mostly used for in-engine modules - file(GLOB MODULE_SCRIPTS "${TORQUE_MODULE_PATH}/*.cmake") - foreach (TORQUE_MODULE_SCRIPT ${MODULE_SCRIPTS}) - include(${TORQUE_MODULE_SCRIPT}) - - # Add this script's collected files to our Engine group - source_group(TREE "${CMAKE_SOURCE_DIR}/Engine" PREFIX "Engine" FILES ${TORQUE_SOURCE_FILES}) - - set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${TORQUE_SOURCE_FILES}) - set(TORQUE_SOURCE_FILES "") - endforeach() - - # Next find sub projects, these can introduce new source files - SUBDIRLIST(POSSIBLE_PROJECTS "${TORQUE_MODULE_PATH}") - foreach (POSSIBLE_PROJECT ${POSSIBLE_PROJECTS}) - # Retrieve the absolute path of this possible project - get_filename_component(POSSIBLE_PROJECT_ABSOLUTEPATH "${POSSIBLE_PROJECT}" - REALPATH BASE_DIR "${TORQUE_MODULE_PATH}") - - if (EXISTS "${POSSIBLE_PROJECT_ABSOLUTEPATH}/CMakeLists.txt") - add_subdirectory("${POSSIBLE_PROJECT_ABSOLUTEPATH}" ${CMAKE_BINARY_DIR}/temp/${POSSIBLE_PROJECT} EXCLUDE_FROM_ALL) - source_group(TREE "${POSSIBLE_PROJECT_ABSOLUTEPATH}" PREFIX "Modules/${POSSIBLE_PROJECT}" FILES ${TORQUE_SOURCE_FILES}) - - set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${TORQUE_SOURCE_FILES}) - set(TORQUE_SOURCE_FILES "") - elseif(NOT EXISTS "${POSSIBLE_PROJECT_ABSOLUTEPATH}/*.cmake") - file(GLOB_RECURSE MODULE_SOURCE "${POSSIBLE_PROJECT_ABSOLUTEPATH}/source/*.h" "${POSSIBLE_PROJECT_ABSOLUTEPATH}/source/*.cpp") - #message(STATUS "MODULE_SOURCE:${MODULE_SOURCE}") - source_group(TREE "${POSSIBLE_PROJECT_ABSOLUTEPATH}" PREFIX "Modules/${POSSIBLE_PROJECT}/" FILES ${MODULE_SOURCE}) - set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${MODULE_SOURCE}) - endif() - endforeach() -endforeach() - -set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES_TEMPORARY}) - -################# Library Post-build Handling ################### -set(TORQUE_LIBRARY_PATHS "${CMAKE_SOURCE_DIR}/Engine/lib" "${TORQUE_APP_GAME_DIRECTORY}/data") -if (NOT "${TORQUE_LIBRARY_USER_PATH}" STREQUAL "") - list(APPEND TORQUE_LIBRARY_PATHS "${TORQUE_LIBRARY_USER_PATH}") -endif() - -foreach (TORQUE_LIBRARY_PATH ${TORQUE_LIBRARY_PATHS}) - # First find simple cmake scripts, mostly used for in-engine libraries - file(GLOB_RECURSE LIBRARY_SCRIPTS "${TORQUE_LIBRARY_PATH}/*Torque_postBuild.cmake") - #message(STATUS "LIBRARY_SCRIPTS:${LIBRARY_SCRIPTS}") - foreach (TORQUE_LIBRARY_SCRIPT ${LIBRARY_SCRIPTS}) - include(${TORQUE_LIBRARY_SCRIPT}) - endforeach() -endforeach() -################# Dynamic File Configuration ################### - -# Prepare Windows RC file -if (WIN32) - set(APPLICATION_ICON_PATH "${CMAKE_SOURCE_DIR}/Tools/CMake/torque.ico") - - configure_file("${CMAKE_SOURCE_DIR}/Tools/CMake/torque-win.rc.in" "${TORQUE_APP_ROOT_DIRECTORY}/source/torque.rc") - set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} "${TORQUE_APP_ROOT_DIRECTORY}/source/torque.rc") -endif (WIN32) - -# Prepare OSX Plist -if (APPLE) - set(MACOSX_RESOURCES "${CMAKE_SOURCE_DIR}/Tools/CMake/torque.icns" - "${TORQUE_APP_GAME_DIRECTORY}/data" - "${TORQUE_APP_GAME_DIRECTORY}/core" - "${TORQUE_APP_GAME_DIRECTORY}/tools" - "${TORQUE_APP_GAME_DIRECTORY}/main.${TORQUE_SCRIPT_EXTENSION}") - - set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_MAC_SOURCES} ${MACOSX_RESOURCES}) - - source_group("Resources" FILES ${MACOSX_RESOURCES}) - - set_source_files_properties(${MACOSX_RESOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") - - set(EXECUTABLE_NAME "${TORQUE_APP_NAME}") - configure_file("${CMAKE_SOURCE_DIR}/Tools/CMake/Info.plist.in" "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" COPYONLY) -endif (APPLE) - -addDef(TORQUE_DEBUG Debug) -addDef(TORQUE_RELEASE "RelWithDebInfo;Release") -addDef(TORQUE_ENABLE_ASSERTS "Debug;RelWithDebInfo") -addDef(TORQUE_DEBUG_GFX_MODE "RelWithDebInfo") -addDef(TORQUE_OGGVORBIS) - -if(NOT TORQUE_SDL) - filterOut("platform/nativeDialogs/fileDialog.cpp" ) -endif() -if(NOT TORQUE_OPENGL) - filterOut("platformSDL/sdlPlatformGL.cpp") -endif() -if (NOT TORQUE_NET_CURL) - filterOut("app/net/httpObject.h" "app/net/httpObject.cpp") -endif() - -################# Executable Generation ################### -if (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) - set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} TORQUE_SHARED) - - # Build the main engine library - add_library(TorqueEngine SHARED ${TORQUE_SOURCE_FILES}) - target_compile_definitions(TorqueEngine PUBLIC ${TORQUE_COMPILE_DEFINITIONS}) - target_link_libraries(TorqueEngine ${TORQUE_LINK_LIBRARIES}) - target_include_directories(TorqueEngine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_BINARY_DIR}/temp" ${TORQUE_INCLUDE_DIRECTORIES}) - - set(TORQUE_SOURCE_FILES "main/main.cpp") - set(TORQUE_LINK_LIBRARIES TorqueEngine) -else() - if(NOT TORQUE_TESTING) - set(TORQUE_SOURCE_FILES "main/main.cpp" ${TORQUE_SOURCE_FILES}) - endif() -endif (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) - -if (APPLE) - add_executable(${TORQUE_APP_NAME} MACOSX_BUNDLE ${TORQUE_SOURCE_FILES}) - set_target_properties(${TORQUE_APP_NAME} PROPERTIES - BUNDLE true - MACOSX_BUNDLE_INFO_PLIST "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" - XCODE_ATTRIBUTE_INSTALL_PATH "/Applications" - XCODE_ATTRIBUTE_SKIP_INSTALL "No" - MACOSX_RPATH TRUE) - -elseif (WIN32) - add_executable(${TORQUE_APP_NAME} WIN32 ${TORQUE_SOURCE_FILES}) - - set(TORQUE_CXX_FLAGS_COMMON_DEFAULT "-DUNICODE -D_UNICODE -D_CRT_SECURE_NO_WARNINGS /MP /Ob2 /Oi /Ot /Oy /GT /Zi /W4 /nologo /GF /EHsc /GS- /Gy- /Qpar- /fp:precise /fp:except- /GR /Zc:wchar_t-" ) - if( TORQUE_CPU_X32 ) - set(TORQUE_CXX_FLAGS_COMMON_DEFAULT "${TORQUE_CXX_FLAGS_COMMON_DEFAULT} /arch:SSE2") - endif() - set(TORQUE_CXX_FLAGS_COMMON ${TORQUE_CXX_FLAGS_COMMON_DEFAULT} CACHE STRING "") - mark_as_advanced(TORQUE_CXX_FLAGS_COMMON) - - set(TORQUE_CXX_FLAGS_EXECUTABLES "/wd4018 /wd4100 /wd4121 /wd4127 /wd4130 /wd4244 /wd4245 /wd4389 /wd4511 /wd4512 /wd4800 /wd4995" CACHE STRING "") - mark_as_advanced(TORQUE_CXX_FLAGS_EXECUTABLES) - - set(TORQUE_CXX_FLAGS "${TORQUE_CXX_FLAGS_COMMON_DEFAULT} ${TORQUE_CXX_FLAGS_EXECUTABLES}" CACHE STRING "") - mark_as_advanced(TORQUE_CXX_FLAGS) - - # NOTE: On Windows, /Zc:wchar_t- is necessary otherwise you get unicode errors - set_target_properties(${TORQUE_APP_NAME} PROPERTIES COMPILE_FLAGS "${TORQUE_CXX_FLAGS}") - if (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) - set_target_properties(TorqueEngine PROPERTIES COMPILE_FLAGS "${TORQUE_CXX_FLAGS_COMMON_DEFAULT}") - endif (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) -else() - add_executable(${TORQUE_APP_NAME} ${TORQUE_SOURCE_FILES}) - - # NOTE: On Linux, we set the rpath to ./ so that shared objects next to the executable are used - set_target_properties(${TORQUE_APP_NAME} PROPERTIES LINK_FLAGS "-Wl,-rpath,./") -endif() - - -if(MSVC) - # Match projectGenerator naming for executables - set(OUTPUT_CONFIG DEBUG MINSIZEREL RELWITHDEBINFO) - set(OUTPUT_SUFFIX DEBUG MINSIZE OPTIMIZEDDEBUG) - foreach(INDEX RANGE 2) - list(GET OUTPUT_CONFIG ${INDEX} CONF) - list(GET OUTPUT_SUFFIX ${INDEX} SUFFIX) - set_property(TARGET ${TORQUE_APP_NAME} PROPERTY OUTPUT_NAME_${CONF} ${TORQUE_APP_NAME}_${SUFFIX}) - if (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) - set_property(TARGET TorqueEngine PROPERTY ${CONF}_POSTFIX "_${SUFFIX}") - set_property(TARGET TorqueEngine PROPERTY ${CONF}_OUTPUT_NAME ${TORQUE_APP_NAME}) - endif (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) - endforeach() - # Set Visual Studio startup project - set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${TORQUE_APP_NAME}) -endif() - -set_property(GLOBAL PROPERTY USE_FOLDERS ON) -foreach (TORQUE_LIBRARY ${TORQUE_LINK_LIBRARIES}) - set_target_properties(${TORQUE_LIBRARY} PROPERTIES - FOLDER "Libraries") -endforeach() - -target_compile_definitions(${TORQUE_APP_NAME} PUBLIC ${TORQUE_COMPILE_DEFINITIONS}) -target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_LIBRARIES}) - if(APPLE) - target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_FRAMEWORKS}) -endif(APPLE) - -if(WIN32) - target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_WINDOWS}) -endif(WIN32) - -if(UNIX AND NOT APPLE) - target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_LINUX}) -endif(UNIX AND NOT APPLE) - -target_link_options(${TORQUE_APP_NAME} PUBLIC ${TORQUE_LINK_OPTIONS}) -if (TORQUE_TARGET_PROPERTIES) - set_target_properties(${TORQUE_APP_NAME} PROPERTIES ${TORQUE_TARGET_PROPERTIES}) -endif (TORQUE_TARGET_PROPERTIES) -target_include_directories(${TORQUE_APP_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_BINARY_DIR}/temp" ${TORQUE_INCLUDE_DIRECTORIES}) - -if(TORQUE_TESTING) - if(WIN32) - target_link_options(${TORQUE_APP_NAME} PRIVATE "/SUBSYSTEM:CONSOLE") - set_target_properties(gtest PROPERTIES COMPILE_FLAGS "/Zc:wchar_t-") - set_target_properties(gmock PROPERTIES COMPILE_FLAGS "/Zc:wchar_t-") - endif() -endif(TORQUE_TESTING) - -append_defs() - -# Process library binaries - these are coming from modules that are providing links to external, precompiled code that should be included -# with the executable. This is done because on Windows, the .lib is separate from the .dll so we can't automatically scan for shared -# objects in our link libraries in that case. -foreach (LIBRARY_BINARY ${TORQUE_ADDITIONAL_LIBRARY_BINARIES}) - if (APPLE) - # For OSX, we want these binaries to be copied to the Frameworks directory - add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}/${TORQUE_APP_NAME}.app/Contents/Frameworks/$(CONFIGURATION)") - else() - # All other platforms expect the file next to the executable - add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}") - endif (APPLE) -endforeach() - -# Process link libraries for dynamic links - we do this on OSX/Linux to ensure the binaries end up in the correct App directory -# as in the root CMake we force everything to be in game. This is necessary because on these platforms these are considered "libraries" -# and not runtime binaries like we configure in the root CMake. We don't globally set library outputs to avoid outputting eg. a files to -# our game directory. -if (UNIX) - get_target_property(GAME_LINK_LIBRARIES ${TORQUE_APP_NAME} LINK_LIBRARIES) - foreach (GAME_LINK_LIBRARY ${GAME_LINK_LIBRARIES}) - # For eg. OSX some links are not valid targets - for example frameworks provided by OS - if (TARGET ${GAME_LINK_LIBRARY}) - get_target_property(LINK_LIBRARY_TYPE ${GAME_LINK_LIBRARY} TYPE) - # Only pay attention to shared libraries and make them output to the app resources - if ("${LINK_LIBRARY_TYPE}" STREQUAL "SHARED_LIBRARY") - if (APPLE) - set_target_properties(${GAME_LINK_LIBRARY} PROPERTIES - XCODE_ATTRIBUTE_INSTALL_PATH "@rpath") - else() - set_target_properties(${GAME_LINK_LIBRARY} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${TORQUE_APP_GAME_DIRECTORY}") - endif(APPLE) - endif() - endif() - endforeach() -endif (UNIX) + include(torqueMacosEngine.cmake) +else() + include(torqueEveryEngine.cmake) +endif(APPLE) \ No newline at end of file diff --git a/Engine/source/torqueEveryEngine.cmake b/Engine/source/torqueEveryEngine.cmake new file mode 100644 index 000000000..03f95a8fd --- /dev/null +++ b/Engine/source/torqueEveryEngine.cmake @@ -0,0 +1,465 @@ +# On Windows we disable CRT Security warnings - this comes from recommendations to use non-portable functions. +if (WIN32) +set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} _CRT_SECURE_NO_WARNINGS WIN32) +endif (WIN32) + +set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} __linux__) +endif (APPLE) + +################# Set Engine Linkages ################### + +# When on Windows, we need to link against winsock and windows codecs +if (WIN32) +set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_WIN_SOURCES}) +endif (WIN32) + +# Linux requires X11 & freetype +if (UNIX AND NOT APPLE) +set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_X11_SOURCES}) +find_package(Freetype REQUIRED) +set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} ${FREETYPE_INCLUDE_DIRS}) +endif (UNIX AND NOT APPLE) + +################# Collect Source Files ################### + +# Handle app +torqueAddSourceDirectories("app" "app/net") + +# Handle console +torqueAddSourceDirectories("console") +torqueAddSourceDirectories("console/torquescript") + +# Handle Platform +torqueAddSourceDirectories("platform" "platform/threads" "platform/async" + "platform/input" "platform/output") + +torqueAddSourceDirectories("platform/nativeDialogs") +# Handle T3D +torqueAddSourceDirectories("T3D/fps" "T3D/fx" "T3D/vehicles" "T3D/physics" + "T3D/decal" "T3D/sfx" "T3D/gameBase" "T3D/turret" + "T3D/lighting" "T3D/gameOBjects" "T3D/components" + "T3D/systems" "T3D/assets" "T3D" "T3D/gameBase/std") + +# Handle TS +torqueAddSourceDirectories("ts" "ts/collada" "ts/assimp" "ts/loader" "ts/arch") + +# Handle SFX - OpenAL is handled as a module later on +torqueAddSourceDirectories("sfx" "sfx/media" "sfx/null") +if(TORQUE_SFX_OPENAL AND NOT TORQUE_DEDICATED) + torqueAddSourceDirectories("sfx/openal") + if(WIN32) + torqueAddSourceDirectories("sfx/openal/win32") + elseif(UNIX AND NOT APPLE) + torqueAddSourceDirectories("sfx/openal/linux") + elseif(APPLE) + torqueAddSourceDirectories("sfx/openal/mac") + endif() +endif() +# Handle GFX +torqueAddSourceDirectories("gfx" "gfx/Null" "gfx/test" "gfx/bitmap" "gfx/bitmap/loaders" + "gfx/util" "gfx/video" "gfx/sim" ) + +if (TORQUE_OPENGL) +torqueAddSourceDirectories("gfx/gl" "gfx/gl/sdl" "gfx/gl/tGL") +endif (TORQUE_OPENGL) + +if (WIN32 AND TORQUE_D3D11) +torqueAddSourceDirectories("gfx/D3D11") +endif (WIN32 AND TORQUE_D3D11) + +# Handle core +torqueAddSourceDirectories("core" "core/stream" "core/strings" "core/util" + "core/util/journal" "core/util/zip" "core/util/compressors") +# Handle GUI +torqueAddSourceDirectories("gui" "gui/buttons" "gui/containers" "gui/controls" "gui/core" + "gui/game" "gui/shiny" "gui/utility" "gui/3d") + +# Handle postFX +torqueAddSourceDirectories("postFx") + +# Handle Windowmanager +torqueAddSourceDirectories("windowManager" "windowManager/torque" "windowManager/sdl") + +# Handle scene +torqueAddSourceDirectories("scene" "scene/culling" "scene/zones" "scene/mixin") + +# Handle math +torqueAddSourceDirectories("math" "math/util") + +# Handle persistence +torqueAddSourceDirectories("persistence/taml" "persistence/taml/binary" "persistence/taml/xml") + +# Handle Cinterface +torqueAddSourceDirectories("cinterface") + +# Handle util +torqueAddSourceDirectories("util" "util/messaging") + +# Handle assets +torqueAddSourceDirectories("assets") + +# Handle Sim +torqueAddSourceDirectories("sim") + +# Handle module +torqueAddSourceDirectories("module") + +# Handle forest +torqueAddSourceDirectories("forest" "forest/ts") +if(TORQUE_OPENGL) +torqueAddSourceDirectories("forest" "forest/glsl") +endif(TORQUE_OPENGL) +if(TORQUE_D3D11) +torqueAddSourceDirectories("forest" "forest/hlsl") +endif(TORQUE_D3D11) + +# Handle shadergen +torqueAddSourceDirectories("shaderGen") + +if (WIN32 AND TORQUE_D3D11) +torqueAddSourceDirectories("shaderGen/HLSL") +endif (WIN32 AND TORQUE_D3D11) + +if (TORQUE_OPENGL) +torqueAddSourceDirectories("shaderGen/GLSL") +endif (TORQUE_OPENGL) + +# Handle terrain +torqueAddSourceDirectories("terrain") + +if (WIN32 AND TORQUE_D3D11) +torqueAddSourceDirectories("terrain/hlsl") +endif (WIN32 AND TORQUE_D3D11) + +if (TORQUE_OPENGL) +torqueAddSourceDirectories("terrain/glsl") +endif (TORQUE_OPENGL) + +# Handle Materials +torqueAddSourceDirectories("materials") + +# Handle collision +torqueAddSourceDirectories("collision") + +# Handle lighting +torqueAddSourceDirectories("lighting" "lighting/common" + "lighting/shadowMap") + +if (TORQUE_ADVANCED_LIGHTING) +torqueAddSourceDirectories("lighting/advanced") + +if (WIN32 AND TORQUE_D3D11) + torqueAddSourceDirectories("lighting/advanced/hlsl") +endif (WIN32 AND TORQUE_D3D11) + +if (TORQUE_OPENGL) + torqueAddSourceDirectories("lighting/advanced/glsl") +endif (TORQUE_OPENGL) +endif (TORQUE_ADVANCED_LIGHTING) + +if (TORQUE_BASIC_LIGHTING) +torqueAddSourceDirectories("lighting/basic" "lighting/basic/shadowMap") +endif (TORQUE_BASIC_LIGHTING) + +# Handle environment +torqueAddSourceDirectories("environment") + +# Handle renderInstance +torqueAddSourceDirectories("renderInstance") + +# Handle i18n +torqueAddSourceDirectories("i18n") + +# Begin handling platform specific stuff +# Handle Platform POSIX +if (UNIX) +torqueAddSourceDirectories("platformPOSIX") + +if(NOT APPLE) + if (TORQUE_CPU_X32 OR TORQUE_CPU_X64) + torqueAddSourceDirectories("platformX86UNIX") + endif (TORQUE_CPU_X32 OR TORQUE_CPU_X64) +endif(NOT APPLE) +endif (UNIX) + +# Handle platformMac +if (APPLE) +torqueAddSourceDirectories("platformMac") +endif (APPLE) + +# Handle platformWin32 +if (WIN32) +torqueAddSourceDirectories("platformWin32" "platformWin32/videoInfo") +endif (WIN32) + +# Handle platformSDL +torqueAddSourceDirectories("platformSDL" "platformSDL/threads") + +# Handle platformX11 +if (UNIX AND NOT APPLE) +torqueAddSourceDirectories("platformX11") +endif (UNIX AND NOT APPLE) + +if(TORQUE_TESTING) +torqueAddSourceDirectories("testing") +set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} TORQUE_SHARED SDL_MAIN_HANDLED) +endif(TORQUE_TESTING) + +# Add the collected files to our engine group +source_group(TREE "${CMAKE_SOURCE_DIR}/Engine/source" PREFIX "Engine" FILES ${TORQUE_SOURCE_FILES}) +file(GLOB_RECURSE TORQUE_APP_GAME_SOURCES "${TORQUE_APP_ROOT_DIRECTORY}/source/*.cpp" "${TORQUE_APP_ROOT_DIRECTORY}/source/*.h") +source_group(TREE "${TORQUE_APP_ROOT_DIRECTORY}/source" PREFIX "Source Files" FILES ${TORQUE_APP_GAME_SOURCES}) +set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} "${TORQUE_APP_ROOT_DIRECTORY}/source") + +################# Engine Module Handling ################### + +set(TORQUE_MODULE_PATHS "${CMAKE_SOURCE_DIR}/Tools/CMake/modules" "${TORQUE_APP_GAME_DIRECTORY}/data") +if (NOT "${TORQUE_MODULE_USER_PATH}" STREQUAL "") +list(APPEND TORQUE_MODULE_PATHS "${TORQUE_MODULE_USER_PATH}") +endif() + +# Before doing module scanning, store away the engine sources - we do this so that modules +# can be placed into the proper filters +set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES} ${TORQUE_APP_GAME_SOURCES}) +set(TORQUE_SOURCE_FILES "") + +foreach (TORQUE_MODULE_PATH ${TORQUE_MODULE_PATHS}) +# First find simple cmake scripts, mostly used for in-engine modules +file(GLOB MODULE_SCRIPTS "${TORQUE_MODULE_PATH}/*.cmake") +foreach (TORQUE_MODULE_SCRIPT ${MODULE_SCRIPTS}) + include(${TORQUE_MODULE_SCRIPT}) + + # Add this script's collected files to our Engine group + source_group(TREE "${CMAKE_SOURCE_DIR}/Engine" PREFIX "Engine" FILES ${TORQUE_SOURCE_FILES}) + + set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${TORQUE_SOURCE_FILES}) + set(TORQUE_SOURCE_FILES "") +endforeach() + +# Next find sub projects, these can introduce new source files +SUBDIRLIST(POSSIBLE_PROJECTS "${TORQUE_MODULE_PATH}") +foreach (POSSIBLE_PROJECT ${POSSIBLE_PROJECTS}) + # Retrieve the absolute path of this possible project + get_filename_component(POSSIBLE_PROJECT_ABSOLUTEPATH "${POSSIBLE_PROJECT}" + REALPATH BASE_DIR "${TORQUE_MODULE_PATH}") + + if (EXISTS "${POSSIBLE_PROJECT_ABSOLUTEPATH}/CMakeLists.txt") + add_subdirectory("${POSSIBLE_PROJECT_ABSOLUTEPATH}" ${CMAKE_BINARY_DIR}/temp/${POSSIBLE_PROJECT} EXCLUDE_FROM_ALL) + source_group(TREE "${POSSIBLE_PROJECT_ABSOLUTEPATH}" PREFIX "Modules/${POSSIBLE_PROJECT}" FILES ${TORQUE_SOURCE_FILES}) + + set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${TORQUE_SOURCE_FILES}) + set(TORQUE_SOURCE_FILES "") + elseif(NOT EXISTS "${POSSIBLE_PROJECT_ABSOLUTEPATH}/*.cmake") + file(GLOB_RECURSE MODULE_SOURCE "${POSSIBLE_PROJECT_ABSOLUTEPATH}/source/*.h" "${POSSIBLE_PROJECT_ABSOLUTEPATH}/source/*.cpp") + #message(STATUS "MODULE_SOURCE:${MODULE_SOURCE}") + source_group(TREE "${POSSIBLE_PROJECT_ABSOLUTEPATH}" PREFIX "Modules/${POSSIBLE_PROJECT}/" FILES ${MODULE_SOURCE}) + set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${MODULE_SOURCE}) + endif() +endforeach() +endforeach() + +set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES_TEMPORARY}) + +################# Library Post-build Handling ################### +set(TORQUE_LIBRARY_PATHS "${CMAKE_SOURCE_DIR}/Engine/lib" "${TORQUE_APP_GAME_DIRECTORY}/data") +if (NOT "${TORQUE_LIBRARY_USER_PATH}" STREQUAL "") +list(APPEND TORQUE_LIBRARY_PATHS "${TORQUE_LIBRARY_USER_PATH}") +endif() + +foreach (TORQUE_LIBRARY_PATH ${TORQUE_LIBRARY_PATHS}) +# First find simple cmake scripts, mostly used for in-engine libraries +file(GLOB_RECURSE LIBRARY_SCRIPTS "${TORQUE_LIBRARY_PATH}/*Torque_postBuild.cmake") + #message(STATUS "LIBRARY_SCRIPTS:${LIBRARY_SCRIPTS}") +foreach (TORQUE_LIBRARY_SCRIPT ${LIBRARY_SCRIPTS}) + include(${TORQUE_LIBRARY_SCRIPT}) +endforeach() +endforeach() +################# Dynamic File Configuration ################### + +# Prepare Windows RC file +if (WIN32) +set(APPLICATION_ICON_PATH "${CMAKE_SOURCE_DIR}/Tools/CMake/torque.ico") + +configure_file("${CMAKE_SOURCE_DIR}/Tools/CMake/torque-win.rc.in" "${TORQUE_APP_ROOT_DIRECTORY}/source/torque.rc") +set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} "${TORQUE_APP_ROOT_DIRECTORY}/source/torque.rc") +endif (WIN32) + +# Prepare OSX Plist +if (APPLE) +set(MACOSX_RESOURCES "${CMAKE_SOURCE_DIR}/Tools/CMake/torque.icns" +"${TORQUE_APP_GAME_DIRECTORY}/data" +"${TORQUE_APP_GAME_DIRECTORY}/core" +"${TORQUE_APP_GAME_DIRECTORY}/tools" +"${TORQUE_APP_GAME_DIRECTORY}/main.${TORQUE_SCRIPT_EXTENSION}") + +set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_MAC_SOURCES} ${MACOSX_RESOURCES}) + +source_group("Resources" FILES ${MACOSX_RESOURCES}) + +set_source_files_properties(${MACOSX_RESOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") + +set(EXECUTABLE_NAME "${TORQUE_APP_NAME}") +configure_file("${CMAKE_SOURCE_DIR}/Tools/CMake/Info.plist.in" "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" COPYONLY) +endif (APPLE) + +addDef(TORQUE_DEBUG Debug) +addDef(TORQUE_RELEASE "RelWithDebInfo;Release") +addDef(TORQUE_ENABLE_ASSERTS "Debug;RelWithDebInfo") +addDef(TORQUE_DEBUG_GFX_MODE "RelWithDebInfo") +addDef(TORQUE_OGGVORBIS) + +if(NOT TORQUE_SDL) +filterOut("platform/nativeDialogs/fileDialog.cpp" ) +endif() +if(NOT TORQUE_OPENGL) +filterOut("platformSDL/sdlPlatformGL.cpp") +endif() +if (NOT TORQUE_NET_CURL) +filterOut("app/net/httpObject.h" "app/net/httpObject.cpp") +endif() + +################# Executable Generation ################### +if (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) +set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} TORQUE_SHARED) + +# Build the main engine library +add_library(TorqueEngine SHARED ${TORQUE_SOURCE_FILES}) +target_compile_definitions(TorqueEngine PUBLIC ${TORQUE_COMPILE_DEFINITIONS}) +target_link_libraries(TorqueEngine ${TORQUE_LINK_LIBRARIES}) +target_include_directories(TorqueEngine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_BINARY_DIR}/temp" ${TORQUE_INCLUDE_DIRECTORIES}) + +set(TORQUE_SOURCE_FILES "main/main.cpp") +set(TORQUE_LINK_LIBRARIES TorqueEngine) +else() +if(NOT TORQUE_TESTING) + set(TORQUE_SOURCE_FILES "main/main.cpp" ${TORQUE_SOURCE_FILES}) +endif() +endif (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) + +if (APPLE) +add_executable(${TORQUE_APP_NAME} MACOSX_BUNDLE ${TORQUE_SOURCE_FILES}) +set_target_properties(${TORQUE_APP_NAME} PROPERTIES +BUNDLE true +MACOSX_BUNDLE_INFO_PLIST "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" +XCODE_ATTRIBUTE_INSTALL_PATH "/Applications" +XCODE_ATTRIBUTE_SKIP_INSTALL "No" +MACOSX_RPATH TRUE) + +elseif (WIN32) +add_executable(${TORQUE_APP_NAME} WIN32 ${TORQUE_SOURCE_FILES}) + + set(TORQUE_CXX_FLAGS_COMMON_DEFAULT "-DUNICODE -D_UNICODE -D_CRT_SECURE_NO_WARNINGS /MP /Ob2 /Oi /Ot /Oy /GT /Zi /W4 /nologo /GF /EHsc /GS- /Gy- /Qpar- /fp:precise /fp:except- /GR /Zc:wchar_t-" ) + if( TORQUE_CPU_X32 ) + set(TORQUE_CXX_FLAGS_COMMON_DEFAULT "${TORQUE_CXX_FLAGS_COMMON_DEFAULT} /arch:SSE2") + endif() + set(TORQUE_CXX_FLAGS_COMMON ${TORQUE_CXX_FLAGS_COMMON_DEFAULT} CACHE STRING "") + mark_as_advanced(TORQUE_CXX_FLAGS_COMMON) + + set(TORQUE_CXX_FLAGS_EXECUTABLES "/wd4018 /wd4100 /wd4121 /wd4127 /wd4130 /wd4244 /wd4245 /wd4389 /wd4511 /wd4512 /wd4800 /wd4995" CACHE STRING "") + mark_as_advanced(TORQUE_CXX_FLAGS_EXECUTABLES) + + set(TORQUE_CXX_FLAGS "${TORQUE_CXX_FLAGS_COMMON_DEFAULT} ${TORQUE_CXX_FLAGS_EXECUTABLES}" CACHE STRING "") + mark_as_advanced(TORQUE_CXX_FLAGS) + +# NOTE: On Windows, /Zc:wchar_t- is necessary otherwise you get unicode errors +set_target_properties(${TORQUE_APP_NAME} PROPERTIES COMPILE_FLAGS "${TORQUE_CXX_FLAGS}") +if (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) + set_target_properties(TorqueEngine PROPERTIES COMPILE_FLAGS "${TORQUE_CXX_FLAGS_COMMON_DEFAULT}") +endif (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) +else() +add_executable(${TORQUE_APP_NAME} ${TORQUE_SOURCE_FILES}) + +# NOTE: On Linux, we set the rpath to ./ so that shared objects next to the executable are used +set_target_properties(${TORQUE_APP_NAME} PROPERTIES LINK_FLAGS "-Wl,-rpath,./") +endif() + + +if(MSVC) + # Match projectGenerator naming for executables + set(OUTPUT_CONFIG DEBUG MINSIZEREL RELWITHDEBINFO) + set(OUTPUT_SUFFIX DEBUG MINSIZE OPTIMIZEDDEBUG) + foreach(INDEX RANGE 2) + list(GET OUTPUT_CONFIG ${INDEX} CONF) + list(GET OUTPUT_SUFFIX ${INDEX} SUFFIX) + set_property(TARGET ${TORQUE_APP_NAME} PROPERTY OUTPUT_NAME_${CONF} ${TORQUE_APP_NAME}_${SUFFIX}) + if (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) + set_property(TARGET TorqueEngine PROPERTY ${CONF}_POSTFIX "_${SUFFIX}") + set_property(TARGET TorqueEngine PROPERTY ${CONF}_OUTPUT_NAME ${TORQUE_APP_NAME}) + endif (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) + endforeach() + # Set Visual Studio startup project + set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${TORQUE_APP_NAME}) +endif() + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) +foreach (TORQUE_LIBRARY ${TORQUE_LINK_LIBRARIES}) +set_target_properties(${TORQUE_LIBRARY} PROPERTIES +FOLDER "Libraries") +endforeach() + +target_compile_definitions(${TORQUE_APP_NAME} PUBLIC ${TORQUE_COMPILE_DEFINITIONS}) +target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_LIBRARIES}) + +if(APPLE) +target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_FRAMEWORKS}) +endif(APPLE) + +if(WIN32) +target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_WINDOWS}) +endif(WIN32) + +if(UNIX AND NOT APPLE) +target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_LINUX}) +endif(UNIX AND NOT APPLE) + +target_link_options(${TORQUE_APP_NAME} PUBLIC ${TORQUE_LINK_OPTIONS}) +if (TORQUE_TARGET_PROPERTIES) +set_target_properties(${TORQUE_APP_NAME} PROPERTIES ${TORQUE_TARGET_PROPERTIES}) +endif (TORQUE_TARGET_PROPERTIES) +target_include_directories(${TORQUE_APP_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_BINARY_DIR}/temp" ${TORQUE_INCLUDE_DIRECTORIES}) + +if(TORQUE_TESTING) +if(WIN32) + target_link_options(${TORQUE_APP_NAME} PRIVATE "/SUBSYSTEM:CONSOLE") + set_target_properties(gtest PROPERTIES COMPILE_FLAGS "/Zc:wchar_t-") + set_target_properties(gmock PROPERTIES COMPILE_FLAGS "/Zc:wchar_t-") + endif() +endif(TORQUE_TESTING) + +append_defs() + +# Process library binaries - these are coming from modules that are providing links to external, precompiled code that should be included +# with the executable. This is done because on Windows, the .lib is separate from the .dll so we can't automatically scan for shared +# objects in our link libraries in that case. +foreach (LIBRARY_BINARY ${TORQUE_ADDITIONAL_LIBRARY_BINARIES}) +if (APPLE) + # For OSX, we want these binaries to be copied to the Frameworks directory + add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}/${TORQUE_APP_NAME}.app/Contents/Frameworks/$(CONFIGURATION)") +else() + # All other platforms expect the file next to the executable + add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}") +endif (APPLE) +endforeach() + +# Process link libraries for dynamic links - we do this on OSX/Linux to ensure the binaries end up in the correct App directory +# as in the root CMake we force everything to be in game. This is necessary because on these platforms these are considered "libraries" +# and not runtime binaries like we configure in the root CMake. We don't globally set library outputs to avoid outputting eg. a files to +# our game directory. +if (UNIX) +get_target_property(GAME_LINK_LIBRARIES ${TORQUE_APP_NAME} LINK_LIBRARIES) +foreach (GAME_LINK_LIBRARY ${GAME_LINK_LIBRARIES}) + # For eg. OSX some links are not valid targets - for example frameworks provided by OS + if (TARGET ${GAME_LINK_LIBRARY}) + get_target_property(LINK_LIBRARY_TYPE ${GAME_LINK_LIBRARY} TYPE) + # Only pay attention to shared libraries and make them output to the app resources + if ("${LINK_LIBRARY_TYPE}" STREQUAL "SHARED_LIBRARY") + if (APPLE) + set_target_properties(${GAME_LINK_LIBRARY} PROPERTIES + XCODE_ATTRIBUTE_INSTALL_PATH "@rpath") + else() + set_target_properties(${GAME_LINK_LIBRARY} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${TORQUE_APP_GAME_DIRECTORY}") + endif(APPLE) + endif() + endif() +endforeach() +endif (UNIX) \ No newline at end of file diff --git a/Engine/source/torqueMacosEngine.cmake b/Engine/source/torqueMacosEngine.cmake new file mode 100644 index 000000000..4ca95cd25 --- /dev/null +++ b/Engine/source/torqueMacosEngine.cmake @@ -0,0 +1,283 @@ + +set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} __MACOSX__) + +################# Collect Source Files ################### + +# Handle app +torqueAddSourceDirectories("app" "app/net") + +# Handle console +torqueAddSourceDirectories("console") +torqueAddSourceDirectories("console/torquescript") + +# Handle Platform +torqueAddSourceDirectories("platform" "platform/threads" "platform/async" + "platform/input" "platform/output") + +torqueAddSourceDirectories("platform/nativeDialogs") +# Handle T3D +torqueAddSourceDirectories("T3D/fps" "T3D/fx" "T3D/vehicles" "T3D/physics" + "T3D/decal" "T3D/sfx" "T3D/gameBase" "T3D/turret" + "T3D/lighting" "T3D/gameOBjects" "T3D/components" + "T3D/systems" "T3D/assets" "T3D" "T3D/gameBase/std") + +# Handle TS +torqueAddSourceDirectories("ts" "ts/collada" "ts/assimp" "ts/loader" "ts/arch") + +# Handle SFX - OpenAL is handled as a module later on +torqueAddSourceDirectories("sfx" "sfx/media" "sfx/null") +if(TORQUE_SFX_OPENAL AND NOT TORQUE_DEDICATED) + torqueAddSourceDirectories("sfx/openal") + torqueAddSourceDirectories("sfx/openal/mac") +endif() + +# Handle GFX +torqueAddSourceDirectories("gfx" "gfx/Null" "gfx/test" "gfx/bitmap" "gfx/bitmap/loaders" + "gfx/util" "gfx/video" "gfx/sim" ) + +if (TORQUE_OPENGL) + torqueAddSourceDirectories("gfx/gl" "gfx/gl/sdl" "gfx/gl/tGL") +endif (TORQUE_OPENGL) + +# Handle core +torqueAddSourceDirectories("core" "core/stream" "core/strings" "core/util" + "core/util/journal" "core/util/zip" "core/util/compressors") +# Handle GUI +torqueAddSourceDirectories("gui" "gui/buttons" "gui/containers" "gui/controls" "gui/core" + "gui/game" "gui/shiny" "gui/utility" "gui/3d") + +# Handle postFX +torqueAddSourceDirectories("postFx") + +# Handle Windowmanager +torqueAddSourceDirectories("windowManager" "windowManager/torque" "windowManager/sdl") + +# Handle scene +torqueAddSourceDirectories("scene" "scene/culling" "scene/zones" "scene/mixin") + +# Handle math +torqueAddSourceDirectories("math" "math/util") + +# Handle persistence +torqueAddSourceDirectories("persistence/taml" "persistence/taml/binary" "persistence/taml/xml") + +# Handle Cinterface +torqueAddSourceDirectories("cinterface") + +# Handle util +torqueAddSourceDirectories("util" "util/messaging") + +# Handle assets +torqueAddSourceDirectories("assets") + +# Handle Sim +torqueAddSourceDirectories("sim") + +# Handle module +torqueAddSourceDirectories("module") + +# Handle forest +torqueAddSourceDirectories("forest" "forest/ts") +if(TORQUE_OPENGL) + torqueAddSourceDirectories("forest" "forest/glsl") +endif(TORQUE_OPENGL) + +# Handle shadergen +torqueAddSourceDirectories("shaderGen") + +if (TORQUE_OPENGL) + torqueAddSourceDirectories("shaderGen/GLSL") +endif (TORQUE_OPENGL) + +# Handle terrain +torqueAddSourceDirectories("terrain") + +if (TORQUE_OPENGL) + torqueAddSourceDirectories("terrain/glsl") +endif (TORQUE_OPENGL) + +# Handle Materials +torqueAddSourceDirectories("materials") + +# Handle collision +torqueAddSourceDirectories("collision") + +# Handle lighting +torqueAddSourceDirectories("lighting" "lighting/common" + "lighting/shadowMap") + +if (TORQUE_ADVANCED_LIGHTING) + torqueAddSourceDirectories("lighting/advanced") + if (TORQUE_OPENGL) + torqueAddSourceDirectories("lighting/advanced/glsl") + endif (TORQUE_OPENGL) +endif (TORQUE_ADVANCED_LIGHTING) + +if (TORQUE_BASIC_LIGHTING) + torqueAddSourceDirectories("lighting/basic" "lighting/basic/shadowMap") +endif (TORQUE_BASIC_LIGHTING) + +# Handle environment +torqueAddSourceDirectories("environment") + +# Handle renderInstance +torqueAddSourceDirectories("renderInstance") + +# Handle i18n +torqueAddSourceDirectories("i18n") + +# Handle posix +torqueAddSourceDirectories("platformPOSIX") +torqueAddSourceDirectories("platformMac") + +# Handle platformSDL +torqueAddSourceDirectories("platformSDL" "platformSDL/threads") + +if(TORQUE_TESTING) + torqueAddSourceDirectories("testing") + set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} TORQUE_SHARED SDL_MAIN_HANDLED) +endif(TORQUE_TESTING) + +# Add the collected files to our engine group +source_group(TREE "${CMAKE_SOURCE_DIR}/Engine/source" PREFIX "Engine" FILES ${TORQUE_SOURCE_FILES}) +file(GLOB_RECURSE TORQUE_APP_GAME_SOURCES "${TORQUE_APP_ROOT_DIRECTORY}/source/*.cpp" "${TORQUE_APP_ROOT_DIRECTORY}/source/*.h") +source_group(TREE "${TORQUE_APP_ROOT_DIRECTORY}/source" PREFIX "Source Files" FILES ${TORQUE_APP_GAME_SOURCES}) +set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} "${TORQUE_APP_ROOT_DIRECTORY}/source") + +################# Engine Module Handling ################### + +set(TORQUE_MODULE_PATHS "${CMAKE_SOURCE_DIR}/Tools/CMake/modules" "${TORQUE_APP_GAME_DIRECTORY}/data") +if (NOT "${TORQUE_MODULE_USER_PATH}" STREQUAL "") + list(APPEND TORQUE_MODULE_PATHS "${TORQUE_MODULE_USER_PATH}") +endif() + +# Before doing module scanning, store away the engine sources - we do this so that modules +# can be placed into the proper filters +set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES} ${TORQUE_APP_GAME_SOURCES}) +set(TORQUE_SOURCE_FILES "") + +foreach (TORQUE_MODULE_PATH ${TORQUE_MODULE_PATHS}) + # First find simple cmake scripts, mostly used for in-engine modules + file(GLOB MODULE_SCRIPTS "${TORQUE_MODULE_PATH}/*.cmake") + foreach (TORQUE_MODULE_SCRIPT ${MODULE_SCRIPTS}) + include(${TORQUE_MODULE_SCRIPT}) + + # Add this script's collected files to our Engine group + source_group(TREE "${CMAKE_SOURCE_DIR}/Engine" PREFIX "Engine" FILES ${TORQUE_SOURCE_FILES}) + + set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${TORQUE_SOURCE_FILES}) + set(TORQUE_SOURCE_FILES "") + endforeach() + + # Next find sub projects, these can introduce new source files + SUBDIRLIST(POSSIBLE_PROJECTS "${TORQUE_MODULE_PATH}") + foreach (POSSIBLE_PROJECT ${POSSIBLE_PROJECTS}) + # Retrieve the absolute path of this possible project + get_filename_component(POSSIBLE_PROJECT_ABSOLUTEPATH "${POSSIBLE_PROJECT}" + REALPATH BASE_DIR "${TORQUE_MODULE_PATH}") + + if (EXISTS "${POSSIBLE_PROJECT_ABSOLUTEPATH}/CMakeLists.txt") + add_subdirectory("${POSSIBLE_PROJECT_ABSOLUTEPATH}" ${CMAKE_BINARY_DIR}/temp/${POSSIBLE_PROJECT} EXCLUDE_FROM_ALL) + source_group(TREE "${POSSIBLE_PROJECT_ABSOLUTEPATH}" PREFIX "Modules/${POSSIBLE_PROJECT}" FILES ${TORQUE_SOURCE_FILES}) + + set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${TORQUE_SOURCE_FILES}) + set(TORQUE_SOURCE_FILES "") + elseif(NOT EXISTS "${POSSIBLE_PROJECT_ABSOLUTEPATH}/*.cmake") + file(GLOB_RECURSE MODULE_SOURCE "${POSSIBLE_PROJECT_ABSOLUTEPATH}/source/*.h" "${POSSIBLE_PROJECT_ABSOLUTEPATH}/source/*.cpp") + #message(STATUS "MODULE_SOURCE:${MODULE_SOURCE}") + source_group(TREE "${POSSIBLE_PROJECT_ABSOLUTEPATH}" PREFIX "Modules/${POSSIBLE_PROJECT}/" FILES ${MODULE_SOURCE}) + set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${MODULE_SOURCE}) + endif() + endforeach() +endforeach() + +set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES_TEMPORARY}) + +################# Library Post-build Handling ################### +set(TORQUE_LIBRARY_PATHS "${CMAKE_SOURCE_DIR}/Engine/lib" "${TORQUE_APP_GAME_DIRECTORY}/data") +if (NOT "${TORQUE_LIBRARY_USER_PATH}" STREQUAL "") + list(APPEND TORQUE_LIBRARY_PATHS "${TORQUE_LIBRARY_USER_PATH}") +endif() + +foreach (TORQUE_LIBRARY_PATH ${TORQUE_LIBRARY_PATHS}) + # First find simple cmake scripts, mostly used for in-engine libraries + file(GLOB_RECURSE LIBRARY_SCRIPTS "${TORQUE_LIBRARY_PATH}/*Torque_postBuild.cmake") + #message(STATUS "LIBRARY_SCRIPTS:${LIBRARY_SCRIPTS}") + foreach (TORQUE_LIBRARY_SCRIPT ${LIBRARY_SCRIPTS}) + include(${TORQUE_LIBRARY_SCRIPT}) + endforeach() +endforeach() +################# Dynamic File Configuration ################### + +set(MACOSX_RESOURCES "${CMAKE_SOURCE_DIR}/Tools/CMake/torque.icns" + "${TORQUE_APP_GAME_DIRECTORY}/data" + "${TORQUE_APP_GAME_DIRECTORY}/core" + "${TORQUE_APP_GAME_DIRECTORY}/tools" + "${TORQUE_APP_GAME_DIRECTORY}/main.${TORQUE_SCRIPT_EXTENSION}") + +set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_MAC_SOURCES} ${MACOSX_RESOURCES}) + +source_group("Resources" FILES ${MACOSX_RESOURCES}) + +set_source_files_properties(${MACOSX_RESOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") + +set(EXECUTABLE_NAME "${TORQUE_APP_NAME}") +configure_file("${CMAKE_SOURCE_DIR}/Tools/CMake/Info.plist.in" "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" COPYONLY) + +addDef(TORQUE_DEBUG Debug) +addDef(TORQUE_RELEASE "RelWithDebInfo;Release") +addDef(TORQUE_ENABLE_ASSERTS "Debug;RelWithDebInfo") +addDef(TORQUE_DEBUG_GFX_MODE "RelWithDebInfo") +addDef(TORQUE_OGGVORBIS) + +if(NOT TORQUE_SDL) + filterOut("platform/nativeDialogs/fileDialog.cpp" ) +endif() +if(NOT TORQUE_OPENGL) + filterOut("platformSDL/sdlPlatformGL.cpp") +endif() +if (NOT TORQUE_NET_CURL) + filterOut("app/net/httpObject.h" "app/net/httpObject.cpp") +endif() + +add_executable(${TORQUE_APP_NAME} MACOSX_BUNDLE ${TORQUE_SOURCE_FILES}) +set_target_properties(${TORQUE_APP_NAME} PROPERTIES + BUNDLE true + MACOSX_BUNDLE_INFO_PLIST "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" + XCODE_ATTRIBUTE_INSTALL_PATH "/Applications" + XCODE_ATTRIBUTE_SKIP_INSTALL "No" + MACOSX_RPATH TRUE) + + set_property(GLOBAL PROPERTY USE_FOLDERS ON) +foreach (TORQUE_LIBRARY ${TORQUE_LINK_LIBRARIES}) + set_target_properties(${TORQUE_LIBRARY} PROPERTIES + FOLDER "Libraries") +endforeach() + +target_compile_definitions(${TORQUE_APP_NAME} PUBLIC ${TORQUE_COMPILE_DEFINITIONS}) +target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_LIBRARIES} ${TORQUE_LINK_FRAMEWORKS}) + +target_link_options(${TORQUE_APP_NAME} PUBLIC ${TORQUE_LINK_OPTIONS}) +if (TORQUE_TARGET_PROPERTIES) + set_target_properties(${TORQUE_APP_NAME} PROPERTIES ${TORQUE_TARGET_PROPERTIES}) +endif (TORQUE_TARGET_PROPERTIES) +target_include_directories(${TORQUE_APP_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_BINARY_DIR}/temp" ${TORQUE_INCLUDE_DIRECTORIES}) + +append_defs() + +foreach (LIBRARY_BINARY ${TORQUE_ADDITIONAL_LIBRARY_BINARIES}) + add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}/${TORQUE_APP_NAME}.app/Contents/Frameworks/$(CONFIGURATION)") +endforeach() + +get_target_property(GAME_LINK_LIBRARIES ${TORQUE_APP_NAME} LINK_LIBRARIES) +foreach (GAME_LINK_LIBRARY ${GAME_LINK_LIBRARIES}) + if (TARGET ${GAME_LINK_LIBRARY}) + get_target_property(LINK_LIBRARY_TYPE ${GAME_LINK_LIBRARY} TYPE) + # Only pay attention to shared libraries and make them output to the app resources + if ("${LINK_LIBRARY_TYPE}" STREQUAL "SHARED_LIBRARY") + set_target_properties(${GAME_LINK_LIBRARY} PROPERTIES + XCODE_ATTRIBUTE_INSTALL_PATH "@rpath") + endif() + endif() +endforeach() + diff --git a/Tools/CMake/torqueMacOSconfigs.cmake b/Tools/CMake/platformconfigs/torqueMacosxconfig.cmake similarity index 99% rename from Tools/CMake/torqueMacOSconfigs.cmake rename to Tools/CMake/platformconfigs/torqueMacosxconfig.cmake index 0e583d0b4..2d1c6e96c 100644 --- a/Tools/CMake/torqueMacOSconfigs.cmake +++ b/Tools/CMake/platformconfigs/torqueMacosxconfig.cmake @@ -1,4 +1,3 @@ -if(APPLE) #detect Architecture enable_language(OBJC) enable_language(OBJCXX) @@ -161,4 +160,4 @@ set(CMAKE_XCODE_ATTRIBUTE_LINKER_DISPLAYS_MANGLED_NAMES[variant=Debug] YES) set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH[variant=Debug] YES) set(CMAKE_XCODE_ATTRIBUTE_ENABLE_TESTABILITY[variant=Debug] YES) -endif(APPLE) \ No newline at end of file +add_subdirectory(${TORQUE_ENGINE_DIRECTORY}) From acf31770212aa124ce72094183e471bccf244886 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Sat, 21 Oct 2023 07:08:12 +0100 Subject: [PATCH 016/122] Revert "example cmake workflow" This reverts commit 233c6a80457bd3aaba3083e0f957444a973533e6. --- CMakeLists.txt | 15 +- Engine/source/CMakeLists.txt | 471 +++++++++++++++++- Engine/source/torqueEveryEngine.cmake | 465 ----------------- Engine/source/torqueMacosEngine.cmake | 283 ----------- ...xconfig.cmake => torqueMacOSconfigs.cmake} | 3 +- 5 files changed, 474 insertions(+), 763 deletions(-) delete mode 100644 Engine/source/torqueEveryEngine.cmake delete mode 100644 Engine/source/torqueMacosEngine.cmake rename Tools/CMake/{platformconfigs/torqueMacosxconfig.cmake => torqueMacOSconfigs.cmake} (99%) diff --git a/CMakeLists.txt b/CMakeLists.txt index c5bec3818..5de867942 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,10 +4,10 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;RelWithDebInfo;Release" CACHE STRING "" FOR include("${CMAKE_SOURCE_DIR}/Tools/CMake/torque_macros.cmake") include("${CMAKE_SOURCE_DIR}/Tools/CMake/torque_configs.cmake") -#file(GLOB OS_SCRIPTS "${CMAKE_SOURCE_DIR}/Tools/CMake/torque*configs.cmake") -#foreach (TORQUE_OS_SCRIPT ${OS_SCRIPTS}) -# include(${TORQUE_OS_SCRIPT}) -#endforeach() +file(GLOB OS_SCRIPTS "${CMAKE_SOURCE_DIR}/Tools/CMake/torque*configs.cmake") +foreach (TORQUE_OS_SCRIPT ${OS_SCRIPTS}) + include(${TORQUE_OS_SCRIPT}) +endforeach() # Ensure multi-core compilation is enabled for everything add_compile_options($<$:/MP>) @@ -28,7 +28,6 @@ set(TORQUE_APP_GAME_DIRECTORY "${TORQUE_APP_ROOT_DIRECTORY}/game") set(TORQUE_LIB_ROOT_DIRECTORY "${CMAKE_SOURCE_DIR}/Engine/lib") set(TORQUE_LIB_TARG_DIRECTORY "${CMAKE_BINARY_DIR}/Engine/lib") set(TORQUE_SOURCE_DIRECTROY "${CMAKE_SOURCE_DIR}/Engine/source") -set(TORQUE_ENGINE_DIRECTORY "${CMAKE_SOURCE_DIR}/Engine") # Ensure all possible configurations end up in the project directory set(CMAKE_INSTALL_PREFIX "${TORQUE_APP_ROOT_DIRECTORY}" CACHE STRING "" FORCE) @@ -75,8 +74,4 @@ endif(NOT TORQUE_INSTALLED_TEMPLATE) # Generate torqueConfig.h in our temp directory configure_file("${CMAKE_SOURCE_DIR}/Tools/CMake/torqueConfig.h.in" "${TORQUE_APP_ROOT_DIRECTORY}/source/torqueConfig.h") -if(APPLE) - include("${CMAKE_SOURCE_DIR}/Tools/CMake/platformconfigs/torqueMacosxconfig.cmake") -else(APPLE) - add_subdirectory(Engine) -endif(APPLE) \ No newline at end of file +add_subdirectory(Engine) diff --git a/Engine/source/CMakeLists.txt b/Engine/source/CMakeLists.txt index ee80caa46..aad4df349 100644 --- a/Engine/source/CMakeLists.txt +++ b/Engine/source/CMakeLists.txt @@ -18,8 +18,471 @@ set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} TORQUE_TESTS_ENABLE set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} "_VARIADIC_MAX=10") endif() -if(APPLE) - include(torqueMacosEngine.cmake) +# On Windows we disable CRT Security warnings - this comes from recommendations to use non-portable functions. +if (WIN32) + set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} _CRT_SECURE_NO_WARNINGS WIN32) +endif (WIN32) + +if (APPLE) + set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} __MACOSX__) +elseif (UNIX) + set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} __linux__) +endif (APPLE) + +################# Set Engine Linkages ################### + +# When on Windows, we need to link against winsock and windows codecs +if (WIN32) + set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_WIN_SOURCES}) +endif (WIN32) + +# Linux requires X11 & freetype +if (UNIX AND NOT APPLE) + set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_X11_SOURCES}) + find_package(Freetype REQUIRED) + set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} ${FREETYPE_INCLUDE_DIRS}) +endif (UNIX AND NOT APPLE) + +################# Collect Source Files ################### + +# Handle app +torqueAddSourceDirectories("app" "app/net") + +# Handle console +torqueAddSourceDirectories("console") +torqueAddSourceDirectories("console/torquescript") + +# Handle Platform +torqueAddSourceDirectories("platform" "platform/threads" "platform/async" + "platform/input" "platform/output") + +torqueAddSourceDirectories("platform/nativeDialogs") +# Handle T3D +torqueAddSourceDirectories("T3D/fps" "T3D/fx" "T3D/vehicles" "T3D/physics" + "T3D/decal" "T3D/sfx" "T3D/gameBase" "T3D/turret" + "T3D/lighting" "T3D/gameOBjects" "T3D/components" + "T3D/systems" "T3D/assets" "T3D" "T3D/gameBase/std") + +# Handle TS +torqueAddSourceDirectories("ts" "ts/collada" "ts/assimp" "ts/loader" "ts/arch") + +# Handle SFX - OpenAL is handled as a module later on +torqueAddSourceDirectories("sfx" "sfx/media" "sfx/null") +if(TORQUE_SFX_OPENAL AND NOT TORQUE_DEDICATED) + torqueAddSourceDirectories("sfx/openal") + if(WIN32) + torqueAddSourceDirectories("sfx/openal/win32") + elseif(UNIX AND NOT APPLE) + torqueAddSourceDirectories("sfx/openal/linux") + elseif(APPLE) + torqueAddSourceDirectories("sfx/openal/mac") + endif() +endif() +# Handle GFX +torqueAddSourceDirectories("gfx" "gfx/Null" "gfx/test" "gfx/bitmap" "gfx/bitmap/loaders" + "gfx/util" "gfx/video" "gfx/sim" ) + +if (TORQUE_OPENGL) + torqueAddSourceDirectories("gfx/gl" "gfx/gl/sdl" "gfx/gl/tGL") +endif (TORQUE_OPENGL) + +if (WIN32 AND TORQUE_D3D11) + torqueAddSourceDirectories("gfx/D3D11") +endif (WIN32 AND TORQUE_D3D11) + +# Handle core +torqueAddSourceDirectories("core" "core/stream" "core/strings" "core/util" + "core/util/journal" "core/util/zip" "core/util/compressors") +# Handle GUI +torqueAddSourceDirectories("gui" "gui/buttons" "gui/containers" "gui/controls" "gui/core" + "gui/game" "gui/shiny" "gui/utility" "gui/3d") + +# Handle postFX +torqueAddSourceDirectories("postFx") + +# Handle Windowmanager +torqueAddSourceDirectories("windowManager" "windowManager/torque" "windowManager/sdl") + +# Handle scene +torqueAddSourceDirectories("scene" "scene/culling" "scene/zones" "scene/mixin") + +# Handle math +torqueAddSourceDirectories("math" "math/util") + +# Handle persistence +torqueAddSourceDirectories("persistence/taml" "persistence/taml/binary" "persistence/taml/xml") + +# Handle Cinterface +torqueAddSourceDirectories("cinterface") + +# Handle util +torqueAddSourceDirectories("util" "util/messaging") + +# Handle assets +torqueAddSourceDirectories("assets") + +# Handle Sim +torqueAddSourceDirectories("sim") + +# Handle module +torqueAddSourceDirectories("module") + +# Handle forest +torqueAddSourceDirectories("forest" "forest/ts") +if(TORQUE_OPENGL) + torqueAddSourceDirectories("forest" "forest/glsl") +endif(TORQUE_OPENGL) +if(TORQUE_D3D11) + torqueAddSourceDirectories("forest" "forest/hlsl") +endif(TORQUE_D3D11) + +# Handle shadergen +torqueAddSourceDirectories("shaderGen") + +if (WIN32 AND TORQUE_D3D11) + torqueAddSourceDirectories("shaderGen/HLSL") +endif (WIN32 AND TORQUE_D3D11) + +if (TORQUE_OPENGL) + torqueAddSourceDirectories("shaderGen/GLSL") +endif (TORQUE_OPENGL) + +# Handle terrain +torqueAddSourceDirectories("terrain") + +if (WIN32 AND TORQUE_D3D11) + torqueAddSourceDirectories("terrain/hlsl") +endif (WIN32 AND TORQUE_D3D11) + +if (TORQUE_OPENGL) + torqueAddSourceDirectories("terrain/glsl") +endif (TORQUE_OPENGL) + +# Handle Materials +torqueAddSourceDirectories("materials") + +# Handle collision +torqueAddSourceDirectories("collision") + +# Handle lighting +torqueAddSourceDirectories("lighting" "lighting/common" + "lighting/shadowMap") + +if (TORQUE_ADVANCED_LIGHTING) + torqueAddSourceDirectories("lighting/advanced") + + if (WIN32 AND TORQUE_D3D11) + torqueAddSourceDirectories("lighting/advanced/hlsl") + endif (WIN32 AND TORQUE_D3D11) + + if (TORQUE_OPENGL) + torqueAddSourceDirectories("lighting/advanced/glsl") + endif (TORQUE_OPENGL) +endif (TORQUE_ADVANCED_LIGHTING) + +if (TORQUE_BASIC_LIGHTING) + torqueAddSourceDirectories("lighting/basic" "lighting/basic/shadowMap") +endif (TORQUE_BASIC_LIGHTING) + +# Handle environment +torqueAddSourceDirectories("environment") + +# Handle renderInstance +torqueAddSourceDirectories("renderInstance") + +# Handle i18n +torqueAddSourceDirectories("i18n") + +# Begin handling platform specific stuff +# Handle Platform POSIX +if (UNIX) + torqueAddSourceDirectories("platformPOSIX") + + if(NOT APPLE) + if (TORQUE_CPU_X32 OR TORQUE_CPU_X64) + torqueAddSourceDirectories("platformX86UNIX") + endif (TORQUE_CPU_X32 OR TORQUE_CPU_X64) + endif(NOT APPLE) +endif (UNIX) + +# Handle platformMac +if (APPLE) + torqueAddSourceDirectories("platformMac") +endif (APPLE) + +# Handle platformWin32 +if (WIN32) + torqueAddSourceDirectories("platformWin32" "platformWin32/videoInfo") +endif (WIN32) + +# Handle platformSDL +torqueAddSourceDirectories("platformSDL" "platformSDL/threads") + +# Handle platformX11 +if (UNIX AND NOT APPLE) + torqueAddSourceDirectories("platformX11") +endif (UNIX AND NOT APPLE) + +if(TORQUE_TESTING) + torqueAddSourceDirectories("testing") + set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} TORQUE_SHARED SDL_MAIN_HANDLED) +endif(TORQUE_TESTING) + +# Add the collected files to our engine group +source_group(TREE "${CMAKE_SOURCE_DIR}/Engine/source" PREFIX "Engine" FILES ${TORQUE_SOURCE_FILES}) +file(GLOB_RECURSE TORQUE_APP_GAME_SOURCES "${TORQUE_APP_ROOT_DIRECTORY}/source/*.cpp" "${TORQUE_APP_ROOT_DIRECTORY}/source/*.h") +source_group(TREE "${TORQUE_APP_ROOT_DIRECTORY}/source" PREFIX "Source Files" FILES ${TORQUE_APP_GAME_SOURCES}) +set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} "${TORQUE_APP_ROOT_DIRECTORY}/source") + +################# Engine Module Handling ################### + +set(TORQUE_MODULE_PATHS "${CMAKE_SOURCE_DIR}/Tools/CMake/modules" "${TORQUE_APP_GAME_DIRECTORY}/data") +if (NOT "${TORQUE_MODULE_USER_PATH}" STREQUAL "") + list(APPEND TORQUE_MODULE_PATHS "${TORQUE_MODULE_USER_PATH}") +endif() + +# Before doing module scanning, store away the engine sources - we do this so that modules +# can be placed into the proper filters +set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES} ${TORQUE_APP_GAME_SOURCES}) +set(TORQUE_SOURCE_FILES "") + +foreach (TORQUE_MODULE_PATH ${TORQUE_MODULE_PATHS}) + # First find simple cmake scripts, mostly used for in-engine modules + file(GLOB MODULE_SCRIPTS "${TORQUE_MODULE_PATH}/*.cmake") + foreach (TORQUE_MODULE_SCRIPT ${MODULE_SCRIPTS}) + include(${TORQUE_MODULE_SCRIPT}) + + # Add this script's collected files to our Engine group + source_group(TREE "${CMAKE_SOURCE_DIR}/Engine" PREFIX "Engine" FILES ${TORQUE_SOURCE_FILES}) + + set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${TORQUE_SOURCE_FILES}) + set(TORQUE_SOURCE_FILES "") + endforeach() + + # Next find sub projects, these can introduce new source files + SUBDIRLIST(POSSIBLE_PROJECTS "${TORQUE_MODULE_PATH}") + foreach (POSSIBLE_PROJECT ${POSSIBLE_PROJECTS}) + # Retrieve the absolute path of this possible project + get_filename_component(POSSIBLE_PROJECT_ABSOLUTEPATH "${POSSIBLE_PROJECT}" + REALPATH BASE_DIR "${TORQUE_MODULE_PATH}") + + if (EXISTS "${POSSIBLE_PROJECT_ABSOLUTEPATH}/CMakeLists.txt") + add_subdirectory("${POSSIBLE_PROJECT_ABSOLUTEPATH}" ${CMAKE_BINARY_DIR}/temp/${POSSIBLE_PROJECT} EXCLUDE_FROM_ALL) + source_group(TREE "${POSSIBLE_PROJECT_ABSOLUTEPATH}" PREFIX "Modules/${POSSIBLE_PROJECT}" FILES ${TORQUE_SOURCE_FILES}) + + set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${TORQUE_SOURCE_FILES}) + set(TORQUE_SOURCE_FILES "") + elseif(NOT EXISTS "${POSSIBLE_PROJECT_ABSOLUTEPATH}/*.cmake") + file(GLOB_RECURSE MODULE_SOURCE "${POSSIBLE_PROJECT_ABSOLUTEPATH}/source/*.h" "${POSSIBLE_PROJECT_ABSOLUTEPATH}/source/*.cpp") + #message(STATUS "MODULE_SOURCE:${MODULE_SOURCE}") + source_group(TREE "${POSSIBLE_PROJECT_ABSOLUTEPATH}" PREFIX "Modules/${POSSIBLE_PROJECT}/" FILES ${MODULE_SOURCE}) + set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${MODULE_SOURCE}) + endif() + endforeach() +endforeach() + +set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES_TEMPORARY}) + +################# Library Post-build Handling ################### +set(TORQUE_LIBRARY_PATHS "${CMAKE_SOURCE_DIR}/Engine/lib" "${TORQUE_APP_GAME_DIRECTORY}/data") +if (NOT "${TORQUE_LIBRARY_USER_PATH}" STREQUAL "") + list(APPEND TORQUE_LIBRARY_PATHS "${TORQUE_LIBRARY_USER_PATH}") +endif() + +foreach (TORQUE_LIBRARY_PATH ${TORQUE_LIBRARY_PATHS}) + # First find simple cmake scripts, mostly used for in-engine libraries + file(GLOB_RECURSE LIBRARY_SCRIPTS "${TORQUE_LIBRARY_PATH}/*Torque_postBuild.cmake") + #message(STATUS "LIBRARY_SCRIPTS:${LIBRARY_SCRIPTS}") + foreach (TORQUE_LIBRARY_SCRIPT ${LIBRARY_SCRIPTS}) + include(${TORQUE_LIBRARY_SCRIPT}) + endforeach() +endforeach() +################# Dynamic File Configuration ################### + +# Prepare Windows RC file +if (WIN32) + set(APPLICATION_ICON_PATH "${CMAKE_SOURCE_DIR}/Tools/CMake/torque.ico") + + configure_file("${CMAKE_SOURCE_DIR}/Tools/CMake/torque-win.rc.in" "${TORQUE_APP_ROOT_DIRECTORY}/source/torque.rc") + set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} "${TORQUE_APP_ROOT_DIRECTORY}/source/torque.rc") +endif (WIN32) + +# Prepare OSX Plist +if (APPLE) + set(MACOSX_RESOURCES "${CMAKE_SOURCE_DIR}/Tools/CMake/torque.icns" + "${TORQUE_APP_GAME_DIRECTORY}/data" + "${TORQUE_APP_GAME_DIRECTORY}/core" + "${TORQUE_APP_GAME_DIRECTORY}/tools" + "${TORQUE_APP_GAME_DIRECTORY}/main.${TORQUE_SCRIPT_EXTENSION}") + + set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_MAC_SOURCES} ${MACOSX_RESOURCES}) + + source_group("Resources" FILES ${MACOSX_RESOURCES}) + + set_source_files_properties(${MACOSX_RESOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") + + set(EXECUTABLE_NAME "${TORQUE_APP_NAME}") + configure_file("${CMAKE_SOURCE_DIR}/Tools/CMake/Info.plist.in" "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" COPYONLY) +endif (APPLE) + +addDef(TORQUE_DEBUG Debug) +addDef(TORQUE_RELEASE "RelWithDebInfo;Release") +addDef(TORQUE_ENABLE_ASSERTS "Debug;RelWithDebInfo") +addDef(TORQUE_DEBUG_GFX_MODE "RelWithDebInfo") +addDef(TORQUE_OGGVORBIS) + +if(NOT TORQUE_SDL) + filterOut("platform/nativeDialogs/fileDialog.cpp" ) +endif() +if(NOT TORQUE_OPENGL) + filterOut("platformSDL/sdlPlatformGL.cpp") +endif() +if (NOT TORQUE_NET_CURL) + filterOut("app/net/httpObject.h" "app/net/httpObject.cpp") +endif() + +################# Executable Generation ################### +if (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) + set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} TORQUE_SHARED) + + # Build the main engine library + add_library(TorqueEngine SHARED ${TORQUE_SOURCE_FILES}) + target_compile_definitions(TorqueEngine PUBLIC ${TORQUE_COMPILE_DEFINITIONS}) + target_link_libraries(TorqueEngine ${TORQUE_LINK_LIBRARIES}) + target_include_directories(TorqueEngine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_BINARY_DIR}/temp" ${TORQUE_INCLUDE_DIRECTORIES}) + + set(TORQUE_SOURCE_FILES "main/main.cpp") + set(TORQUE_LINK_LIBRARIES TorqueEngine) else() - include(torqueEveryEngine.cmake) -endif(APPLE) \ No newline at end of file + if(NOT TORQUE_TESTING) + set(TORQUE_SOURCE_FILES "main/main.cpp" ${TORQUE_SOURCE_FILES}) + endif() +endif (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) + +if (APPLE) + add_executable(${TORQUE_APP_NAME} MACOSX_BUNDLE ${TORQUE_SOURCE_FILES}) + set_target_properties(${TORQUE_APP_NAME} PROPERTIES + BUNDLE true + MACOSX_BUNDLE_INFO_PLIST "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" + XCODE_ATTRIBUTE_INSTALL_PATH "/Applications" + XCODE_ATTRIBUTE_SKIP_INSTALL "No" + MACOSX_RPATH TRUE) + +elseif (WIN32) + add_executable(${TORQUE_APP_NAME} WIN32 ${TORQUE_SOURCE_FILES}) + + set(TORQUE_CXX_FLAGS_COMMON_DEFAULT "-DUNICODE -D_UNICODE -D_CRT_SECURE_NO_WARNINGS /MP /Ob2 /Oi /Ot /Oy /GT /Zi /W4 /nologo /GF /EHsc /GS- /Gy- /Qpar- /fp:precise /fp:except- /GR /Zc:wchar_t-" ) + if( TORQUE_CPU_X32 ) + set(TORQUE_CXX_FLAGS_COMMON_DEFAULT "${TORQUE_CXX_FLAGS_COMMON_DEFAULT} /arch:SSE2") + endif() + set(TORQUE_CXX_FLAGS_COMMON ${TORQUE_CXX_FLAGS_COMMON_DEFAULT} CACHE STRING "") + mark_as_advanced(TORQUE_CXX_FLAGS_COMMON) + + set(TORQUE_CXX_FLAGS_EXECUTABLES "/wd4018 /wd4100 /wd4121 /wd4127 /wd4130 /wd4244 /wd4245 /wd4389 /wd4511 /wd4512 /wd4800 /wd4995" CACHE STRING "") + mark_as_advanced(TORQUE_CXX_FLAGS_EXECUTABLES) + + set(TORQUE_CXX_FLAGS "${TORQUE_CXX_FLAGS_COMMON_DEFAULT} ${TORQUE_CXX_FLAGS_EXECUTABLES}" CACHE STRING "") + mark_as_advanced(TORQUE_CXX_FLAGS) + + # NOTE: On Windows, /Zc:wchar_t- is necessary otherwise you get unicode errors + set_target_properties(${TORQUE_APP_NAME} PROPERTIES COMPILE_FLAGS "${TORQUE_CXX_FLAGS}") + if (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) + set_target_properties(TorqueEngine PROPERTIES COMPILE_FLAGS "${TORQUE_CXX_FLAGS_COMMON_DEFAULT}") + endif (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) +else() + add_executable(${TORQUE_APP_NAME} ${TORQUE_SOURCE_FILES}) + + # NOTE: On Linux, we set the rpath to ./ so that shared objects next to the executable are used + set_target_properties(${TORQUE_APP_NAME} PROPERTIES LINK_FLAGS "-Wl,-rpath,./") +endif() + + +if(MSVC) + # Match projectGenerator naming for executables + set(OUTPUT_CONFIG DEBUG MINSIZEREL RELWITHDEBINFO) + set(OUTPUT_SUFFIX DEBUG MINSIZE OPTIMIZEDDEBUG) + foreach(INDEX RANGE 2) + list(GET OUTPUT_CONFIG ${INDEX} CONF) + list(GET OUTPUT_SUFFIX ${INDEX} SUFFIX) + set_property(TARGET ${TORQUE_APP_NAME} PROPERTY OUTPUT_NAME_${CONF} ${TORQUE_APP_NAME}_${SUFFIX}) + if (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) + set_property(TARGET TorqueEngine PROPERTY ${CONF}_POSTFIX "_${SUFFIX}") + set_property(TARGET TorqueEngine PROPERTY ${CONF}_OUTPUT_NAME ${TORQUE_APP_NAME}) + endif (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) + endforeach() + # Set Visual Studio startup project + set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${TORQUE_APP_NAME}) +endif() + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) +foreach (TORQUE_LIBRARY ${TORQUE_LINK_LIBRARIES}) + set_target_properties(${TORQUE_LIBRARY} PROPERTIES + FOLDER "Libraries") +endforeach() + +target_compile_definitions(${TORQUE_APP_NAME} PUBLIC ${TORQUE_COMPILE_DEFINITIONS}) +target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_LIBRARIES}) + +if(APPLE) + target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_FRAMEWORKS}) +endif(APPLE) + +if(WIN32) + target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_WINDOWS}) +endif(WIN32) + +if(UNIX AND NOT APPLE) + target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_LINUX}) +endif(UNIX AND NOT APPLE) + +target_link_options(${TORQUE_APP_NAME} PUBLIC ${TORQUE_LINK_OPTIONS}) +if (TORQUE_TARGET_PROPERTIES) + set_target_properties(${TORQUE_APP_NAME} PROPERTIES ${TORQUE_TARGET_PROPERTIES}) +endif (TORQUE_TARGET_PROPERTIES) +target_include_directories(${TORQUE_APP_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_BINARY_DIR}/temp" ${TORQUE_INCLUDE_DIRECTORIES}) + +if(TORQUE_TESTING) + if(WIN32) + target_link_options(${TORQUE_APP_NAME} PRIVATE "/SUBSYSTEM:CONSOLE") + set_target_properties(gtest PROPERTIES COMPILE_FLAGS "/Zc:wchar_t-") + set_target_properties(gmock PROPERTIES COMPILE_FLAGS "/Zc:wchar_t-") + endif() +endif(TORQUE_TESTING) + +append_defs() + +# Process library binaries - these are coming from modules that are providing links to external, precompiled code that should be included +# with the executable. This is done because on Windows, the .lib is separate from the .dll so we can't automatically scan for shared +# objects in our link libraries in that case. +foreach (LIBRARY_BINARY ${TORQUE_ADDITIONAL_LIBRARY_BINARIES}) + if (APPLE) + # For OSX, we want these binaries to be copied to the Frameworks directory + add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}/${TORQUE_APP_NAME}.app/Contents/Frameworks/$(CONFIGURATION)") + else() + # All other platforms expect the file next to the executable + add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}") + endif (APPLE) +endforeach() + +# Process link libraries for dynamic links - we do this on OSX/Linux to ensure the binaries end up in the correct App directory +# as in the root CMake we force everything to be in game. This is necessary because on these platforms these are considered "libraries" +# and not runtime binaries like we configure in the root CMake. We don't globally set library outputs to avoid outputting eg. a files to +# our game directory. +if (UNIX) + get_target_property(GAME_LINK_LIBRARIES ${TORQUE_APP_NAME} LINK_LIBRARIES) + foreach (GAME_LINK_LIBRARY ${GAME_LINK_LIBRARIES}) + # For eg. OSX some links are not valid targets - for example frameworks provided by OS + if (TARGET ${GAME_LINK_LIBRARY}) + get_target_property(LINK_LIBRARY_TYPE ${GAME_LINK_LIBRARY} TYPE) + # Only pay attention to shared libraries and make them output to the app resources + if ("${LINK_LIBRARY_TYPE}" STREQUAL "SHARED_LIBRARY") + if (APPLE) + set_target_properties(${GAME_LINK_LIBRARY} PROPERTIES + XCODE_ATTRIBUTE_INSTALL_PATH "@rpath") + else() + set_target_properties(${GAME_LINK_LIBRARY} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${TORQUE_APP_GAME_DIRECTORY}") + endif(APPLE) + endif() + endif() + endforeach() +endif (UNIX) diff --git a/Engine/source/torqueEveryEngine.cmake b/Engine/source/torqueEveryEngine.cmake deleted file mode 100644 index 03f95a8fd..000000000 --- a/Engine/source/torqueEveryEngine.cmake +++ /dev/null @@ -1,465 +0,0 @@ -# On Windows we disable CRT Security warnings - this comes from recommendations to use non-portable functions. -if (WIN32) -set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} _CRT_SECURE_NO_WARNINGS WIN32) -endif (WIN32) - -set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} __linux__) -endif (APPLE) - -################# Set Engine Linkages ################### - -# When on Windows, we need to link against winsock and windows codecs -if (WIN32) -set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_WIN_SOURCES}) -endif (WIN32) - -# Linux requires X11 & freetype -if (UNIX AND NOT APPLE) -set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_X11_SOURCES}) -find_package(Freetype REQUIRED) -set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} ${FREETYPE_INCLUDE_DIRS}) -endif (UNIX AND NOT APPLE) - -################# Collect Source Files ################### - -# Handle app -torqueAddSourceDirectories("app" "app/net") - -# Handle console -torqueAddSourceDirectories("console") -torqueAddSourceDirectories("console/torquescript") - -# Handle Platform -torqueAddSourceDirectories("platform" "platform/threads" "platform/async" - "platform/input" "platform/output") - -torqueAddSourceDirectories("platform/nativeDialogs") -# Handle T3D -torqueAddSourceDirectories("T3D/fps" "T3D/fx" "T3D/vehicles" "T3D/physics" - "T3D/decal" "T3D/sfx" "T3D/gameBase" "T3D/turret" - "T3D/lighting" "T3D/gameOBjects" "T3D/components" - "T3D/systems" "T3D/assets" "T3D" "T3D/gameBase/std") - -# Handle TS -torqueAddSourceDirectories("ts" "ts/collada" "ts/assimp" "ts/loader" "ts/arch") - -# Handle SFX - OpenAL is handled as a module later on -torqueAddSourceDirectories("sfx" "sfx/media" "sfx/null") -if(TORQUE_SFX_OPENAL AND NOT TORQUE_DEDICATED) - torqueAddSourceDirectories("sfx/openal") - if(WIN32) - torqueAddSourceDirectories("sfx/openal/win32") - elseif(UNIX AND NOT APPLE) - torqueAddSourceDirectories("sfx/openal/linux") - elseif(APPLE) - torqueAddSourceDirectories("sfx/openal/mac") - endif() -endif() -# Handle GFX -torqueAddSourceDirectories("gfx" "gfx/Null" "gfx/test" "gfx/bitmap" "gfx/bitmap/loaders" - "gfx/util" "gfx/video" "gfx/sim" ) - -if (TORQUE_OPENGL) -torqueAddSourceDirectories("gfx/gl" "gfx/gl/sdl" "gfx/gl/tGL") -endif (TORQUE_OPENGL) - -if (WIN32 AND TORQUE_D3D11) -torqueAddSourceDirectories("gfx/D3D11") -endif (WIN32 AND TORQUE_D3D11) - -# Handle core -torqueAddSourceDirectories("core" "core/stream" "core/strings" "core/util" - "core/util/journal" "core/util/zip" "core/util/compressors") -# Handle GUI -torqueAddSourceDirectories("gui" "gui/buttons" "gui/containers" "gui/controls" "gui/core" - "gui/game" "gui/shiny" "gui/utility" "gui/3d") - -# Handle postFX -torqueAddSourceDirectories("postFx") - -# Handle Windowmanager -torqueAddSourceDirectories("windowManager" "windowManager/torque" "windowManager/sdl") - -# Handle scene -torqueAddSourceDirectories("scene" "scene/culling" "scene/zones" "scene/mixin") - -# Handle math -torqueAddSourceDirectories("math" "math/util") - -# Handle persistence -torqueAddSourceDirectories("persistence/taml" "persistence/taml/binary" "persistence/taml/xml") - -# Handle Cinterface -torqueAddSourceDirectories("cinterface") - -# Handle util -torqueAddSourceDirectories("util" "util/messaging") - -# Handle assets -torqueAddSourceDirectories("assets") - -# Handle Sim -torqueAddSourceDirectories("sim") - -# Handle module -torqueAddSourceDirectories("module") - -# Handle forest -torqueAddSourceDirectories("forest" "forest/ts") -if(TORQUE_OPENGL) -torqueAddSourceDirectories("forest" "forest/glsl") -endif(TORQUE_OPENGL) -if(TORQUE_D3D11) -torqueAddSourceDirectories("forest" "forest/hlsl") -endif(TORQUE_D3D11) - -# Handle shadergen -torqueAddSourceDirectories("shaderGen") - -if (WIN32 AND TORQUE_D3D11) -torqueAddSourceDirectories("shaderGen/HLSL") -endif (WIN32 AND TORQUE_D3D11) - -if (TORQUE_OPENGL) -torqueAddSourceDirectories("shaderGen/GLSL") -endif (TORQUE_OPENGL) - -# Handle terrain -torqueAddSourceDirectories("terrain") - -if (WIN32 AND TORQUE_D3D11) -torqueAddSourceDirectories("terrain/hlsl") -endif (WIN32 AND TORQUE_D3D11) - -if (TORQUE_OPENGL) -torqueAddSourceDirectories("terrain/glsl") -endif (TORQUE_OPENGL) - -# Handle Materials -torqueAddSourceDirectories("materials") - -# Handle collision -torqueAddSourceDirectories("collision") - -# Handle lighting -torqueAddSourceDirectories("lighting" "lighting/common" - "lighting/shadowMap") - -if (TORQUE_ADVANCED_LIGHTING) -torqueAddSourceDirectories("lighting/advanced") - -if (WIN32 AND TORQUE_D3D11) - torqueAddSourceDirectories("lighting/advanced/hlsl") -endif (WIN32 AND TORQUE_D3D11) - -if (TORQUE_OPENGL) - torqueAddSourceDirectories("lighting/advanced/glsl") -endif (TORQUE_OPENGL) -endif (TORQUE_ADVANCED_LIGHTING) - -if (TORQUE_BASIC_LIGHTING) -torqueAddSourceDirectories("lighting/basic" "lighting/basic/shadowMap") -endif (TORQUE_BASIC_LIGHTING) - -# Handle environment -torqueAddSourceDirectories("environment") - -# Handle renderInstance -torqueAddSourceDirectories("renderInstance") - -# Handle i18n -torqueAddSourceDirectories("i18n") - -# Begin handling platform specific stuff -# Handle Platform POSIX -if (UNIX) -torqueAddSourceDirectories("platformPOSIX") - -if(NOT APPLE) - if (TORQUE_CPU_X32 OR TORQUE_CPU_X64) - torqueAddSourceDirectories("platformX86UNIX") - endif (TORQUE_CPU_X32 OR TORQUE_CPU_X64) -endif(NOT APPLE) -endif (UNIX) - -# Handle platformMac -if (APPLE) -torqueAddSourceDirectories("platformMac") -endif (APPLE) - -# Handle platformWin32 -if (WIN32) -torqueAddSourceDirectories("platformWin32" "platformWin32/videoInfo") -endif (WIN32) - -# Handle platformSDL -torqueAddSourceDirectories("platformSDL" "platformSDL/threads") - -# Handle platformX11 -if (UNIX AND NOT APPLE) -torqueAddSourceDirectories("platformX11") -endif (UNIX AND NOT APPLE) - -if(TORQUE_TESTING) -torqueAddSourceDirectories("testing") -set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} TORQUE_SHARED SDL_MAIN_HANDLED) -endif(TORQUE_TESTING) - -# Add the collected files to our engine group -source_group(TREE "${CMAKE_SOURCE_DIR}/Engine/source" PREFIX "Engine" FILES ${TORQUE_SOURCE_FILES}) -file(GLOB_RECURSE TORQUE_APP_GAME_SOURCES "${TORQUE_APP_ROOT_DIRECTORY}/source/*.cpp" "${TORQUE_APP_ROOT_DIRECTORY}/source/*.h") -source_group(TREE "${TORQUE_APP_ROOT_DIRECTORY}/source" PREFIX "Source Files" FILES ${TORQUE_APP_GAME_SOURCES}) -set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} "${TORQUE_APP_ROOT_DIRECTORY}/source") - -################# Engine Module Handling ################### - -set(TORQUE_MODULE_PATHS "${CMAKE_SOURCE_DIR}/Tools/CMake/modules" "${TORQUE_APP_GAME_DIRECTORY}/data") -if (NOT "${TORQUE_MODULE_USER_PATH}" STREQUAL "") -list(APPEND TORQUE_MODULE_PATHS "${TORQUE_MODULE_USER_PATH}") -endif() - -# Before doing module scanning, store away the engine sources - we do this so that modules -# can be placed into the proper filters -set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES} ${TORQUE_APP_GAME_SOURCES}) -set(TORQUE_SOURCE_FILES "") - -foreach (TORQUE_MODULE_PATH ${TORQUE_MODULE_PATHS}) -# First find simple cmake scripts, mostly used for in-engine modules -file(GLOB MODULE_SCRIPTS "${TORQUE_MODULE_PATH}/*.cmake") -foreach (TORQUE_MODULE_SCRIPT ${MODULE_SCRIPTS}) - include(${TORQUE_MODULE_SCRIPT}) - - # Add this script's collected files to our Engine group - source_group(TREE "${CMAKE_SOURCE_DIR}/Engine" PREFIX "Engine" FILES ${TORQUE_SOURCE_FILES}) - - set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${TORQUE_SOURCE_FILES}) - set(TORQUE_SOURCE_FILES "") -endforeach() - -# Next find sub projects, these can introduce new source files -SUBDIRLIST(POSSIBLE_PROJECTS "${TORQUE_MODULE_PATH}") -foreach (POSSIBLE_PROJECT ${POSSIBLE_PROJECTS}) - # Retrieve the absolute path of this possible project - get_filename_component(POSSIBLE_PROJECT_ABSOLUTEPATH "${POSSIBLE_PROJECT}" - REALPATH BASE_DIR "${TORQUE_MODULE_PATH}") - - if (EXISTS "${POSSIBLE_PROJECT_ABSOLUTEPATH}/CMakeLists.txt") - add_subdirectory("${POSSIBLE_PROJECT_ABSOLUTEPATH}" ${CMAKE_BINARY_DIR}/temp/${POSSIBLE_PROJECT} EXCLUDE_FROM_ALL) - source_group(TREE "${POSSIBLE_PROJECT_ABSOLUTEPATH}" PREFIX "Modules/${POSSIBLE_PROJECT}" FILES ${TORQUE_SOURCE_FILES}) - - set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${TORQUE_SOURCE_FILES}) - set(TORQUE_SOURCE_FILES "") - elseif(NOT EXISTS "${POSSIBLE_PROJECT_ABSOLUTEPATH}/*.cmake") - file(GLOB_RECURSE MODULE_SOURCE "${POSSIBLE_PROJECT_ABSOLUTEPATH}/source/*.h" "${POSSIBLE_PROJECT_ABSOLUTEPATH}/source/*.cpp") - #message(STATUS "MODULE_SOURCE:${MODULE_SOURCE}") - source_group(TREE "${POSSIBLE_PROJECT_ABSOLUTEPATH}" PREFIX "Modules/${POSSIBLE_PROJECT}/" FILES ${MODULE_SOURCE}) - set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${MODULE_SOURCE}) - endif() -endforeach() -endforeach() - -set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES_TEMPORARY}) - -################# Library Post-build Handling ################### -set(TORQUE_LIBRARY_PATHS "${CMAKE_SOURCE_DIR}/Engine/lib" "${TORQUE_APP_GAME_DIRECTORY}/data") -if (NOT "${TORQUE_LIBRARY_USER_PATH}" STREQUAL "") -list(APPEND TORQUE_LIBRARY_PATHS "${TORQUE_LIBRARY_USER_PATH}") -endif() - -foreach (TORQUE_LIBRARY_PATH ${TORQUE_LIBRARY_PATHS}) -# First find simple cmake scripts, mostly used for in-engine libraries -file(GLOB_RECURSE LIBRARY_SCRIPTS "${TORQUE_LIBRARY_PATH}/*Torque_postBuild.cmake") - #message(STATUS "LIBRARY_SCRIPTS:${LIBRARY_SCRIPTS}") -foreach (TORQUE_LIBRARY_SCRIPT ${LIBRARY_SCRIPTS}) - include(${TORQUE_LIBRARY_SCRIPT}) -endforeach() -endforeach() -################# Dynamic File Configuration ################### - -# Prepare Windows RC file -if (WIN32) -set(APPLICATION_ICON_PATH "${CMAKE_SOURCE_DIR}/Tools/CMake/torque.ico") - -configure_file("${CMAKE_SOURCE_DIR}/Tools/CMake/torque-win.rc.in" "${TORQUE_APP_ROOT_DIRECTORY}/source/torque.rc") -set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} "${TORQUE_APP_ROOT_DIRECTORY}/source/torque.rc") -endif (WIN32) - -# Prepare OSX Plist -if (APPLE) -set(MACOSX_RESOURCES "${CMAKE_SOURCE_DIR}/Tools/CMake/torque.icns" -"${TORQUE_APP_GAME_DIRECTORY}/data" -"${TORQUE_APP_GAME_DIRECTORY}/core" -"${TORQUE_APP_GAME_DIRECTORY}/tools" -"${TORQUE_APP_GAME_DIRECTORY}/main.${TORQUE_SCRIPT_EXTENSION}") - -set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_MAC_SOURCES} ${MACOSX_RESOURCES}) - -source_group("Resources" FILES ${MACOSX_RESOURCES}) - -set_source_files_properties(${MACOSX_RESOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") - -set(EXECUTABLE_NAME "${TORQUE_APP_NAME}") -configure_file("${CMAKE_SOURCE_DIR}/Tools/CMake/Info.plist.in" "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" COPYONLY) -endif (APPLE) - -addDef(TORQUE_DEBUG Debug) -addDef(TORQUE_RELEASE "RelWithDebInfo;Release") -addDef(TORQUE_ENABLE_ASSERTS "Debug;RelWithDebInfo") -addDef(TORQUE_DEBUG_GFX_MODE "RelWithDebInfo") -addDef(TORQUE_OGGVORBIS) - -if(NOT TORQUE_SDL) -filterOut("platform/nativeDialogs/fileDialog.cpp" ) -endif() -if(NOT TORQUE_OPENGL) -filterOut("platformSDL/sdlPlatformGL.cpp") -endif() -if (NOT TORQUE_NET_CURL) -filterOut("app/net/httpObject.h" "app/net/httpObject.cpp") -endif() - -################# Executable Generation ################### -if (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) -set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} TORQUE_SHARED) - -# Build the main engine library -add_library(TorqueEngine SHARED ${TORQUE_SOURCE_FILES}) -target_compile_definitions(TorqueEngine PUBLIC ${TORQUE_COMPILE_DEFINITIONS}) -target_link_libraries(TorqueEngine ${TORQUE_LINK_LIBRARIES}) -target_include_directories(TorqueEngine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_BINARY_DIR}/temp" ${TORQUE_INCLUDE_DIRECTORIES}) - -set(TORQUE_SOURCE_FILES "main/main.cpp") -set(TORQUE_LINK_LIBRARIES TorqueEngine) -else() -if(NOT TORQUE_TESTING) - set(TORQUE_SOURCE_FILES "main/main.cpp" ${TORQUE_SOURCE_FILES}) -endif() -endif (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) - -if (APPLE) -add_executable(${TORQUE_APP_NAME} MACOSX_BUNDLE ${TORQUE_SOURCE_FILES}) -set_target_properties(${TORQUE_APP_NAME} PROPERTIES -BUNDLE true -MACOSX_BUNDLE_INFO_PLIST "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" -XCODE_ATTRIBUTE_INSTALL_PATH "/Applications" -XCODE_ATTRIBUTE_SKIP_INSTALL "No" -MACOSX_RPATH TRUE) - -elseif (WIN32) -add_executable(${TORQUE_APP_NAME} WIN32 ${TORQUE_SOURCE_FILES}) - - set(TORQUE_CXX_FLAGS_COMMON_DEFAULT "-DUNICODE -D_UNICODE -D_CRT_SECURE_NO_WARNINGS /MP /Ob2 /Oi /Ot /Oy /GT /Zi /W4 /nologo /GF /EHsc /GS- /Gy- /Qpar- /fp:precise /fp:except- /GR /Zc:wchar_t-" ) - if( TORQUE_CPU_X32 ) - set(TORQUE_CXX_FLAGS_COMMON_DEFAULT "${TORQUE_CXX_FLAGS_COMMON_DEFAULT} /arch:SSE2") - endif() - set(TORQUE_CXX_FLAGS_COMMON ${TORQUE_CXX_FLAGS_COMMON_DEFAULT} CACHE STRING "") - mark_as_advanced(TORQUE_CXX_FLAGS_COMMON) - - set(TORQUE_CXX_FLAGS_EXECUTABLES "/wd4018 /wd4100 /wd4121 /wd4127 /wd4130 /wd4244 /wd4245 /wd4389 /wd4511 /wd4512 /wd4800 /wd4995" CACHE STRING "") - mark_as_advanced(TORQUE_CXX_FLAGS_EXECUTABLES) - - set(TORQUE_CXX_FLAGS "${TORQUE_CXX_FLAGS_COMMON_DEFAULT} ${TORQUE_CXX_FLAGS_EXECUTABLES}" CACHE STRING "") - mark_as_advanced(TORQUE_CXX_FLAGS) - -# NOTE: On Windows, /Zc:wchar_t- is necessary otherwise you get unicode errors -set_target_properties(${TORQUE_APP_NAME} PROPERTIES COMPILE_FLAGS "${TORQUE_CXX_FLAGS}") -if (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) - set_target_properties(TorqueEngine PROPERTIES COMPILE_FLAGS "${TORQUE_CXX_FLAGS_COMMON_DEFAULT}") -endif (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) -else() -add_executable(${TORQUE_APP_NAME} ${TORQUE_SOURCE_FILES}) - -# NOTE: On Linux, we set the rpath to ./ so that shared objects next to the executable are used -set_target_properties(${TORQUE_APP_NAME} PROPERTIES LINK_FLAGS "-Wl,-rpath,./") -endif() - - -if(MSVC) - # Match projectGenerator naming for executables - set(OUTPUT_CONFIG DEBUG MINSIZEREL RELWITHDEBINFO) - set(OUTPUT_SUFFIX DEBUG MINSIZE OPTIMIZEDDEBUG) - foreach(INDEX RANGE 2) - list(GET OUTPUT_CONFIG ${INDEX} CONF) - list(GET OUTPUT_SUFFIX ${INDEX} SUFFIX) - set_property(TARGET ${TORQUE_APP_NAME} PROPERTY OUTPUT_NAME_${CONF} ${TORQUE_APP_NAME}_${SUFFIX}) - if (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) - set_property(TARGET TorqueEngine PROPERTY ${CONF}_POSTFIX "_${SUFFIX}") - set_property(TARGET TorqueEngine PROPERTY ${CONF}_OUTPUT_NAME ${TORQUE_APP_NAME}) - endif (TORQUE_DYNAMIC_LIBRARY AND NOT TORQUE_TESTING) - endforeach() - # Set Visual Studio startup project - set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${TORQUE_APP_NAME}) -endif() - -set_property(GLOBAL PROPERTY USE_FOLDERS ON) -foreach (TORQUE_LIBRARY ${TORQUE_LINK_LIBRARIES}) -set_target_properties(${TORQUE_LIBRARY} PROPERTIES -FOLDER "Libraries") -endforeach() - -target_compile_definitions(${TORQUE_APP_NAME} PUBLIC ${TORQUE_COMPILE_DEFINITIONS}) -target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_LIBRARIES}) - -if(APPLE) -target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_FRAMEWORKS}) -endif(APPLE) - -if(WIN32) -target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_WINDOWS}) -endif(WIN32) - -if(UNIX AND NOT APPLE) -target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_LINUX}) -endif(UNIX AND NOT APPLE) - -target_link_options(${TORQUE_APP_NAME} PUBLIC ${TORQUE_LINK_OPTIONS}) -if (TORQUE_TARGET_PROPERTIES) -set_target_properties(${TORQUE_APP_NAME} PROPERTIES ${TORQUE_TARGET_PROPERTIES}) -endif (TORQUE_TARGET_PROPERTIES) -target_include_directories(${TORQUE_APP_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_BINARY_DIR}/temp" ${TORQUE_INCLUDE_DIRECTORIES}) - -if(TORQUE_TESTING) -if(WIN32) - target_link_options(${TORQUE_APP_NAME} PRIVATE "/SUBSYSTEM:CONSOLE") - set_target_properties(gtest PROPERTIES COMPILE_FLAGS "/Zc:wchar_t-") - set_target_properties(gmock PROPERTIES COMPILE_FLAGS "/Zc:wchar_t-") - endif() -endif(TORQUE_TESTING) - -append_defs() - -# Process library binaries - these are coming from modules that are providing links to external, precompiled code that should be included -# with the executable. This is done because on Windows, the .lib is separate from the .dll so we can't automatically scan for shared -# objects in our link libraries in that case. -foreach (LIBRARY_BINARY ${TORQUE_ADDITIONAL_LIBRARY_BINARIES}) -if (APPLE) - # For OSX, we want these binaries to be copied to the Frameworks directory - add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}/${TORQUE_APP_NAME}.app/Contents/Frameworks/$(CONFIGURATION)") -else() - # All other platforms expect the file next to the executable - add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}") -endif (APPLE) -endforeach() - -# Process link libraries for dynamic links - we do this on OSX/Linux to ensure the binaries end up in the correct App directory -# as in the root CMake we force everything to be in game. This is necessary because on these platforms these are considered "libraries" -# and not runtime binaries like we configure in the root CMake. We don't globally set library outputs to avoid outputting eg. a files to -# our game directory. -if (UNIX) -get_target_property(GAME_LINK_LIBRARIES ${TORQUE_APP_NAME} LINK_LIBRARIES) -foreach (GAME_LINK_LIBRARY ${GAME_LINK_LIBRARIES}) - # For eg. OSX some links are not valid targets - for example frameworks provided by OS - if (TARGET ${GAME_LINK_LIBRARY}) - get_target_property(LINK_LIBRARY_TYPE ${GAME_LINK_LIBRARY} TYPE) - # Only pay attention to shared libraries and make them output to the app resources - if ("${LINK_LIBRARY_TYPE}" STREQUAL "SHARED_LIBRARY") - if (APPLE) - set_target_properties(${GAME_LINK_LIBRARY} PROPERTIES - XCODE_ATTRIBUTE_INSTALL_PATH "@rpath") - else() - set_target_properties(${GAME_LINK_LIBRARY} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${TORQUE_APP_GAME_DIRECTORY}") - endif(APPLE) - endif() - endif() -endforeach() -endif (UNIX) \ No newline at end of file diff --git a/Engine/source/torqueMacosEngine.cmake b/Engine/source/torqueMacosEngine.cmake deleted file mode 100644 index 4ca95cd25..000000000 --- a/Engine/source/torqueMacosEngine.cmake +++ /dev/null @@ -1,283 +0,0 @@ - -set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} __MACOSX__) - -################# Collect Source Files ################### - -# Handle app -torqueAddSourceDirectories("app" "app/net") - -# Handle console -torqueAddSourceDirectories("console") -torqueAddSourceDirectories("console/torquescript") - -# Handle Platform -torqueAddSourceDirectories("platform" "platform/threads" "platform/async" - "platform/input" "platform/output") - -torqueAddSourceDirectories("platform/nativeDialogs") -# Handle T3D -torqueAddSourceDirectories("T3D/fps" "T3D/fx" "T3D/vehicles" "T3D/physics" - "T3D/decal" "T3D/sfx" "T3D/gameBase" "T3D/turret" - "T3D/lighting" "T3D/gameOBjects" "T3D/components" - "T3D/systems" "T3D/assets" "T3D" "T3D/gameBase/std") - -# Handle TS -torqueAddSourceDirectories("ts" "ts/collada" "ts/assimp" "ts/loader" "ts/arch") - -# Handle SFX - OpenAL is handled as a module later on -torqueAddSourceDirectories("sfx" "sfx/media" "sfx/null") -if(TORQUE_SFX_OPENAL AND NOT TORQUE_DEDICATED) - torqueAddSourceDirectories("sfx/openal") - torqueAddSourceDirectories("sfx/openal/mac") -endif() - -# Handle GFX -torqueAddSourceDirectories("gfx" "gfx/Null" "gfx/test" "gfx/bitmap" "gfx/bitmap/loaders" - "gfx/util" "gfx/video" "gfx/sim" ) - -if (TORQUE_OPENGL) - torqueAddSourceDirectories("gfx/gl" "gfx/gl/sdl" "gfx/gl/tGL") -endif (TORQUE_OPENGL) - -# Handle core -torqueAddSourceDirectories("core" "core/stream" "core/strings" "core/util" - "core/util/journal" "core/util/zip" "core/util/compressors") -# Handle GUI -torqueAddSourceDirectories("gui" "gui/buttons" "gui/containers" "gui/controls" "gui/core" - "gui/game" "gui/shiny" "gui/utility" "gui/3d") - -# Handle postFX -torqueAddSourceDirectories("postFx") - -# Handle Windowmanager -torqueAddSourceDirectories("windowManager" "windowManager/torque" "windowManager/sdl") - -# Handle scene -torqueAddSourceDirectories("scene" "scene/culling" "scene/zones" "scene/mixin") - -# Handle math -torqueAddSourceDirectories("math" "math/util") - -# Handle persistence -torqueAddSourceDirectories("persistence/taml" "persistence/taml/binary" "persistence/taml/xml") - -# Handle Cinterface -torqueAddSourceDirectories("cinterface") - -# Handle util -torqueAddSourceDirectories("util" "util/messaging") - -# Handle assets -torqueAddSourceDirectories("assets") - -# Handle Sim -torqueAddSourceDirectories("sim") - -# Handle module -torqueAddSourceDirectories("module") - -# Handle forest -torqueAddSourceDirectories("forest" "forest/ts") -if(TORQUE_OPENGL) - torqueAddSourceDirectories("forest" "forest/glsl") -endif(TORQUE_OPENGL) - -# Handle shadergen -torqueAddSourceDirectories("shaderGen") - -if (TORQUE_OPENGL) - torqueAddSourceDirectories("shaderGen/GLSL") -endif (TORQUE_OPENGL) - -# Handle terrain -torqueAddSourceDirectories("terrain") - -if (TORQUE_OPENGL) - torqueAddSourceDirectories("terrain/glsl") -endif (TORQUE_OPENGL) - -# Handle Materials -torqueAddSourceDirectories("materials") - -# Handle collision -torqueAddSourceDirectories("collision") - -# Handle lighting -torqueAddSourceDirectories("lighting" "lighting/common" - "lighting/shadowMap") - -if (TORQUE_ADVANCED_LIGHTING) - torqueAddSourceDirectories("lighting/advanced") - if (TORQUE_OPENGL) - torqueAddSourceDirectories("lighting/advanced/glsl") - endif (TORQUE_OPENGL) -endif (TORQUE_ADVANCED_LIGHTING) - -if (TORQUE_BASIC_LIGHTING) - torqueAddSourceDirectories("lighting/basic" "lighting/basic/shadowMap") -endif (TORQUE_BASIC_LIGHTING) - -# Handle environment -torqueAddSourceDirectories("environment") - -# Handle renderInstance -torqueAddSourceDirectories("renderInstance") - -# Handle i18n -torqueAddSourceDirectories("i18n") - -# Handle posix -torqueAddSourceDirectories("platformPOSIX") -torqueAddSourceDirectories("platformMac") - -# Handle platformSDL -torqueAddSourceDirectories("platformSDL" "platformSDL/threads") - -if(TORQUE_TESTING) - torqueAddSourceDirectories("testing") - set(TORQUE_COMPILE_DEFINITIONS ${TORQUE_COMPILE_DEFINITIONS} TORQUE_SHARED SDL_MAIN_HANDLED) -endif(TORQUE_TESTING) - -# Add the collected files to our engine group -source_group(TREE "${CMAKE_SOURCE_DIR}/Engine/source" PREFIX "Engine" FILES ${TORQUE_SOURCE_FILES}) -file(GLOB_RECURSE TORQUE_APP_GAME_SOURCES "${TORQUE_APP_ROOT_DIRECTORY}/source/*.cpp" "${TORQUE_APP_ROOT_DIRECTORY}/source/*.h") -source_group(TREE "${TORQUE_APP_ROOT_DIRECTORY}/source" PREFIX "Source Files" FILES ${TORQUE_APP_GAME_SOURCES}) -set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} "${TORQUE_APP_ROOT_DIRECTORY}/source") - -################# Engine Module Handling ################### - -set(TORQUE_MODULE_PATHS "${CMAKE_SOURCE_DIR}/Tools/CMake/modules" "${TORQUE_APP_GAME_DIRECTORY}/data") -if (NOT "${TORQUE_MODULE_USER_PATH}" STREQUAL "") - list(APPEND TORQUE_MODULE_PATHS "${TORQUE_MODULE_USER_PATH}") -endif() - -# Before doing module scanning, store away the engine sources - we do this so that modules -# can be placed into the proper filters -set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES} ${TORQUE_APP_GAME_SOURCES}) -set(TORQUE_SOURCE_FILES "") - -foreach (TORQUE_MODULE_PATH ${TORQUE_MODULE_PATHS}) - # First find simple cmake scripts, mostly used for in-engine modules - file(GLOB MODULE_SCRIPTS "${TORQUE_MODULE_PATH}/*.cmake") - foreach (TORQUE_MODULE_SCRIPT ${MODULE_SCRIPTS}) - include(${TORQUE_MODULE_SCRIPT}) - - # Add this script's collected files to our Engine group - source_group(TREE "${CMAKE_SOURCE_DIR}/Engine" PREFIX "Engine" FILES ${TORQUE_SOURCE_FILES}) - - set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${TORQUE_SOURCE_FILES}) - set(TORQUE_SOURCE_FILES "") - endforeach() - - # Next find sub projects, these can introduce new source files - SUBDIRLIST(POSSIBLE_PROJECTS "${TORQUE_MODULE_PATH}") - foreach (POSSIBLE_PROJECT ${POSSIBLE_PROJECTS}) - # Retrieve the absolute path of this possible project - get_filename_component(POSSIBLE_PROJECT_ABSOLUTEPATH "${POSSIBLE_PROJECT}" - REALPATH BASE_DIR "${TORQUE_MODULE_PATH}") - - if (EXISTS "${POSSIBLE_PROJECT_ABSOLUTEPATH}/CMakeLists.txt") - add_subdirectory("${POSSIBLE_PROJECT_ABSOLUTEPATH}" ${CMAKE_BINARY_DIR}/temp/${POSSIBLE_PROJECT} EXCLUDE_FROM_ALL) - source_group(TREE "${POSSIBLE_PROJECT_ABSOLUTEPATH}" PREFIX "Modules/${POSSIBLE_PROJECT}" FILES ${TORQUE_SOURCE_FILES}) - - set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${TORQUE_SOURCE_FILES}) - set(TORQUE_SOURCE_FILES "") - elseif(NOT EXISTS "${POSSIBLE_PROJECT_ABSOLUTEPATH}/*.cmake") - file(GLOB_RECURSE MODULE_SOURCE "${POSSIBLE_PROJECT_ABSOLUTEPATH}/source/*.h" "${POSSIBLE_PROJECT_ABSOLUTEPATH}/source/*.cpp") - #message(STATUS "MODULE_SOURCE:${MODULE_SOURCE}") - source_group(TREE "${POSSIBLE_PROJECT_ABSOLUTEPATH}" PREFIX "Modules/${POSSIBLE_PROJECT}/" FILES ${MODULE_SOURCE}) - set(TORQUE_SOURCE_FILES_TEMPORARY ${TORQUE_SOURCE_FILES_TEMPORARY} ${MODULE_SOURCE}) - endif() - endforeach() -endforeach() - -set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES_TEMPORARY}) - -################# Library Post-build Handling ################### -set(TORQUE_LIBRARY_PATHS "${CMAKE_SOURCE_DIR}/Engine/lib" "${TORQUE_APP_GAME_DIRECTORY}/data") -if (NOT "${TORQUE_LIBRARY_USER_PATH}" STREQUAL "") - list(APPEND TORQUE_LIBRARY_PATHS "${TORQUE_LIBRARY_USER_PATH}") -endif() - -foreach (TORQUE_LIBRARY_PATH ${TORQUE_LIBRARY_PATHS}) - # First find simple cmake scripts, mostly used for in-engine libraries - file(GLOB_RECURSE LIBRARY_SCRIPTS "${TORQUE_LIBRARY_PATH}/*Torque_postBuild.cmake") - #message(STATUS "LIBRARY_SCRIPTS:${LIBRARY_SCRIPTS}") - foreach (TORQUE_LIBRARY_SCRIPT ${LIBRARY_SCRIPTS}) - include(${TORQUE_LIBRARY_SCRIPT}) - endforeach() -endforeach() -################# Dynamic File Configuration ################### - -set(MACOSX_RESOURCES "${CMAKE_SOURCE_DIR}/Tools/CMake/torque.icns" - "${TORQUE_APP_GAME_DIRECTORY}/data" - "${TORQUE_APP_GAME_DIRECTORY}/core" - "${TORQUE_APP_GAME_DIRECTORY}/tools" - "${TORQUE_APP_GAME_DIRECTORY}/main.${TORQUE_SCRIPT_EXTENSION}") - -set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_MAC_SOURCES} ${MACOSX_RESOURCES}) - -source_group("Resources" FILES ${MACOSX_RESOURCES}) - -set_source_files_properties(${MACOSX_RESOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") - -set(EXECUTABLE_NAME "${TORQUE_APP_NAME}") -configure_file("${CMAKE_SOURCE_DIR}/Tools/CMake/Info.plist.in" "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" COPYONLY) - -addDef(TORQUE_DEBUG Debug) -addDef(TORQUE_RELEASE "RelWithDebInfo;Release") -addDef(TORQUE_ENABLE_ASSERTS "Debug;RelWithDebInfo") -addDef(TORQUE_DEBUG_GFX_MODE "RelWithDebInfo") -addDef(TORQUE_OGGVORBIS) - -if(NOT TORQUE_SDL) - filterOut("platform/nativeDialogs/fileDialog.cpp" ) -endif() -if(NOT TORQUE_OPENGL) - filterOut("platformSDL/sdlPlatformGL.cpp") -endif() -if (NOT TORQUE_NET_CURL) - filterOut("app/net/httpObject.h" "app/net/httpObject.cpp") -endif() - -add_executable(${TORQUE_APP_NAME} MACOSX_BUNDLE ${TORQUE_SOURCE_FILES}) -set_target_properties(${TORQUE_APP_NAME} PROPERTIES - BUNDLE true - MACOSX_BUNDLE_INFO_PLIST "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" - XCODE_ATTRIBUTE_INSTALL_PATH "/Applications" - XCODE_ATTRIBUTE_SKIP_INSTALL "No" - MACOSX_RPATH TRUE) - - set_property(GLOBAL PROPERTY USE_FOLDERS ON) -foreach (TORQUE_LIBRARY ${TORQUE_LINK_LIBRARIES}) - set_target_properties(${TORQUE_LIBRARY} PROPERTIES - FOLDER "Libraries") -endforeach() - -target_compile_definitions(${TORQUE_APP_NAME} PUBLIC ${TORQUE_COMPILE_DEFINITIONS}) -target_link_libraries(${TORQUE_APP_NAME} ${TORQUE_LINK_LIBRARIES} ${TORQUE_LINK_FRAMEWORKS}) - -target_link_options(${TORQUE_APP_NAME} PUBLIC ${TORQUE_LINK_OPTIONS}) -if (TORQUE_TARGET_PROPERTIES) - set_target_properties(${TORQUE_APP_NAME} PROPERTIES ${TORQUE_TARGET_PROPERTIES}) -endif (TORQUE_TARGET_PROPERTIES) -target_include_directories(${TORQUE_APP_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_BINARY_DIR}/temp" ${TORQUE_INCLUDE_DIRECTORIES}) - -append_defs() - -foreach (LIBRARY_BINARY ${TORQUE_ADDITIONAL_LIBRARY_BINARIES}) - add_custom_command(TARGET ${TORQUE_APP_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${LIBRARY_BINARY} "${TORQUE_APP_GAME_DIRECTORY}/${TORQUE_APP_NAME}.app/Contents/Frameworks/$(CONFIGURATION)") -endforeach() - -get_target_property(GAME_LINK_LIBRARIES ${TORQUE_APP_NAME} LINK_LIBRARIES) -foreach (GAME_LINK_LIBRARY ${GAME_LINK_LIBRARIES}) - if (TARGET ${GAME_LINK_LIBRARY}) - get_target_property(LINK_LIBRARY_TYPE ${GAME_LINK_LIBRARY} TYPE) - # Only pay attention to shared libraries and make them output to the app resources - if ("${LINK_LIBRARY_TYPE}" STREQUAL "SHARED_LIBRARY") - set_target_properties(${GAME_LINK_LIBRARY} PROPERTIES - XCODE_ATTRIBUTE_INSTALL_PATH "@rpath") - endif() - endif() -endforeach() - diff --git a/Tools/CMake/platformconfigs/torqueMacosxconfig.cmake b/Tools/CMake/torqueMacOSconfigs.cmake similarity index 99% rename from Tools/CMake/platformconfigs/torqueMacosxconfig.cmake rename to Tools/CMake/torqueMacOSconfigs.cmake index 2d1c6e96c..0e583d0b4 100644 --- a/Tools/CMake/platformconfigs/torqueMacosxconfig.cmake +++ b/Tools/CMake/torqueMacOSconfigs.cmake @@ -1,3 +1,4 @@ +if(APPLE) #detect Architecture enable_language(OBJC) enable_language(OBJCXX) @@ -160,4 +161,4 @@ set(CMAKE_XCODE_ATTRIBUTE_LINKER_DISPLAYS_MANGLED_NAMES[variant=Debug] YES) set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH[variant=Debug] YES) set(CMAKE_XCODE_ATTRIBUTE_ENABLE_TESTABILITY[variant=Debug] YES) -add_subdirectory(${TORQUE_ENGINE_DIRECTORY}) +endif(APPLE) \ No newline at end of file From 852ed8f2258c186fd27399be115734d1a1a2d884 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Sat, 21 Oct 2023 23:19:02 +0100 Subject: [PATCH 017/122] Sfx playlist asset working (#1109) * GroundWork -Reverted SFXPlaylist since it is going to be made from an asset now instead. -Added extra options to soundAssets description. -SFXPlaylist may need an onAdd function * Update sfxController.cpp * SFXPlaylist data -Added sfxPlaylist init persist fields for the slots to sound asset -Added logic to fil sfxPlaylist if more than 1 slot is filled * Update SoundAsset.cpp to stop git ci complaining, assetImporter........ * Update SoundAsset.h * sfxPlaylist -Fix: incomplete type error -Added onAdd and onRemove to playlist -SoundAsset getProfile define now returns playlist if the asset is a playlist. * Update SoundAsset.h -updated asset array to return playlist or profile depending on what the asset is * SFXPlaylist working -SFXPlaylist works AudioChannelDefault gets its volume set to 0 for some reason and was throwing off making sfxPlaylist inaudible. Still an exception when closing if using a playlist trips on line 355 of sfxSound * Update sfxSound.h * setSoundFile index null fix * Update SoundAsset.h * Update SoundAsset.h * netstream safety in case of a null asset assignment * Update sfxController.cpp added safeties around a null playlist trying to play. * Update with Az's asset err code changes --------- Co-authored-by: AzaezelX --- Engine/source/T3D/assets/SoundAsset.cpp | 295 +++++++++++++++--- Engine/source/T3D/assets/SoundAsset.h | 81 +++-- Engine/source/T3D/fx/explosion.cpp | 2 +- Engine/source/T3D/fx/splash.cpp | 2 +- .../T3D/gameBase/gameConnectionEvents.cpp | 4 +- Engine/source/T3D/sfx/sfxEmitter.cpp | 33 +- Engine/source/T3D/sfx/sfxEmitter.h | 2 +- Engine/source/T3D/shapeBase.cpp | 2 +- Engine/source/T3D/shapeImage.cpp | 4 +- Engine/source/sfx/sfxController.cpp | 13 +- Engine/source/sfx/sfxPlayList.cpp | 247 +++++++++------ Engine/source/sfx/sfxPlayList.h | 46 +-- Engine/source/sfx/sfxSound.h | 2 +- Engine/source/sfx/sfxSource.cpp | 62 ++-- 14 files changed, 550 insertions(+), 245 deletions(-) diff --git a/Engine/source/T3D/assets/SoundAsset.cpp b/Engine/source/T3D/assets/SoundAsset.cpp index dd6ab9a0a..3f3a3bf25 100644 --- a/Engine/source/T3D/assets/SoundAsset.cpp +++ b/Engine/source/T3D/assets/SoundAsset.cpp @@ -44,6 +44,10 @@ #include "sfx/sfxSource.h" #endif +#ifndef _SFXPROFILE_H_ +#include "sfx/sfxProfile.h" +#endif // !_SFXPROFILE_H_ + // Debug Profiling. #include "platform/profiler.h" #include "sfx/sfxTypes.h" @@ -119,8 +123,28 @@ const String SoundAsset::mErrCodeStrings[] = SoundAsset::SoundAsset() : AssetBase() { - mSoundFile = StringTable->EmptyString(); - mSoundPath = StringTable->EmptyString(); + dMemset(mPlaylist.mSlots.mReplayMode, 0, sizeof(mPlaylist.mSlots.mReplayMode)); + dMemset(mPlaylist.mSlots.mTransitionIn, 0, sizeof(mPlaylist.mSlots.mTransitionIn)); + dMemset(mPlaylist.mSlots.mRepeatCount, 0, sizeof(mPlaylist.mSlots.mRepeatCount)); + dMemset(mPlaylist.mSlots.mState, 0, sizeof(mPlaylist.mSlots.mState)); + dMemset(mPlaylist.mSlots.mTrack, 0, sizeof(mPlaylist.mSlots.mTrack)); + dMemset(mPlaylist.mSlots.mStateMode, 0, sizeof(mPlaylist.mSlots.mStateMode)); + + for (U32 i = 0; i < SFXPlayList::NUM_SLOTS; i++) + { + mSoundFile[i] = StringTable->EmptyString(); + mSoundPath[i] = StringTable->EmptyString(); + + mPlaylist.mSlots.mTransitionOut[i] = SFXPlayList::TRANSITION_Wait; + mPlaylist.mSlots.mVolumeScale.mValue[i] = 1.f; + mPlaylist.mSlots.mPitchScale.mValue[i] = 1.f; + mPlaylist.mSlots.mFadeTimeIn.mValue[i] = -1.f; // Don't touch by default. + mPlaylist.mSlots.mFadeTimeOut.mValue[i] = -1.f; // Don't touch by default. + mPlaylist.mSlots.mMinDistance.mValue[i] = -1.f; // Don't touch by default. + mPlaylist.mSlots.mMaxDistance.mValue[i] = -1.f; // Don't touch by default. + + } + mSubtitleString = StringTable->EmptyString(); mLoadedState = AssetErrCode::NotLoaded; @@ -143,6 +167,14 @@ SoundAsset::SoundAsset() mProfileDesc.mPriority = 1.0f; mProfileDesc.mSourceGroup = NULL; + mIsPlaylist = false; + + mPlaylist.mNumSlotsToPlay = SFXPlayList::SFXPlaylistSettings::NUM_SLOTS; + mPlaylist.mRandomMode = SFXPlayList::RANDOM_NotRandom; + mPlaylist.mTrace = false; + mPlaylist.mLoopMode = SFXPlayList::LOOP_All; + mPlaylist.mActiveSlots = 12; + } //----------------------------------------------------------------------------- @@ -158,9 +190,79 @@ void SoundAsset::initPersistFields() docsURL; // Call parent. Parent::initPersistFields(); + addArray("slots", SFXPlayList::SFXPlaylistSettings::NUM_SLOTS); + addProtectedField("soundFile", TypeAssetLooseFilePath, Offset(mSoundFile, SoundAsset), + &_setSoundFile, &_getSoundFile, SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, "Path to the sound file."); - addProtectedField("soundFile", TypeAssetLooseFilePath, Offset(mSoundFile, SoundAsset), - &setSoundFile, &getSoundFile, "Path to the sound file."); + addField("replay", TYPEID< SFXPlayList::EReplayMode >(), Offset(mPlaylist.mSlots.mReplayMode, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Behavior when an already playing sound is encountered on this slot from a previous cycle.\n" + "Each slot can have an arbitrary number of sounds playing on it from previous cycles. This field determines " + "how SFXController will handle these sources."); + addField("transitionIn", TYPEID< SFXPlayList::ETransitionMode >(), Offset(mPlaylist.mSlots.mTransitionIn, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Behavior when moving into this slot.\n" + "After the delayIn time has expired (if any), this slot determines what the controller " + "will do before actually playing the slot."); + addField("transitionOut", TYPEID< SFXPlayList::ETransitionMode >(), Offset(mPlaylist.mSlots.mTransitionOut, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Behavior when moving out of this slot.\n" + "After the #detailTimeOut has expired (if any), this slot determines what the controller " + "will do before moving on to the next slot."); + addField("delayTimeIn", TypeF32, Offset(mPlaylist.mSlots.mDelayTimeIn.mValue, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Seconds to wait after moving into slot before #transitionIn."); + addField("delayTimeInVariance", TypePoint2F, Offset(mPlaylist.mSlots.mDelayTimeIn.mVariance, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Bounds on randomization of #delayTimeIn.\n\n" + "@ref SFXPlayList_randomization\n"); + addField("delayTimeOut", TypeF32, Offset(mPlaylist.mSlots.mDelayTimeOut.mValue, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Seconds to wait before moving out of slot after #transitionOut."); + addField("delayTimeOutVariance", TypePoint2F, Offset(mPlaylist.mSlots.mDelayTimeOut.mVariance, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Bounds on randomization of #delayTimeOut.\n\n" + "@ref SFXPlayList_randomization\n"); + addField("fadeTimeIn", TypeF32, Offset(mPlaylist.mSlots.mFadeTimeIn.mValue, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Seconds to fade sound in (-1 to use the track's own fadeInTime.)\n" + "@see SFXDescription::fadeTimeIn"); + addField("fadeTimeInVariance", TypePoint2F, Offset(mPlaylist.mSlots.mFadeTimeIn.mVariance, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Bounds on randomization of #fadeInTime.\n\n" + "@ref SFXPlayList_randomization\n"); + addField("fadeTimeOut", TypeF32, Offset(mPlaylist.mSlots.mFadeTimeOut.mValue, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Seconds to fade sound out (-1 to use the track's own fadeOutTime.)\n" + "@see SFXDescription::fadeTimeOut"); + addField("fadeTimeOutVariance", TypePoint2F, Offset(mPlaylist.mSlots.mFadeTimeOut.mVariance, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Bounds on randomization of #fadeOutTime\n\n" + "@ref SFXPlayList_randomization\n"); + addField("referenceDistance", TypeF32, Offset(mPlaylist.mSlots.mMinDistance.mValue, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "@c referenceDistance to set for 3D sounds in this slot (<1 to use @c referenceDistance of track's own description).\n" + "@see SFXDescription::referenceDistance"); + addField("referenceDistanceVariance", TypePoint2F, Offset(mPlaylist.mSlots.mMinDistance.mVariance, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Bounds on randomization of #referenceDistance.\n\n" + "@ref SFXPlayList_randomization\n"); + addField("maxDistance", TypeF32, Offset(mPlaylist.mSlots.mMaxDistance.mValue, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "@c maxDistance to apply to 3D sounds in this slot (<1 to use @c maxDistance of track's own description).\n" + "@see SFXDescription::maxDistance"); + addField("maxDistanceVariance", TypePoint2F, Offset(mPlaylist.mSlots.mMaxDistance.mVariance, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Bounds on randomization of #maxDistance.\n\n" + "@ref SFXPlayList_randomization\n"); + addField("volumeScale", TypeF32, Offset(mPlaylist.mSlots.mVolumeScale.mValue, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Scale factor to apply to volume of sounds played on this list slot.\n" + "This value will scale the actual volume level set on the track assigned to the slot, i.e. a value of 0.5 will " + "cause the track to play at half-volume."); + addField("volumeScaleVariance", TypePoint2F, Offset(mPlaylist.mSlots.mVolumeScale.mVariance, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Bounds on randomization of #volumeScale.\n\n" + "@ref SFXPlayList_randomization\n"); + addField("pitchScale", TypeF32, Offset(mPlaylist.mSlots.mPitchScale.mValue, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Scale factor to apply to pitch of sounds played on this list slot.\n" + "This value will scale the actual pitch set on the track assigned to the slot, i.e. a value of 0.5 will " + "cause the track to play at half its assigned speed."); + addField("pitchScaleVariance", TypePoint2F, Offset(mPlaylist.mSlots.mPitchScale.mVariance, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Bounds on randomization of #pitchScale.\n\n" + "@ref SFXPlayList_randomization\n"); + addField("repeatCount", TypeS32, Offset(mPlaylist.mSlots.mRepeatCount, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Number of times to loop this slot."); + addField("state", TypeSFXStateName, Offset(mPlaylist.mSlots.mState, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "State that must be active for this slot to play.\n\n" + "@ref SFXPlayList_states"); + addField("stateMode", TYPEID< SFXPlayList::EStateMode >(), Offset(mPlaylist.mSlots.mStateMode, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, + "Behavior when assigned state is deactivated while slot is playing.\n\n" + "@ref SFXPlayList_states"); + endArray("slots"); addField("pitchAdjust", TypeF32, Offset(mProfileDesc.mPitch, SoundAsset), "Adjustment of the pitch value 1 is default."); addField("volumeAdjust", TypeF32, Offset(mProfileDesc.mVolume, SoundAsset), "Adjustment to the volume."); @@ -170,16 +272,33 @@ void SoundAsset::initPersistFields() addField("isStreaming", TypeBool, Offset(mProfileDesc.mIsStreaming, SoundAsset), "Use streaming."); //....why? addField("useHardware", TypeBool, Offset(mProfileDesc.mUseHardware, SoundAsset), "Use hardware mixing for this sound."); - addField("minDistance", TypeF32, Offset(mProfileDesc.mMinDistance, SoundAsset), "Minimum distance for sound."); - // more like it. - addField("maxDistance", TypeF32, Offset(mProfileDesc.mMaxDistance, SoundAsset), "Max distance for sound."); - addField("coneInsideAngle", TypeS32, Offset(mProfileDesc.mConeInsideAngle, SoundAsset), "Cone inside angle."); - addField("coneOutsideAngle", TypeS32, Offset(mProfileDesc.mConeOutsideAngle, SoundAsset), "Cone outside angle."); - addField("coneOutsideVolume", TypeF32, Offset(mProfileDesc.mConeOutsideVolume, SoundAsset), "Cone outside volume."); - addField("rolloffFactor", TypeF32, Offset(mProfileDesc.mRolloffFactor, SoundAsset), "Rolloff factor."); - addField("scatterDistance", TypePoint3F, Offset(mProfileDesc.mScatterDistance, SoundAsset), "Randomization to the spacial position of the sound."); addField("sourceGroup", TypeSFXSourceName, Offset(mProfileDesc.mSourceGroup, SoundAsset), "Group that sources playing with this description should be put into."); + addField("preload", TypeBool, Offset(mPreload, SoundAsset), "Whether to preload sound data when the profile is added to system."); + addGroup("Fading"); + addField("fadeInTime", TypeF32, Offset(mProfileDesc.mFadeInTime, SoundAsset), "Number of seconds to gradually fade in volume from zero when playback starts."); + addField("fadeOutTime", TypeF32, Offset(mProfileDesc.mFadeOutTime, SoundAsset), "Number of seconds to gradually fade out volume down to zero when playback is stopped or paused."); + addField("fadeInEase", TypeEaseF, Offset(mProfileDesc.mFadeInEase, SoundAsset), "Easing curve for fade-in transition."); + addField("fadeOutEase", TypeEaseF, Offset(mProfileDesc.mFadeOutEase, SoundAsset), "Easing curve for fade-out transition."); + addField("fadeLoops", TypeBool, Offset(mProfileDesc.mFadeLoops, SoundAsset), "Fade each cycle of a loop in and/or out; otherwise only fade-in first cycle."); + endGroup("Fading"); + + addGroup("3D"); + addField("minDistance", TypeF32, Offset(mProfileDesc.mMinDistance, SoundAsset), "Minimum distance for sound."); + addField("maxDistance", TypeF32, Offset(mProfileDesc.mMaxDistance, SoundAsset), "Max distance for sound."); + addField("coneInsideAngle", TypeS32, Offset(mProfileDesc.mConeInsideAngle, SoundAsset), "Cone inside angle."); + addField("coneOutsideAngle", TypeS32, Offset(mProfileDesc.mConeOutsideAngle, SoundAsset), "Cone outside angle."); + addField("coneOutsideVolume", TypeF32, Offset(mProfileDesc.mConeOutsideVolume, SoundAsset), "Cone outside volume."); + addField("rolloffFactor", TypeF32, Offset(mProfileDesc.mRolloffFactor, SoundAsset), "Rolloff factor."); + addField("scatterDistance", TypePoint3F, Offset(mProfileDesc.mScatterDistance, SoundAsset), "Randomization to the spacial position of the sound."); + endGroup("3D"); + + addGroup("Playlist settings"); + addField("random", TYPEID< SFXPlayList::ERandomMode >(), Offset(mPlaylist.mRandomMode, SoundAsset), "Slot playback order randomization pattern."); + addField("loopMode", TYPEID< SFXPlayList::ELoopMode >(), Offset(mPlaylist.mLoopMode, SoundAsset), "Behavior when description has looping enabled."); + addField("numSlotsToPlay", TypeS32, Offset(mPlaylist.mNumSlotsToPlay, SoundAsset), "Number of slots to play."); + addField("trace", TypeBool, Offset(mPlaylist.mTrace, SoundAsset), "Enable/disable execution tracing for this playlist (local only)."); + endGroup("Playlist settings"); } //------------------------------------------------------------------------------ @@ -193,67 +312,153 @@ void SoundAsset::copyTo(SimObject* object) void SoundAsset::initializeAsset(void) { Parent::initializeAsset(); + for (U32 i = 0; i < SFXPlayList::SFXPlaylistSettings::NUM_SLOTS; i++) + { + if (i == 0 && mSoundFile[i] == StringTable->EmptyString()) + return; - if (mSoundFile == StringTable->EmptyString()) - return; + if (mSoundFile[i] == StringTable->EmptyString()) + break; - mSoundPath = getOwned() ? expandAssetFilePath(mSoundFile) : mSoundPath; + mSoundPath[i] = getOwned() ? expandAssetFilePath(mSoundFile[i]) : mSoundPath[i]; + } + + //loadSound(slotCount); + //mSoundPath = getOwned() ? expandAssetFilePath(mSoundFile) : mSoundPath; //loadSound(); } void SoundAsset::_onResourceChanged(const Torque::Path &path) { - if (path != Torque::Path(mSoundPath)) - return; + for (U32 i = 0; i < SFXPlayList::NUM_SLOTS; i++) + { + if (path != Torque::Path(mSoundPath[i])) + return; + } refreshAsset(); + //loadSound(slotCount); //loadSound(); } void SoundAsset::onAssetRefresh(void) { - if (mSoundFile == StringTable->EmptyString()) - return; + for (U32 i = 0; i < SFXPlayList::SFXPlaylistSettings::NUM_SLOTS; i++) + { + if (i == 0 && mSoundFile[i] == StringTable->EmptyString()) + return; + if (mSoundFile[i] == StringTable->EmptyString()) + break; + + mSoundPath[i] = getOwned() ? expandAssetFilePath(mSoundFile[i]) : mSoundPath[i]; + } + + //loadSound(slotCount); //Update - mSoundPath = getOwned() ? expandAssetFilePath(mSoundFile) : mSoundPath; + //mSoundPath = getOwned() ? expandAssetFilePath(mSoundFile) : mSoundPath; //loadSound(); } bool SoundAsset::loadSound() { if (mLoadedState == AssetErrCode::Ok) return true; - if (mSoundPath) + + // find out how many active slots we have. + U32 numSlots = 0; + for (U32 i = 0; i < SFXPlayList::SFXPlaylistSettings::NUM_SLOTS; i++) { - if (!Torque::FS::IsFile(mSoundPath)) - { - Con::errorf("SoundAsset::initializeAsset: Attempted to load file %s but it was not valid!", mSoundFile); - mLoadedState = BadFileReference; - mSFXProfile.setDescription(NULL); - mSFXProfile.setSoundFileName(StringTable->insert(StringTable->EmptyString())); - mSFXProfile.setPreload(false); + if (i == 0 && mSoundPath[i] == StringTable->EmptyString()) return false; - } - else - {// = new SFXProfile(mProfileDesc, mSoundFile, mPreload); - if (mProfileDesc.mSourceGroup == NULL) - mProfileDesc.mSourceGroup = dynamic_cast(Sim::findObject("AudioChannelMaster")); - mSFXProfile.setDescription(&mProfileDesc); - mSFXProfile.setSoundFileName(mSoundPath); - mSFXProfile.setPreload(mPreload); - //give it a nudge to preload if required - mSFXProfile.getBuffer(); - } + if (mSoundPath[i] == StringTable->EmptyString()) + break; + numSlots++; } + + + if (numSlots > 1) + { + mIsPlaylist = true; + + for (U32 i = 0; i < numSlots; i++) + { + if (mSoundPath[i]) + { + if (!Torque::FS::IsFile(mSoundPath[i])) + { + Con::errorf("SoundAsset::initializeAsset: Attempted to load file %s but it was not valid!", mSoundFile[i]); + mLoadedState = BadFileReference; + mSFXProfile[i].setDescription(NULL); + mSFXProfile[i].setSoundFileName(StringTable->insert(StringTable->EmptyString())); + mSFXProfile[i].setPreload(false); + return false; + } + else + {// = new SFXProfile(mProfileDesc, mSoundFile, mPreload); + if (mProfileDesc.mSourceGroup == NULL) + mProfileDesc.mSourceGroup = dynamic_cast(Sim::findObject("AudioChannelMaster")); + SFXProfile* trackProfile = new SFXProfile(); + trackProfile->setDescription(&mProfileDesc); + trackProfile->setSoundFileName(mSoundPath[i]); + trackProfile->setPreload(mPreload); + trackProfile->getBuffer(); + + mSFXProfile[i] = *trackProfile; + + mPlaylist.mSlots.mTrack[i] = trackProfile; + + } + } + } + + mPlaylist.setDescription(&mProfileDesc); + } + else + { + if (mSoundPath[0]) + { + if (!Torque::FS::IsFile(mSoundPath[0])) + { + Con::errorf("SoundAsset::initializeAsset: Attempted to load file %s but it was not valid!", mSoundFile[0]); + mLoadedState = BadFileReference; + mSFXProfile[0].setDescription(NULL); + mSFXProfile[0].setSoundFileName(StringTable->insert(StringTable->EmptyString())); + mSFXProfile[0].setPreload(false); + return false; + } + else + {// = new SFXProfile(mProfileDesc, mSoundFile, mPreload); + if (mProfileDesc.mSourceGroup == NULL) + mProfileDesc.mSourceGroup = dynamic_cast(Sim::findObject("AudioChannelMaster")); + mSFXProfile[0].setDescription(&mProfileDesc); + mSFXProfile[0].setSoundFileName(mSoundPath[0]); + mSFXProfile[0].setPreload(mPreload); + + //give it a nudge to preload if required + mSFXProfile[0].getBuffer(); + } + + } + } + mChangeSignal.trigger(); mLoadedState = Ok; return true; } -void SoundAsset::setSoundFile(const char* pSoundFile) +StringTableEntry SoundAsset::getSoundFile(const char* pSoundFile, const U32 slotId) +{ + for (U32 i = 0; i < 12; i++) + { + if(mSoundFile[i] == pSoundFile) + return mSoundFile[i]; + } +} + +void SoundAsset::setSoundFile(const char* pSoundFile, const U32 slotId) { // Sanity! AssertFatal(pSoundFile != NULL, "Cannot use a NULL sound file."); @@ -261,12 +466,12 @@ void SoundAsset::setSoundFile(const char* pSoundFile) // Fetch sound file. pSoundFile = StringTable->insert(pSoundFile, true); - // Ignore no change, - if (pSoundFile == mSoundFile) + //Ignore no change, + if (pSoundFile == mSoundFile[slotId]) return; // Update. - mSoundFile = getOwned() ? expandAssetFilePath(pSoundFile) : pSoundFile; + mSoundFile[slotId] = getOwned() ? expandAssetFilePath(pSoundFile) : pSoundFile; // Refresh the asset. refreshAsset(); @@ -353,11 +558,11 @@ DefineEngineMethod(SoundAsset, playSound, S32, (Point3F position), (Point3F::Zer "Plays the sound for this asset.\n" "@return (sound plays).\n") { - if (object->getSfxProfile()) + if (object->getSFXTrack()) { MatrixF transform; transform.setPosition(position); - SFXSource* source = SFX->playOnce(object->getSfxProfile(), &transform, NULL, -1); + SFXSource* source = SFX->playOnce(object->getSFXTrack(), &transform, NULL, -1); if(source) return source->getId(); else diff --git a/Engine/source/T3D/assets/SoundAsset.h b/Engine/source/T3D/assets/SoundAsset.h index 63f81194a..ccd10627b 100644 --- a/Engine/source/T3D/assets/SoundAsset.h +++ b/Engine/source/T3D/assets/SoundAsset.h @@ -55,6 +55,11 @@ #include "sfx/sfxDescription.h" #endif // !_SFXDESCRIPTION_H_ + +#ifndef _SFXTRACK_H_ +#include "sfx/sfxTrack.h" +#endif + #ifndef _SFXPROFILE_H_ #include "sfx/sfxProfile.h" #endif // !_SFXPROFILE_H_ @@ -63,8 +68,17 @@ #include "core/resourceManager.h" #endif +#ifndef _SFXPLAYLIST_H_ +#include "sfx/sfxPlayList.h" +#endif + +#ifndef _SFXTYPES_H_ +#include "sfx/sfxTypes.h" +#endif + #include "assetMacroHelpers.h" class SFXResource; +class SFXPlayList; //----------------------------------------------------------------------------- class SoundAsset : public AssetBase @@ -73,13 +87,17 @@ class SoundAsset : public AssetBase typedef AssetPtr ConcreteAssetPtr; protected: - StringTableEntry mSoundFile; - StringTableEntry mSoundPath; - SFXProfile mSFXProfile; + StringTableEntry mSoundFile[12]; + StringTableEntry mSoundPath[12]; + SFXProfile mSFXProfile[12]; + SFXDescription mProfileDesc; + SFXPlayList mPlaylist; // subtitles StringTableEntry mSubtitleString; bool mPreload; + bool mIsPlaylist; + //SFXPlayList::SlotData mSlots; /*These will be needed in the refactor! Resource mSoundResource; @@ -132,17 +150,20 @@ public: virtual void copyTo(SimObject* object); //SFXResource* getSound() { return mSoundResource; } - Resource getSoundResource() { loadSound(); return mSFXProfile.getResource(); } + Resource getSoundResource(const U32 slotId = 0) { loadSound(); return mSFXProfile[slotId].getResource(); } /// Declare Console Object. DECLARE_CONOBJECT(SoundAsset); - void setSoundFile(const char* pSoundFile); + void setSoundFile(const char* pSoundFile, const U32 slotId = 0); bool loadSound(); - inline StringTableEntry getSoundFile(void) const { return mSoundFile; }; - inline StringTableEntry getSoundPath(void) const { return mSoundPath; }; - SFXProfile* getSfxProfile() { return &mSFXProfile; } + StringTableEntry getSoundFile(const char* pSoundFile, const U32 slotId = 0); + inline StringTableEntry getSoundPath(const U32 slotId = 0) const { return mSoundPath[slotId]; }; + SFXProfile* getSfxProfile(const U32 slotId = 0) { return &mSFXProfile[slotId]; } + SFXPlayList* getSfxPlaylist() { return &mPlaylist; } + SFXTrack* getSFXTrack() { return mIsPlaylist ? dynamic_cast(&mPlaylist) : dynamic_cast(&mSFXProfile[0]); } SFXDescription* getSfxDescription() { return &mProfileDesc; } + bool isPlaylist(){ return mIsPlaylist; } bool isLoop() { return mProfileDesc.mIsLooping; } bool is3D() { return mProfileDesc.mIs3D; } @@ -156,8 +177,8 @@ protected: void _onResourceChanged(const Torque::Path & path); virtual void onAssetRefresh(void); - static bool setSoundFile(void *obj, const char *index, const char *data) { static_cast(obj)->setSoundFile(data); return false; } - static const char* getSoundFile(void* obj, const char* data) { return static_cast(obj)->getSoundFile(); } + static bool _setSoundFile(void *obj, const char *index, const char *data) { static_cast(obj)->setSoundFile(data, index ? dAtoi(index) : 0); return false; } + static const char* _getSoundFile(void* obj, const char* data) { return static_cast(obj)->getSoundFile(data); } }; DefineConsoleType(TypeSoundAssetPtr, SoundAsset) @@ -175,7 +196,7 @@ DefineConsoleType(TypeSoundAssetId, String) StringTableEntry m##name##Name; \ StringTableEntry m##name##AssetId;\ AssetPtr m##name##Asset = NULL;\ - SFXProfile* m##name##Profile = NULL;\ + SFXTrack* m##name##Profile = NULL;\ SFXDescription* m##name##Desc = NULL;\ SimObjectId m##name##SFXId = 0;\ public: \ @@ -268,11 +289,12 @@ public: \ {\ return m##name;\ }\ - SFXProfile* get##name##Profile()\ + SFXTrack* get##name##Profile()\ {\ if (get##name() != StringTable->EmptyString() && m##name##Asset.notNull()){\ - m##name##Profile = m##name##Asset->getSfxProfile();\ - return m##name##Profile;}\ + m##name##Profile = m##name##Asset->getSFXTrack(); \ + return m##name##Profile;\ + }\ return NULL;\ }\ SFXDescription* get##name##Description()\ @@ -308,9 +330,9 @@ public: \ {\ if(stream->writeFlag(Sim::findObject(m##name##Name)))\ {\ - SFXTrack* sndTrack;\ - Sim::findObject(m##name##Name, sndTrack);\ + SFXTrack* sndTrack = get##name##Profile();\ stream->writeRangedU32(SimObjectId(sndTrack->getId()), DataBlockObjectIdFirst, DataBlockObjectIdLast);\ + sfxWrite(stream, sndTrack);\ }\ else\ {\ @@ -330,7 +352,10 @@ public: \ {\ if(stream->readFlag())\ {\ + String errorStr;\ m##name##SFXId = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );\ + sfxReadAndResolve(stream, &m##name##Profile, errorStr);\ + Con::errorf("%s", errorStr.c_str());\ }\ else\ {\ @@ -359,7 +384,7 @@ public: \ StringTableEntry m##name##Name[max]; \ StringTableEntry m##name##AssetId[max];\ AssetPtr m##name##Asset[max];\ - SFXProfile* m##name##Profile[max];\ + SFXTrack* m##name##Profile[max];\ SimObjectId m##name##SFXId[max];\ public: \ const StringTableEntry get##name##File(const U32& index) const { return m##name##Name[index]; }\ @@ -461,10 +486,10 @@ public: \ return ResourceManager::get().load( "" );\ return m##name[id];\ }\ - SFXProfile* get##name##Profile(const U32& id)\ + SFXTrack* get##name##Profile(const U32& id)\ {\ - if (get##name(id) != StringTable->EmptyString() && m##name##Asset[id].notNull())\ - return m##name##Asset[id]->getSfxProfile();\ + if (m##name##Asset[id].notNull())\ + return m##name##Asset[id]->getSFXTrack(); \ return NULL;\ }\ bool is##name##Valid(const U32& id) {return (get##name(id) != StringTable->EmptyString() && m##name##Asset[id] && m##name##Asset[id]->getStatus() == AssetBase::Ok); } @@ -518,9 +543,12 @@ if (m##name##AssetId[index] != StringTable->EmptyString())\ {\ if(stream->writeFlag(Sim::findObject(m##name##Name[index])))\ {\ - SFXTrack* sndTrack;\ - Sim::findObject(m##name##Name[index], sndTrack);\ - stream->writeRangedU32(SimObjectId(sndTrack->getId()), DataBlockObjectIdFirst, DataBlockObjectIdLast);\ + SFXTrack* sndTrack = get##name##Profile(index);\ + if(stream->writeFlag(sndTrack != nullptr))\ + {\ + stream->writeRangedU32(SimObjectId(sndTrack->getId()), DataBlockObjectIdFirst, DataBlockObjectIdLast);\ + sfxWrite(stream, sndTrack);\ + }\ }\ else\ {\ @@ -540,7 +568,12 @@ if (m##name##AssetId[index] != StringTable->EmptyString())\ {\ if(stream->readFlag())\ {\ - m##name##SFXId[index] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );\ + String errorStr;\ + if(stream->readFlag())\ + {\ + m##name##SFXId[index] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );\ + sfxReadAndResolve(stream, &m##name##Profile[index], errorStr);\ + }\ }\ else\ {\ diff --git a/Engine/source/T3D/fx/explosion.cpp b/Engine/source/T3D/fx/explosion.cpp index d9cfc1367..d4d621cdf 100644 --- a/Engine/source/T3D/fx/explosion.cpp +++ b/Engine/source/T3D/fx/explosion.cpp @@ -1403,7 +1403,7 @@ bool Explosion::explode() resetWorldBox(); } - SFXProfile* sound_prof = mDataBlock->getSoundProfile(); + SFXProfile* sound_prof = static_cast(mDataBlock->getSoundProfile()); if (sound_prof) { soundProfile_clone = sound_prof->cloneAndPerformSubstitutions(ss_object, ss_index); diff --git a/Engine/source/T3D/fx/splash.cpp b/Engine/source/T3D/fx/splash.cpp index 3b8c6da24..07c3a52f3 100644 --- a/Engine/source/T3D/fx/splash.cpp +++ b/Engine/source/T3D/fx/splash.cpp @@ -686,7 +686,7 @@ void Splash::spawnExplosion() /// could just play the explosion one, but explosion could be weapon specific, /// splash sound could be liquid specific. food for thought. - SFXProfile* sound_prof = mDataBlock->getSoundProfile(); + SFXTrack* sound_prof = mDataBlock->getSoundProfile(); if (sound_prof) { SFX->playOnce(sound_prof, &getTransform()); diff --git a/Engine/source/T3D/gameBase/gameConnectionEvents.cpp b/Engine/source/T3D/gameBase/gameConnectionEvents.cpp index 184d504bf..a24a80c87 100644 --- a/Engine/source/T3D/gameBase/gameConnectionEvents.cpp +++ b/Engine/source/T3D/gameBase/gameConnectionEvents.cpp @@ -387,9 +387,9 @@ void SimSoundAssetEvent::process(NetConnection* con) { if (mAsset->is3D()) - SFX->playOnce(mAsset->getSfxProfile(), &mTransform); + SFX->playOnce(mAsset->getSFXTrack(), &mTransform); else - SFX->playOnce(mAsset->getSfxProfile()); + SFX->playOnce(mAsset->getSFXTrack()); } diff --git a/Engine/source/T3D/sfx/sfxEmitter.cpp b/Engine/source/T3D/sfx/sfxEmitter.cpp index 2ff998eec..961e3efb1 100644 --- a/Engine/source/T3D/sfx/sfxEmitter.cpp +++ b/Engine/source/T3D/sfx/sfxEmitter.cpp @@ -106,8 +106,7 @@ SFXEmitter::SFXEmitter() mDescription.mFadeInTime = -1.f; mDescription.mFadeOutTime = -1.f; mInstanceDescription = &mDescription; - mLocalProfile.mFilename = StringTable->EmptyString(); - mLocalProfile._registerSignals(); + mLocalProfile = NULL; INIT_ASSET(Sound); @@ -119,7 +118,9 @@ SFXEmitter::SFXEmitter() SFXEmitter::~SFXEmitter() { - mLocalProfile.onRemove(); + if(mLocalProfile != NULL) + mLocalProfile->onRemove(); + SFX_DELETE( mSource ); } @@ -653,7 +654,7 @@ void SFXEmitter::_update() SFXStatus prevState = mSource ? mSource->getStatus() : SFXStatusNull; // are we overriding the asset properties? - bool useTrackDescriptionOnly = (mUseTrackDescriptionOnly && mSoundAsset.notNull() && mSoundAsset->getSfxProfile()); + bool useTrackDescriptionOnly = (mUseTrackDescriptionOnly && mSoundAsset.notNull() && getSoundProfile()); if (mSoundAsset.notNull()) { @@ -662,12 +663,12 @@ void SFXEmitter::_update() else mInstanceDescription = &mDescription; - mLocalProfile = *mSoundAsset->getSfxProfile(); - } - // Make sure all the settings are valid. - mInstanceDescription->validate(); - mLocalProfile.setDescription(mInstanceDescription); + mLocalProfile = getSoundProfile(); + // Make sure all the settings are valid. + mInstanceDescription->validate(); + mLocalProfile->setDescription(mInstanceDescription); + } const MatrixF& transform = getTransform(); const VectorF& velocity = getVelocity(); @@ -676,12 +677,12 @@ void SFXEmitter::_update() if( mDirty.test( Track | Is3D | IsLooping | IsStreaming | TrackOnly ) ) { SFX_DELETE( mSource ); - if (mLocalProfile.getSoundFileName().isNotEmpty()) + if (getSoundProfile()) { - mSource = SFX->createSource(&mLocalProfile, &transform, &velocity); + mSource = SFX->createSource(mLocalProfile, &transform, &velocity); if (!mSource) Con::errorf("SFXEmitter::_update() - failed to create sound for track %i (%s)", - mSoundAsset->getSfxProfile()->getId(), mSoundAsset->getSfxProfile()->getName()); + getSoundProfile()->getId(), getSoundProfile()->getName()); // If we're supposed to play when the emitter is // added to the scene then also restart playback @@ -1043,8 +1044,8 @@ SFXStatus SFXEmitter::_getPlaybackStatus() const bool SFXEmitter::is3D() const { - if( mSoundAsset.notNull() && mSoundAsset->getSfxProfile() != NULL ) - return mSoundAsset->getSfxProfile()->getDescription()->mIs3D; + if( mSoundAsset.notNull() ) + return mSoundAsset->getSfxDescription()->mIs3D; else return mInstanceDescription->mIs3D; } @@ -1080,8 +1081,8 @@ void SFXEmitter::setScale( const VectorF &scale ) { F32 maxDistance; - if( mUseTrackDescriptionOnly && mSoundAsset.notNull() && mSoundAsset->getSfxProfile()) - maxDistance = mSoundAsset->getSfxProfile()->getDescription()->mMaxDistance; + if( mUseTrackDescriptionOnly && mSoundAsset.notNull() && getSoundProfile()) + maxDistance = mSoundAsset->getSfxDescription()->mMaxDistance; else { // Use the average of the three coords. diff --git a/Engine/source/T3D/sfx/sfxEmitter.h b/Engine/source/T3D/sfx/sfxEmitter.h index 134b6828c..103a92faf 100644 --- a/Engine/source/T3D/sfx/sfxEmitter.h +++ b/Engine/source/T3D/sfx/sfxEmitter.h @@ -116,7 +116,7 @@ class SFXEmitter : public SceneObject /// A local profile object used to coax the /// sound system to play a custom sound. - SFXProfile mLocalProfile; + SFXTrack* mLocalProfile; /// The description used by the local profile. SFXDescription mDescription; diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index c130b57b4..e6656fb15 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -2303,7 +2303,7 @@ void ShapeBase::updateAudioState(SoundThread& st) // if asset is valid, play if (st.asset->isAssetValid() ) { - st.sound = SFX->createSource( st.asset->getSfxProfile() , &getTransform() ); + st.sound = SFX->createSource( st.asset->getSFXTrack() , &getTransform() ); if ( st.sound ) st.sound->play(); } diff --git a/Engine/source/T3D/shapeImage.cpp b/Engine/source/T3D/shapeImage.cpp index c71c6f938..9ecddfc21 100644 --- a/Engine/source/T3D/shapeImage.cpp +++ b/Engine/source/T3D/shapeImage.cpp @@ -2785,7 +2785,7 @@ void ShapeBase::setImageState(U32 imageSlot, U32 newState, bool force) // Delete any loooping sounds that were in the previous state. // this is the crazy bit =/ needs to know prev state in order to stop sounds. // lastState does not return an id for the prev state so we keep track of it. - if (lastState->sound && lastState->sound->getSfxProfile()->getDescription()->mIsLooping) + if (lastState->sound && lastState->sound->getSFXTrack()->getDescription()->mIsLooping) { for (Vector::iterator i = image.mSoundSources.begin(); i != image.mSoundSources.end(); i++) SFX_DELETE((*i)); @@ -2799,7 +2799,7 @@ void ShapeBase::setImageState(U32 imageSlot, U32 newState, bool force) if (stateData.sound) { const Point3F& velocity = getVelocity(); - image.addSoundSource(SFX->createSource(stateData.sound->getSfxProfile(), &getRenderTransform(), &velocity)); + image.addSoundSource(SFX->createSource(stateData.sound->getSFXTrack(), &getRenderTransform(), &velocity)); } if (stateData.soundTrack) { diff --git a/Engine/source/sfx/sfxController.cpp b/Engine/source/sfx/sfxController.cpp index 0de38ad39..2e1c71b7f 100644 --- a/Engine/source/sfx/sfxController.cpp +++ b/Engine/source/sfx/sfxController.cpp @@ -167,7 +167,7 @@ void SFXController::_compileList( SFXPlayList* playList ) // If there's no track in this slot, ignore it. - if( !playList->getTrackProfile(slotIndex)) + if( !playList->getSlots().mTrack[slotIndex]) continue; // If this is a looped slot and the list is not set to loop @@ -394,7 +394,13 @@ bool SFXController::_execInsn() case OP_Play: { SFXPlayList* playList = getPlayList(); - SFXTrack* track = playList->getTrackProfile(insn.mSlotIndex); + if (playList == NULL) + { + endUpdate = true; + break; + } + + SFXTrack* track = playList->getSlots().mTrack[insn.mSlotIndex]; // Handle existing sources playing on this slot and find // whether we need to start a new source. @@ -817,6 +823,9 @@ void SFXController::_update() SFXPlayList* playList = getPlayList(); + if (!playList) + Parent::stop(); + // Check all sources against the current state setup and // take appropriate actions. diff --git a/Engine/source/sfx/sfxPlayList.cpp b/Engine/source/sfx/sfxPlayList.cpp index 4cc6a56e3..b763e4a14 100644 --- a/Engine/source/sfx/sfxPlayList.cpp +++ b/Engine/source/sfx/sfxPlayList.cpp @@ -23,6 +23,7 @@ #include "sfx/sfxPlayList.h" #include "sfx/sfxState.h" #include "sfx/sfxTypes.h" +#include "sfx/sfxDescription.h" #include "core/stream/bitStream.h" #include "math/mRandom.h" #include "math/mathTypes.h" @@ -218,10 +219,23 @@ SFXPlayList::SFXPlayList() : mRandomMode( RANDOM_NotRandom ), mLoopMode( LOOP_All ), mTrace( false ), - mNumSlotsToPlay( NUM_SLOTS ) + mNumSlotsToPlay( NUM_SLOTS ), + mActiveSlots(12) { - for (U32 i=0;iisTempClone()) + { + delete mDescription; + mDescription = 0; + } } //----------------------------------------------------------------------------- @@ -250,10 +264,10 @@ void SFXPlayList::initPersistFields() addArray( "slots", NUM_SLOTS ); - INITPERSISTFIELD_SOUNDASSET_ARRAY( Track, NUM_SLOTS, SFXPlayList, + addField("track", TypeSFXTrackName, Offset(mSlots.mTrack, SFXPlayList), NUM_SLOTS, "Track to play in this slot.\n" "This must be set for the slot to be considered for playback. Other settings for a slot " - "will not take effect except this field is set." ); + "will not take effect except this field is set."); addField( "replay", TYPEID< EReplayMode >(), Offset( mSlots.mReplayMode, SFXPlayList ), NUM_SLOTS, "Behavior when an already playing sound is encountered on this slot from a previous cycle.\n" "Each slot can have an arbitrary number of sounds playing on it from previous cycles. This field determines " @@ -340,32 +354,64 @@ void SFXPlayList::initPersistFields() //----------------------------------------------------------------------------- +U32 SFXPlayList::getNumSlots() +{ + U32 trackCount = 0; + for (U32 i = 0; i < NUM_SLOTS; i++) + { + if (mSlots.mTrack[i] == NULL) + { + return i; + } + trackCount++; + } + + return trackCount; +} + +bool SFXPlayList::isLooping() const +{ + // pretty useless in playlist, looping handled differently. + return false; +} + +bool SFXPlayList::onAdd() +{ + if (!Parent::onAdd()) + return false; + + mActiveSlots = getNumSlots(); + + validate(); + + return true; +} + +void SFXPlayList::onRemove() +{ + Parent::onRemove(); +} + bool SFXPlayList::preload( bool server, String& errorStr ) { if( !Parent::preload( server, errorStr ) ) return false; + + mActiveSlots = getNumSlots(); validate(); // Resolve SFXTracks and SFXStates on client. - + if( !server ) { - for( U32 i = 0; i < NUM_SLOTS; ++ i ) + for( U32 i = 0; i < mActiveSlots; ++ i ) { - StringTableEntry track = getTrack(i); - if (track != StringTable->EmptyString()) - { - _setTrack(getTrack(i), i); - if (!getTrackProfile(i)) - { - Con::errorf("SFXPlayList::Preload() - unable to find sfxProfile for asset %s", mTrackAssetId[i]); + if (!sfxResolve(&mSlots.mTrack[i], errorStr)) return false; - } - if (!sfxResolve(&mSlots.mState[i], errorStr)) + if (!sfxResolve(&mSlots.mState[i], errorStr)) return false; - } } } @@ -382,55 +428,57 @@ void SFXPlayList::packData( BitStream* stream ) stream->writeInt( mLoopMode, NUM_LOOP_MODE_BITS ); stream->writeInt( mNumSlotsToPlay, NUM_SLOTS_TO_PLAY_BITS ); - #define FOR_EACH_SLOT \ - for( U32 i = 0; i < NUM_SLOTS; ++ i ) - - FOR_EACH_SLOT stream->writeInt( mSlots.mReplayMode[ i ], NUM_REPLAY_MODE_BITS ); - FOR_EACH_SLOT stream->writeInt( mSlots.mTransitionIn[ i ], NUM_TRANSITION_MODE_BITS ); - FOR_EACH_SLOT stream->writeInt( mSlots.mTransitionOut[ i ], NUM_TRANSITION_MODE_BITS ); - FOR_EACH_SLOT stream->writeInt( mSlots.mStateMode[ i ], NUM_STATE_MODE_BITS ); - - FOR_EACH_SLOT if (stream->writeFlag(mSlots.mFadeTimeIn.mValue[ i ] != -1 )) - stream->write( mSlots.mFadeTimeIn.mValue[ i ] ); - FOR_EACH_SLOT if (stream->writeFlag( mSlots.mFadeTimeIn.mVariance[ i ][ 0 ] > 0)) - stream->write(mSlots.mFadeTimeIn.mVariance[ i ][ 0 ] ); - FOR_EACH_SLOT if (stream->writeFlag( mSlots.mFadeTimeIn.mVariance[ i ][ 1 ] > 0)) - stream->write(mSlots.mFadeTimeIn.mVariance[ i ][ 1 ] ); - FOR_EACH_SLOT if (stream->writeFlag(mSlots.mFadeTimeOut.mValue[ i ] != -1 )) - stream->write( mSlots.mFadeTimeOut.mValue[ i ] ); - FOR_EACH_SLOT if (stream->writeFlag(mSlots.mFadeTimeOut.mVariance[i][0] > 0)) - stream->write(mSlots.mFadeTimeOut.mVariance[i][0]); - FOR_EACH_SLOT if (stream->writeFlag(mSlots.mFadeTimeOut.mVariance[i][1] > 0)) - stream->write(mSlots.mFadeTimeOut.mVariance[i][1]); - FOR_EACH_SLOT if (stream->writeFlag(mSlots.mDelayTimeIn.mValue[ i ] > 0)) - stream->write(mSlots.mDelayTimeIn.mValue[ i ] ); - FOR_EACH_SLOT if (stream->writeFlag(mSlots.mDelayTimeIn.mVariance[ i ][ 0 ] > 0)) - stream->write(mSlots.mDelayTimeIn.mVariance[ i ][ 0 ] ); - FOR_EACH_SLOT if (stream->writeFlag(mSlots.mDelayTimeIn.mVariance[ i ][ 1 ] > 0)) - stream->write(mSlots.mDelayTimeIn.mVariance[ i ][ 1 ] ); - FOR_EACH_SLOT if (stream->writeFlag(mSlots.mDelayTimeOut.mValue[ i ] > 0)) - stream->write(mSlots.mDelayTimeOut.mValue[ i ] ); - FOR_EACH_SLOT if (stream->writeFlag(mSlots.mDelayTimeOut.mVariance[ i ][ 0 ] > 0)) - stream->write(mSlots.mDelayTimeOut.mVariance[ i ][ 0 ] ); - FOR_EACH_SLOT if (stream->writeFlag(mSlots.mDelayTimeOut.mVariance[ i ][ 1 ] > 0)) - stream->write(mSlots.mDelayTimeOut.mVariance[ i ][ 1 ] ); - FOR_EACH_SLOT if (stream->writeFlag(mSlots.mVolumeScale.mValue[ i ] != 1)) - stream->write(mSlots.mVolumeScale.mValue[ i ] ); - FOR_EACH_SLOT if (stream->writeFlag(mSlots.mVolumeScale.mVariance[ i ][ 0 ] > 0)) - stream->write(mSlots.mVolumeScale.mVariance[ i ][ 0 ] ); - FOR_EACH_SLOT if (stream->writeFlag(mSlots.mVolumeScale.mVariance[ i ][ 1 ] > 0)) - stream->write(mSlots.mVolumeScale.mVariance[ i ][ 1 ] ); - FOR_EACH_SLOT if (stream->writeFlag(mSlots.mPitchScale.mValue[ i ] != 1)) - stream->write(mSlots.mPitchScale.mValue[ i ] ); - FOR_EACH_SLOT if (stream->writeFlag(mSlots.mPitchScale.mVariance[ i ][ 0 ] > 0)) - stream->write(mSlots.mPitchScale.mVariance[ i ][ 0 ] ); - FOR_EACH_SLOT if (stream->writeFlag(mSlots.mPitchScale.mVariance[ i ][ 1 ] > 0)) - stream->write(mSlots.mPitchScale.mVariance[ i ][ 1 ] ); - FOR_EACH_SLOT if (stream->writeFlag( mSlots.mRepeatCount[ i ] > 0)) - stream->write( mSlots.mRepeatCount[ i ] ); - - FOR_EACH_SLOT sfxWrite( stream, mSlots.mState[ i ] ); - FOR_EACH_SLOT PACKDATA_SOUNDASSET_ARRAY(Track, i); + stream->writeInt(mActiveSlots, 8); + + for (U32 i = 0; i < mActiveSlots; ++i) + { + stream->writeInt(mSlots.mReplayMode[i], NUM_REPLAY_MODE_BITS); + stream->writeInt(mSlots.mTransitionIn[i], NUM_TRANSITION_MODE_BITS); + stream->writeInt(mSlots.mTransitionOut[i], NUM_TRANSITION_MODE_BITS); + stream->writeInt(mSlots.mStateMode[i], NUM_STATE_MODE_BITS); + + if (stream->writeFlag(mSlots.mFadeTimeIn.mValue[i] != -1)) + stream->write(mSlots.mFadeTimeIn.mValue[i]); + if (stream->writeFlag(mSlots.mFadeTimeIn.mVariance[i][0] > 0)) + stream->write(mSlots.mFadeTimeIn.mVariance[i][0]); + if (stream->writeFlag(mSlots.mFadeTimeIn.mVariance[i][1] > 0)) + stream->write(mSlots.mFadeTimeIn.mVariance[i][1]); + if (stream->writeFlag(mSlots.mFadeTimeOut.mValue[i] != -1)) + stream->write(mSlots.mFadeTimeOut.mValue[i]); + if (stream->writeFlag(mSlots.mFadeTimeOut.mVariance[i][0] > 0)) + stream->write(mSlots.mFadeTimeOut.mVariance[i][0]); + if (stream->writeFlag(mSlots.mFadeTimeOut.mVariance[i][1] > 0)) + stream->write(mSlots.mFadeTimeOut.mVariance[i][1]); + if (stream->writeFlag(mSlots.mDelayTimeIn.mValue[i] > 0)) + stream->write(mSlots.mDelayTimeIn.mValue[i]); + if (stream->writeFlag(mSlots.mDelayTimeIn.mVariance[i][0] > 0)) + stream->write(mSlots.mDelayTimeIn.mVariance[i][0]); + if (stream->writeFlag(mSlots.mDelayTimeIn.mVariance[i][1] > 0)) + stream->write(mSlots.mDelayTimeIn.mVariance[i][1]); + if (stream->writeFlag(mSlots.mDelayTimeOut.mValue[i] > 0)) + stream->write(mSlots.mDelayTimeOut.mValue[i]); + if (stream->writeFlag(mSlots.mDelayTimeOut.mVariance[i][0] > 0)) + stream->write(mSlots.mDelayTimeOut.mVariance[i][0]); + if (stream->writeFlag(mSlots.mDelayTimeOut.mVariance[i][1] > 0)) + stream->write(mSlots.mDelayTimeOut.mVariance[i][1]); + if (stream->writeFlag(mSlots.mVolumeScale.mValue[i] != 1)) + stream->write(mSlots.mVolumeScale.mValue[i]); + if (stream->writeFlag(mSlots.mVolumeScale.mVariance[i][0] > 0)) + stream->write(mSlots.mVolumeScale.mVariance[i][0]); + if (stream->writeFlag(mSlots.mVolumeScale.mVariance[i][1] > 0)) + stream->write(mSlots.mVolumeScale.mVariance[i][1]); + if (stream->writeFlag(mSlots.mPitchScale.mValue[i] != 1)) + stream->write(mSlots.mPitchScale.mValue[i]); + if (stream->writeFlag(mSlots.mPitchScale.mVariance[i][0] > 0)) + stream->write(mSlots.mPitchScale.mVariance[i][0]); + if (stream->writeFlag(mSlots.mPitchScale.mVariance[i][1] > 0)) + stream->write(mSlots.mPitchScale.mVariance[i][1]); + if (stream->writeFlag(mSlots.mRepeatCount[i] > 0)) + stream->write(mSlots.mRepeatCount[i]); + + sfxWrite(stream, mSlots.mState[i]); + sfxWrite(stream, mSlots.mTrack[i]); + } } //----------------------------------------------------------------------------- @@ -442,36 +490,39 @@ void SFXPlayList::unpackData( BitStream* stream ) mRandomMode = ( ERandomMode ) stream->readInt( NUM_RANDOM_MODE_BITS ); mLoopMode = ( ELoopMode ) stream->readInt( NUM_LOOP_MODE_BITS ); mNumSlotsToPlay = stream->readInt( NUM_SLOTS_TO_PLAY_BITS ); - - FOR_EACH_SLOT mSlots.mReplayMode[ i ] = ( EReplayMode ) stream->readInt( NUM_REPLAY_MODE_BITS ); - FOR_EACH_SLOT mSlots.mTransitionIn[ i ] = ( ETransitionMode ) stream->readInt( NUM_TRANSITION_MODE_BITS ); - FOR_EACH_SLOT mSlots.mTransitionOut[ i ] = ( ETransitionMode ) stream->readInt( NUM_TRANSITION_MODE_BITS ); - FOR_EACH_SLOT mSlots.mStateMode[ i ] = ( EStateMode ) stream->readInt( NUM_STATE_MODE_BITS ); - - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mFadeTimeIn.mValue[ i ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mFadeTimeIn.mVariance[ i ][ 0 ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mFadeTimeIn.mVariance[ i ][ 1 ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mFadeTimeOut.mValue[ i ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mFadeTimeOut.mVariance[ i ][ 0 ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mFadeTimeOut.mVariance[ i ][ 1 ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mDelayTimeIn.mValue[ i ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mDelayTimeIn.mVariance[ i ][ 0 ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mDelayTimeIn.mVariance[ i ][ 1 ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mDelayTimeOut.mValue[ i ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mDelayTimeOut.mVariance[ i ][ 0 ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mDelayTimeOut.mVariance[ i ][ 1 ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mVolumeScale.mValue[ i ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mVolumeScale.mVariance[ i ][ 0 ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mVolumeScale.mVariance[ i ][ 1 ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mPitchScale.mValue[ i ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mPitchScale.mVariance[ i ][ 0 ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mPitchScale.mVariance[ i ][ 1 ] );} - FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mRepeatCount[ i ] );} - - FOR_EACH_SLOT sfxRead( stream, &mSlots.mState[ i ] ); - FOR_EACH_SLOT UNPACKDATA_SOUNDASSET_ARRAY(Track, i); - - #undef FOR_EACH_SLOT + + mActiveSlots = stream->readInt(8); + + for (U32 i = 0; i < mActiveSlots; ++i) + { + mSlots.mReplayMode[i] = (EReplayMode)stream->readInt(NUM_REPLAY_MODE_BITS); + mSlots.mTransitionIn[i] = (ETransitionMode)stream->readInt(NUM_TRANSITION_MODE_BITS); + mSlots.mTransitionOut[i] = (ETransitionMode)stream->readInt(NUM_TRANSITION_MODE_BITS); + mSlots.mStateMode[i] = (EStateMode)stream->readInt(NUM_STATE_MODE_BITS); + + if (stream->readFlag()) { stream->read(&mSlots.mFadeTimeIn.mValue[i]); } + if (stream->readFlag()) { stream->read(&mSlots.mFadeTimeIn.mVariance[i][0]); } + if (stream->readFlag()) { stream->read(&mSlots.mFadeTimeIn.mVariance[i][1]); } + if (stream->readFlag()) { stream->read(&mSlots.mFadeTimeOut.mValue[i]); } + if (stream->readFlag()) { stream->read(&mSlots.mFadeTimeOut.mVariance[i][0]); } + if (stream->readFlag()) { stream->read(&mSlots.mFadeTimeOut.mVariance[i][1]); } + if (stream->readFlag()) { stream->read(&mSlots.mDelayTimeIn.mValue[i]); } + if (stream->readFlag()) { stream->read(&mSlots.mDelayTimeIn.mVariance[i][0]); } + if (stream->readFlag()) { stream->read(&mSlots.mDelayTimeIn.mVariance[i][1]); } + if (stream->readFlag()) { stream->read(&mSlots.mDelayTimeOut.mValue[i]); } + if (stream->readFlag()) { stream->read(&mSlots.mDelayTimeOut.mVariance[i][0]); } + if (stream->readFlag()) { stream->read(&mSlots.mDelayTimeOut.mVariance[i][1]); } + if (stream->readFlag()) { stream->read(&mSlots.mVolumeScale.mValue[i]); } + if (stream->readFlag()) { stream->read(&mSlots.mVolumeScale.mVariance[i][0]); } + if (stream->readFlag()) { stream->read(&mSlots.mVolumeScale.mVariance[i][1]); } + if (stream->readFlag()) { stream->read(&mSlots.mPitchScale.mValue[i]); } + if (stream->readFlag()) { stream->read(&mSlots.mPitchScale.mVariance[i][0]); } + if (stream->readFlag()) { stream->read(&mSlots.mPitchScale.mVariance[i][1]); } + if (stream->readFlag()) { stream->read(&mSlots.mRepeatCount[i]); } + + sfxRead(stream, &mSlots.mState[i]); + sfxRead(stream, &mSlots.mTrack[i]); + } } //----------------------------------------------------------------------------- @@ -486,8 +537,8 @@ void SFXPlayList::inspectPostApply() void SFXPlayList::validate() { - if( mNumSlotsToPlay > NUM_SLOTS ) - mNumSlotsToPlay = NUM_SLOTS; + if( mNumSlotsToPlay > mActiveSlots ) + mNumSlotsToPlay = mActiveSlots; mSlots.mFadeTimeIn.validate(); mSlots.mFadeTimeOut.validate(); diff --git a/Engine/source/sfx/sfxPlayList.h b/Engine/source/sfx/sfxPlayList.h index 1043bf988..8fa0df89c 100644 --- a/Engine/source/sfx/sfxPlayList.h +++ b/Engine/source/sfx/sfxPlayList.h @@ -30,13 +30,8 @@ #include "sfx/sfxTrack.h" #endif -#ifndef SOUND_ASSET_H -#include "T3D/assets/SoundAsset.h" -#endif - - class SFXState; - +class SFXDescription; /// A playback list of SFXTracks. /// @@ -79,7 +74,7 @@ class SFXPlayList : public SFXTrack typedef SFXTrack Parent; - enum + enum SFXPlaylistSettings { /// Number of slots in a playlist. /// @@ -261,6 +256,9 @@ class SFXPlayList : public SFXTrack /// is playing. EStateMode mStateMode[ NUM_SLOTS ]; + /// Track to play in this slot. + SFXTrack* mTrack[NUM_SLOTS]; + SlotData() { dMemset( mReplayMode, 0, sizeof( mReplayMode ) ); @@ -268,6 +266,7 @@ class SFXPlayList : public SFXTrack dMemset( mTransitionOut, 0, sizeof( mTransitionOut ) ); dMemset( mRepeatCount, 0, sizeof( mRepeatCount ) ); dMemset( mState, 0, sizeof( mState ) ); + dMemset( mTrack, 0, sizeof( mTrack ) ); dMemset( mStateMode, 0, sizeof( mStateMode ) ); for( U32 i = 0; i < NUM_SLOTS; ++ i ) @@ -282,31 +281,33 @@ class SFXPlayList : public SFXTrack } } }; - DECLARE_SOUNDASSET_ARRAY(SFXPlayList, Track, NUM_SLOTS); - DECLARE_ASSET_ARRAY_SETGET(SFXPlayList, Track); - protected: - + public: + // moved to public for soundasset + /// Trace interpreter execution. This field is not networked. bool mTrace; - + /// Select slots at random. ERandomMode mRandomMode; - + /// Loop over slots in this list. ELoopMode mLoopMode; - + /// Number of slots to play from list. This can be used, for example, /// to create a list of tracks where only a single track is selected and /// played for each cycle. U32 mNumSlotsToPlay; - + /// Data for each of the playlist slots. SlotData mSlots; - - public: - + + U32 mActiveSlots; + SFXPlayList(); + + /// The destructor. + virtual ~SFXPlayList(); /// Make all settings conform to constraints. void validate(); @@ -324,7 +325,7 @@ class SFXPlayList : public SFXTrack ELoopMode getLoopMode() const { return mLoopMode; } /// Return the total number of slots in the list. - U32 getNumSlots() const { return NUM_SLOTS; } + U32 getNumSlots(); /// Return the slot data for this list. const SlotData& getSlots() const { return mSlots; } @@ -332,8 +333,13 @@ class SFXPlayList : public SFXTrack DECLARE_CONOBJECT( SFXPlayList ); DECLARE_CATEGORY( "SFX" ); DECLARE_DESCRIPTION( "A playback list of SFXProfiles or nested SFXPlayLists." ); - + + // SFXTrack. + virtual bool isLooping() const; + // SimDataBlock. + bool onAdd(); + void onRemove(); virtual bool preload( bool server, String& errorStr ); virtual void packData( BitStream* stream ); virtual void unpackData( BitStream* stream ); diff --git a/Engine/source/sfx/sfxSound.h b/Engine/source/sfx/sfxSound.h index 364f2ed59..7494e3846 100644 --- a/Engine/source/sfx/sfxSound.h +++ b/Engine/source/sfx/sfxSound.h @@ -143,7 +143,7 @@ class SFXSound : public SFXSource, bool isBlocked() const { return ( mVoice && mVoice->getStatus() == SFXStatusBlocked ); } /// Returns true if this is a continuously streaming source. - bool isStreaming() const { return mDescription->mIsStreaming; } + bool isStreaming() const { return mDescription ? mDescription->mIsStreaming : false; } /// Returns true if the source's associated data is ready for playback. bool isReady() const; diff --git a/Engine/source/sfx/sfxSource.cpp b/Engine/source/sfx/sfxSource.cpp index 51e09d795..4a1765c53 100644 --- a/Engine/source/sfx/sfxSource.cpp +++ b/Engine/source/sfx/sfxSource.cpp @@ -192,18 +192,18 @@ SFXSource::SFXSource() mSavedStatus( SFXStatusNull ), mStatusCallback( NULL ), mDescription( NULL ), - mVolume( 1.f ), - mPreFadeVolume( 1.f ), - mFadedVolume( 1.f ), - mModulativeVolume( 1.f ), - mPreAttenuatedVolume( 1.f ), - mAttenuatedVolume( 1.f ), + mVolume( 1.0f ), + mPreFadeVolume( 1.0f ), + mFadedVolume( 1.0f ), + mModulativeVolume( 1.0f ), + mPreAttenuatedVolume( 1.0f ), + mAttenuatedVolume( 1.0f ), mPriority( 0 ), - mModulativePriority( 1.f ), + mModulativePriority( 1.0f ), mEffectivePriority( 0 ), mPitch( 1.f ), - mModulativePitch( 1.f ), - mEffectivePitch( 1.f ), + mModulativePitch( 1.0f ), + mEffectivePitch( 1.0f ), mTransform( true ), mVelocity( 0, 0, 0 ), mMinDistance( 1 ), @@ -213,14 +213,14 @@ SFXSource::SFXSource() mConeOutsideVolume( 1 ), mDistToListener( 0.f ), mTransformScattered( false ), - mFadeInTime( 0.f ), - mFadeOutTime( 0.f ), - mFadeInPoint( -1.f ), - mFadeOutPoint( -1.f ), + mFadeInTime( 0.0f ), + mFadeOutTime( 0.0f ), + mFadeInPoint( -1.0f ), + mFadeOutPoint( -1.0f ), mFadeSegmentType( FadeSegmentNone ), mFadeSegmentEase( NULL ), - mFadeSegmentStartPoint( 0.f ), - mFadeSegmentEndPoint( 0.f ), + mFadeSegmentStartPoint( 0.0f ), + mFadeSegmentEndPoint( 0.0f ), mSavedFadeTime( -1.f ), mPlayStartTick( 0 ) { @@ -236,17 +236,17 @@ SFXSource::SFXSource( SFXTrack* track, SFXDescription* description ) mTrack( track ), mDescription( description ), mVolume( 1.f ), - mPreFadeVolume( 1.f ), - mFadedVolume( 1.f ), - mModulativeVolume( 1.f ), - mPreAttenuatedVolume( 1.f ), - mAttenuatedVolume( 1.f ), + mPreFadeVolume( 1.0f ), + mFadedVolume( 1.0f ), + mModulativeVolume( 1.0f ), + mPreAttenuatedVolume( 1.0f ), + mAttenuatedVolume( 1.0f ), mPriority( 0 ), - mModulativePriority( 1.f ), + mModulativePriority( 1.0f ), mEffectivePriority( 0 ), - mPitch( 1.f ), - mModulativePitch( 1.f ), - mEffectivePitch( 1.f ), + mPitch( 1.0f ), + mModulativePitch( 1.0f ), + mEffectivePitch( 1.0f ), mTransform( true ), mVelocity( 0, 0, 0 ), mMinDistance( 1 ), @@ -256,15 +256,15 @@ SFXSource::SFXSource( SFXTrack* track, SFXDescription* description ) mConeOutsideVolume( 1 ), mDistToListener( 0.f ), mTransformScattered( false ), - mFadeInTime( 0.f ), - mFadeOutTime( 0.f ), - mFadeInPoint( -1.f ), - mFadeOutPoint( -1.f ), + mFadeInTime( 0.0f ), + mFadeOutTime( 0.0f ), + mFadeInPoint( -1.0f ), + mFadeOutPoint( -1.0f ), mFadeSegmentType( FadeSegmentNone ), mFadeSegmentEase( NULL ), - mFadeSegmentStartPoint( 0.f ), - mFadeSegmentEndPoint( 0.f ), - mSavedFadeTime( -1.f ), + mFadeSegmentStartPoint( 0.0f ), + mFadeSegmentEndPoint( 0.0f ), + mSavedFadeTime( -1.0f ), mPlayStartTick( 0 ) { VECTOR_SET_ASSOCIATION( mParameters ); From 54959f0d194f42ec78cecbb50add8b91e4dc74f7 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Sun, 22 Oct 2023 00:09:19 +0100 Subject: [PATCH 018/122] SFXEmitter play pause and stop buttons (#1115) -Add buttons to the inspector for SFXEmitter that will play pause and stop the sfxEmitter. NOTE: Purely effects the state of the emitter when in the editor this will not effect sfxEmitter functionality in a level. --- Engine/source/T3D/sfx/sfxEmitter.cpp | 146 ++++++++++++++++++ Engine/source/T3D/sfx/sfxEmitter.h | 22 ++- .../images/toolbar/pausebutton_h.png | Bin 0 -> 237 bytes .../toolbar/pausebutton_h_image.asset.taml | 8 + .../images/toolbar/pausebutton_n.png | Bin 0 -> 145 bytes .../toolbar/pausebutton_n_image.asset.taml | 8 + .../images/toolbar/stopbutton_h.png | Bin 0 -> 221 bytes .../toolbar/stopbutton_h_image.asset.taml | 8 + .../images/toolbar/stopbutton_n.png | Bin 0 -> 143 bytes .../toolbar/stopbutton_n_image.asset.taml | 8 + 10 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_h_image.asset.taml create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_n_image.asset.taml create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_h_image.asset.taml create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_n_image.asset.taml diff --git a/Engine/source/T3D/sfx/sfxEmitter.cpp b/Engine/source/T3D/sfx/sfxEmitter.cpp index 961e3efb1..967da296a 100644 --- a/Engine/source/T3D/sfx/sfxEmitter.cpp +++ b/Engine/source/T3D/sfx/sfxEmitter.cpp @@ -89,6 +89,120 @@ ColorI SFXEmitter::smRenderColorOutsideVolume( 255, 0, 0, 255 ); ColorI SFXEmitter::smRenderColorRangeSphere( 200, 0, 0, 90 ); + +//----------------------------------------------------------------------------- + +ConsoleType(SoundControls, TypeSoundControls, bool, "") + +ConsoleGetType(TypeSoundControls) +{ + return ""; +} +ConsoleSetType(TypeSoundControls) +{ +} + +IMPLEMENT_CONOBJECT(GuiInspectorTypeSoundControls); +ConsoleDocClass(GuiInspectorTypeSoundControls, + "@brief Inspector field type for Controlling playback of sounds\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeSoundControls::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType(TypeSoundControls)->setInspectorFieldType("GuiInspectorTypeSoundControls"); +} + +GuiControl* GuiInspectorTypeSoundControls::constructEditControl() +{ + // Create base filename edit controls + GuiControl* retCtrl = Parent::constructEditControl(); + if (retCtrl == NULL) + return retCtrl; + + char szBuffer[512]; + + setDataField(StringTable->insert("targetObject"), NULL, mInspector->getInspectObject()->getIdString()); + + mPlayButton = new GuiBitmapButtonCtrl(); + dSprintf(szBuffer, sizeof(szBuffer), "%d.play();", mInspector->getInspectObject()->getId()); + mPlayButton->setField("Command", szBuffer); + + mPlayButton->setBitmap(StringTable->insert("ToolsModule:playbutton_n_image")); + + mPlayButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile"); + mPlayButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile"); + mPlayButton->setDataField(StringTable->insert("hovertime"), NULL, "1000"); + mPlayButton->setDataField(StringTable->insert("tooltip"), NULL, "Play this sound emitter"); + + mPlayButton->registerObject(); + addObject(mPlayButton); + + mPauseButton = new GuiBitmapButtonCtrl(); + dSprintf(szBuffer, sizeof(szBuffer), "%d.pause();", mInspector->getInspectObject()->getId()); + mPauseButton->setField("Command", szBuffer); + + mPauseButton->setBitmap(StringTable->insert("ToolsModule:pausebutton_n_image")); + + mPauseButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile"); + mPauseButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile"); + mPauseButton->setDataField(StringTable->insert("hovertime"), NULL, "1000"); + mPauseButton->setDataField(StringTable->insert("tooltip"), NULL, "Pause this sound emitter"); + + mPauseButton->registerObject(); + addObject(mPauseButton); + + mStopButton = new GuiBitmapButtonCtrl(); + dSprintf(szBuffer, sizeof(szBuffer), "%d.stop();", mInspector->getInspectObject()->getId()); + mStopButton->setField("Command", szBuffer); + + mStopButton->setBitmap(StringTable->insert("ToolsModule:stopbutton_n_image")); + + mStopButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile"); + mStopButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile"); + mStopButton->setDataField(StringTable->insert("hovertime"), NULL, "1000"); + mStopButton->setDataField(StringTable->insert("tooltip"), NULL, "Stop this sound emitter"); + + mStopButton->registerObject(); + addObject(mStopButton); + + return retCtrl; +} + +bool GuiInspectorTypeSoundControls::updateRects() +{ + S32 dividerPos, dividerMargin; + mInspector->getDivider(dividerPos, dividerMargin); + Point2I fieldExtent = getExtent(); + Point2I fieldPos = getPosition(); + + bool resized = mEdit->resize(mEditCtrlRect.point, mEditCtrlRect.extent); + + if (mPlayButton != NULL) + { + RectI shapeEdRect(2, 2, 16, 16); + resized |= mPlayButton->resize(shapeEdRect.point, shapeEdRect.extent); + } + + if (mPauseButton != NULL) + { + RectI shapeEdRect(20, 2, 16, 16); + resized |= mPauseButton->resize(shapeEdRect.point, shapeEdRect.extent); + } + + if (mStopButton != NULL) + { + RectI shapeEdRect(38, 2, 16, 16); + resized |= mStopButton->resize(shapeEdRect.point, shapeEdRect.extent); + } + + return resized; +} + + //----------------------------------------------------------------------------- SFXEmitter::SFXEmitter() @@ -192,6 +306,8 @@ void SFXEmitter::initPersistFields() addGroup( "Sound" ); + addField("Controls", TypeSoundControls, 0, ""); + addField( "playOnAdd", TypeBool, Offset( mPlayOnAdd, SFXEmitter ), "Whether playback of the emitter's sound should start as soon as the emitter object is added to the level.\n" "If this is true, the emitter will immediately start to play when the level is loaded." ); @@ -366,6 +482,7 @@ U32 SFXEmitter::packUpdate(NetConnection* con, U32 mask, BitStream* stream) // Write the source playback state. stream->writeFlag( mask & SourcePlayMask ); + stream->writeFlag( mask & SourcePauseMask ); stream->writeFlag( mask & SourceStopMask ); return retMask; @@ -491,6 +608,8 @@ void SFXEmitter::unpackUpdate( NetConnection *conn, BitStream *stream ) // Check the source playback masks. if ( stream->readFlag() ) // SourcePlayMask play(); + if (stream->readFlag()) //SourcePauseMask + pause(); if ( stream->readFlag() ) // SourceStopMask stop(); } @@ -1000,6 +1119,23 @@ void SFXEmitter::play() //----------------------------------------------------------------------------- +void SFXEmitter::pause() +{ + if (mSource) + mSource->pause(); + else + { + // By clearing the playback masks first we + // ensure the last playback command called + // within a single tick is the one obeyed. + clearMaskBits(AllSourceMasks); + + setMaskBits(SourcePauseMask); + } +} + +//----------------------------------------------------------------------------- + void SFXEmitter::stop() { if ( mSource ) @@ -1114,6 +1250,15 @@ DefineEngineMethod( SFXEmitter, play, void, (),, //----------------------------------------------------------------------------- +DefineEngineMethod(SFXEmitter, pause, void, (), , + "Manually pause playback of the emitter's sound.\n" + "If this is called on the server-side object, the pause command will be related to all client-side ghosts.\n") +{ + object->pause(); +} + +//----------------------------------------------------------------------------- + DefineEngineMethod( SFXEmitter, stop, void, (),, "Manually stop playback of the emitter's sound.\n" "If this is called on the server-side object, the stop command will be related to all client-side ghosts.\n" ) @@ -1131,3 +1276,4 @@ DefineEngineMethod( SFXEmitter, getSource, SFXSource*, (),, { return object->getSource(); } + diff --git a/Engine/source/T3D/sfx/sfxEmitter.h b/Engine/source/T3D/sfx/sfxEmitter.h index 103a92faf..067471820 100644 --- a/Engine/source/T3D/sfx/sfxEmitter.h +++ b/Engine/source/T3D/sfx/sfxEmitter.h @@ -41,6 +41,21 @@ class SFXSource; class SFXTrack; +DefineConsoleType(TypeSoundControls, bool) +class GuiInspectorTypeSoundControls : public GuiInspectorField +{ + typedef GuiInspectorField Parent; +public: + GuiBitmapButtonCtrl* mPlayButton; + GuiBitmapButtonCtrl* mPauseButton; + GuiBitmapButtonCtrl* mStopButton; + + DECLARE_CONOBJECT(GuiInspectorTypeSoundControls); + static void consoleInit(); + + virtual GuiControl* constructEditControl(); + virtual bool updateRects(); +}; //RDTODO: make 3D sound emitters yield their source when being culled /// The SFXEmitter is used to place 2D or 3D sounds into a @@ -69,7 +84,8 @@ class SFXEmitter : public SceneObject DirtyUpdateMask = BIT(2), SourcePlayMask = BIT(3), - SourceStopMask = BIT(4), + SourcePauseMask = BIT(4), + SourceStopMask = BIT(5), AllSourceMasks = SourcePlayMask | SourceStopMask, }; @@ -219,6 +235,10 @@ class SFXEmitter : public SceneObject /// the emitter source is not already playing. void play(); + /// Sends network event to pause playback if + /// the emitter source is already playing. + void pause(); + /// Sends network event to stop emitter /// playback on all ghosted clients. void stop(); diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_h.png new file mode 100644 index 0000000000000000000000000000000000000000..6a02d7d425e3d809fb4d5232fa0018ecdaac59fc GIT binary patch literal 237 zcmeAS@N?(olHy`uVBq!ia0vp^vOp}&!3HE7PPGUDDb50q$YKTtZeb8+WSBKa0w_4& z)5S5wqx0>wjl3-eJg)l%gYO(ZFVpv*va#-zx^OYWar7N6eUtRc?+E&r~@aV7I0`9$uhvc39urE5}@!#yo i{LPp30$zPQ{GUOcU)kBJaUn0zsSKX3elF{r5}E*0lv{8B literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_h_image.asset.taml b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_h_image.asset.taml new file mode 100644 index 000000000..7a643ed81 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_h_image.asset.taml @@ -0,0 +1,8 @@ + diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_n.png new file mode 100644 index 0000000000000000000000000000000000000000..6fefd102bed1a96f860d016b903e5e3c49fc329f GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^vOp}&!3HE7PPGUDDb50q$YKTtZeb8+WSBKa0x0P0 z>EamT(V6_`|9^Yty^O+lb7i)cL`0ox@>VMP7rN$TRplZxWz{T63AdmReNk3fFIOyG pJgqOvtNwt+Ed%MTB`2b|7#d~x4Eg@fVFVh-;OXk;vd$@?2>@WxE5HB% literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_n_image.asset.taml b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_n_image.asset.taml new file mode 100644 index 000000000..dc5c3ebb4 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_n_image.asset.taml @@ -0,0 +1,8 @@ + diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_h.png new file mode 100644 index 0000000000000000000000000000000000000000..bcb3b3f89f5045999a0a867d5d1aa9f9bf26397d GIT binary patch literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^vOp}&!3HE7PPGUDDb50q$YKTtZeb8+WSBKa0w~z; z>EamT(fM}TLEZxj9Ip1g!CRCqCUU><@q6GRd(bq?-T(a9?=cE3kw`wA zV##pAGJbP|=F+M&O@+FRR;v2XYZw%snT9#Psr{$?tCL-7iq*z5p8YO diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_n.png new file mode 100644 index 0000000000000000000000000000000000000000..c45cd993c3c140d962dac6513df54c9956a07920 GIT binary patch literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^vOp}&!3HE7PPGUDDb50q$YKTtZeb8+WSBKa0x0O{ z>EamT(V6_`|9^Yty^O+lb7i)cL`0ox@_v++qH^?PRQ1myx12}H>J{#~vRg~8TnhAd m*IjC#Ad@?zH_9qSmz9CblgqG_?FT>5Fa}RoKbLh*2~7a#%qboK literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_n_image.asset.taml b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_n_image.asset.taml new file mode 100644 index 000000000..11eed81f7 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_n_image.asset.taml @@ -0,0 +1,8 @@ + From be3d26d9e60dd9bc576fb128701ff645daf4d338 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Sun, 22 Oct 2023 00:47:14 +0100 Subject: [PATCH 019/122] SDL_FILESYSTEM -Set the base dir based on build type -enable only building active arch on debug --- CMakeLists.txt | 8 ++++---- Engine/source/CMakeLists.txt | 6 ++++++ Tools/CMake/Info.plist.in | 2 +- Tools/CMake/torqueMacOSconfigs.cmake | 1 - 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5de867942..0f1be940a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,10 +4,6 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;RelWithDebInfo;Release" CACHE STRING "" FOR include("${CMAKE_SOURCE_DIR}/Tools/CMake/torque_macros.cmake") include("${CMAKE_SOURCE_DIR}/Tools/CMake/torque_configs.cmake") -file(GLOB OS_SCRIPTS "${CMAKE_SOURCE_DIR}/Tools/CMake/torque*configs.cmake") -foreach (TORQUE_OS_SCRIPT ${OS_SCRIPTS}) - include(${TORQUE_OS_SCRIPT}) -endforeach() # Ensure multi-core compilation is enabled for everything add_compile_options($<$:/MP>) @@ -74,4 +70,8 @@ endif(NOT TORQUE_INSTALLED_TEMPLATE) # Generate torqueConfig.h in our temp directory configure_file("${CMAKE_SOURCE_DIR}/Tools/CMake/torqueConfig.h.in" "${TORQUE_APP_ROOT_DIRECTORY}/source/torqueConfig.h") +if(APPLE) +include("${CMAKE_SOURCE_DIR}/Tools/CMake/torqueMacOSconfigs.cmake") +endif(APPLE) + add_subdirectory(Engine) diff --git a/Engine/source/CMakeLists.txt b/Engine/source/CMakeLists.txt index aad4df349..10d600012 100644 --- a/Engine/source/CMakeLists.txt +++ b/Engine/source/CMakeLists.txt @@ -366,6 +366,12 @@ if (APPLE) MACOSX_BUNDLE_INFO_PLIST "${TORQUE_APP_ROOT_DIRECTORY}/source/Info.plist" XCODE_ATTRIBUTE_INSTALL_PATH "/Applications" XCODE_ATTRIBUTE_SKIP_INSTALL "No" + XCODE_ATTRIBUTE_LINKER_DISPLAYS_MANGLED_NAMES[variant=Debug] YES + XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH[variant=Debug] YES + XCODE_ATTRIBUTE_ENABLE_TESTABILITY[variant=Debug] YES + XCODE_ATTRIBUTE_SDL_FILE_DIR[variant=Debug] parent + XCODE_ATTRIBUTE_SDL_FILE_DIR[variant=RelWithDebInfo] parent + XCODE_ATTRIBUTE_SDL_FILE_DIR[variant=Release] resource MACOSX_RPATH TRUE) elseif (WIN32) diff --git a/Tools/CMake/Info.plist.in b/Tools/CMake/Info.plist.in index 0c6657131..cfe6d5943 100644 --- a/Tools/CMake/Info.plist.in +++ b/Tools/CMake/Info.plist.in @@ -17,6 +17,6 @@ CFBundleVersion 1.0 SDL_FILESYSTEM_BASE_DIR_TYPE - resource + $(SDL_FILE_DIR) diff --git a/Tools/CMake/torqueMacOSconfigs.cmake b/Tools/CMake/torqueMacOSconfigs.cmake index 0e583d0b4..a0b3c42db 100644 --- a/Tools/CMake/torqueMacOSconfigs.cmake +++ b/Tools/CMake/torqueMacOSconfigs.cmake @@ -48,7 +48,6 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET "11" CACHE STRING "" FORCE) set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "" FORCE) set(CMAKE_XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET[arch=arm64] "11.0" CACHE STRING "arm 64 minimum deployment target" FORCE) set(CMAKE_XCODE_ATTRIBUTE_SDKROOT macosx) -set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "NO") set(CMAKE_SYSTEM_NAME Darwin) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) From dbb6359d80edd52db032198e9de8ae866845119d Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Sun, 22 Oct 2023 00:56:57 +0100 Subject: [PATCH 020/122] Update CMakeLists.txt add remaining torque requirements --- Engine/source/CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Engine/source/CMakeLists.txt b/Engine/source/CMakeLists.txt index 10d600012..b800d7981 100644 --- a/Engine/source/CMakeLists.txt +++ b/Engine/source/CMakeLists.txt @@ -313,7 +313,13 @@ if (APPLE) "${TORQUE_APP_GAME_DIRECTORY}/data" "${TORQUE_APP_GAME_DIRECTORY}/core" "${TORQUE_APP_GAME_DIRECTORY}/tools" - "${TORQUE_APP_GAME_DIRECTORY}/main.${TORQUE_SCRIPT_EXTENSION}") + "${TORQUE_APP_GAME_DIRECTORY}/main.${TORQUE_SCRIPT_EXTENSION}" + "${TORQUE_APP_GAME_DIRECTORY}/${TORQUE_APP_NAME}.torsion" + "${TORQUE_APP_GAME_DIRECTORY}/Template.torsion.exports") + + if(TORQUE_TESTING) + set(MACOSX_RESOURCES ${MACOSX_RESOURCES} "${TORQUE_APP_GAME_DIRECTORY}/runTests.${TORQUE_SCRIPT_EXTENSION}") + endif() set(TORQUE_SOURCE_FILES ${TORQUE_SOURCE_FILES} ${TORQUE_PLATFORM_MAC_SOURCES} ${MACOSX_RESOURCES}) From c2d1e9d654cf05d7d4312e36035ded10794d0dea Mon Sep 17 00:00:00 2001 From: Areloch Date: Sun, 22 Oct 2023 00:47:29 -0500 Subject: [PATCH 021/122] Expands functionality of MenuBuilder to act as primary API for building out menus Shifts "Help" menubar entry in world editor to use new API structure as example/test Removes extraneous 'MainEditor' Adds EditorCore module Moved Menubuilder to EditorCore module Fixes Help Menu editor settings so they properly point at modern documentation and forum URLs Fixes handling of MenuBar so when inserting new items, ensures the menubar refreshes as would be expected Adds remove function to menubar to remove a menu Removes old commented console methods from menubar file Adds checks for onMouseDown and onMouseUp for PopupMenu so items that are submenus aren't clickable like normal items --- Engine/source/gui/editor/guiMenuBar.cpp | 980 +----------------- Engine/source/gui/editor/guiMenuBar.h | 1 + Engine/source/gui/editor/guiPopupMenuCtrl.cpp | 20 + .../MainEditor/guis/MainEditorWindow.gui | 485 --------- .../game/tools/editorCore/editorCore.module | 14 + .../game/tools/editorCore/editorCore.tscript | 12 + .../scripts/menuBar/menuBuilder.ed.tscript | 443 ++++++++ Templates/BaseGame/game/tools/settings.xml | 6 +- Templates/BaseGame/game/tools/tools.tscript | 2 +- .../worldEditor/scripts/menus.ed.tscript | 25 +- 10 files changed, 547 insertions(+), 1441 deletions(-) delete mode 100644 Templates/BaseGame/game/tools/MainEditor/guis/MainEditorWindow.gui create mode 100644 Templates/BaseGame/game/tools/editorCore/editorCore.module create mode 100644 Templates/BaseGame/game/tools/editorCore/editorCore.tscript create mode 100644 Templates/BaseGame/game/tools/editorCore/scripts/menuBar/menuBuilder.ed.tscript diff --git a/Engine/source/gui/editor/guiMenuBar.cpp b/Engine/source/gui/editor/guiMenuBar.cpp index 2c7367cd1..0be2ed9a2 100644 --- a/Engine/source/gui/editor/guiMenuBar.cpp +++ b/Engine/source/gui/editor/guiMenuBar.cpp @@ -143,945 +143,6 @@ IMPLEMENT_CALLBACK( GuiMenuBar, onMenuItemSelect, void, ( S32 menuId, const char "@see GuiTickCtrl\n\n" ); -//------------------------------------------------------------------------------ -// console methods -//------------------------------------------------------------------------------ - -/*DefineEngineMethod( GuiMenuBar, clearMenus, void, (),, - "@brief Clears all the menus from the menu bar.\n\n" - "@tsexample\n" - "// Inform the GuiMenuBar control to clear all menus from itself.\n" - "%thisGuiMenuBar.clearMenus();\n" - "@endtsexample\n\n" - "@see GuiTickCtrl") -{ - object->clearMenus(); -} - -DefineEngineMethod( GuiMenuBar, setMenuMargins, void, (S32 horizontalMargin, S32 verticalMargin, S32 bitmapToTextSpacing),, - "@brief Sets the menu rendering margins: horizontal, vertical, bitmap spacing.\n\n" - "Detailed description\n\n" - "@param horizontalMargin Number of pixels on the left and right side of a menu's text.\n" - "@param verticalMargin Number of pixels on the top and bottom of a menu's text.\n" - "@param bitmapToTextSpacing Number of pixels between a menu's bitmap and text.\n" - "@tsexample\n" - "// Define the horizontalMargin\n" - "%horizontalMargin = \"5\";\n\n" - "// Define the verticalMargin\n" - "%verticalMargin = \"5\";\n\n" - "// Define the bitmapToTextSpacing\n" - "%bitmapToTextSpacing = \"12\";\n\n" - "// Inform the GuiMenuBar control to set its margins based on the defined values.\n" - "%thisGuiMenuBar.setMenuMargins(%horizontalMargin,%verticalMargin,%bitmapToTextSpacing);\n" - "@endtsexample\n\n" - "@see GuiTickCtrl") -{ - object->mHorizontalMargin = horizontalMargin; - object->mVerticalMargin = verticalMargin; - object->mBitmapMargin = bitmapToTextSpacing; -} - -DefineEngineMethod(GuiMenuBar, addMenu, void, (const char* menuText, S32 menuId),, - "@brief Adds a new menu to the menu bar.\n\n" - "@param menuText Text to display for the new menu item.\n" - "@param menuId ID for the new menu item.\n" - "@tsexample\n" - "// Define the menu text\n" - "%menuText = \"New Menu\";\n\n" - "// Define the menu ID.\n" - "%menuId = \"2\";\n\n" - "// Inform the GuiMenuBar control to add the new menu\n" - "%thisGuiMenuBar.addMenu(%menuText,%menuId);\n" - "@endtsexample\n\n" - "@see GuiTickCtrl") -{ - if(dIsdigit(menuText[0])) - { - Con::errorf("Cannot add menu %s (id = %s). First character of a menu's text cannot be a digit.", menuText, menuId); - return; - } - object->addMenu(menuText, menuId); -} - -DefineEngineMethod(GuiMenuBar, addMenuItem, void, (const char* targetMenu, const char* menuItemText, S32 menuItemId, const char* accelerator, int checkGroup, const char *cmd), - ("","",0,nullAsType(),-1,""), - "@brief Adds a menu item to the specified menu. The menu argument can be either the text of a menu or its id.\n\n" - "@param menu Menu name or menu Id to add the new item to.\n" - "@param menuItemText Text for the new menu item.\n" - "@param menuItemId Id for the new menu item.\n" - "@param accelerator Accelerator key for the new menu item.\n" - "@param checkGroup Check group to include this menu item in.\n" - "@tsexample\n" - "// Define the menu we wish to add the item to\n" - "%targetMenu = \"New Menu\"; or %menu = \"4\";\n\n" - "// Define the text for the new menu item\n" - "%menuItemText = \"Menu Item\";\n\n" - "// Define the id for the new menu item\n" - "%menuItemId = \"3\";\n\n" - "// Set the accelerator key to toggle this menu item with\n" - "%accelerator = \"n\";\n\n" - "// Define the Check Group that this menu item will be in, if we want it to be in a check group. -1 sets it in no check group.\n" - "%checkGroup = \"4\";\n\n" - "// Inform the GuiMenuBar control to add the new menu item with the defined fields\n" - "%thisGuiMenuBar.addMenuItem(%menu,%menuItemText,%menuItemId,%accelerator,%checkGroup);\n" - "@endtsexample\n\n" - "@see GuiTickCtrl") -{ - if(dIsdigit(menuItemText[0])) - { - Con::errorf("Cannot add menu item %s (id = %s). First character of a menu item's text cannot be a digit.", menuItemText, menuItemId); - return; - } - GuiMenuBar::Menu *menu = object->findMenu(targetMenu); - if(!menu) - { - Con::errorf("Cannot find menu %s for addMenuItem.", targetMenu); - return; - } - object->addMenuItem(menu, menuItemText, menuItemId, accelerator != NULL ? accelerator : "", checkGroup == -1 ? -1 : checkGroup, cmd); -} - -DefineEngineMethod(GuiMenuBar, setMenuItemEnable, void, (const char* menuTarget, const char* menuItemTarget, bool enabled),, - "@brief sets the menu item to enabled or disabled based on the enable parameter.\n" - "The specified menu and menu item can either be text or ids.\n\n" - "Detailed description\n\n" - "@param menuTarget Menu to work in\n" - "@param menuItemTarget The menu item inside of the menu to enable or disable\n" - "@param enabled Boolean enable / disable value.\n" - "@tsexample\n" - "// Define the menu\n" - "%menu = \"New Menu\"; or %menu = \"4\";\n\n" - "// Define the menu item\n" - "%menuItem = \"New Menu Item\"; or %menuItem = \"2\";\n\n" - "// Define the enabled state\n" - "%enabled = \"true\";\n\n" - "// Inform the GuiMenuBar control to set the enabled state of the requested menu item\n" - "%thisGuiMenuBar.setMenuItemEnable(%menu,%menuItme,%enabled);\n" - "@endtsexample\n\n" - "@see GuiTickCtrl") -{ - GuiMenuBar::Menu *menu = object->findMenu(menuTarget); - if(!menu) - { - Con::errorf("Cannot find menu %s for setMenuItemEnable.", menuTarget); - return; - } - GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget); - if(!menuItem) - { - Con::errorf("Cannot find menu item %s for setMenuItemEnable.", menuItemTarget); - return; - } - menuItem->enabled = enabled; -} - -DefineEngineMethod(GuiMenuBar, setCheckmarkBitmapIndex, void, (S32 bitmapindex),, - "@brief Sets the menu bitmap index for the check mark image.\n\n" - "@param bitmapIndex Bitmap index for the check mark image.\n" - "@tsexample\n" - "// Define the bitmap index\n" - "%bitmapIndex = \"2\";\n\n" - "// Inform the GuiMenuBar control of the proper bitmap index for the check mark image\n" - "%thisGuiMenuBar.setCheckmarkBitmapIndex(%bitmapIndex);\n" - "@endtsexample\n\n" - "@see GuiTickCtrl") -{ - object->mCheckmarkBitmapIndex = bitmapindex; -} - -DefineEngineMethod(GuiMenuBar, setMenuItemChecked, void, (const char* menuTarget, const char* menuItemTarget, bool checked),, - "@brief Sets the menu item bitmap to a check mark, which by default is the first element in\n" - "the bitmap array (although this may be changed with setCheckmarkBitmapIndex()).\n" - "Any other menu items in the menu with the same check group become unchecked if they are checked.\n\n" - "@param menuTarget Menu to work in\n" - "@param menuItem Menu item to affect\n" - "@param checked Whether we are setting it to checked or not\n" - "@tsexample\n" - "" - "@endtsexample\n\n" - "@return If not void, return value and description\n\n" - "@see References") -{ - GuiMenuBar::Menu *menu = object->findMenu(menuTarget); - if(!menu) - { - Con::errorf("Cannot find menu %s for setMenuItemChecked.", menuTarget); - return; - } - GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget); - if(!menuItem) - { - Con::errorf("Cannot find menu item %s for setMenuItemChecked.", menuItemTarget); - return; - } - if(checked && menuItem->checkGroup != -1) - { - // first, uncheck everything in the group: - for(GuiMenuBar::MenuItem *itemWalk = menu->firstMenuItem; itemWalk; itemWalk = itemWalk->nextMenuItem) - if(itemWalk->checkGroup == menuItem->checkGroup && itemWalk->bitmapIndex == object->mCheckmarkBitmapIndex) - itemWalk->bitmapIndex = -1; - } - menuItem->bitmapIndex = checked ? object->mCheckmarkBitmapIndex : -1; -} - -DefineEngineMethod(GuiMenuBar, setMenuText, void, (const char* menuTarget, const char* newMenuText),, - "@brief Sets the text of the specified menu to the new string.\n\n" - "@param menuTarget Menu to affect\n" - "@param newMenuText New menu text\n" - "@tsexample\n" - "// Define the menu to affect" - "%menu = \"New Menu\"; or %menu = \"3\";\n\n" - "// Define the text to change the menu to\n" - "%newMenuText = \"Still a New Menu\";\n\n" - "// Inform the GuiMenuBar control to change the defined menu to the defined text\n" - "%thisGuiMenuBar.setMenuText(%menu,%newMenuText);\n" - "@endtsexample\n\n" - "@see GuiTickCtrl") -{ - if(dIsdigit(menuTarget[0])) - { - Con::errorf("Cannot name menu %s to %s. First character of a menu's text cannot be a digit.", menuTarget, newMenuText); - return; - } - GuiMenuBar::Menu *menu = object->findMenu(menuTarget); - if(!menu) - { - Con::errorf("Cannot find menu %s for setMenuText.", menuTarget); - return; - } - dFree(menu->text); - menu->text = dStrdup(newMenuText); - object->menuBarDirty = true; -} - -DefineEngineMethod(GuiMenuBar, setMenuBitmapIndex, void, (const char* menuTarget, S32 bitmapindex, bool bitmaponly, bool drawborder),, - "@brief Sets the bitmap index for the menu and toggles rendering only the bitmap.\n\n" - "@param menuTarget Menu to affect\n" - "@param bitmapindex Bitmap index to set for the menu\n" - "@param bitmaponly If true, only the bitmap will be rendered\n" - "@param drawborder If true, a border will be drawn around the menu.\n" - "@tsexample\n" - "// Define the menuTarget to affect\n" - "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" - "// Set the bitmap index\n" - "%bitmapIndex = \"5\";\n\n" - "// Set if we are only to render the bitmap or not\n" - "%bitmaponly = \"true\";\n\n" - "// Set if we are rendering a border or not\n" - "%drawborder = \"true\";\n\n" - "// Inform the GuiMenuBar of the bitmap and rendering changes\n" - "%thisGuiMenuBar.setMenuBitmapIndex(%menuTarget,%bitmapIndex,%bitmapOnly,%drawBorder);\n" - "@endtsexample\n\n" - "@see GuiTickCtrl") -{ - GuiMenuBar::Menu *menu = object->findMenu(menuTarget); - if(!menu) - { - Con::errorf("Cannot find menu %s for setMenuBitmapIndex.", menuTarget); - return; - } - - menu->bitmapIndex = bitmapindex; - menu->drawBitmapOnly = bitmaponly; - menu->drawBorder = drawborder; - - object->menuBarDirty = true; -} - -DefineEngineMethod(GuiMenuBar, setMenuVisible, void, (const char* menuTarget, bool visible),, - "@brief Sets the whether or not to display the specified menu.\n\n" - "@param menuTarget Menu item to affect\n" - "@param visible Whether the menu item will be visible or not\n" - "@tsexample\n" - "// Define the menu to work with\n" - "%menuTarget = \"New Menu\"; or %menuTarget = \"4\";\n\n" - "// Define if the menu should be visible or not\n" - "%visible = \"true\";\n\n" - "// Inform the GuiMenuBar control of the new visibility state for the defined menu\n" - "%thisGuiMenuBar.setMenuVisible(%menuTarget,%visible);\n" - "@endtsexample\n\n" - "@see GuiTickCtrl") -{ - GuiMenuBar::Menu *menu = object->findMenu(menuTarget); - if(!menu) - { - Con::errorf("Cannot find menu %s for setMenuVisible.", menuTarget); - return; - } - menu->visible = visible; - object->menuBarDirty = true; - object->setUpdate(); -} - -DefineEngineMethod(GuiMenuBar, setMenuItemText, void, (const char* menuTarget, const char* menuItemTarget, const char* newMenuItemText),, - "@brief Sets the text of the specified menu item to the new string.\n\n" - "@param menuTarget Menu to affect\n" - "@param menuItem Menu item in the menu to change the text at\n" - "@param newMenuItemText New menu text\n" - "@tsexample\n" - "// Define the menuTarget\n" - "%menuTarget = \"New Menu\"; or %menuTarget = \"4\";\n\n" - "// Define the menuItem\n" - "%menuItem = \"New Menu Item\"; or %menuItem = \"2\";\n\n" - "// Define the new text for the menu item\n" - "%newMenuItemText = \"Very New Menu Item\";\n\n" - "// Inform the GuiMenuBar control to change the defined menu item with the new text\n" - "%thisGuiMenuBar.setMenuItemText(%menuTarget,%menuItem,%newMenuItemText);\n" - "@endtsexample\n\n" - "@see GuiTickCtrl") -{ - if(dIsdigit(newMenuItemText[0])) - { - Con::errorf("Cannot name menu item %s to %s. First character of a menu item's text cannot be a digit.", menuItemTarget, newMenuItemText); - return; - } - GuiMenuBar::Menu *menu = object->findMenu(menuTarget); - if(!menu) - { - Con::errorf("Cannot find menu %s for setMenuItemText.", menuTarget); - return; - } - GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget); - if(!menuItem) - { - Con::errorf("Cannot find menu item %s for setMenuItemText.", menuItemTarget); - return; - } - dFree(menuItem->text); - menuItem->text = dStrdup(newMenuItemText); -} - -DefineEngineMethod(GuiMenuBar, setMenuItemVisible, void, (const char* menuTarget, const char* menuItemTarget, bool isVisible),, - "@brief Brief Description.\n\n" - "Detailed description\n\n" - "@param menuTarget Menu to affect the menu item in\n" - "@param menuItem Menu item to affect\n" - "@param isVisible Visible state to set the menu item to.\n" - "@tsexample\n" - "// Define the menuTarget\n" - "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" - "// Define the menuItem\n" - "%menuItem = \"New Menu Item\"; or %menuItem = \"2\";\n\n" - "// Define the visibility state\n" - "%isVisible = \"true\";\n\n" - "// Inform the GuiMenuBarControl of the visibility state of the defined menu item\n" - "%thisGuiMenuBar.setMenuItemVisible(%menuTarget,%menuItem,%isVisible);\n" - "@endtsexample\n\n" - "@see GuiTickCtrl") -{ - GuiMenuBar::Menu *menu = object->findMenu(menuTarget); - if(!menu) - { - Con::errorf("Cannot find menu %s for setMenuItemVisible.", menuTarget); - return; - } - GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget); - if(!menuItem) - { - Con::errorf("Cannot find menu item %s for setMenuItemVisible.", menuItemTarget); - return; - } - menuItem->visible = isVisible; -} - -DefineEngineMethod(GuiMenuBar, setMenuItemBitmap, void, (const char* menuTarget, const char* menuItemTarget, S32 bitmapIndex),, - "@brief Sets the specified menu item bitmap index in the bitmap array. Setting the item's index to -1 will remove any bitmap.\n\n" - "@param menuTarget Menu to affect the menuItem in\n" - "@param menuItem Menu item to affect\n" - "@param bitmapIndex Bitmap index to set the menu item to\n" - "@tsexample\n" - "// Define the menuTarget\n" - "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" - "// Define the menuItem\"\n" - "%menuItem = \"New Menu Item\"; or %menuItem = \"2\";\n\n" - "// Define the bitmapIndex\n" - "%bitmapIndex = \"6\";\n\n" - "// Inform the GuiMenuBar control to set the menu item to the defined bitmap\n" - "%thisGuiMenuBar.setMenuItemBitmap(%menuTarget,%menuItem,%bitmapIndex);\n" - "@endtsexample\n\n" - "@see GuiTickCtrl") -{ - GuiMenuBar::Menu *menu = object->findMenu(menuTarget); - if(!menu) - { - Con::errorf("Cannot find menu %s for setMenuItemBitmap.", menuTarget); - return; - } - GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget); - if(!menuItem) - { - Con::errorf("Cannot find menu item %s for setMenuItemBitmap.", menuItemTarget); - return; - } - menuItem->bitmapIndex = bitmapIndex; -} - -DefineEngineMethod(GuiMenuBar, removeMenuItem, void, (const char* menuTarget, const char* menuItemTarget),, - "@brief Removes the specified menu item from the menu.\n\n" - "@param menuTarget Menu to affect the menu item in\n" - "@param menuItem Menu item to affect\n" - "@tsexample\n" - "// Define the menuTarget\n" - "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" - "// Define the menuItem\n" - "%menuItem = \"New Menu Item\"; or %menuItem = \"5\";\n\n" - "// Request the GuiMenuBar control to remove the define menu item\n" - "%thisGuiMenuBar.removeMenuItem(%menuTarget,%menuItem);\n\n" - "@endtsexample\n\n" - "@see GuiTickCtrl") -{ - GuiMenuBar::Menu *menu = object->findMenu(menuTarget); - if(!menu) - { - Con::errorf("Cannot find menu %s for removeMenuItem.", menuTarget); - return; - } - GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget); - if(!menuItem) - { - Con::errorf("Cannot find menu item %s for removeMenuItem.", menuItemTarget); - return; - } - object->removeMenuItem(menu, menuItem); -} - -DefineEngineMethod(GuiMenuBar, clearMenuItems, void, (const char* menuTarget),, - "@brief Removes all the menu items from the specified menu.\n\n" - "@param menuTarget Menu to remove all items from\n" - "@tsexample\n" - "// Define the menuTarget\n" - "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" - "// Inform the GuiMenuBar control to clear all menu items from the defined menu\n" - "%thisGuiMenuBar.clearMenuItems(%menuTarget);\n" - "@endtsexample\n\n" - "@see GuiTickCtrl") -{ - GuiMenuBar::Menu *menu = object->findMenu(menuTarget); - if(!menu) - { - //Con::errorf("Cannot find menu %s for clearMenuItems.", menuTarget); - return; - } - object->clearMenuItems(menu); -} - -DefineEngineMethod( GuiMenuBar, removeMenu, void, (const char* menuTarget),, - "@brief Removes the specified menu from the menu bar.\n\n" - "@param menuTarget Menu to remove from the menu bar\n" - "@tsexample\n" - "// Define the menuTarget\n" - "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" - "// Inform the GuiMenuBar to remove the defined menu from the menu bar\n" - "%thisGuiMenuBar.removeMenu(%menuTarget);\n" - "@endtsexample\n\n" - "@see GuiTickCtrl") -{ - GuiMenuBar::Menu *menu = object->findMenu(menuTarget); - if(!menu) - { - //Con::errorf("Cannot find menu %s for removeMenu.", menuTarget); - return; - } - object->clearMenuItems(menu); - object->menuBarDirty = true; -} - -//------------------------------------------------------------------------------ -// Submenu console methods -//------------------------------------------------------------------------------ - -DefineEngineMethod(GuiMenuBar, setMenuItemSubmenuState, void, (const char* menuTarget, const char* menuItem, bool isSubmenu),, - "@brief Sets the given menu item to be a submenu.\n\n" - "@param menuTarget Menu to affect a submenu in\n" - "@param menuItem Menu item to affect\n" - "@param isSubmenu Whether or not the menuItem will become a subMenu or not\n" - "@tsexample\n" - "// Define the menuTarget\n" - "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" - "// Define the menuItem\n" - "%menuItem = \"New Menu Item\"; or %menuItem = \"5\";\n\n" - "// Define whether or not the Menu Item is a sub menu or not\n" - "%isSubmenu = \"true\";\n\n" - "// Inform the GuiMenuBar control to set the defined menu item to be a submenu or not.\n" - "%thisGuiMenuBar.setMenuItemSubmenuState(%menuTarget,%menuItem,%isSubmenu);\n" - "@endtsexample\n\n" - "@see GuiTickCtrl") -{ - GuiMenuBar::Menu *menu = object->findMenu(menuTarget); - if(!menu) - { - Con::errorf("Cannot find menu %s for setMenuItemSubmenuState.", menuTarget); - return; - } - - GuiMenuBar::MenuItem *menuitem = object->findMenuItem(menu, menuItem); - if(!menuitem) - { - Con::errorf("Cannot find menuitem %s for setMenuItemSubmenuState.", menuItem); - return; - } - - menuitem->isSubmenu = isSubmenu; -} - -DefineEngineMethod(GuiMenuBar, addSubmenuItem, void, (const char* menuTarget, const char* menuItem, const char* submenuItemText, - int submenuItemId, const char* accelerator, int checkGroup),, - "@brief Adds a menu item to the specified menu. The menu argument can be either the text of a menu or its id.\n\n" - "@param menuTarget Menu to affect a submenu in\n" - "@param menuItem Menu item to affect\n" - "@param submenuItemText Text to show for the new submenu\n" - "@param submenuItemId Id for the new submenu\n" - "@param accelerator Accelerator key for the new submenu\n" - "@param checkGroup Which check group the new submenu should be in, or -1 for none.\n" - "@tsexample\n" - "// Define the menuTarget\n" - "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" - "// Define the menuItem\n" - "%menuItem = \"New Menu Item\"; or %menuItem = \"5\";\n\n" - "// Define the text for the new submenu\n" - "%submenuItemText = \"New Submenu Item\";\n\n" - "// Define the id for the new submenu\n" - "%submenuItemId = \"4\";\n\n" - "// Define the accelerator key for the new submenu\n" - "%accelerator = \"n\";\n\n" - "// Define the checkgroup for the new submenu\n" - "%checkgroup = \"7\";\n\n" - "// Request the GuiMenuBar control to add the new submenu with the defined information\n" - "%thisGuiMenuBar.addSubmenuItem(%menuTarget,%menuItem,%submenuItemText,%submenuItemId,%accelerator,%checkgroup);\n" - "@endtsexample\n\n" - "@see GuiTickCtrl\n") -{ - if(dIsdigit(submenuItemText[0])) - { - Con::errorf("Cannot add submenu item %s (id = %s). First character of a menu item's text cannot be a digit.", submenuItemText, submenuItemId); - return; - } - - GuiMenuBar::Menu *menu = object->findMenu(menuTarget); - if(!menu) - { - Con::errorf("Cannot find menu %s for addMenuItem.", menuTarget); - return; - } - - GuiMenuBar::MenuItem *menuitem = object->findMenuItem(menu, menuItem); - if(!menuitem) - { - Con::errorf("Cannot find menuitem %s for addSubmenuItem.", menuItem); - return; - } - - object->addSubmenuItem(menu, menuitem, submenuItemText, submenuItemId, !accelerator ? "" : accelerator, checkGroup == -1 ? -1 : checkGroup); -} - -DefineEngineMethod(GuiMenuBar, clearSubmenuItems, void, (const char* menuTarget, const char* menuItem),, - "@brief Removes all the menu items from the specified submenu.\n\n" - "@param menuTarget Menu to affect a submenu in\n" - "@param menuItem Menu item to affect\n" - "@tsexample\n" - "// Define the menuTarget\n" - "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" - "// Define the menuItem\n" - "%menuItem = \"New Menu Item\"; or %menuItem = \"5\";\n\n" - "// Inform the GuiMenuBar to remove all submenu items from the defined menu item\n" - "%thisGuiMenuBar.clearSubmenuItems(%menuTarget,%menuItem);\n\n" - "@endtsexample\n\n" - "@see GuiControl") -{ - GuiMenuBar::Menu *menu = object->findMenu(menuTarget); - if(!menu) - { - Con::errorf("Cannot find menu %s for clearSubmenuItems.", menuTarget); - return; - } - - GuiMenuBar::MenuItem *menuitem = object->findMenuItem(menu, menuItem); - if(!menuitem) - { - Con::errorf("Cannot find menuitem %s for clearSubmenuItems.", menuItem); - return; - } - - object->clearSubmenuItems(menuitem); -} - -DefineEngineMethod(GuiMenuBar, setSubmenuItemChecked, void, (const char* menuTarget, const char* menuItemTarget, const char* submenuItemText, bool checked),, - "@brief Sets the menu item bitmap to a check mark, which by default is the first element in the\n" - "bitmap array (although this may be changed with setCheckmarkBitmapIndex()).\n" - "Any other menu items in the menu with the same check group become unchecked if they are checked.\n\n" - "@param menuTarget Menu to affect a submenu in\n" - "@param menuItem Menu item to affect\n" - "@param submenuItemText Text to show for submenu\n" - "@param checked Whether or not this submenu item will be checked.\n" - "@tsexample\n" - "// Define the menuTarget\n" - "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" - "// Define the menuItem\n" - "%menuItem = \"New Menu Item\"; or %menuItem = \"5\";\n\n" - "// Define the text for the new submenu\n" - "%submenuItemText = \"Submenu Item\";\n\n" - "// Define if this submenu item should be checked or not\n" - "%checked = \"true\";\n\n" - "// Inform the GuiMenuBar control to set the checked state of the defined submenu item\n" - "%thisGuiMenuBar.setSubmenuItemChecked(%menuTarget,%menuItem,%submenuItemText,%checked);\n" - "@endtsexample\n\n" - "@return If not void, return value and description\n\n" - "@see References") -{ - // Find the parent menu - GuiMenuBar::Menu *menu = object->findMenu(menuTarget); - if(!menu) - { - Con::errorf("Cannot find menu %s for setSubmenuItemChecked.", menuTarget); - return; - } - - // Find the parent menu item - GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget); - if(!menuItem) - { - Con::errorf("Cannot find menu item %s for setSubmenuItemChecked.", menuItemTarget); - return; - } - - // Find the submenu item - GuiMenuBar::MenuItem *submenuItem = object->findSubmenuItem(menu, menuItemTarget, submenuItemText); - if(!submenuItem) - { - Con::errorf("Cannot find submenu item %s for setSubmenuItemChecked.", submenuItemText); - return; - } - - if(checked && submenuItem->checkGroup != -1) - { - // first, uncheck everything in the group: - for(GuiMenuBar::MenuItem *itemWalk = menuItem->submenu->firstMenuItem; itemWalk; itemWalk = itemWalk->nextMenuItem) - if(itemWalk->checkGroup == submenuItem->checkGroup && itemWalk->bitmapIndex == object->mCheckmarkBitmapIndex) - itemWalk->bitmapIndex = -1; - } - submenuItem->bitmapIndex = checked ? object->mCheckmarkBitmapIndex : -1; -} - -//------------------------------------------------------------------------------ -// menu management methods -//------------------------------------------------------------------------------ -GuiMenuBar::Menu* GuiMenuBar::sCreateMenu(const char *menuText, U32 menuId) -{ - // allocate the menu - Menu *newMenu = new Menu; - newMenu->text = dStrdup(menuText); - newMenu->id = menuId; - newMenu->nextMenu = NULL; - newMenu->firstMenuItem = NULL; - newMenu->visible = true; - - // Menu bitmap variables - newMenu->bitmapIndex = -1; - newMenu->drawBitmapOnly = false; - newMenu->drawBorder = true; - - return newMenu; -} - -void GuiMenuBar::addMenu(GuiMenuBar::Menu *newMenu, S32 pos) -{ - // add it to the menu list - menuBarDirty = true; - if (pos == -1) - mMenuList.push_back(newMenu); - else - mMenuList.insert(pos, newMenu); -} - -void GuiMenuBar::addMenu(const char *menuText, U32 menuId) -{ - Menu *newMenu = sCreateMenu(menuText, menuId); - - addMenu(newMenu); -} - -GuiMenuBar::Menu *GuiMenuBar::findMenu(const char *menu) -{ - if(dIsdigit(menu[0])) - { - U32 id = dAtoi(menu); - for (U32 i = 0; i < mMenuList.size(); ++i) - if (id == mMenuList[i].id) - return mMenuList[i]; - return NULL; - } - else - { - for (U32 i = 0; i < mMenuList.size(); ++i) - if (!dStricmp(menu, mMenuList[i].text)) - return mMenuList[i]; - return NULL; - } -} - -GuiMenuBar::MenuItem *GuiMenuBar::findMenuItem(Menu *menu, const char *menuItem) -{ - if(dIsdigit(menuItem[0])) - { - U32 id = dAtoi(menuItem); - for(MenuItem *walk = menu->firstMenuItem; walk; walk = walk->nextMenuItem) - if(id == walk->id) - return walk; - return NULL; - } - else - { - for(MenuItem *walk = menu->firstMenuItem; walk; walk = walk->nextMenuItem) - if(!dStricmp(menuItem, walk->text)) - return walk; - return NULL; - } -} - -void GuiMenuBar::removeMenu(Menu *menu) -{ - menuBarDirty = true; - clearMenuItems(menu); - - for (U32 i = 0; i < mMenuList.size(); ++i) - { - if (mMenuList[i] == menu) - { - mMenuList.erase(i); - break; - } - } -} - -void GuiMenuBar::removeMenuItem(Menu *menu, MenuItem *menuItem) -{ - for(MenuItem **walk = &menu->firstMenuItem; *walk; walk = &(*walk)->nextMenuItem) - { - if(*walk == menuItem) - { - *walk = menuItem->nextMenuItem; - break; - } - } - - // If this is a submenu, then be sure to clear the submenu's items - if(menuItem->isSubmenu) - { - clearSubmenuItems(menuItem); - } - - dFree(menuItem->text); - dFree(menuItem->accelerator); - delete menuItem; -} - -GuiMenuBar::MenuItem* GuiMenuBar::addMenuItem(Menu *menu, const char *text, U32 id, const char *accelerator, S32 checkGroup, const char *cmd ) -{ - // allocate the new menu item - MenuItem *newMenuItem = new MenuItem; - newMenuItem->text = dStrdup(text); - if(accelerator[0]) - newMenuItem->accelerator = dStrdup(accelerator); - else - newMenuItem->accelerator = NULL; - newMenuItem->cmd = cmd; - newMenuItem->id = id; - newMenuItem->checkGroup = checkGroup; - newMenuItem->nextMenuItem = NULL; - newMenuItem->acceleratorIndex = 0; - newMenuItem->enabled = text[0] != '-'; - newMenuItem->visible = true; - newMenuItem->bitmapIndex = -1; - - // Default to not having a submenu - newMenuItem->isSubmenu = false; - newMenuItem->submenu = NULL; - newMenuItem->submenuParentMenu = NULL; - - // link it into the menu's menu item list - if(menu) - { - MenuItem **walk = &menu->firstMenuItem; - while(*walk) - walk = &(*walk)->nextMenuItem; - *walk = newMenuItem; - } - - return newMenuItem; -} - -GuiMenuBar::MenuItem* GuiMenuBar::addMenuItem(Menu *menu, MenuItem* newMenuItem) -{ - // link it into the menu's menu item list - if(menu) - { - MenuItem **walk = &menu->firstMenuItem; - while(*walk) - walk = &(*walk)->nextMenuItem; - *walk = newMenuItem; - } - - return newMenuItem; -} - -void GuiMenuBar::clearMenuItems(Menu *menu) -{ - while(menu->firstMenuItem) - removeMenuItem(menu, menu->firstMenuItem); -} - -void GuiMenuBar::clearMenus() -{ - mMenuList.clear(); -} - -void GuiMenuBar::attachToMenuBar(Menu* menu, S32 pos) -{ - addMenu(menu, pos); -} - -void GuiMenuBar::removeFromMenuBar(Menu* menu) -{ - menuBarDirty = true; - - for (U32 i = 0; i < mMenuList.size(); ++i) - { - if (mMenuList[i] == menu) - { - mMenuList.erase(i); - break; - } - } -} - -//------------------------------------------------------------------------------ -// Submenu methods -//------------------------------------------------------------------------------ - -// This method will return the MenuItem class of of a submenu's menu item given -// its parent menu and parent menuitem. If the menuitem ID is used, then the submenu -// ID must also be used. -GuiMenuBar::MenuItem *GuiMenuBar::findSubmenuItem(Menu *menu, const char *menuItem, const char *submenuItem) -{ - if(dIsdigit(menuItem[0])) - { - // Search by ID - U32 id = dAtoi(menuItem); - for(MenuItem *walk = menu->firstMenuItem; walk; walk = walk->nextMenuItem) - if(id == walk->id) - { - if(walk->isSubmenu && walk->submenu) - { - return GuiMenuBar::findMenuItem(walk->submenu, submenuItem); - } - return NULL; - } - return NULL; - } - else - { - // Search by name - for(MenuItem *walk = menu->firstMenuItem; walk; walk = walk->nextMenuItem) - if(!dStricmp(menuItem, walk->text)) - { - if(walk->isSubmenu && walk->submenu) - { - return GuiMenuBar::findMenuItem(walk->submenu, submenuItem); - } - return NULL; - } - return NULL; - } -} - -GuiMenuBar::MenuItem* GuiMenuBar::findSubmenuItem(MenuItem *menuItem, const char *submenuItem) -{ - if( !menuItem->isSubmenu ) - return NULL; - - return GuiMenuBar::findMenuItem( menuItem->submenu, submenuItem ); -} - -// Add a menuitem to the given submenu -void GuiMenuBar::addSubmenuItem(Menu *menu, MenuItem *submenu, const char *text, U32 id, const char *accelerator, S32 checkGroup) -{ - // Check that the given menu item supports a submenu - if(submenu && !submenu->isSubmenu) - { - Con::errorf("GuiMenuBar::addSubmenuItem: Attempting to add menuitem '%s' to an invalid submenu",text); - return; - } - - // allocate the new menu item - MenuItem *newMenuItem = new MenuItem; - newMenuItem->text = dStrdup(text); - if(accelerator[0]) - newMenuItem->accelerator = dStrdup(accelerator); - else - newMenuItem->accelerator = NULL; - newMenuItem->id = id; - newMenuItem->checkGroup = checkGroup; - newMenuItem->nextMenuItem = NULL; - newMenuItem->acceleratorIndex = 0; - newMenuItem->enabled = (dStrlen(text) > 1 || text[0] != '-'); - newMenuItem->visible = true; - newMenuItem->bitmapIndex = -1; - - // Default to not having a submenu - newMenuItem->isSubmenu = false; - newMenuItem->submenu = NULL; - - // Point back to the submenu's menu - newMenuItem->submenuParentMenu = menu; - - // link it into the menu's menu item list - MenuItem **walk = &submenu->submenu->firstMenuItem; - while(*walk) - walk = &(*walk)->nextMenuItem; - *walk = newMenuItem; -} - -void GuiMenuBar::addSubmenuItem(Menu *menu, MenuItem *submenu, MenuItem *newMenuItem ) -{ - AssertFatal( submenu && newMenuItem, ""); - - // Point back to the submenu's menu - newMenuItem->submenuParentMenu = menu; - - // link it into the menu's menu item list - MenuItem **walk = &submenu->submenu->firstMenuItem; - while(*walk) - walk = &(*walk)->nextMenuItem; - *walk = newMenuItem; -} - -// Remove a submenu item -void GuiMenuBar::removeSubmenuItem(MenuItem *menuItem, MenuItem *submenuItem) -{ - // Check that the given menu item supports a submenu - if(menuItem && !menuItem->isSubmenu) - { - Con::errorf("GuiMenuBar::removeSubmenuItem: Attempting to remove submenuitem '%s' from an invalid submenu",submenuItem->text); - return; - } - - GuiMenuBar::removeMenuItem(menuItem->submenu, submenuItem); -} - -// Clear all menuitems from a submenu -void GuiMenuBar::clearSubmenuItems(MenuItem *menuitem) -{ - // Check that the given menu item supports a submenu - if(menuitem && !menuitem->isSubmenu) - { - Con::errorf("GuiMenuBar::clearSubmenuItems: Attempting to clear an invalid submenu"); - return; - } - - while(menuitem->submenu->firstMenuItem) - removeSubmenuItem(menuitem, menuitem->submenu->firstMenuItem); -} -*/ //------------------------------------------------------------------------------ // initialization, input and render methods //------------------------------------------------------------------------------ @@ -1456,7 +517,10 @@ void GuiMenuBar::insert(SimObject* pObject, S32 pos) { PopupMenu* menu = dynamic_cast(pObject); if (menu == nullptr) + { + Con::errorf("GuiMenuBar::insert() - attempted to insert non-popupMenu object: %d", pObject->getId()); return; + } MenuEntry newMenu; newMenu.pos = pos >= mMenuList.size() || pos == -1 ? pos = mMenuList.size() : pos; @@ -1471,6 +535,28 @@ void GuiMenuBar::insert(SimObject* pObject, S32 pos) mMenuList.push_back(newMenu); else mMenuList.insert(pos, newMenu); + + menuBarDirty = true; //ensure we refresh +} + +void GuiMenuBar::remove(SimObject* pObject) +{ + PopupMenu* menu = dynamic_cast(pObject); + if (menu == nullptr) + { + Con::errorf("GuiMenuBar::remove() - attempted to remove non-popupMenu object: %d", pObject->getId()); + return; + } + + for(U32 i=0; i < mMenuList.size(); i++) + { + if(mMenuList[i].popupMenu == menu) + { + mMenuList.erase(i); + menuBarDirty = true; //ensure we refresh + return; + } + } } PopupMenu* GuiMenuBar::getMenu(U32 index) @@ -1525,9 +611,25 @@ DefineEngineMethod(GuiMenuBar, getMenu, S32, (S32 index), (0), "(Index)") //----------------------------------------------------------------------------- DefineEngineMethod(GuiMenuBar, insert, void, (SimObject* pObject, S32 pos), (nullAsType(), -1), "(object, pos) insert object at position") { + if(pObject == nullptr) + { + Con::errorf("GuiMenuBar::insert() - null object"); + return; + } object->insert(pObject, pos); } +DefineEngineMethod(GuiMenuBar, remove, void, (SimObject* pObject), (nullAsType()), "(object, pos) remove object") +{ + if (pObject == nullptr) + { + Con::errorf("GuiMenuBar::remove() - null object"); + return; + } + object->remove(pObject); +} + + DefineEngineMethod(GuiMenuBar, findMenu, S32, (const char* barTitle), (""), "(barTitle)") { PopupMenu* menu = object->findMenu(barTitle); diff --git a/Engine/source/gui/editor/guiMenuBar.h b/Engine/source/gui/editor/guiMenuBar.h index 985df8de6..d52672392 100644 --- a/Engine/source/gui/editor/guiMenuBar.h +++ b/Engine/source/gui/editor/guiMenuBar.h @@ -110,6 +110,7 @@ public: void processTick(); void insert(SimObject* pObject, S32 pos); + void remove(SimObject* pObject); static void initPersistFields(); diff --git a/Engine/source/gui/editor/guiPopupMenuCtrl.cpp b/Engine/source/gui/editor/guiPopupMenuCtrl.cpp index 2835eb521..2a72c09b0 100644 --- a/Engine/source/gui/editor/guiPopupMenuCtrl.cpp +++ b/Engine/source/gui/editor/guiPopupMenuCtrl.cpp @@ -210,11 +210,31 @@ bool GuiPopupMenuTextListCtrl::onKeyDown(const GuiEvent &event) void GuiPopupMenuTextListCtrl::onMouseDown(const GuiEvent &event) { + if(mLastHighlightedMenuIdx != -1) + { + //See if we're trying to click on a submenu + if(mList[mLastHighlightedMenuIdx].text[1] != 1) + { + //yep, so abort + return; + } + } + Parent::onMouseDown(event); } void GuiPopupMenuTextListCtrl::onMouseUp(const GuiEvent &event) { + if (mLastHighlightedMenuIdx != -1) + { + //See if we're trying to click on a submenu + if (mList[mLastHighlightedMenuIdx].text[1] != 1) + { + //yep, so abort + return; + } + } + Parent::onMouseUp(event); S32 selectionIndex = getSelectedCell().y; diff --git a/Templates/BaseGame/game/tools/MainEditor/guis/MainEditorWindow.gui b/Templates/BaseGame/game/tools/MainEditor/guis/MainEditorWindow.gui deleted file mode 100644 index 6d40a9164..000000000 --- a/Templates/BaseGame/game/tools/MainEditor/guis/MainEditorWindow.gui +++ /dev/null @@ -1,485 +0,0 @@ -//--- OBJECT WRITE BEGIN --- -$guiContent = new GuiContainer(NewEditorGui) { - margin = "0 0 0 0"; - padding = "0 0 0 0"; - anchorTop = "1"; - anchorBottom = "0"; - anchorLeft = "1"; - anchorRight = "0"; - position = "0 0"; - extent = "1920 1080"; - minExtent = "8 8"; - horizSizing = "right"; - vertSizing = "bottom"; - profile = "ToolsGuiDefaultProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "1"; - canSave = "1"; - canSaveDynamicFields = "1"; - enabled = "1"; - - new GuiSplitContainer(NewEditorGuiLayout) { - orientation = "Vertical"; - splitterSize = "2"; - splitPoint = "1661 100"; - fixedPanel = "None"; - useMinExtent="0"; - fixedSize = "260"; - docking = "None"; - margin = "0 0 0 0"; - padding = "0 0 0 0"; - anchorTop = "1"; - anchorBottom = "0"; - anchorLeft = "1"; - anchorRight = "0"; - position = "0 0"; - extent = "1921 1081"; - minExtent = "64 64"; - horizSizing = "width"; - vertSizing = "height"; - profile = "GuiDefaultProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "1"; - canSave = "1"; - canSaveDynamicFields = "0"; - - new GuiPanel() { - docking = "Client"; - margin = "0 0 0 0"; - padding = "0 0 0 0"; - anchorTop = "1"; - anchorBottom = "0"; - anchorLeft = "1"; - anchorRight = "0"; - position = "0 0"; - extent = "1659 1081"; - minExtent = "0 0"; - horizSizing = "right"; - vertSizing = "bottom"; - profile = "GuiDefaultProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "1"; - internalName = "Panel1"; - canSave = "1"; - canSaveDynamicFields = "0"; - - new GuiSplitContainer(Editor_ToolsMainSplit) { - orientation = "Vertical"; - splitterSize = "2"; - splitPoint = "230 100"; - fixedPanel = "None"; - useMinExtent="0"; - fixedSize = "1429"; - docking = "None"; - margin = "0 0 0 0"; - padding = "0 0 0 0"; - anchorTop = "1"; - anchorBottom = "0"; - anchorLeft = "1"; - anchorRight = "0"; - position = "0 0"; - extent = "1659 1081"; - minExtent = "64 64"; - horizSizing = "width"; - vertSizing = "height"; - profile = "GuiDefaultProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "1"; - canSave = "1"; - canSaveDynamicFields = "0"; - - new GuiPanel(Editor_ToolSidebarPanel) { - docking = "Client"; - margin = "0 0 0 0"; - padding = "0 0 0 0"; - anchorTop = "1"; - anchorBottom = "0"; - anchorLeft = "1"; - anchorRight = "0"; - position = "0 0"; - extent = "228 1081"; - minExtent = "0 0"; - horizSizing = "right"; - vertSizing = "bottom"; - profile = "GuiDefaultProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "1"; - internalName = "Panel1"; - canSave = "1"; - canSaveDynamicFields = "0"; - - new GuiWindowCtrl(Editor_ToolsSidebarWindow) { - text = "Tool Settings"; - resizeWidth = "0"; - resizeHeight = "0"; - canMove = "0"; - canClose = "0"; - canMinimize = "0"; - canMaximize = "0"; - canCollapse = "0"; - edgeSnap = "1"; - margin = "0 0 0 0"; - padding = "0 0 0 0"; - anchorTop = "1"; - anchorBottom = "0"; - anchorLeft = "1"; - anchorRight = "0"; - position = "0 0"; - extent = "228 1081"; - minExtent = "8 2"; - horizSizing = "width"; - vertSizing = "height"; - profile = "GuiWindowProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "1"; - canSave = "1"; - canSaveDynamicFields = "0"; - }; - }; - new GuiPanel(Editor_MainWindowPanel) { - docking = "Client"; - margin = "0 0 0 0"; - padding = "0 0 0 0"; - anchorTop = "1"; - anchorBottom = "0"; - anchorLeft = "1"; - anchorRight = "0"; - position = "232 0"; - extent = "1427 1081"; - minExtent = "0 0"; - horizSizing = "right"; - vertSizing = "bottom"; - profile = "GuiDefaultProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "1"; - internalName = "panel2"; - canSave = "1"; - canSaveDynamicFields = "0"; - - new GuiSplitContainer(Editor_MainViewSplit) { - orientation = "Horizontal"; - splitterSize = "2"; - splitPoint = "182 745"; - fixedPanel = "None"; - useMinExtent="0"; - fixedSize = "273"; - docking = "None"; - margin = "0 0 0 0"; - padding = "0 0 0 0"; - anchorTop = "1"; - anchorBottom = "0"; - anchorLeft = "1"; - anchorRight = "0"; - position = "0 0"; - extent = "1427 1081"; - minExtent = "64 64"; - horizSizing = "width"; - vertSizing = "height"; - profile = "GuiDefaultProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "1"; - canSave = "1"; - canSaveDynamicFields = "0"; - - new GuiPanel(Editor_MainViewPanel) { - docking = "Client"; - margin = "0 0 0 0"; - padding = "0 0 0 0"; - anchorTop = "1"; - anchorBottom = "0"; - anchorLeft = "1"; - anchorRight = "0"; - position = "0 0"; - extent = "1427 743"; - minExtent = "0 0"; - horizSizing = "right"; - vertSizing = "bottom"; - profile = "GuiDefaultProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "1"; - internalName = "Panel1"; - canSave = "1"; - canSaveDynamicFields = "0"; - - new GuiTabBookCtrl(Editor_MainViewTabBook) { - tabPosition = "Top"; - tabMargin = "7"; - minTabWidth = "64"; - tabHeight = "20"; - allowReorder = "0"; - defaultPage = "-1"; - selectedPage = "-1"; - frontTabPadding = "0"; - margin = "0 0 0 0"; - padding = "0 0 0 0"; - anchorTop = "1"; - anchorBottom = "0"; - anchorLeft = "1"; - anchorRight = "0"; - position = "0 0"; - extent = "1427 743"; - minExtent = "8 2"; - horizSizing = "width"; - vertSizing = "height"; - profile = "ToolsGuiTabBookProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "1"; - canSave = "1"; - canSaveDynamicFields = "0"; - }; - new GuiBitmapButtonCtrl(Editor_InspectorSidebarButton) { - bitmap = "tools/gui/images/iconAdd.png"; - bitmapMode = "Stretched"; - autoFitExtents = "0"; - useModifiers = "0"; - useStates = "1"; - masked = "0"; - groupNum = "-1"; - buttonType = "PushButton"; - useMouseEvents = "0"; - position = "1411 61"; - extent = "15 15"; - minExtent = "8 2"; - horizSizing = "left"; - vertSizing = "bottom"; - profile = "GuiDefaultProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "0"; - canSave = "1"; - canSaveDynamicFields = "0"; - }; - new GuiBitmapButtonCtrl(Editor_ToolsSidebarButton) { - bitmap = "tools/gui/images/iconAdd.png"; - bitmapMode = "Stretched"; - autoFitExtents = "0"; - useModifiers = "0"; - useStates = "1"; - masked = "0"; - groupNum = "-1"; - buttonType = "PushButton"; - useMouseEvents = "0"; - position = "0 61"; - extent = "15 15"; - minExtent = "8 2"; - horizSizing = "right"; - vertSizing = "bottom"; - profile = "GuiDefaultProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "0"; - canSave = "1"; - canSaveDynamicFields = "0"; - }; - new GuiBitmapButtonCtrl(Editor_AssetBrowserButton) { - bitmap = "tools/gui/images/iconAdd.png"; - bitmapMode = "Stretched"; - autoFitExtents = "0"; - useModifiers = "0"; - useStates = "1"; - masked = "0"; - groupNum = "-1"; - buttonType = "PushButton"; - useMouseEvents = "0"; - position = "1411 725"; - extent = "15 15"; - minExtent = "8 2"; - horizSizing = "left"; - vertSizing = "top"; - profile = "GuiDefaultProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "0"; - canSave = "1"; - canSaveDynamicFields = "0"; - }; - new GuiBitmapButtonCtrl(Editor_VisibilityOptionsButton) { - bitmap = "tools/gui/images/visible"; - bitmapMode = "Stretched"; - autoFitExtents = "0"; - useModifiers = "0"; - useStates = "1"; - masked = "0"; - groupNum = "-1"; - buttonType = "PushButton"; - useMouseEvents = "0"; - position = "16 30"; - extent = "18 18"; - minExtent = "8 2"; - horizSizing = "right"; - vertSizing = "bottom"; - profile = "GuiDefaultProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "0"; - canSave = "1"; - canSaveDynamicFields = "0"; - }; - }; - new GuiPanel(Editor_AssetBrowserPanel) { - docking = "Client"; - margin = "0 0 0 0"; - padding = "0 0 0 0"; - anchorTop = "1"; - anchorBottom = "0"; - anchorLeft = "1"; - anchorRight = "0"; - position = "0 747"; - extent = "1427 334"; - minExtent = "16 2"; - horizSizing = "right"; - vertSizing = "bottom"; - profile = "GuiDefaultProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "1"; - internalName = "panel2"; - canSave = "1"; - canSaveDynamicFields = "0"; - }; - }; - }; - }; - }; - new GuiPanel(Editor_InspectorPanel) { - docking = "Client"; - margin = "0 0 0 0"; - padding = "0 0 0 0"; - anchorTop = "1"; - anchorBottom = "0"; - anchorLeft = "1"; - anchorRight = "0"; - position = "1663 0"; - extent = "258 1081"; - minExtent = "0 0"; - horizSizing = "right"; - vertSizing = "bottom"; - profile = "GuiDefaultProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "1"; - internalName = "panel2"; - canSave = "1"; - canSaveDynamicFields = "0"; - - new GuiSplitContainer() { - orientation = "Horizontal"; - splitterSize = "2"; - splitPoint = "182 556"; - fixedPanel = "None"; - useMinExtent="0"; - fixedSize = "100"; - docking = "None"; - margin = "0 0 0 0"; - padding = "0 0 0 0"; - anchorTop = "1"; - anchorBottom = "0"; - anchorLeft = "1"; - anchorRight = "0"; - position = "0 0"; - extent = "258 1081"; - minExtent = "64 64"; - horizSizing = "right"; - vertSizing = "bottom"; - profile = "GuiDefaultProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "1"; - canSave = "1"; - canSaveDynamicFields = "0"; - - new GuiPanel(Editor_SceneTreePanel) { - docking = "Client"; - margin = "0 0 0 0"; - padding = "0 0 0 0"; - anchorTop = "1"; - anchorBottom = "0"; - anchorLeft = "1"; - anchorRight = "0"; - position = "0 0"; - extent = "258 554"; - minExtent = "0 0"; - horizSizing = "right"; - vertSizing = "bottom"; - profile = "GuiDefaultProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "1"; - internalName = "Panel1"; - canSave = "1"; - canSaveDynamicFields = "0"; - }; - new GuiPanel(Editor_PropertiesPanel) { - docking = "Client"; - margin = "0 0 0 0"; - padding = "0 0 0 0"; - anchorTop = "1"; - anchorBottom = "0"; - anchorLeft = "1"; - anchorRight = "0"; - position = "0 558"; - extent = "258 523"; - minExtent = "0 0"; - horizSizing = "right"; - vertSizing = "bottom"; - profile = "GuiDefaultProfile"; - visible = "1"; - active = "1"; - tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "1"; - internalName = "panel2"; - canSave = "1"; - canSaveDynamicFields = "0"; - }; - }; - }; - }; -}; -//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/editorCore/editorCore.module b/Templates/BaseGame/game/tools/editorCore/editorCore.module new file mode 100644 index 000000000..2c107aea5 --- /dev/null +++ b/Templates/BaseGame/game/tools/editorCore/editorCore.module @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/editorCore/editorCore.tscript b/Templates/BaseGame/game/tools/editorCore/editorCore.tscript new file mode 100644 index 000000000..3a63b6a5d --- /dev/null +++ b/Templates/BaseGame/game/tools/editorCore/editorCore.tscript @@ -0,0 +1,12 @@ + +function EditorCore::onCreate(%this) +{ + exec("./scripts/menuBar/menuBuilder.ed.tscript"); + + MenuBuilder::init(); +} + +function EditorCore::onDestroy(%this) +{ + MenuBuilder::clearMenus(); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/editorCore/scripts/menuBar/menuBuilder.ed.tscript b/Templates/BaseGame/game/tools/editorCore/scripts/menuBar/menuBuilder.ed.tscript new file mode 100644 index 000000000..fdd7637ae --- /dev/null +++ b/Templates/BaseGame/game/tools/editorCore/scripts/menuBar/menuBuilder.ed.tscript @@ -0,0 +1,443 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Menu Builder Helper Class +//----------------------------------------------------------------------------- +/// @class MenuBuilder +/// @brief Create Dynamic Context and MenuBar Menus +/// +/// +/// Summary : The MenuBuilder script class exists merely as a helper for creating +/// popup menu's for use in torque editors. It is setup as a single +/// object with dynamic fields starting with item[0]..[n] that describe +/// how to create the menu in question. An example is below. +/// +/// isPopup : isPopup is a persistent field on PopupMenu console class which +/// when specified to true will allow you to perform .showPopup(x,y) +/// commands which allow popupmenu's to be used/reused as menubar menus +/// as well as context menus. +/// +/// barPosition : barPosition indicates which index on the menu bar (0 = leftmost) +/// to place this menu if it is attached. Use the attachToMenuBar() command +/// to attach a menu. +/// +/// barName : barName specifies the visible name of a menu item that is attached +/// to the global menubar. +/// +/// canvas : The GuiCanvas object the menu should be attached to. This defaults to +/// the global Canvas object if unspecified. +/// +/// Remarks : If you wish to use a menu as a context popup menu, isPopup must be +/// specified as true at the creation time of the menu. +/// +/// +/// @li @b item[n] (String) TAB (String) TAB (String) : A Menu Item Definition. +/// @code item[0] = "Open File..." TAB "Ctrl O" TAB "Something::OpenFile"; @endcode +/// +/// @li @b isPopup (bool) : If Specified the menu will be considered a popup menu and should be used via .showPopup() +/// @code isPopup = true; @endcode +/// +/// +/// Example : Creating a @b MenuBar Menu +/// @code +/// %%editMenu = new PopupMenu() +/// { +/// barPosition = 3; +/// barName = "View"; +/// superClass = "MenuBuilder"; +/// item[0] = "Undo" TAB "Ctrl Z" TAB "levelBuilderUndo(1);"; +/// item[1] = "Redo" TAB "Ctrl Y" TAB "levelBuilderRedo(1);"; +/// item[2] = "-"; +/// }; +/// +/// %%editMenu.attachToMenuBar( 1, "Edit" ); +/// +/// @endcode +/// +/// +/// Example : Creating a @b Context (Popup) Menu +/// @code +/// %%contextMenu = new PopupMenu() +/// { +/// superClass = MenuBuilder; +/// isPopup = true; +/// item[0] = "My Super Cool Item" TAB "Ctrl 2" TAB "echo(\"Clicked Super Cool Item\");"; +/// item[1] = "-"; +/// }; +/// +/// %%contextMenu.showPopup(); +/// @endcode +/// +/// +/// Example : Modifying a Menu +/// @code +/// %%editMenu = new PopupMenu() +/// { +/// item[0] = "Foo" TAB "Ctrl F" TAB "echo(\"clicked Foo\")"; +/// item[1] = "-"; +/// }; +/// %%editMenu.addItem( 2, "Bar" TAB "Ctrl B" TAB "echo(\"clicked Bar\")" ); +/// %%editMenu.removeItem( 0 ); +/// %%editMenu.addItem( 0, "Modified Foo" TAB "Ctrl F" TAB "echo(\"clicked modified Foo\")" ); +/// @endcode +/// +/// +/// @see PopupMenu +/// +//------------------------------------------------------------------------------ + +//============================================================================== +// Static functions for managing the menus + +// Static function +function MenuBuilder::init() +{ + new ArrayObject(MenuBuilderMenuList){}; +} + +// Static function +function MenuBuilder::clearMenus() +{ + MenuBuilderMenuList.empty(); +} + +// Static function +function MenuBuilder::addMenuToMenubar(%menuBar, %menu, %pos) +{ + if(%pos $= "") + %pos = -1; //if we don't specify, throw it on the end + + %menuBar.insert(%menu, %pos); +} + +// Static function +function MenuBuilder::newMenu(%title, %className) +{ + if(%title $= "") + { + error("MenuBuilder::newMenu() - menu requires title!"); + return 0; + } + + if(%className $= "") + %className = "EditorWorldMenu"; + + %newMenu = new PopupMenu() { + superClass = "MenuBuilder"; + class = %className; + barTitle = %title; + + numItems = 0; + isSubmenu = false; + }; + + MenuBuilderMenuList.add(%newMenu); + + return %newMenu; +} + +function MenuBuilder::newItem(%this, %itemLabel, %command, %accelerator, %pos, %icon) +{ + if(%pos < 0) + { + error("MenuBuilder::addItem() - position must be greater than 0!"); + return; + } + + if(%pos $= "") + { + %pos = %this.numItems; + } + else + { + //ok, we need to nudge all indexed items up from the insert position and up + for(%i = %this.numItems; %i > %pos; %i--) + { + %this.item[%i+1] = %this.item[%i]; + } + } + + if(isObject(%command)) + %this.item[%pos] = %itemLabel TAB %command; + else + %this.item[%pos] = %itemLabel TAB %accelerator TAB %command TAB %icon; + + %this.numItems++; + + %this.reloadItems(); +} + +function MenuBuilder::newSubmenu(%this, %title, %className, %pos) +{ + if(%title $= "") + { + error("MenuBuilder::addSubmenu() - menu requires title!"); + return 0; + } + + if(%pos < 0) + { + error("MenuBuilder::addSubmenu() - position must be greater than 0!"); + return; + } + + if(%className $= "") + %className = "EditorWorldMenu"; + + %newMenu = new PopupMenu() { + superClass = "MenuBuilder"; + class = %className; + barTitle = %title; + + numItems = 0; + isSubmenu = true; + }; + + %this.newItem(%pos, %title TAB %newMenu); + + return %newMenu; +} + +function MenuBuilder::newSeparator(%this, %pos) +{ + if(%pos < 0) + { + error("MenuBuilder::addItem() - position must be greater than 0!"); + return; + } + + if(%pos $= "") + { + %pos = %this.numItems; + } + else + { + //ok, we need to nudge all indexed items up from the insert position and up + for(%i = %this.numItems; %i > %pos; %i--) + { + %this.item[%i+1] = %this.item[%i]; + } + } + + %this.item[%pos] = "-"; + %this.numItems++; + + %this.reloadItems(); +} + +// Static function +function MenuBuilder::findMenu(%title) +{ + for(%i=0; %i < MenuBuilderMenuList.count(); %i++) + { + %menu = MenuBuilderMenuList.getObject(%i); + if(%menu.barTitle $= %title) + { + return %menu; + } + } + + return 0; +} + +function MenuBuilder::findMenu(%this, %title) +{ + for(%i=0; %i < %this.numItems; %i++) + { + %item = %this.item[%i]; + %menu = getField(%item, 1); + if(isObject(%menu)) + { + if(%menu.barTitle $= %title) + return %menu; + } + } + + return 0; +} + +function MenuBuilder::findItem(%this, %itemLabel) +{ + for(%i=0; %i < %this.numItems; %i++) + { + %item = %this.item[%i]; + if(getField(%item, 0) $= %itemLabel) + return %i; + } + + return -1; +} +//============================================================================== +//============================================================================== +// Adds one item to the menu. +// if %item is skipped or "", we will use %item[#], which was set when the menu was created. +// if %item is provided, then we update %item[#]. +function MenuBuilder::addItem(%this, %pos, %item) +{ + if(%item $= "") + %item = %this.item[%pos]; + + if(%item !$= %this.item[%pos]) + %this.item[%pos] = %item; + + %name = getField(%item, 0); + %accel = getField(%item, 1); + %cmd = getField(%item, 2); + %bitmapIdx = getField(%item, 3); + + // We replace the [this] token with our object ID + %cmd = strreplace( %cmd, "[this]", %this ); + %this.item[%pos] = setField( %item, 2, %cmd ); + + if(isObject(%accel)) + { + // If %accel is an object, we want to add a sub menu + %this.insertSubmenu(%pos, %name, %accel); + } + else + { + %this.insertItem(%pos, %name !$= "-" ? %name : "", %accel, %cmd, %bitmapIdx $= "" ? -1 : %bitmapIdx); + } +} + +function MenuBuilder::appendItem(%this, %item) +{ + %this.addItem(%this.getItemCount(), %item); +} + +function MenuBuilder::onAdd(%this) +{ + if(! isObject(%this.canvas)) + %this.canvas = Canvas; + + for(%i = 0;%this.item[%i] !$= "";%i++) + { + %this.addItem(%i); + } +} + +function MenuBuilder::reloadItems(%this) +{ + %this.clearItems(); + + for(%i = 0;%this.item[%i] !$= "";%i++) + { + %this.addItem(%i); + } +} + +function MenuBuilder::onRemove(%this) +{ + if(%this.isMethod("removeFromMenuBar")) + %this.removeFromMenuBar(); +} + +////////////////////////////////////////////////////////////////////////// + +function MenuBuilder::onSelectItem(%this, %id, %text) +{ + %cmd = getField(%this.item[%id], 2); + if(%cmd !$= "") + { + eval( %cmd ); + return true; + } + return false; +} + +/// Sets a new name on an existing menu item. +function MenuBuilder::setItemName( %this, %id, %name ) +{ + %item = %this.item[%id]; + %accel = getField(%item, 1); + %this.setItem( %id, %name, %accel ); +} + +/// Sets a new command on an existing menu item. +function MenuBuilder::setItemCommand( %this, %id, %command ) +{ + %this.item[%id] = setField( %this.item[%id], 2, %command ); +} + +/// (SimID this) +/// Wraps the attachToMenuBar call so that it does not require knowledge of +/// barName or barIndex to be removed/attached. This makes the individual +/// MenuBuilder items very easy to add and remove dynamically from a bar. +/// +function MenuBuilder::attachToMenuBar( %this ) +{ + if( %this.barName $= "" ) + { + error("MenuBuilder::attachToMenuBar - Menu property 'barName' not specified."); + return false; + } + + if( %this.barPosition < 0 ) + { + error("MenuBuilder::attachToMenuBar - Menu " SPC %this.barName SPC "property 'barPosition' is invalid, must be zero or greater."); + return false; + } + + Parent::attachToMenuBar( %this, %this.canvas, %this.barPosition, %this.barName ); +} + +////////////////////////////////////////////////////////////////////////// + +// Callbacks from PopupMenu. These callbacks are now passed on to submenus +// in C++, which was previously not the case. Thus, no longer anything to +// do in these. I am keeping the callbacks in case they are needed later. + +function MenuBuilder::onAttachToMenuBar(%this, %canvas, %pos, %title) +{ +} + +function MenuBuilder::onRemoveFromMenuBar(%this, %canvas) +{ +} + +////////////////////////////////////////////////////////////////////////// + +/// Method called to setup default state for the menu. Expected to be overriden +/// on an individual menu basis. See the mission editor for an example. +function MenuBuilder::setupDefaultState(%this) +{ + for(%i = 0;%this.item[%i] !$= "";%i++) + { + %name = getField(%this.item[%i], 0); + %accel = getField(%this.item[%i], 1); + %cmd = getField(%this.item[%i], 2); + + // Pass on to sub menus + if(isObject(%accel)) + %accel.setupDefaultState(); + } +} + +/// Method called to easily enable or disable all items in a menu. +function MenuBuilder::enableAllItems(%this, %enable) +{ + for(%i = 0; %this.item[%i] !$= ""; %i++) + { + %this.enableItem(%i, %enable); + } +} diff --git a/Templates/BaseGame/game/tools/settings.xml b/Templates/BaseGame/game/tools/settings.xml index efec48b49..3d22b0ffd 100644 --- a/Templates/BaseGame/game/tools/settings.xml +++ b/Templates/BaseGame/game/tools/settings.xml @@ -123,7 +123,7 @@ ../../../Documentation/Torque 3D - Script Manual.chm http://www.garagegames.com/products/torque-3d/documentation/user + name="documentationURL">https://docs.torque3d.org @@ -410,9 +410,9 @@ ../../../Documentation/Torque 3D - Script Manual.chm http://www.garagegames.com/products/torque-3d/documentation/user + name="documentationURL">https://docs.torque3d.org http://www.garagegames.com/products/torque-3d/forums + name="forumURL">https://torque3d.org/ diff --git a/Templates/BaseGame/game/tools/tools.tscript b/Templates/BaseGame/game/tools/tools.tscript index c9e0e01f5..47101f55a 100644 --- a/Templates/BaseGame/game/tools/tools.tscript +++ b/Templates/BaseGame/game/tools/tools.tscript @@ -16,7 +16,7 @@ function ToolsModule::onCreate(%this) // to find exactly which subsystems should be readied before kicking things off. // ---------------------------------------------------------------------------- - //ModuleDatabase.LoadExplicit( "MainEditor" ); + ModuleDatabase.LoadExplicit( "EditorCore" ); //ModuleDatabase.LoadExplicit( "Tools_ObjectViewer" ); } diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.tscript b/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.tscript index ea9b26918..46a725d4d 100644 --- a/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.tscript +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.tscript @@ -364,19 +364,18 @@ function EditorGui::buildMenus(%this) %this.menuBar.insert(%toolsMenu); // Help Menu - %helpMenu = new PopupMenu() - { - superClass = "MenuBuilder"; - class = "EditorHelpMenu"; - - barTitle = "Help"; - - item[0] = "Online Documentation..." TAB "Alt F1" TAB "gotoWebPage(EWorldEditor.documentationURL);"; - item[1] = "Offline User Guide..." TAB "" TAB "gotoWebPage(EWorldEditor.documentationLocal);"; - item[2] = "Offline Reference Guide..." TAB "" TAB "shellexecute(EWorldEditor.documentationReference);"; - item[3] = "Torque 3D Forums..." TAB "" TAB "gotoWebPage(EWorldEditor.forumURL);"; - }; - %this.menuBar.insert(%helpMenu); + %helpMenu = MenuBuilder::newMenu("Help", "EditorHelpMenu"); + %helpMenu.newItem("Online Documentation...", "gotoWebPage(EWorldEditor.documentationURL);", "Alt F1"); + %helpMenu.newItem("Offline User Guide...", "gotoWebPage(EWorldEditor.documentationLocal);"); + %helpMenu.newItem("Offline Reference Guide...", "shellexecute(EWorldEditor.documentationReference);"); + %helpMenu.newItem("Torque 3D Forums...", "gotoWebPage(EWorldEditor.forumURL);"); + + //These files don't currently exist, so we're going to disable them until such a time + //as they exist again + %helpMenu.enableItem(1, false); + %helpMenu.enableItem(2, false); + + MenuBuilder::addMenuToMenubar(%this.menubar, %helpMenu); // Menus that are added/removed dynamically (temporary) From c3ea12f9dfe49dc706c3fe59ba2e41592964f814 Mon Sep 17 00:00:00 2001 From: Areloch Date: Sun, 22 Oct 2023 11:32:54 -0500 Subject: [PATCH 022/122] Ensures that when the asset is edited via the asset properties window, the asset is refreshed in the backend systems as well --- .../BaseGame/game/tools/assetBrowser/scripts/editAsset.tscript | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/editAsset.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/editAsset.tscript index 55c5b8308..226635dba 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/editAsset.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/editAsset.tscript @@ -9,6 +9,8 @@ function AssetBrowser_editAsset::saveAsset(%this) %assetType = AssetDatabase.getAssetType(%this.editedAssetId); %assetDef = AssetDatabase.acquireAsset(%this.editedAssetId); + %assetDef.refreshAsset(); + AssetBrowser.call("on" @ %assetType @ "Changed", %assetDef); AssetDatabase.releaseAsset(%this.editedAssetId); From 4bf7b0d5c06d98986553eec98aa224d3904cad43 Mon Sep 17 00:00:00 2001 From: Areloch Date: Tue, 24 Oct 2023 17:36:58 -0500 Subject: [PATCH 023/122] Adds an Add menubar item to the World Editor menubar that populates SceneObject classes for spawnablility based on the categories assigned to the class itself --- .../scripts/menuBar/menuBuilder.ed.tscript | 85 +++++++++++-------- .../worldEditor/scripts/menus.ed.tscript | 46 ++++++++++ 2 files changed, 96 insertions(+), 35 deletions(-) diff --git a/Templates/BaseGame/game/tools/editorCore/scripts/menuBar/menuBuilder.ed.tscript b/Templates/BaseGame/game/tools/editorCore/scripts/menuBar/menuBuilder.ed.tscript index fdd7637ae..ce563a76e 100644 --- a/Templates/BaseGame/game/tools/editorCore/scripts/menuBar/menuBuilder.ed.tscript +++ b/Templates/BaseGame/game/tools/editorCore/scripts/menuBar/menuBuilder.ed.tscript @@ -158,46 +158,25 @@ function MenuBuilder::newMenu(%title, %className) function MenuBuilder::newItem(%this, %itemLabel, %command, %accelerator, %pos, %icon) { - if(%pos < 0) - { - error("MenuBuilder::addItem() - position must be greater than 0!"); - return; - } - - if(%pos $= "") - { - %pos = %this.numItems; - } - else - { - //ok, we need to nudge all indexed items up from the insert position and up - for(%i = %this.numItems; %i > %pos; %i--) - { - %this.item[%i+1] = %this.item[%i]; - } - } - if(isObject(%command)) - %this.item[%pos] = %itemLabel TAB %command; + %item = %itemLabel TAB %command; else - %this.item[%pos] = %itemLabel TAB %accelerator TAB %command TAB %icon; - - %this.numItems++; - - %this.reloadItems(); + %item = %itemLabel TAB %accelerator TAB %command TAB %icon; + + %this.setItemPosition(%item, %pos); } function MenuBuilder::newSubmenu(%this, %title, %className, %pos) { if(%title $= "") { - error("MenuBuilder::addSubmenu() - menu requires title!"); + error("MenuBuilder::newSubmenu() - menu requires title!"); return 0; } if(%pos < 0) { - error("MenuBuilder::addSubmenu() - position must be greater than 0!"); + error("MenuBuilder::newSubmenu() - position must be greater than 0!"); return; } @@ -213,17 +192,30 @@ function MenuBuilder::newSubmenu(%this, %title, %className, %pos) isSubmenu = true; }; - %this.newItem(%pos, %title TAB %newMenu); + if(%pos $= "") + %pos = %this.numItems; - return %newMenu; + if(%this.setItemPosition(%title TAB %newMenu, %pos)) + { + return %newMenu; + } + + return 0; } function MenuBuilder::newSeparator(%this, %pos) +{ + %item = "-"; + + %this.setItemPosition(%item, %pos); +} + +function MenuBuilder::setItemPosition(%this, %item, %pos) { if(%pos < 0) { - error("MenuBuilder::addItem() - position must be greater than 0!"); - return; + error("MenuBuilder::setItemPosition() - position must be greater than 0!"); + return false; } if(%pos $= "") @@ -239,29 +231,44 @@ function MenuBuilder::newSeparator(%this, %pos) } } - %this.item[%pos] = "-"; - %this.numItems++; + %this.item[%pos] = %item; + %this.numItems++; %this.reloadItems(); + + return true; } // Static function -function MenuBuilder::findMenu(%title) +function MenuBuilder::findMenu(%title, %recurse) { + if(%recurse $= "") + %recurse = false; + for(%i=0; %i < MenuBuilderMenuList.count(); %i++) { %menu = MenuBuilderMenuList.getObject(%i); + if(%menu.barTitle $= %title) { return %menu; } + else if(%recurse) + { + %subMenu = %menu.findMenu(%title, %recurse); + if(isObject(%submenu)) + return %subMenu; + } } return 0; } -function MenuBuilder::findMenu(%this, %title) +function MenuBuilder::findMenu(%this, %title, %recurse) { + if(%recurse $= "") + %recurse = false; + for(%i=0; %i < %this.numItems; %i++) { %item = %this.item[%i]; @@ -269,7 +276,15 @@ function MenuBuilder::findMenu(%this, %title) if(isObject(%menu)) { if(%menu.barTitle $= %title) + { return %menu; + } + else if(%recurse) + { + %subMenu = %menu.findMenu(%title, %recurse); + if(isObject(%submenu)) + return %subMenu; + } } } diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.tscript b/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.tscript index 46a725d4d..ccb4779ca 100644 --- a/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.tscript +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.tscript @@ -465,6 +465,52 @@ function EditorGui::buildMenus(%this) Item[24] = "Unmount Selected Object" TAB "" TAB "EditorUnmount();"; }; } + + %addMenu = MenuBuilder::newMenu("Add"); + + %enumeratedClasses = enumerateConsoleClasses("SceneObject"); + for(%c=0; %c < getFieldCount(%enumeratedClasses); %c++) + { + %class = getField(%enumeratedClasses, %c); + + %category = getCategoryOfClass(%class); + + if(%category $= "") + { + error("Attempted to fetch category of class " @ %class @ " but none were found."); + continue; + } + + %parentMenu = %addMenu; //start at the top + for(%cat=0; %cat < getFieldCount(%category); %cat++) + { + %subCat = getField(%category, %cat); + %targetSubmenu = %parentMenu.findMenu(%subCat); + if(!isObject(%targetSubmenu)) + { + %targetSubmenu = %parentMenu.newSubmenu(%subCat); + } + + %parentMenu = %targetSubmenu; + } + + %buildfunc = ""; + %class = %class; + %method = "build" @ %buildfunc; + if( !ObjectBuilderGui.isMethod( %method ) ) + %method = "build" @ %class; + + if( !ObjectBuilderGui.isMethod( %method ) ) + %cmd = "return new " @ %class @ "();"; + else + %cmd = "ObjectBuilderGui." @ %method @ "();"; + + %createCmd = "ObjectBuilderGui.newObjectCallback = \"ObjectCreator.onFinishCreateObject\"; ObjectCreator.createObject( \"" @ %cmd @ "\" );"; + + %targetSubmenu.newItem(%class, %createCmd, ""); + } + + MenuBuilder::addMenuToMenubar(%this.menubar, %addMenu, 4); } ////////////////////////////////////////////////////////////////////////// From 473e566b70ad0366320eaa47c17b4c6cc9137cd6 Mon Sep 17 00:00:00 2001 From: Areloch Date: Tue, 24 Oct 2023 17:53:37 -0500 Subject: [PATCH 024/122] Adds a separate gui profile for the popupmenus themselves that has a lighter border color, making it easier to keep track of the layout Fixes the C++ asset creation and management function names so the AB can create C++ assets now removes duplicate menuBuilder script file --- Engine/source/gui/editor/popupMenu.cpp | 2 +- .../scripts/assetTypes/cpp.tscript | 10 +- .../tools/base/menuBar/menuBuilder.ed.tscript | 260 ------------------ .../game/tools/gui/profiles.ed.tscript | 6 + 4 files changed, 12 insertions(+), 266 deletions(-) delete mode 100644 Templates/BaseGame/game/tools/base/menuBar/menuBuilder.ed.tscript diff --git a/Engine/source/gui/editor/popupMenu.cpp b/Engine/source/gui/editor/popupMenu.cpp index 35bb8130c..b5dc3cb69 100644 --- a/Engine/source/gui/editor/popupMenu.cpp +++ b/Engine/source/gui/editor/popupMenu.cpp @@ -303,7 +303,7 @@ void PopupMenu::showPopup(GuiCanvas *owner, S32 x /* = -1 */, S32 y /* = -1 */) Sim::findObject("PopUpMenuControl", backgroundCtrl); GuiControlProfile* profile; - Sim::findObject("ToolsGuiMenuBarProfile", profile); + Sim::findObject("ToolsGuiPopupMenuProfile", profile); if (!profile) return; diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/cpp.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/cpp.tscript index cbf40f97f..da45e2059 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/cpp.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/cpp.tscript @@ -24,7 +24,7 @@ function AssetBrowser::buildCppAssetPreview(%this, %assetDef, %previewData) %previewData.tooltip = %assetDef.assetName; } -function AssetBrowser::createCpp(%this) +function AssetBrowser::createCppAsset(%this) { %moduleName = AssetBrowser.newAssetSettings.moduleName; %modulePath = "data/" @ %moduleName; @@ -170,12 +170,12 @@ function AssetBrowser::createCpp(%this) return ""; } -function AssetBrowser::editCpp(%this, %assetDef) +function AssetBrowser::editCppAsset(%this, %assetDef) { } //Renames the asset -function AssetBrowser::renameCpp(%this, %assetDef, %newAssetName) +function AssetBrowser::renameCppAsset(%this, %assetDef, %newAssetName) { %newCodeLooseFilename = renameAssetLooseFile(%assetDef.codefile, %newAssetName); @@ -195,13 +195,13 @@ function AssetBrowser::renameCpp(%this, %assetDef, %newAssetName) } //Deletes the asset -function AssetBrowser::deleteCpp(%this, %assetDef) +function AssetBrowser::deleteCppAsset(%this, %assetDef) { AssetDatabase.deleteAsset(%assetDef.getAssetId(), true); } //Moves the asset to a new path/module -function AssetBrowser::moveCpp(%this, %assetDef, %destination) +function AssetBrowser::moveCppAsset(%this, %assetDef, %destination) { %currentModule = AssetDatabase.getAssetModule(%assetDef.getAssetId()); %targetModule = AssetBrowser.dirHandler.getModuleFromAddress(%destination); diff --git a/Templates/BaseGame/game/tools/base/menuBar/menuBuilder.ed.tscript b/Templates/BaseGame/game/tools/base/menuBar/menuBuilder.ed.tscript deleted file mode 100644 index 883c32bf9..000000000 --- a/Templates/BaseGame/game/tools/base/menuBar/menuBuilder.ed.tscript +++ /dev/null @@ -1,260 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// Menu Builder Helper Class -//----------------------------------------------------------------------------- -/// @class MenuBuilder -/// @brief Create Dynamic Context and MenuBar Menus -/// -/// -/// Summary : The MenuBuilder script class exists merely as a helper for creating -/// popup menu's for use in torque editors. It is setup as a single -/// object with dynamic fields starting with item[0]..[n] that describe -/// how to create the menu in question. An example is below. -/// -/// isPopup : isPopup is a persistent field on PopupMenu console class which -/// when specified to true will allow you to perform .showPopup(x,y) -/// commands which allow popupmenu's to be used/reused as menubar menus -/// as well as context menus. -/// -/// barPosition : barPosition indicates which index on the menu bar (0 = leftmost) -/// to place this menu if it is attached. Use the attachToMenuBar() command -/// to attach a menu. -/// -/// barName : barName specifies the visible name of a menu item that is attached -/// to the global menubar. -/// -/// canvas : The GuiCanvas object the menu should be attached to. This defaults to -/// the global Canvas object if unspecified. -/// -/// Remarks : If you wish to use a menu as a context popup menu, isPopup must be -/// specified as true at the creation time of the menu. -/// -/// -/// @li @b item[n] (String) TAB (String) TAB (String) : A Menu Item Definition. -/// @code item[0] = "Open File..." TAB "Ctrl O" TAB "Something::OpenFile"; @endcode -/// -/// @li @b isPopup (bool) : If Specified the menu will be considered a popup menu and should be used via .showPopup() -/// @code isPopup = true; @endcode -/// -/// -/// Example : Creating a @b MenuBar Menu -/// @code -/// %%editMenu = new PopupMenu() -/// { -/// barPosition = 3; -/// barName = "View"; -/// superClass = "MenuBuilder"; -/// item[0] = "Undo" TAB "Ctrl Z" TAB "levelBuilderUndo(1);"; -/// item[1] = "Redo" TAB "Ctrl Y" TAB "levelBuilderRedo(1);"; -/// item[2] = "-"; -/// }; -/// -/// %%editMenu.attachToMenuBar( 1, "Edit" ); -/// -/// @endcode -/// -/// -/// Example : Creating a @b Context (Popup) Menu -/// @code -/// %%contextMenu = new PopupMenu() -/// { -/// superClass = MenuBuilder; -/// isPopup = true; -/// item[0] = "My Super Cool Item" TAB "Ctrl 2" TAB "echo(\"Clicked Super Cool Item\");"; -/// item[1] = "-"; -/// }; -/// -/// %%contextMenu.showPopup(); -/// @endcode -/// -/// -/// Example : Modifying a Menu -/// @code -/// %%editMenu = new PopupMenu() -/// { -/// item[0] = "Foo" TAB "Ctrl F" TAB "echo(\"clicked Foo\")"; -/// item[1] = "-"; -/// }; -/// %%editMenu.addItem( 2, "Bar" TAB "Ctrl B" TAB "echo(\"clicked Bar\")" ); -/// %%editMenu.removeItem( 0 ); -/// %%editMenu.addItem( 0, "Modified Foo" TAB "Ctrl F" TAB "echo(\"clicked modified Foo\")" ); -/// @endcode -/// -/// -/// @see PopupMenu -/// -//----------------------------------------------------------------------------- - -// Adds one item to the menu. -// if %item is skipped or "", we will use %item[#], which was set when the menu was created. -// if %item is provided, then we update %item[#]. -function MenuBuilder::addItem(%this, %pos, %item) -{ - if(%item $= "") - %item = %this.item[%pos]; - - if(%item !$= %this.item[%pos]) - %this.item[%pos] = %item; - - %name = getField(%item, 0); - %accel = getField(%item, 1); - %cmd = getField(%item, 2); - %bitmapIdx = getField(%item, 3); - - // We replace the [this] token with our object ID - %cmd = strreplace( %cmd, "[this]", %this ); - %this.item[%pos] = setField( %item, 2, %cmd ); - - if(isObject(%accel)) - { - // If %accel is an object, we want to add a sub menu - %this.insertSubmenu(%pos, %name, %accel); - } - else - { - %this.insertItem(%pos, %name !$= "-" ? %name : "", %accel, %cmd, %bitmapIdx $= "" ? -1 : %bitmapIdx); - } -} - -function MenuBuilder::appendItem(%this, %item) -{ - %this.addItem(%this.getItemCount(), %item); -} - -function MenuBuilder::onAdd(%this) -{ - if(! isObject(%this.canvas)) - %this.canvas = Canvas; - - for(%i = 0;%this.item[%i] !$= "";%i++) - { - %this.addItem(%i); - } -} - -function MenuBuilder::reloadItems(%this) -{ - %this.clearItems(); - - for(%i = 0;%this.item[%i] !$= "";%i++) - { - %this.addItem(%i); - } -} - -function MenuBuilder::onRemove(%this) -{ - if(%this.isMethod("removeFromMenuBar")) - %this.removeFromMenuBar(); -} - -////////////////////////////////////////////////////////////////////////// - -function MenuBuilder::onSelectItem(%this, %id, %text) -{ - %cmd = getField(%this.item[%id], 2); - if(%cmd !$= "") - { - eval( %cmd ); - return true; - } - return false; -} - -/// Sets a new name on an existing menu item. -function MenuBuilder::setItemName( %this, %id, %name ) -{ - %item = %this.item[%id]; - %accel = getField(%item, 1); - %this.setItem( %id, %name, %accel ); -} - -/// Sets a new command on an existing menu item. -function MenuBuilder::setItemCommand( %this, %id, %command ) -{ - %this.item[%id] = setField( %this.item[%id], 2, %command ); -} - -/// (SimID this) -/// Wraps the attachToMenuBar call so that it does not require knowledge of -/// barName or barIndex to be removed/attached. This makes the individual -/// MenuBuilder items very easy to add and remove dynamically from a bar. -/// -function MenuBuilder::attachToMenuBar( %this ) -{ - if( %this.barName $= "" ) - { - error("MenuBuilder::attachToMenuBar - Menu property 'barName' not specified."); - return false; - } - - if( %this.barPosition < 0 ) - { - error("MenuBuilder::attachToMenuBar - Menu " SPC %this.barName SPC "property 'barPosition' is invalid, must be zero or greater."); - return false; - } - - Parent::attachToMenuBar( %this, %this.canvas, %this.barPosition, %this.barName ); -} - -////////////////////////////////////////////////////////////////////////// - -// Callbacks from PopupMenu. These callbacks are now passed on to submenus -// in C++, which was previously not the case. Thus, no longer anything to -// do in these. I am keeping the callbacks in case they are needed later. - -function MenuBuilder::onAttachToMenuBar(%this, %canvas, %pos, %title) -{ -} - -function MenuBuilder::onRemoveFromMenuBar(%this, %canvas) -{ -} - -////////////////////////////////////////////////////////////////////////// - -/// Method called to setup default state for the menu. Expected to be overriden -/// on an individual menu basis. See the mission editor for an example. -function MenuBuilder::setupDefaultState(%this) -{ - for(%i = 0;%this.item[%i] !$= "";%i++) - { - %name = getField(%this.item[%i], 0); - %accel = getField(%this.item[%i], 1); - %cmd = getField(%this.item[%i], 2); - - // Pass on to sub menus - if(isObject(%accel)) - %accel.setupDefaultState(); - } -} - -/// Method called to easily enable or disable all items in a menu. -function MenuBuilder::enableAllItems(%this, %enable) -{ - for(%i = 0; %this.item[%i] !$= ""; %i++) - { - %this.enableItem(%i, %enable); - } -} diff --git a/Templates/BaseGame/game/tools/gui/profiles.ed.tscript b/Templates/BaseGame/game/tools/gui/profiles.ed.tscript index 770c2ad61..f34968591 100644 --- a/Templates/BaseGame/game/tools/gui/profiles.ed.tscript +++ b/Templates/BaseGame/game/tools/gui/profiles.ed.tscript @@ -1395,6 +1395,12 @@ singleton GuiControlProfile( ToolsGuiMenuBarProfile ) fontType = $Gui::fontTypeRegular; }; +singleton GuiControlProfile( ToolsGuiPopupMenuProfile : ToolsGuiMenuBarProfile ) +{ + borderColor = EditorSettings.value("Theme/dividerLightColor"); + borderColorHL = EditorSettings.value("Theme/dividerMidColor"); +}; + singleton GuiControlProfile( ToolsMenubarProfile : ToolsGuiDefaultProfile ) { bitmap = "./menubar"; From e8271413e1a6d860a5b7e754fb88e39d12535698 Mon Sep 17 00:00:00 2001 From: James Urquhart Date: Sat, 28 Oct 2023 19:39:21 +0100 Subject: [PATCH 025/122] Set ri.object for castRay so convex shape editing works --- Engine/source/scene/sceneContainer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Engine/source/scene/sceneContainer.cpp b/Engine/source/scene/sceneContainer.cpp index 2b9610d5d..024d833aa 100644 --- a/Engine/source/scene/sceneContainer.cpp +++ b/Engine/source/scene/sceneContainer.cpp @@ -318,6 +318,7 @@ struct SceneRayHelper xformedEnd.convolveInverse(ptr->mObjScale); RayInfo ri; + ri.object = ptr; ri.generateTexCoord = info->generateTexCoord; if (mFunc && !mFunc(&ri)) From 0303cadcc003bdf32e91d2669d0657b834b63ef7 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 31 Oct 2023 12:00:35 -0500 Subject: [PATCH 026/122] imageasset entries can be blank don't spam the console. and definitely don't try and load it --- Engine/source/T3D/assets/ImageAsset.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Engine/source/T3D/assets/ImageAsset.cpp b/Engine/source/T3D/assets/ImageAsset.cpp index 325acfe09..c3a2db842 100644 --- a/Engine/source/T3D/assets/ImageAsset.cpp +++ b/Engine/source/T3D/assets/ImageAsset.cpp @@ -247,13 +247,10 @@ U32 ImageAsset::getAssetById(StringTableEntry assetId, AssetPtr* ima { if (imageAsset->isNull()) { - (*imageAsset)->loadImage(); - //Well that's bad, loading the fallback failed. - Con::warnf("ImageAsset::getAssetById - Finding of asset with id %s failed with no fallback asset", assetId); return AssetErrCode::Failed; } - //handle noshape not being loaded itself + //handle fallback not being loaded itself if ((*imageAsset)->mLoadedState == BadFileReference) { (*imageAsset)->loadImage(); From 97d7d2e9922fe18d40e53f4b146fe7b03aa9c220 Mon Sep 17 00:00:00 2001 From: James Urquhart Date: Sat, 4 Nov 2023 22:06:13 +0000 Subject: [PATCH 027/122] Alternate fix for castRay issue --- Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.cpp | 4 ++-- Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.h | 2 +- Engine/source/scene/sceneContainer.cpp | 3 +-- Engine/source/scene/sceneContainer.h | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.cpp b/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.cpp index 8acd5ef16..c37aff66b 100644 --- a/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.cpp +++ b/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.cpp @@ -1768,10 +1768,10 @@ void GuiConvexEditorCtrl::submitUndo( UndoType type, const Vector mIsDirty = true; } -bool GuiConvexEditorCtrl::_cursorCastCallback( RayInfo* ri ) +bool GuiConvexEditorCtrl::_cursorCastCallback( SceneObject* object ) { // Reject anything that's not a ConvexShape. - return dynamic_cast< ConvexShape* >( ri->object ); + return dynamic_cast< ConvexShape* >( object ); } bool GuiConvexEditorCtrl::_cursorCast( const Gui3DMouseEvent &event, ConvexShape **hitShape, S32 *hitFace ) diff --git a/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.h b/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.h index 1600a4e50..1c64e964a 100644 --- a/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.h +++ b/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.h @@ -169,7 +169,7 @@ protected: void _renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *matInst ); bool _cursorCast( const Gui3DMouseEvent &event, ConvexShape **hitShape, S32 *hitFace ); - static bool _cursorCastCallback( RayInfo* ri ); + static bool _cursorCastCallback( SceneObject* object ); protected: diff --git a/Engine/source/scene/sceneContainer.cpp b/Engine/source/scene/sceneContainer.cpp index 024d833aa..57bb9760b 100644 --- a/Engine/source/scene/sceneContainer.cpp +++ b/Engine/source/scene/sceneContainer.cpp @@ -318,10 +318,9 @@ struct SceneRayHelper xformedEnd.convolveInverse(ptr->mObjScale); RayInfo ri; - ri.object = ptr; ri.generateTexCoord = info->generateTexCoord; - if (mFunc && !mFunc(&ri)) + if (mFunc && !mFunc(ptr)) return false; bool result = false; diff --git a/Engine/source/scene/sceneContainer.h b/Engine/source/scene/sceneContainer.h index ce6f6a023..9892a3bc5 100644 --- a/Engine/source/scene/sceneContainer.h +++ b/Engine/source/scene/sceneContainer.h @@ -668,7 +668,7 @@ class SceneContainer /// @name Line intersection /// @{ - typedef bool ( *CastRayCallback )( RayInfo* ri ); + typedef bool ( *CastRayCallback )( SceneObject* object ); /// Test against collision geometry -- fast. bool castRay( const Point3F &start, const Point3F &end, U32 mask, RayInfo* info, CastRayCallback callback = NULL ); From 12d0688abd3e5a69e1230afaa6358dcf33632e37 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sun, 5 Nov 2023 15:30:19 -0600 Subject: [PATCH 028/122] fix playAudio crash --- Engine/source/T3D/shapeBase.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index e6656fb15..878afe0cc 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -2301,8 +2301,9 @@ void ShapeBase::updateAudioState(SoundThread& st) if ( isGhost() ) { // if asset is valid, play - if (st.asset->isAssetValid() ) + if (st.asset.notNull()) { + st.asset->loadSound(); st.sound = SFX->createSource( st.asset->getSFXTrack() , &getTransform() ); if ( st.sound ) st.sound->play(); From fbcfe02098b27ad71f2e618ce19db4a4a0e09e5f Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Wed, 8 Nov 2023 20:42:47 -0600 Subject: [PATCH 029/122] clean up "Add" menubar categoes on the 3d object side via the following: SceneObject now defaults to a category of "misc" getCategoryOfClass now checks parent classes for what categories they hold so that variants can inherit categories are in one of a few rough groups and subgroups depending on actual mapper needs/usages --- Engine/source/T3D/accumulationVolume.h | 2 +- Engine/source/T3D/aiPlayer.h | 1 + Engine/source/T3D/camera.h | 2 +- Engine/source/T3D/convexShape.h | 1 + Engine/source/T3D/debris.h | 1 + Engine/source/T3D/decal/decalManager.cpp | 1 + Engine/source/T3D/decal/decalManager.h | 1 + Engine/source/T3D/fx/explosion.h | 1 + Engine/source/T3D/fx/fxFoliageReplicator.h | 1 + Engine/source/T3D/fx/fxShapeReplicator.h | 2 ++ Engine/source/T3D/fx/groundCover.h | 1 + Engine/source/T3D/fx/lightning.h | 1 + Engine/source/T3D/fx/particleEmitter.h | 1 + Engine/source/T3D/fx/particleEmitterNode.h | 1 + Engine/source/T3D/fx/precipitation.h | 1 + Engine/source/T3D/fx/ribbon.h | 1 + Engine/source/T3D/fx/ribbonNode.h | 1 + Engine/source/T3D/fx/splash.h | 1 + Engine/source/T3D/gameBase/gameBase.h | 2 ++ Engine/source/T3D/groundPlane.h | 1 + Engine/source/T3D/item.h | 1 + Engine/source/T3D/lightBase.h | 1 + Engine/source/T3D/lighting/boxEnvironmentProbe.h | 1 + Engine/source/T3D/lighting/reflectionProbe.h | 1 + Engine/source/T3D/lighting/skylight.h | 1 + .../source/T3D/lighting/sphereEnvironmentProbe.h | 1 + Engine/source/T3D/missionMarker.h | 4 +++- Engine/source/T3D/notesObject.h | 1 + Engine/source/T3D/occlusionVolume.h | 2 +- Engine/source/T3D/pathCamera.h | 1 + Engine/source/T3D/pathShape.h | 1 + Engine/source/T3D/physicalZone.h | 1 + Engine/source/T3D/physics/physicsDebris.h | 1 + Engine/source/T3D/physics/physicsForce.h | 4 ++-- Engine/source/T3D/physics/physicsShape.h | 1 + Engine/source/T3D/player.h | 1 + Engine/source/T3D/pointLight.h | 1 + Engine/source/T3D/portal.h | 3 ++- Engine/source/T3D/prefab.h | 1 + Engine/source/T3D/projectile.h | 1 + Engine/source/T3D/rigidShape.h | 1 + Engine/source/T3D/sfx/sfxEmitter.h | 2 +- Engine/source/T3D/sfx/sfxSpace.h | 2 +- Engine/source/T3D/spotLight.h | 1 + Engine/source/T3D/staticShape.h | 1 + Engine/source/T3D/trigger.h | 1 + Engine/source/T3D/tsStatic.h | 1 + Engine/source/T3D/vehicles/flyingVehicle.h | 1 + Engine/source/T3D/vehicles/hoverVehicle.h | 1 + Engine/source/T3D/vehicles/vehicle.h | 1 + Engine/source/T3D/vehicles/vehicleBlocker.h | 1 + Engine/source/T3D/vehicles/wheeledVehicle.h | 1 + Engine/source/T3D/zone.h | 2 +- Engine/source/afx/afxCamera.h | 3 +-- Engine/source/afx/afxChoreographer.h | 3 +-- Engine/source/afx/afxEffectGroup.h | 1 - Engine/source/afx/afxEffectWrapper.h | 3 --- Engine/source/afx/afxEffectron.h | 2 -- Engine/source/afx/afxMagicMissile.h | 3 +-- Engine/source/afx/afxMagicSpell.h | 2 -- Engine/source/afx/afxResidueMgr.h | 2 +- Engine/source/afx/afxSelectron.h | 3 +-- Engine/source/afx/afxSpellBook.h | 3 +-- .../afx/afxZodiacGroundPlaneRenderer_T3D.h | 1 - .../source/afx/afxZodiacMeshRoadRenderer_T3D.h | 1 - .../source/afx/afxZodiacPolysoupRenderer_T3D.h | 1 - Engine/source/afx/afxZodiacTerrainRenderer_T3D.h | 1 - Engine/source/afx/arcaneFX.cpp | 1 - Engine/source/afx/ce/afxAnimClip.h | 1 - Engine/source/afx/ce/afxAnimLock.h | 1 - Engine/source/afx/ce/afxAreaDamage.h | 1 - Engine/source/afx/ce/afxAudioBank.h | 1 - Engine/source/afx/ce/afxBillboard.h | 3 +-- Engine/source/afx/ce/afxCameraPuppet.h | 1 - Engine/source/afx/ce/afxCameraShake.h | 1 - Engine/source/afx/ce/afxCollisionEvent.h | 1 - Engine/source/afx/ce/afxConsoleMessage.h | 1 - Engine/source/afx/ce/afxDamage.h | 1 - Engine/source/afx/ce/afxFootSwitch.h | 1 - Engine/source/afx/ce/afxGuiController.h | 1 - Engine/source/afx/ce/afxGuiText.h | 1 - Engine/source/afx/ce/afxLight.h | 1 - Engine/source/afx/ce/afxLightBase_T3D.h | 1 - Engine/source/afx/ce/afxMachineGun.h | 1 - Engine/source/afx/ce/afxModel.h | 3 +-- Engine/source/afx/ce/afxMooring.h | 3 +-- Engine/source/afx/ce/afxMultiLight.h | 1 - Engine/source/afx/ce/afxParticleEmitter.h | 5 ----- Engine/source/afx/ce/afxPhraseEffect.h | 1 - Engine/source/afx/ce/afxPhysicalZone.h | 1 - Engine/source/afx/ce/afxPlayerMovement.h | 1 - Engine/source/afx/ce/afxPlayerPuppet.h | 1 - Engine/source/afx/ce/afxPointLight_T3D.h | 1 - Engine/source/afx/ce/afxProjectile.h | 3 +-- Engine/source/afx/ce/afxScriptEvent.h | 1 - Engine/source/afx/ce/afxSpotLight_T3D.h | 1 - Engine/source/afx/ce/afxStaticShape.h | 3 +-- Engine/source/afx/ce/afxVolumeLight.h | 1 - Engine/source/afx/ce/afxZodiac.h | 1 - Engine/source/afx/ce/afxZodiacPlane.h | 3 +-- Engine/source/afx/ea/afxEA_TLKLight.cpp | 3 +-- Engine/source/afx/ea/afxEA_Zodiac.cpp | 2 +- Engine/source/afx/forces/afxF_Drag.cpp | 1 - Engine/source/afx/forces/afxF_Gravity.cpp | 1 - Engine/source/afx/forces/afxXM_Force.cpp | 1 - Engine/source/afx/rpg/afxRPGMagicSpell.h | 1 - Engine/source/afx/util/afxParticlePool.h | 2 -- Engine/source/afx/util/afxPath.h | 1 - Engine/source/afx/xm/afxXM_Aim.cpp | 1 - Engine/source/afx/xm/afxXM_AltitudeConform.cpp | 1 - Engine/source/afx/xm/afxXM_BoxAdapt.cpp | 1 - Engine/source/afx/xm/afxXM_BoxConform.cpp | 1 - Engine/source/afx/xm/afxXM_BoxHeightOffset.cpp | 1 - Engine/source/afx/xm/afxXM_Freeze.cpp | 1 - Engine/source/afx/xm/afxXM_GroundConform.cpp | 1 - Engine/source/afx/xm/afxXM_HeightSampler.cpp | 1 - Engine/source/afx/xm/afxXM_MountedImageNode.cpp | 1 - Engine/source/afx/xm/afxXM_Offset.cpp | 2 -- Engine/source/afx/xm/afxXM_Oscillate.cpp | 1 - .../source/afx/xm/afxXM_OscillateZodiacColor.cpp | 1 - Engine/source/afx/xm/afxXM_PathConform.cpp | 1 - Engine/source/afx/xm/afxXM_PivotNodeOffset.cpp | 1 - Engine/source/afx/xm/afxXM_RandomRot.cpp | 1 - Engine/source/afx/xm/afxXM_Scale.cpp | 1 - Engine/source/afx/xm/afxXM_Shockwave.cpp | 1 - Engine/source/afx/xm/afxXM_Spin.cpp | 1 - Engine/source/afx/xm/afxXM_VelocityOffset.cpp | 1 - Engine/source/afx/xm/afxXM_WaveBase.h | 2 -- Engine/source/afx/xm/afxXM_WaveColor.cpp | 2 -- Engine/source/afx/xm/afxXM_WaveScalar.cpp | 2 -- Engine/source/afx/xm/afxXfmMod.h | 2 -- Engine/source/console/consoleObject.cpp | 16 ++++++++++++---- Engine/source/environment/VolumetricFog.h | 1 + .../source/environment/VolumetricFogRTManager.h | 3 ++- Engine/source/environment/basicClouds.h | 1 + Engine/source/environment/cloudLayer.h | 1 + Engine/source/environment/decalRoad.h | 1 + Engine/source/environment/meshRoad.h | 1 + Engine/source/environment/scatterSky.h | 1 + Engine/source/environment/skyBox.h | 3 ++- Engine/source/environment/skySphere.h | 1 + Engine/source/environment/sun.h | 3 ++- Engine/source/environment/timeOfDay.h | 3 ++- Engine/source/environment/waterObject.h | 1 + Engine/source/forest/forest.h | 1 + Engine/source/forest/forestWindEmitter.h | 3 ++- .../gui/worldEditor/guiConvexShapeEditorCtrl.cpp | 4 ++-- .../gui/worldEditor/guiConvexShapeEditorCtrl.h | 2 +- Engine/source/navigation/coverPoint.h | 1 + Engine/source/navigation/navMesh.h | 1 + Engine/source/navigation/navPath.h | 1 + Engine/source/scene/sceneContainer.cpp | 3 +-- Engine/source/scene/sceneContainer.h | 2 +- Engine/source/scene/sceneObject.h | 1 + Engine/source/scene/simPath.h | 2 ++ Engine/source/terrain/terrData.h | 1 + .../tools/worldEditor/scripts/menus.ed.tscript | 13 +++++++++++-- 157 files changed, 127 insertions(+), 131 deletions(-) diff --git a/Engine/source/T3D/accumulationVolume.h b/Engine/source/T3D/accumulationVolume.h index 4c4049b6c..2a42315bf 100644 --- a/Engine/source/T3D/accumulationVolume.h +++ b/Engine/source/T3D/accumulationVolume.h @@ -74,7 +74,7 @@ class AccumulationVolume : public ScenePolyhedralSpace // SimObject. DECLARE_CONOBJECT( AccumulationVolume ); DECLARE_DESCRIPTION( "Allows objects in an area to have accumulation effect applied." ); - DECLARE_CATEGORY( "3D Scene" ); + DECLARE_CATEGORY( "Area" ); virtual bool onAdd(); virtual void onRemove(); diff --git a/Engine/source/T3D/aiPlayer.h b/Engine/source/T3D/aiPlayer.h index 524b0ba16..adeb3a009 100644 --- a/Engine/source/T3D/aiPlayer.h +++ b/Engine/source/T3D/aiPlayer.h @@ -150,6 +150,7 @@ protected: public: DECLARE_CONOBJECT( AIPlayer ); + DECLARE_CATEGORY("Actor \t AI"); AIPlayer(); ~AIPlayer(); diff --git a/Engine/source/T3D/camera.h b/Engine/source/T3D/camera.h index 8f3b1ac35..e54c90ab1 100644 --- a/Engine/source/T3D/camera.h +++ b/Engine/source/T3D/camera.h @@ -249,7 +249,7 @@ class Camera: public ShapeBase virtual void unpackUpdate( NetConnection* conn, BitStream* stream ); DECLARE_CONOBJECT( Camera ); - DECLARE_CATEGORY( "Game" ); + DECLARE_CATEGORY("Actor \t Controllable"); DECLARE_DESCRIPTION( "Represents a position, direction and field of view to render a scene from." ); static F32 getMovementSpeed() { return smMovementSpeed; } bool isCamera() const { return true; } diff --git a/Engine/source/T3D/convexShape.h b/Engine/source/T3D/convexShape.h index cf02165b6..45a2bb551 100644 --- a/Engine/source/T3D/convexShape.h +++ b/Engine/source/T3D/convexShape.h @@ -194,6 +194,7 @@ public: virtual ~ConvexShape(); DECLARE_CONOBJECT( ConvexShape ); + DECLARE_CATEGORY("Object \t Simple"); // ConsoleObject static void initPersistFields(); diff --git a/Engine/source/T3D/debris.h b/Engine/source/T3D/debris.h index 83224f190..4f896d184 100644 --- a/Engine/source/T3D/debris.h +++ b/Engine/source/T3D/debris.h @@ -179,6 +179,7 @@ public: void setRotAngles( const Point3F &angles ){ mRotAngles = angles; } DECLARE_CONOBJECT(Debris); + DECLARE_CATEGORY("UNLISTED"); private: SimObject* ss_object; diff --git a/Engine/source/T3D/decal/decalManager.cpp b/Engine/source/T3D/decal/decalManager.cpp index 2c683275a..bf1e7ad2a 100644 --- a/Engine/source/T3D/decal/decalManager.cpp +++ b/Engine/source/T3D/decal/decalManager.cpp @@ -87,6 +87,7 @@ const U32 DecalManager::smMaxIndices = 10000; DecalManager *gDecalManager = NULL; IMPLEMENT_CONOBJECT(DecalManager); +DECLARE_CATEGORY("UNLISTED"); ConsoleDoc( "@defgroup Decals\n" diff --git a/Engine/source/T3D/decal/decalManager.h b/Engine/source/T3D/decal/decalManager.h index 9e03c59c7..4ab636f06 100644 --- a/Engine/source/T3D/decal/decalManager.h +++ b/Engine/source/T3D/decal/decalManager.h @@ -270,6 +270,7 @@ class DecalManager : public SceneObject // SimObject. DECLARE_CONOBJECT( DecalManager ); + DECLARE_CATEGORY("UNLISTED"); static void consoleInit(); }; diff --git a/Engine/source/T3D/fx/explosion.h b/Engine/source/T3D/fx/explosion.h index eac392e11..ab17cf6d3 100644 --- a/Engine/source/T3D/fx/explosion.h +++ b/Engine/source/T3D/fx/explosion.h @@ -205,6 +205,7 @@ class Explosion : public GameBase, public ISceneLight void setCollideType( U32 cType ){ mCollideType = cType; } DECLARE_CONOBJECT(Explosion); + DECLARE_CATEGORY("UNLISTED"); static void initPersistFields(); private: SimObject* ss_object; diff --git a/Engine/source/T3D/fx/fxFoliageReplicator.h b/Engine/source/T3D/fx/fxFoliageReplicator.h index 0d8224652..901ac128b 100644 --- a/Engine/source/T3D/fx/fxFoliageReplicator.h +++ b/Engine/source/T3D/fx/fxFoliageReplicator.h @@ -390,6 +390,7 @@ public: // Declare Console Object. DECLARE_CONOBJECT(fxFoliageReplicator); + DECLARE_CATEGORY("UNLISTED"); }; #pragma warning( pop ) #endif // _FOLIAGEREPLICATOR_H_ diff --git a/Engine/source/T3D/fx/fxShapeReplicator.h b/Engine/source/T3D/fx/fxShapeReplicator.h index 6d894ae8a..08df63506 100644 --- a/Engine/source/T3D/fx/fxShapeReplicator.h +++ b/Engine/source/T3D/fx/fxShapeReplicator.h @@ -59,6 +59,7 @@ public: void setTransform(const MatrixF & mat) { Parent::setTransform(mat); setRenderTransform(mat); }; DECLARE_CONOBJECT(fxShapeReplicatedStatic); + DECLARE_CATEGORY("UNLISTED"); }; @@ -187,6 +188,7 @@ public: // Declare Console Object. DECLARE_CONOBJECT(fxShapeReplicator); + DECLARE_CATEGORY("UNLISTED"); }; #endif // _SHAPEREPLICATOR_H_ diff --git a/Engine/source/T3D/fx/groundCover.h b/Engine/source/T3D/fx/groundCover.h index 906a96a7e..3cd03887a 100644 --- a/Engine/source/T3D/fx/groundCover.h +++ b/Engine/source/T3D/fx/groundCover.h @@ -123,6 +123,7 @@ public: ~GroundCover(); DECLARE_CONOBJECT(GroundCover); + DECLARE_CATEGORY("Environment \t BackGround"); static void consoleInit(); static void initPersistFields(); diff --git a/Engine/source/T3D/fx/lightning.h b/Engine/source/T3D/fx/lightning.h index be3b65319..d68f6c1d2 100644 --- a/Engine/source/T3D/fx/lightning.h +++ b/Engine/source/T3D/fx/lightning.h @@ -239,6 +239,7 @@ class Lightning : public GameBase void processEvent(LightningStrikeEvent*); DECLARE_CONOBJECT(Lightning); + DECLARE_CATEGORY("Environment \t Weather"); static void initPersistFields(); U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); diff --git a/Engine/source/T3D/fx/particleEmitter.h b/Engine/source/T3D/fx/particleEmitter.h index 32bf5beca..49a11c906 100644 --- a/Engine/source/T3D/fx/particleEmitter.h +++ b/Engine/source/T3D/fx/particleEmitter.h @@ -168,6 +168,7 @@ class ParticleEmitter : public GameBase ~ParticleEmitter(); DECLARE_CONOBJECT(ParticleEmitter); + DECLARE_CATEGORY("UNLISTED"); static Point3F mWindVelocity; static void setWindVelocity( const Point3F &vel ){ mWindVelocity = vel; } diff --git a/Engine/source/T3D/fx/particleEmitterNode.h b/Engine/source/T3D/fx/particleEmitterNode.h index 48b7c4306..24c45e697 100644 --- a/Engine/source/T3D/fx/particleEmitterNode.h +++ b/Engine/source/T3D/fx/particleEmitterNode.h @@ -103,6 +103,7 @@ class ParticleEmitterNode : public GameBase void advanceTime(F32 dt); DECLARE_CONOBJECT(ParticleEmitterNode); + DECLARE_CATEGORY("Environment \t FX"); static void initPersistFields(); U32 packUpdate (NetConnection *conn, U32 mask, BitStream* stream); diff --git a/Engine/source/T3D/fx/precipitation.h b/Engine/source/T3D/fx/precipitation.h index eb3a540e0..04bc02f46 100644 --- a/Engine/source/T3D/fx/precipitation.h +++ b/Engine/source/T3D/fx/precipitation.h @@ -280,6 +280,7 @@ class Precipitation : public GameBase bool onNewDataBlock( GameBaseData *dptr, bool reload ); DECLARE_CONOBJECT(Precipitation); + DECLARE_CATEGORY("Environment \t Weather"); static void initPersistFields(); U32 packUpdate(NetConnection*, U32 mask, BitStream* stream); diff --git a/Engine/source/T3D/fx/ribbon.h b/Engine/source/T3D/fx/ribbon.h index 954dd3cf0..b10041ba3 100644 --- a/Engine/source/T3D/fx/ribbon.h +++ b/Engine/source/T3D/fx/ribbon.h @@ -124,6 +124,7 @@ public: ~Ribbon(); DECLARE_CONOBJECT(Ribbon); + DECLARE_CATEGORY("UNLISTED"); static void initPersistFields(); bool onNewDataBlock(GameBaseData*,bool); void onRemove(); diff --git a/Engine/source/T3D/fx/ribbonNode.h b/Engine/source/T3D/fx/ribbonNode.h index d4be52fa8..a071f87a2 100644 --- a/Engine/source/T3D/fx/ribbonNode.h +++ b/Engine/source/T3D/fx/ribbonNode.h @@ -87,6 +87,7 @@ public: void advanceTime(F32 dt); DECLARE_CONOBJECT(RibbonNode); + DECLARE_CATEGORY("Environment \t FX"); static void initPersistFields(); U32 packUpdate (NetConnection *conn, U32 mask, BitStream* stream); diff --git a/Engine/source/T3D/fx/splash.h b/Engine/source/T3D/fx/splash.h index a395739ff..523fd6e19 100644 --- a/Engine/source/T3D/fx/splash.h +++ b/Engine/source/T3D/fx/splash.h @@ -195,6 +195,7 @@ public: bool onNewDataBlock( GameBaseData *dptr, bool reload ); DECLARE_CONOBJECT(Splash); + DECLARE_CATEGORY("UNLISTED"); }; diff --git a/Engine/source/T3D/gameBase/gameBase.h b/Engine/source/T3D/gameBase/gameBase.h index 4f1aee2ef..bda0aa479 100644 --- a/Engine/source/T3D/gameBase/gameBase.h +++ b/Engine/source/T3D/gameBase/gameBase.h @@ -105,6 +105,7 @@ public: // The derived class should provide the following: DECLARE_CONOBJECT(GameBaseData); + DECLARE_CATEGORY("Datablock"); GameBaseData(); static void initPersistFields(); bool preload(bool server, String &errorStr); @@ -442,6 +443,7 @@ public: #endif DECLARE_CONOBJECT (GameBase ); + DECLARE_CATEGORY("UNLISTED"); /// @name Callbacks /// @{ diff --git a/Engine/source/T3D/groundPlane.h b/Engine/source/T3D/groundPlane.h index 9e57c325c..4b9ded740 100644 --- a/Engine/source/T3D/groundPlane.h +++ b/Engine/source/T3D/groundPlane.h @@ -60,6 +60,7 @@ public: typedef SceneObject Parent; DECLARE_CONOBJECT( GroundPlane ); + DECLARE_CATEGORY("Environment \t BackGround"); GroundPlane(); virtual ~GroundPlane(); diff --git a/Engine/source/T3D/item.h b/Engine/source/T3D/item.h index 2fe968fc5..611d64738 100644 --- a/Engine/source/T3D/item.h +++ b/Engine/source/T3D/item.h @@ -159,6 +159,7 @@ class Item: public ShapeBase public: DECLARE_CONOBJECT(Item); + DECLARE_CATEGORY("Item"); Item(); diff --git a/Engine/source/T3D/lightBase.h b/Engine/source/T3D/lightBase.h index bdaf38b15..503292c16 100644 --- a/Engine/source/T3D/lightBase.h +++ b/Engine/source/T3D/lightBase.h @@ -111,6 +111,7 @@ public: void inspectPostApply(); static void initPersistFields(); DECLARE_CONOBJECT(LightBase); + DECLARE_CATEGORY("UNLISTED"); // NetObject U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); diff --git a/Engine/source/T3D/lighting/boxEnvironmentProbe.h b/Engine/source/T3D/lighting/boxEnvironmentProbe.h index b70bd4ee5..3d825caa1 100644 --- a/Engine/source/T3D/lighting/boxEnvironmentProbe.h +++ b/Engine/source/T3D/lighting/boxEnvironmentProbe.h @@ -69,6 +69,7 @@ public: // Declare this object as a ConsoleObject so that we can // instantiate it into the world and network it DECLARE_CONOBJECT(BoxEnvironmentProbe); + DECLARE_CATEGORY("Lighting \t Probes"); //-------------------------------------------------------------------------- // Object Editing diff --git a/Engine/source/T3D/lighting/reflectionProbe.h b/Engine/source/T3D/lighting/reflectionProbe.h index 2b3751cf8..deca62318 100644 --- a/Engine/source/T3D/lighting/reflectionProbe.h +++ b/Engine/source/T3D/lighting/reflectionProbe.h @@ -274,6 +274,7 @@ public: // Declare this object as a ConsoleObject so that we can // instantiate it into the world and network it DECLARE_CONOBJECT(ReflectionProbe); + DECLARE_CATEGORY("UNLISTED"); //-------------------------------------------------------------------------- // Object Editing diff --git a/Engine/source/T3D/lighting/skylight.h b/Engine/source/T3D/lighting/skylight.h index 6db62a019..9735b0b3b 100644 --- a/Engine/source/T3D/lighting/skylight.h +++ b/Engine/source/T3D/lighting/skylight.h @@ -68,6 +68,7 @@ public: // Declare this object as a ConsoleObject so that we can // instantiate it into the world and network it DECLARE_CONOBJECT(Skylight); + DECLARE_CATEGORY("Lighting \t Probes"); //-------------------------------------------------------------------------- // Object Editing diff --git a/Engine/source/T3D/lighting/sphereEnvironmentProbe.h b/Engine/source/T3D/lighting/sphereEnvironmentProbe.h index 3df0b98b5..154d48747 100644 --- a/Engine/source/T3D/lighting/sphereEnvironmentProbe.h +++ b/Engine/source/T3D/lighting/sphereEnvironmentProbe.h @@ -64,6 +64,7 @@ public: // Declare this object as a ConsoleObject so that we can // instantiate it into the world and network it DECLARE_CONOBJECT(SphereEnvironmentProbe); + DECLARE_CATEGORY("Lighting \t Probes"); //-------------------------------------------------------------------------- // Object Editing diff --git a/Engine/source/T3D/missionMarker.h b/Engine/source/T3D/missionMarker.h index 3468553e5..db72eff64 100644 --- a/Engine/source/T3D/missionMarker.h +++ b/Engine/source/T3D/missionMarker.h @@ -85,6 +85,7 @@ class MissionMarker : public ShapeBase void unpackUpdate(NetConnection *conn, BitStream *stream); DECLARE_CONOBJECT(MissionMarker); + DECLARE_CATEGORY("Markers"); static void initPersistFields(); }; @@ -217,6 +218,7 @@ class CameraBookmark : public MissionMarker static void initPersistFields(); DECLARE_CONOBJECT(CameraBookmark); + DECLARE_CATEGORY("Markers"); /*DECLARE_CALLBACK( void, onAdd, () ); DECLARE_CALLBACK( void, onRemove, () ); DECLARE_CALLBACK( void, onGroupAdd, () ); @@ -224,4 +226,4 @@ class CameraBookmark : public MissionMarker DECLARE_CALLBACK( void, onInspectPostApply, () );*/ }; -#endif // _MISSIONMARKER_H_ \ No newline at end of file +#endif // _MISSIONMARKER_H_ diff --git a/Engine/source/T3D/notesObject.h b/Engine/source/T3D/notesObject.h index 80d16cd69..ace55804f 100644 --- a/Engine/source/T3D/notesObject.h +++ b/Engine/source/T3D/notesObject.h @@ -67,6 +67,7 @@ public: // Declare this object as a ConsoleObject so that we can // instantiate it into the world and network it DECLARE_CONOBJECT(NotesObject); + DECLARE_CATEGORY("Markers"); //-------------------------------------------------------------------------- // Object Editing diff --git a/Engine/source/T3D/occlusionVolume.h b/Engine/source/T3D/occlusionVolume.h index 49222bb94..64945115d 100644 --- a/Engine/source/T3D/occlusionVolume.h +++ b/Engine/source/T3D/occlusionVolume.h @@ -64,7 +64,7 @@ class OcclusionVolume : public ScenePolyhedralSpace // SimObject. DECLARE_CONOBJECT( OcclusionVolume ); DECLARE_DESCRIPTION( "A visibility blocking volume." ); - DECLARE_CATEGORY( "3D Scene" ); + DECLARE_CATEGORY("Area"); virtual bool onAdd(); diff --git a/Engine/source/T3D/pathCamera.h b/Engine/source/T3D/pathCamera.h index b511917cb..91060796f 100644 --- a/Engine/source/T3D/pathCamera.h +++ b/Engine/source/T3D/pathCamera.h @@ -92,6 +92,7 @@ private: public: DECLARE_CONOBJECT(PathCamera); + DECLARE_CATEGORY("Cinematic"); DECLARE_CALLBACK( void, onNode, (S32 node)); diff --git a/Engine/source/T3D/pathShape.h b/Engine/source/T3D/pathShape.h index 351d2d19c..9748ff04d 100644 --- a/Engine/source/T3D/pathShape.h +++ b/Engine/source/T3D/pathShape.h @@ -79,6 +79,7 @@ private: public: DECLARE_CONOBJECT(PathShape); + DECLARE_CATEGORY("Cinematic"); PathShape(); ~PathShape(); diff --git a/Engine/source/T3D/physicalZone.h b/Engine/source/T3D/physicalZone.h index 004ce6cb3..d265f0c87 100644 --- a/Engine/source/T3D/physicalZone.h +++ b/Engine/source/T3D/physicalZone.h @@ -77,6 +77,7 @@ class PhysicalZone : public SceneObject // SimObject DECLARE_CONOBJECT(PhysicalZone); + DECLARE_CATEGORY("Area"); static void consoleInit(); static void initPersistFields(); bool onAdd(); diff --git a/Engine/source/T3D/physics/physicsDebris.h b/Engine/source/T3D/physics/physicsDebris.h index 2a4a3d0fc..917307339 100644 --- a/Engine/source/T3D/physics/physicsDebris.h +++ b/Engine/source/T3D/physics/physicsDebris.h @@ -129,6 +129,7 @@ public: virtual ~PhysicsDebris(); DECLARE_CONOBJECT(PhysicsDebris); + DECLARE_CATEGORY("UNLISTED"); static void initPersistFields(); bool onAdd(); diff --git a/Engine/source/T3D/physics/physicsForce.h b/Engine/source/T3D/physics/physicsForce.h index 15257fd06..c48425d46 100644 --- a/Engine/source/T3D/physics/physicsForce.h +++ b/Engine/source/T3D/physics/physicsForce.h @@ -48,7 +48,7 @@ public: virtual ~PhysicsForce(); DECLARE_CONOBJECT( PhysicsForce ); - + DECLARE_CATEGORY("UNLISTED"); // SimObject static void initPersistFields(); bool onAdd(); @@ -88,4 +88,4 @@ protected: }; -#endif // _T3D_PHYSICS_PHYSICSFORCE_H_ \ No newline at end of file +#endif // _T3D_PHYSICS_PHYSICSFORCE_H_ diff --git a/Engine/source/T3D/physics/physicsShape.h b/Engine/source/T3D/physics/physicsShape.h index ebbad92d2..989409262 100644 --- a/Engine/source/T3D/physics/physicsShape.h +++ b/Engine/source/T3D/physics/physicsShape.h @@ -229,6 +229,7 @@ public: virtual ~PhysicsShape(); DECLARE_CONOBJECT( PhysicsShape ); + DECLARE_CATEGORY("Object \t Destructable"); /// Returns the PhysicsShapeData datablock. PhysicsShapeData* getDataBlock() { return static_cast( Parent::getDataBlock() ); } diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h index cd216fa53..b4fe44b40 100644 --- a/Engine/source/T3D/player.h +++ b/Engine/source/T3D/player.h @@ -697,6 +697,7 @@ protected: public: DECLARE_CONOBJECT(Player); + DECLARE_CATEGORY("Actor \t Controllable"); Player(); ~Player(); diff --git a/Engine/source/T3D/pointLight.h b/Engine/source/T3D/pointLight.h index 1a0923970..ad1f60bb9 100644 --- a/Engine/source/T3D/pointLight.h +++ b/Engine/source/T3D/pointLight.h @@ -47,6 +47,7 @@ public: // ConsoleObject DECLARE_CONOBJECT( PointLight ); + DECLARE_CATEGORY("Lighting \t Lights"); static void initPersistFields(); // SceneObject diff --git a/Engine/source/T3D/portal.h b/Engine/source/T3D/portal.h index 994e50dac..a9574b57f 100644 --- a/Engine/source/T3D/portal.h +++ b/Engine/source/T3D/portal.h @@ -185,7 +185,8 @@ class Portal : public Zone // SimObject. DECLARE_CONOBJECT( Portal ); - + DECLARE_CATEGORY("Area"); + static void initPersistFields(); static void consoleInit(); diff --git a/Engine/source/T3D/prefab.h b/Engine/source/T3D/prefab.h index 95af5e7ed..6e8cc9a7b 100644 --- a/Engine/source/T3D/prefab.h +++ b/Engine/source/T3D/prefab.h @@ -57,6 +57,7 @@ public: virtual ~Prefab(); DECLARE_CONOBJECT(Prefab); + DECLARE_CATEGORY("Object \t Collection"); static void initPersistFields(); diff --git a/Engine/source/T3D/projectile.h b/Engine/source/T3D/projectile.h index 9aa0690d0..8e8f9542a 100644 --- a/Engine/source/T3D/projectile.h +++ b/Engine/source/T3D/projectile.h @@ -187,6 +187,7 @@ public: ~Projectile(); DECLARE_CONOBJECT(Projectile); + DECLARE_CATEGORY("UNLISTED"); // SimObject bool onAdd(); diff --git a/Engine/source/T3D/rigidShape.h b/Engine/source/T3D/rigidShape.h index fe5d3623a..1e465557c 100644 --- a/Engine/source/T3D/rigidShape.h +++ b/Engine/source/T3D/rigidShape.h @@ -308,6 +308,7 @@ public: void unpackUpdate(NetConnection *conn, BitStream *stream); DECLARE_CONOBJECT(RigidShape); + DECLARE_CATEGORY("Object \t Destructable"); }; diff --git a/Engine/source/T3D/sfx/sfxEmitter.h b/Engine/source/T3D/sfx/sfxEmitter.h index 067471820..f21ef168d 100644 --- a/Engine/source/T3D/sfx/sfxEmitter.h +++ b/Engine/source/T3D/sfx/sfxEmitter.h @@ -260,7 +260,7 @@ class SFXEmitter : public SceneObject DECLARE_CONOBJECT( SFXEmitter ); DECLARE_DESCRIPTION( "A 3D object emitting sound." ); - DECLARE_CATEGORY( "3D Sound" ); + DECLARE_CATEGORY("Environment \t FX"); }; #endif // _SFXEMITTER_H_ diff --git a/Engine/source/T3D/sfx/sfxSpace.h b/Engine/source/T3D/sfx/sfxSpace.h index 028a85fdf..354a91a11 100644 --- a/Engine/source/T3D/sfx/sfxSpace.h +++ b/Engine/source/T3D/sfx/sfxSpace.h @@ -55,7 +55,7 @@ class SFXSpace : public SceneAmbientSoundObject< ScenePolyhedralObject< SceneSpa // SimObject. DECLARE_CONOBJECT( SFXSpace ); DECLARE_DESCRIPTION( "A box volume that defines an ambient sound space." ); - DECLARE_CATEGORY( "3D Sound" ); + DECLARE_CATEGORY("Area"); static void consoleInit(); }; diff --git a/Engine/source/T3D/spotLight.h b/Engine/source/T3D/spotLight.h index 2552f2321..20e6426f6 100644 --- a/Engine/source/T3D/spotLight.h +++ b/Engine/source/T3D/spotLight.h @@ -51,6 +51,7 @@ public: // ConsoleObject DECLARE_CONOBJECT( SpotLight ); + DECLARE_CATEGORY("Lighting \t Lights"); static void initPersistFields(); // SceneObject diff --git a/Engine/source/T3D/staticShape.h b/Engine/source/T3D/staticShape.h index 7403b9e3a..a97375847 100644 --- a/Engine/source/T3D/staticShape.h +++ b/Engine/source/T3D/staticShape.h @@ -75,6 +75,7 @@ protected: public: DECLARE_CONOBJECT(StaticShape); + DECLARE_CATEGORY("Object \t Destructable"); StaticShape(); ~StaticShape(); diff --git a/Engine/source/T3D/trigger.h b/Engine/source/T3D/trigger.h index c5814edbb..74ab150b8 100644 --- a/Engine/source/T3D/trigger.h +++ b/Engine/source/T3D/trigger.h @@ -127,6 +127,7 @@ class Trigger : public GameBase // SimObject DECLARE_CONOBJECT(Trigger); + DECLARE_CATEGORY("Area"); DECLARE_CALLBACK( void, onAdd, ( U32 objectId ) ); DECLARE_CALLBACK( void, onRemove, ( U32 objectId ) ); diff --git a/Engine/source/T3D/tsStatic.h b/Engine/source/T3D/tsStatic.h index 6aa8b8141..fb870b88a 100644 --- a/Engine/source/T3D/tsStatic.h +++ b/Engine/source/T3D/tsStatic.h @@ -236,6 +236,7 @@ public: ~TSStatic(); DECLARE_CONOBJECT(TSStatic); + DECLARE_CATEGORY("Object \t Simple"); static void initPersistFields(); /// returns the shape asset used for this object StringTableEntry getTypeHint() const override { return (getShapeAsset()) ? getShapeAsset()->getAssetName(): StringTable->EmptyString(); } diff --git a/Engine/source/T3D/vehicles/flyingVehicle.h b/Engine/source/T3D/vehicles/flyingVehicle.h index 9567e7e03..96beed99a 100644 --- a/Engine/source/T3D/vehicles/flyingVehicle.h +++ b/Engine/source/T3D/vehicles/flyingVehicle.h @@ -180,6 +180,7 @@ class FlyingVehicle: public Vehicle U32 getCollisionMask(); public: DECLARE_CONOBJECT(FlyingVehicle); + DECLARE_CATEGORY("Actor \t Controllable"); static void initPersistFields(); FlyingVehicle(); diff --git a/Engine/source/T3D/vehicles/hoverVehicle.h b/Engine/source/T3D/vehicles/hoverVehicle.h index a49311f03..a39cefdc6 100644 --- a/Engine/source/T3D/vehicles/hoverVehicle.h +++ b/Engine/source/T3D/vehicles/hoverVehicle.h @@ -200,6 +200,7 @@ class HoverVehicle : public Vehicle void advanceTime(F32 dt); DECLARE_CONOBJECT(HoverVehicle); + DECLARE_CATEGORY("Actor \t Controllable"); // static void initPersistFields(); U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); diff --git a/Engine/source/T3D/vehicles/vehicle.h b/Engine/source/T3D/vehicles/vehicle.h index ff6b29817..6fc31d43b 100644 --- a/Engine/source/T3D/vehicles/vehicle.h +++ b/Engine/source/T3D/vehicles/vehicle.h @@ -165,6 +165,7 @@ public: /// @} DECLARE_CONOBJECT(Vehicle); + DECLARE_CATEGORY("UNLISTED"); }; diff --git a/Engine/source/T3D/vehicles/vehicleBlocker.h b/Engine/source/T3D/vehicles/vehicleBlocker.h index e2861bbe7..d670da0a9 100644 --- a/Engine/source/T3D/vehicles/vehicleBlocker.h +++ b/Engine/source/T3D/vehicles/vehicleBlocker.h @@ -52,6 +52,7 @@ class VehicleBlocker : public SceneObject ~VehicleBlocker(); DECLARE_CONOBJECT(VehicleBlocker); + DECLARE_CATEGORY("Area"); static void initPersistFields(); U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); diff --git a/Engine/source/T3D/vehicles/wheeledVehicle.h b/Engine/source/T3D/vehicles/wheeledVehicle.h index 93f197757..1c9e9bff3 100644 --- a/Engine/source/T3D/vehicles/wheeledVehicle.h +++ b/Engine/source/T3D/vehicles/wheeledVehicle.h @@ -225,6 +225,7 @@ class WheeledVehicle: public Vehicle public: DECLARE_CONOBJECT(WheeledVehicle); + DECLARE_CATEGORY("Actor \t Controllable"); static void initPersistFields(); WheeledVehicle(); diff --git a/Engine/source/T3D/zone.h b/Engine/source/T3D/zone.h index 0f4e1e471..3aedcc93a 100644 --- a/Engine/source/T3D/zone.h +++ b/Engine/source/T3D/zone.h @@ -68,7 +68,7 @@ class Zone : public SceneAmbientSoundObject< ScenePolyhedralZone > // SimObject DECLARE_CONOBJECT( Zone ); DECLARE_DESCRIPTION( "A volume that encloses objects for visibility culling." ); - DECLARE_CATEGORY( "3D" ); + DECLARE_CATEGORY( "Area" ); static void consoleInit(); }; diff --git a/Engine/source/afx/afxCamera.h b/Engine/source/afx/afxCamera.h index e04afaa13..78e4bcb98 100644 --- a/Engine/source/afx/afxCamera.h +++ b/Engine/source/afx/afxCamera.h @@ -51,7 +51,6 @@ struct afxCameraData: public ShapeBaseData { // DECLARE_CONOBJECT(afxCameraData); - DECLARE_CATEGORY("AFX"); static void initPersistFields(); virtual void packData(BitStream* stream); virtual void unpackData(BitStream* stream); @@ -144,7 +143,7 @@ public: bool isCamera() const { return true; } DECLARE_CONOBJECT(afxCamera); - DECLARE_CATEGORY("AFX"); + DECLARE_CATEGORY("UNLISTED"); private: // 3POV SECTION void cam_update_3pov(F32 dt, bool on_server); diff --git a/Engine/source/afx/afxChoreographer.h b/Engine/source/afx/afxChoreographer.h index e3b83585e..70a301350 100644 --- a/Engine/source/afx/afxChoreographer.h +++ b/Engine/source/afx/afxChoreographer.h @@ -57,7 +57,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxChoreographerData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// @@ -200,7 +199,7 @@ public: virtual void impactNotify(const Point3F& p, const Point3F& n, SceneObject*) { } DECLARE_CONOBJECT(afxChoreographer); - DECLARE_CATEGORY("AFX"); + DECLARE_CATEGORY("UNLISTED"); // CONSTRAINT REMAPPING << protected: diff --git a/Engine/source/afx/afxEffectGroup.h b/Engine/source/afx/afxEffectGroup.h index 2942bddb0..87a99522e 100644 --- a/Engine/source/afx/afxEffectGroup.h +++ b/Engine/source/afx/afxEffectGroup.h @@ -96,7 +96,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxEffectGroupData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/afxEffectWrapper.h b/Engine/source/afx/afxEffectWrapper.h index 37a0e9fcb..b12678795 100644 --- a/Engine/source/afx/afxEffectWrapper.h +++ b/Engine/source/afx/afxEffectWrapper.h @@ -100,7 +100,6 @@ public: virtual void gather_cons_defs(Vector& defs) { }; DECLARE_CONOBJECT(afxEffectBaseData); - DECLARE_CATEGORY("AFX"); }; //class afxEffectWrapperData : public GameBaseData, public afxEffectDefs @@ -207,7 +206,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxEffectWrapperData); - DECLARE_CATEGORY("AFX"); }; inline bool afxEffectWrapperData::testExecConditions(U32 conditions) @@ -382,7 +380,6 @@ public: static afxEffectWrapper* ew_create(afxChoreographer*, afxEffectWrapperData*, afxConstraintMgr*, F32 time_factor, S32 group_index=0); DECLARE_CONOBJECT(afxEffectWrapper); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/afxEffectron.h b/Engine/source/afx/afxEffectron.h index 9c05a48b3..bb66c988b 100644 --- a/Engine/source/afx/afxEffectron.h +++ b/Engine/source/afx/afxEffectron.h @@ -82,7 +82,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxEffectronData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// @@ -178,7 +177,6 @@ public: void finish_startup(); DECLARE_CONOBJECT(afxEffectron); - DECLARE_CATEGORY("AFX"); private: void process_server(); diff --git a/Engine/source/afx/afxMagicMissile.h b/Engine/source/afx/afxMagicMissile.h index 4b3430918..957066a4b 100644 --- a/Engine/source/afx/afxMagicMissile.h +++ b/Engine/source/afx/afxMagicMissile.h @@ -218,7 +218,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxMagicMissileData); - DECLARE_CATEGORY("AFX"); public: /*C*/ afxMagicMissileData(const afxMagicMissileData&, bool = false); @@ -400,7 +399,7 @@ public: virtual void onDeleteNotify(SimObject*); DECLARE_CONOBJECT(afxMagicMissile); - DECLARE_CATEGORY("AFX"); + DECLARE_CATEGORY("UNLISTED"); static void initPersistFields(); diff --git a/Engine/source/afx/afxMagicSpell.h b/Engine/source/afx/afxMagicSpell.h index f7545bdb8..c97886802 100644 --- a/Engine/source/afx/afxMagicSpell.h +++ b/Engine/source/afx/afxMagicSpell.h @@ -122,7 +122,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxMagicSpellData); - DECLARE_CATEGORY("AFX"); /// @name Callbacks /// @{ @@ -320,7 +319,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxMagicSpell); - DECLARE_CATEGORY("AFX"); private: void process_server(); diff --git a/Engine/source/afx/afxResidueMgr.h b/Engine/source/afx/afxResidueMgr.h index 5219f331b..a4066828a 100644 --- a/Engine/source/afx/afxResidueMgr.h +++ b/Engine/source/afx/afxResidueMgr.h @@ -171,7 +171,7 @@ public: static void setMaster(afxResidueMgr* m) { the_mgr = m; } DECLARE_CONOBJECT(afxResidueMgr); - DECLARE_CATEGORY("AFX"); + DECLARE_CATEGORY("UNLISTED"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/afxSelectron.h b/Engine/source/afx/afxSelectron.h index f7b0c4e50..deca54fdf 100644 --- a/Engine/source/afx/afxSelectron.h +++ b/Engine/source/afx/afxSelectron.h @@ -106,7 +106,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxSelectronData); - DECLARE_CATEGORY("AFX"); }; inline bool afxSelectronData::matches(U32 mask, U8 style) @@ -219,7 +218,7 @@ public: void finish_startup(); DECLARE_CONOBJECT(afxSelectron); - DECLARE_CATEGORY("AFX"); + DECLARE_CATEGORY("UNLISTED"); private: void process_server(); diff --git a/Engine/source/afx/afxSpellBook.h b/Engine/source/afx/afxSpellBook.h index 37a564104..e6bf8d580 100644 --- a/Engine/source/afx/afxSpellBook.h +++ b/Engine/source/afx/afxSpellBook.h @@ -68,7 +68,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxSpellBookData); - DECLARE_CATEGORY("AFX"); }; inline bool afxSpellBookData::verifyPageSlot(S32 page, S32 slot) @@ -128,7 +127,7 @@ public: F32 getCooldownFactor(S32 page, S32 slot); DECLARE_CONOBJECT(afxSpellBook); - DECLARE_CATEGORY("AFX"); + DECLARE_CATEGORY("UNLISTED"); }; inline S32 afxSpellBook::getPageSlotIndex(S32 page, S32 slot) diff --git a/Engine/source/afx/afxZodiacGroundPlaneRenderer_T3D.h b/Engine/source/afx/afxZodiacGroundPlaneRenderer_T3D.h index 862c2bc86..5e8deafb9 100644 --- a/Engine/source/afx/afxZodiacGroundPlaneRenderer_T3D.h +++ b/Engine/source/afx/afxZodiacGroundPlaneRenderer_T3D.h @@ -83,7 +83,6 @@ public: // ConsoleObject DECLARE_CONOBJECT(afxZodiacGroundPlaneRenderer); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/afxZodiacMeshRoadRenderer_T3D.h b/Engine/source/afx/afxZodiacMeshRoadRenderer_T3D.h index bc8477bef..8fd5e10f7 100644 --- a/Engine/source/afx/afxZodiacMeshRoadRenderer_T3D.h +++ b/Engine/source/afx/afxZodiacMeshRoadRenderer_T3D.h @@ -84,7 +84,6 @@ public: // ConsoleObject DECLARE_CONOBJECT(afxZodiacMeshRoadRenderer); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/afxZodiacPolysoupRenderer_T3D.h b/Engine/source/afx/afxZodiacPolysoupRenderer_T3D.h index a01dffd34..b28fba9c1 100644 --- a/Engine/source/afx/afxZodiacPolysoupRenderer_T3D.h +++ b/Engine/source/afx/afxZodiacPolysoupRenderer_T3D.h @@ -84,7 +84,6 @@ public: // ConsoleObject DECLARE_CONOBJECT(afxZodiacPolysoupRenderer); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/afxZodiacTerrainRenderer_T3D.h b/Engine/source/afx/afxZodiacTerrainRenderer_T3D.h index cf93ded16..1f0f804a5 100644 --- a/Engine/source/afx/afxZodiacTerrainRenderer_T3D.h +++ b/Engine/source/afx/afxZodiacTerrainRenderer_T3D.h @@ -84,7 +84,6 @@ public: // ConsoleObject DECLARE_CONOBJECT(afxZodiacTerrainRenderer); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/arcaneFX.cpp b/Engine/source/afx/arcaneFX.cpp index 4e67124d9..dccaca6c9 100644 --- a/Engine/source/afx/arcaneFX.cpp +++ b/Engine/source/afx/arcaneFX.cpp @@ -83,7 +83,6 @@ public: } DECLARE_CONOBJECT(ClientZoneInEvent); - DECLARE_CATEGORY("AFX"); }; IMPLEMENT_CO_SERVEREVENT_V1(ClientZoneInEvent); diff --git a/Engine/source/afx/ce/afxAnimClip.h b/Engine/source/afx/ce/afxAnimClip.h index 4b1dbb0f3..19bd3ccfa 100644 --- a/Engine/source/afx/ce/afxAnimClip.h +++ b/Engine/source/afx/ce/afxAnimClip.h @@ -73,7 +73,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxAnimClipData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxAnimLock.h b/Engine/source/afx/ce/afxAnimLock.h index fea98194b..8aa9cf348 100644 --- a/Engine/source/afx/ce/afxAnimLock.h +++ b/Engine/source/afx/ce/afxAnimLock.h @@ -40,7 +40,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxAnimLockData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxAreaDamage.h b/Engine/source/afx/ce/afxAreaDamage.h index 4386acd4e..dd8a2b30a 100644 --- a/Engine/source/afx/ce/afxAreaDamage.h +++ b/Engine/source/afx/ce/afxAreaDamage.h @@ -54,7 +54,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxAreaDamageData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxAudioBank.h b/Engine/source/afx/ce/afxAudioBank.h index e7f9ee4d3..1f1fa42e8 100644 --- a/Engine/source/afx/ce/afxAudioBank.h +++ b/Engine/source/afx/ce/afxAudioBank.h @@ -59,7 +59,6 @@ public: virtual bool allowSubstitutions() const { return true; } DECLARE_CONOBJECT(afxAudioBank); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxBillboard.h b/Engine/source/afx/ce/afxBillboard.h index 8c6532539..da382ac20 100644 --- a/Engine/source/afx/ce/afxBillboard.h +++ b/Engine/source/afx/ce/afxBillboard.h @@ -74,7 +74,6 @@ public: void onChangeTexture() {} DECLARE_CONOBJECT(afxBillboardData); - DECLARE_CATEGORY("AFX"); }; typedef afxBillboardData::BlendStyle afxBillboard_BlendStyle; @@ -117,7 +116,7 @@ public: void _renderBillboard(ObjectRenderInst*, SceneRenderState*, BaseMatInstance*); DECLARE_CONOBJECT(afxBillboard); - DECLARE_CATEGORY("AFX"); + DECLARE_CATEGORY("UNLISTED"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxCameraPuppet.h b/Engine/source/afx/ce/afxCameraPuppet.h index 4226e1242..da9f4eb7a 100644 --- a/Engine/source/afx/ce/afxCameraPuppet.h +++ b/Engine/source/afx/ce/afxCameraPuppet.h @@ -55,7 +55,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxCameraPuppetData); - DECLARE_CATEGORY("AFX"); }; diff --git a/Engine/source/afx/ce/afxCameraShake.h b/Engine/source/afx/ce/afxCameraShake.h index ca0bfb439..32d4393ca 100644 --- a/Engine/source/afx/ce/afxCameraShake.h +++ b/Engine/source/afx/ce/afxCameraShake.h @@ -49,7 +49,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxCameraShakeData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxCollisionEvent.h b/Engine/source/afx/ce/afxCollisionEvent.h index 31f6e09a1..29cb7b5a6 100644 --- a/Engine/source/afx/ce/afxCollisionEvent.h +++ b/Engine/source/afx/ce/afxCollisionEvent.h @@ -51,7 +51,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxCollisionEventData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxConsoleMessage.h b/Engine/source/afx/ce/afxConsoleMessage.h index 224107181..e91705a00 100644 --- a/Engine/source/afx/ce/afxConsoleMessage.h +++ b/Engine/source/afx/ce/afxConsoleMessage.h @@ -46,7 +46,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxConsoleMessageData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxDamage.h b/Engine/source/afx/ce/afxDamage.h index 19b6e7943..40249ef6b 100644 --- a/Engine/source/afx/ce/afxDamage.h +++ b/Engine/source/afx/ce/afxDamage.h @@ -55,7 +55,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxDamageData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxFootSwitch.h b/Engine/source/afx/ce/afxFootSwitch.h index 6420756e9..c15f4299a 100644 --- a/Engine/source/afx/ce/afxFootSwitch.h +++ b/Engine/source/afx/ce/afxFootSwitch.h @@ -48,7 +48,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxFootSwitchData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxGuiController.h b/Engine/source/afx/ce/afxGuiController.h index 90db3249c..744ddb7f3 100644 --- a/Engine/source/afx/ce/afxGuiController.h +++ b/Engine/source/afx/ce/afxGuiController.h @@ -50,7 +50,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxGuiControllerData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxGuiText.h b/Engine/source/afx/ce/afxGuiText.h index fa03de46c..a8f06af9d 100644 --- a/Engine/source/afx/ce/afxGuiText.h +++ b/Engine/source/afx/ce/afxGuiText.h @@ -49,7 +49,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxGuiTextData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxLight.h b/Engine/source/afx/ce/afxLight.h index 11181fdc5..84e2bd1cd 100644 --- a/Engine/source/afx/ce/afxLight.h +++ b/Engine/source/afx/ce/afxLight.h @@ -30,7 +30,6 @@ struct afxLightData : public GameBaseData { typedef GameBaseData Parent; DECLARE_CONOBJECT(afxLightData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxLightBase_T3D.h b/Engine/source/afx/ce/afxLightBase_T3D.h index 1a517d02d..0913c8b10 100644 --- a/Engine/source/afx/ce/afxLightBase_T3D.h +++ b/Engine/source/afx/ce/afxLightBase_T3D.h @@ -65,7 +65,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxT3DLightBaseData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxMachineGun.h b/Engine/source/afx/ce/afxMachineGun.h index f01941f55..7a8612a48 100644 --- a/Engine/source/afx/ce/afxMachineGun.h +++ b/Engine/source/afx/ce/afxMachineGun.h @@ -51,7 +51,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxMachineGunData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxModel.h b/Engine/source/afx/ce/afxModel.h index ad8cb4c99..ec972a439 100644 --- a/Engine/source/afx/ce/afxModel.h +++ b/Engine/source/afx/ce/afxModel.h @@ -98,7 +98,6 @@ public: void onSequenceChanged() {} DECLARE_CONOBJECT(afxModelData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// @@ -162,7 +161,7 @@ public: F32 getAnimClipDuration(const char* clip); DECLARE_CONOBJECT(afxModel); - DECLARE_CATEGORY("AFX"); + DECLARE_CATEGORY("UNLISTED"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxMooring.h b/Engine/source/afx/ce/afxMooring.h index d6cfb0b77..266331b21 100644 --- a/Engine/source/afx/ce/afxMooring.h +++ b/Engine/source/afx/ce/afxMooring.h @@ -52,7 +52,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxMooringData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// @@ -94,7 +93,7 @@ public: virtual void prepRenderImage(SceneRenderState*); DECLARE_CONOBJECT(afxMooring); - DECLARE_CATEGORY("AFX"); + DECLARE_CATEGORY("UNLISTED"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxMultiLight.h b/Engine/source/afx/ce/afxMultiLight.h index 9a2261909..f6bae942f 100644 --- a/Engine/source/afx/ce/afxMultiLight.h +++ b/Engine/source/afx/ce/afxMultiLight.h @@ -30,7 +30,6 @@ struct afxMultiLightData : public GameBaseData { typedef GameBaseData Parent; DECLARE_CONOBJECT(afxMultiLightData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxParticleEmitter.h b/Engine/source/afx/ce/afxParticleEmitter.h index a7053a12b..69fb79c91 100644 --- a/Engine/source/afx/ce/afxParticleEmitter.h +++ b/Engine/source/afx/ce/afxParticleEmitter.h @@ -67,7 +67,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxParticleEmitterData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// @@ -92,7 +91,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxParticleEmitterVectorData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// @@ -121,7 +119,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxParticleEmitterConeData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// @@ -165,7 +162,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxParticleEmitterPathData); - DECLARE_CATEGORY("AFX"); }; typedef afxParticleEmitterPathData::PathOriginType afxParticleEmitterPath_OriginType; @@ -197,7 +193,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxParticleEmitterDiscData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxPhraseEffect.h b/Engine/source/afx/ce/afxPhraseEffect.h index ad189d787..827ac9ca9 100644 --- a/Engine/source/afx/ce/afxPhraseEffect.h +++ b/Engine/source/afx/ce/afxPhraseEffect.h @@ -103,7 +103,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxPhraseEffectData); - DECLARE_CATEGORY("AFX"); }; typedef afxPhraseEffectData::MatchType afxPhraseEffect_MatchType; diff --git a/Engine/source/afx/ce/afxPhysicalZone.h b/Engine/source/afx/ce/afxPhysicalZone.h index 379e25b12..02012c5a0 100644 --- a/Engine/source/afx/ce/afxPhysicalZone.h +++ b/Engine/source/afx/ce/afxPhysicalZone.h @@ -57,7 +57,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxPhysicalZoneData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxPlayerMovement.h b/Engine/source/afx/ce/afxPlayerMovement.h index 725f388e3..13920d85f 100644 --- a/Engine/source/afx/ce/afxPlayerMovement.h +++ b/Engine/source/afx/ce/afxPlayerMovement.h @@ -62,7 +62,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxPlayerMovementData); - DECLARE_CATEGORY("AFX"); }; typedef afxPlayerMovementData::OpType afxPlayerMovement_OpType; diff --git a/Engine/source/afx/ce/afxPlayerPuppet.h b/Engine/source/afx/ce/afxPlayerPuppet.h index ca6624bd9..d8d96727a 100644 --- a/Engine/source/afx/ce/afxPlayerPuppet.h +++ b/Engine/source/afx/ce/afxPlayerPuppet.h @@ -55,7 +55,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxPlayerPuppetData); - DECLARE_CATEGORY("AFX"); }; diff --git a/Engine/source/afx/ce/afxPointLight_T3D.h b/Engine/source/afx/ce/afxPointLight_T3D.h index 0f40ba199..de5215aa8 100644 --- a/Engine/source/afx/ce/afxPointLight_T3D.h +++ b/Engine/source/afx/ce/afxPointLight_T3D.h @@ -48,7 +48,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxT3DPointLightData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxProjectile.h b/Engine/source/afx/ce/afxProjectile.h index 74609a4d3..d3bbff312 100644 --- a/Engine/source/afx/ce/afxProjectile.h +++ b/Engine/source/afx/ce/afxProjectile.h @@ -72,7 +72,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxProjectileData); - DECLARE_CATEGORY("AFX"); }; typedef afxProjectileData::LaunchDirType afxProjectile_LaunchDirType; @@ -109,7 +108,7 @@ public: virtual void explode(const Point3F& p, const Point3F& n, const U32 collideType); DECLARE_CONOBJECT(afxProjectile); - DECLARE_CATEGORY("AFX"); + DECLARE_CATEGORY("UNLISTED"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxScriptEvent.h b/Engine/source/afx/ce/afxScriptEvent.h index 234e50cf3..39a32f79f 100644 --- a/Engine/source/afx/ce/afxScriptEvent.h +++ b/Engine/source/afx/ce/afxScriptEvent.h @@ -49,7 +49,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxScriptEventData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxSpotLight_T3D.h b/Engine/source/afx/ce/afxSpotLight_T3D.h index 077850784..9fc5a2f4e 100644 --- a/Engine/source/afx/ce/afxSpotLight_T3D.h +++ b/Engine/source/afx/ce/afxSpotLight_T3D.h @@ -50,7 +50,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxT3DSpotLightData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxStaticShape.h b/Engine/source/afx/ce/afxStaticShape.h index d58ce4a0a..fce34d053 100644 --- a/Engine/source/afx/ce/afxStaticShape.h +++ b/Engine/source/afx/ce/afxStaticShape.h @@ -54,7 +54,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxStaticShapeData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// @@ -90,7 +89,7 @@ public: void setVisibility(bool flag) { mIs_visible = flag; } DECLARE_CONOBJECT(afxStaticShape); - DECLARE_CATEGORY("AFX"); + DECLARE_CATEGORY("UNLISTED"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxVolumeLight.h b/Engine/source/afx/ce/afxVolumeLight.h index 9b56c4f27..c74ce7f95 100644 --- a/Engine/source/afx/ce/afxVolumeLight.h +++ b/Engine/source/afx/ce/afxVolumeLight.h @@ -30,7 +30,6 @@ struct afxVolumeLightData : public GameBaseData { typedef GameBaseData Parent; DECLARE_CONOBJECT(afxVolumeLightData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ce/afxZodiac.h b/Engine/source/afx/ce/afxZodiac.h index 917ee5490..0a5104f7b 100644 --- a/Engine/source/afx/ce/afxZodiac.h +++ b/Engine/source/afx/ce/afxZodiac.h @@ -124,7 +124,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxZodiacData); - DECLARE_CATEGORY("AFX"); }; typedef afxZodiacData::BlendType afxZodiac_BlendType; diff --git a/Engine/source/afx/ce/afxZodiacPlane.h b/Engine/source/afx/ce/afxZodiacPlane.h index 7b2d7aec2..bb2b6ed33 100644 --- a/Engine/source/afx/ce/afxZodiacPlane.h +++ b/Engine/source/afx/ce/afxZodiacPlane.h @@ -96,7 +96,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxZodiacPlaneData); - DECLARE_CATEGORY("AFX"); }; typedef afxZodiacPlaneData::BlendType afxZodiacPlane_BlendType; @@ -142,7 +141,7 @@ public: void _renderZodiacPlane(ObjectRenderInst*, SceneRenderState*, BaseMatInstance*); DECLARE_CONOBJECT(afxZodiacPlane); - DECLARE_CATEGORY("AFX"); + DECLARE_CATEGORY("UNLISTED"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ea/afxEA_TLKLight.cpp b/Engine/source/afx/ea/afxEA_TLKLight.cpp index 160558cd9..6d76f8ac0 100644 --- a/Engine/source/afx/ea/afxEA_TLKLight.cpp +++ b/Engine/source/afx/ea/afxEA_TLKLight.cpp @@ -34,7 +34,6 @@ struct sgLightObjectData : public GameBaseData { typedef GameBaseData Parent; DECLARE_CONOBJECT(sgLightObjectData); - DECLARE_CATEGORY("AFX"); }; IMPLEMENT_CO_DATABLOCK_V1(sgLightObjectData); @@ -76,4 +75,4 @@ bool afxEA_TLKLightDesc::requiresStop(const afxEffectWrapperData* ew, const afxE return (timing.lifetime < 0); } -//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// \ No newline at end of file +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/ea/afxEA_Zodiac.cpp b/Engine/source/afx/ea/afxEA_Zodiac.cpp index dcaa05ee4..c42a36837 100644 --- a/Engine/source/afx/ea/afxEA_Zodiac.cpp +++ b/Engine/source/afx/ea/afxEA_Zodiac.cpp @@ -77,7 +77,7 @@ public: static void initPersistFields(); //DECLARE_CONOBJECT(afxEA_Zodiac); - DECLARE_CATEGORY("AFX"); + DECLARE_CATEGORY("UNLISTED"); }; //IMPLEMENT_CONOBJECT(afxEA_Zodiac); diff --git a/Engine/source/afx/forces/afxF_Drag.cpp b/Engine/source/afx/forces/afxF_Drag.cpp index 2eab36584..91fc394ae 100644 --- a/Engine/source/afx/forces/afxF_Drag.cpp +++ b/Engine/source/afx/forces/afxF_Drag.cpp @@ -50,7 +50,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxF_DragData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/forces/afxF_Gravity.cpp b/Engine/source/afx/forces/afxF_Gravity.cpp index b91665031..946a412de 100644 --- a/Engine/source/afx/forces/afxF_Gravity.cpp +++ b/Engine/source/afx/forces/afxF_Gravity.cpp @@ -48,7 +48,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxF_GravityData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/forces/afxXM_Force.cpp b/Engine/source/afx/forces/afxXM_Force.cpp index 2b8ab6278..eaabd9332 100644 --- a/Engine/source/afx/forces/afxXM_Force.cpp +++ b/Engine/source/afx/forces/afxXM_Force.cpp @@ -64,7 +64,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_ForceData); - DECLARE_CATEGORY("AFX"); }; class afxXM_Force : public afxXM_WeightedBase, public afxEffectDefs diff --git a/Engine/source/afx/rpg/afxRPGMagicSpell.h b/Engine/source/afx/rpg/afxRPGMagicSpell.h index 832898547..17e6ac0b4 100644 --- a/Engine/source/afx/rpg/afxRPGMagicSpell.h +++ b/Engine/source/afx/rpg/afxRPGMagicSpell.h @@ -97,7 +97,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxRPGMagicSpellData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/util/afxParticlePool.h b/Engine/source/afx/util/afxParticlePool.h index d09b0dcd7..e4cfd1408 100644 --- a/Engine/source/afx/util/afxParticlePool.h +++ b/Engine/source/afx/util/afxParticlePool.h @@ -55,7 +55,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxParticlePoolData); - DECLARE_CATEGORY("AFX"); }; typedef afxParticlePoolData::PoolType afxParticlePool_PoolType; @@ -135,7 +134,6 @@ public: void setSortPriority(S8 priority); DECLARE_CONOBJECT(afxParticlePool); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/util/afxPath.h b/Engine/source/afx/util/afxPath.h index 50ce218e8..dc931641c 100644 --- a/Engine/source/afx/util/afxPath.h +++ b/Engine/source/afx/util/afxPath.h @@ -94,7 +94,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxPathData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/xm/afxXM_Aim.cpp b/Engine/source/afx/xm/afxXM_Aim.cpp index fb0bac753..8256ebf0b 100644 --- a/Engine/source/afx/xm/afxXM_Aim.cpp +++ b/Engine/source/afx/xm/afxXM_Aim.cpp @@ -54,7 +54,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_AimData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/xm/afxXM_AltitudeConform.cpp b/Engine/source/afx/xm/afxXM_AltitudeConform.cpp index ba9faf695..5c7228cad 100644 --- a/Engine/source/afx/xm/afxXM_AltitudeConform.cpp +++ b/Engine/source/afx/xm/afxXM_AltitudeConform.cpp @@ -53,7 +53,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_AltitudeConformData); - DECLARE_CATEGORY("AFX"); }; class afxXM_AltitudeConform : public afxXM_WeightedBase diff --git a/Engine/source/afx/xm/afxXM_BoxAdapt.cpp b/Engine/source/afx/xm/afxXM_BoxAdapt.cpp index e5fd631e6..d033c144a 100644 --- a/Engine/source/afx/xm/afxXM_BoxAdapt.cpp +++ b/Engine/source/afx/xm/afxXM_BoxAdapt.cpp @@ -52,7 +52,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_BoxAdaptData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/xm/afxXM_BoxConform.cpp b/Engine/source/afx/xm/afxXM_BoxConform.cpp index 10c99d8c3..130e4421c 100644 --- a/Engine/source/afx/xm/afxXM_BoxConform.cpp +++ b/Engine/source/afx/xm/afxXM_BoxConform.cpp @@ -53,7 +53,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_BoxConformData); - DECLARE_CATEGORY("AFX"); }; class afxXM_BoxConform : public afxXM_Base diff --git a/Engine/source/afx/xm/afxXM_BoxHeightOffset.cpp b/Engine/source/afx/xm/afxXM_BoxHeightOffset.cpp index 96fce645a..d7418ba52 100644 --- a/Engine/source/afx/xm/afxXM_BoxHeightOffset.cpp +++ b/Engine/source/afx/xm/afxXM_BoxHeightOffset.cpp @@ -55,7 +55,6 @@ public: #endif DECLARE_CONOBJECT(afxXM_BoxHeightOffsetData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/xm/afxXM_Freeze.cpp b/Engine/source/afx/xm/afxXM_Freeze.cpp index 901715ffa..c7872f94a 100644 --- a/Engine/source/afx/xm/afxXM_Freeze.cpp +++ b/Engine/source/afx/xm/afxXM_Freeze.cpp @@ -53,7 +53,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_FreezeData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/xm/afxXM_GroundConform.cpp b/Engine/source/afx/xm/afxXM_GroundConform.cpp index 088ca5224..79fe44253 100644 --- a/Engine/source/afx/xm/afxXM_GroundConform.cpp +++ b/Engine/source/afx/xm/afxXM_GroundConform.cpp @@ -55,7 +55,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_GroundConformData); - DECLARE_CATEGORY("AFX"); }; class afxXM_GroundConform : public afxXM_WeightedBase diff --git a/Engine/source/afx/xm/afxXM_HeightSampler.cpp b/Engine/source/afx/xm/afxXM_HeightSampler.cpp index 261bb5488..eec80c9e6 100644 --- a/Engine/source/afx/xm/afxXM_HeightSampler.cpp +++ b/Engine/source/afx/xm/afxXM_HeightSampler.cpp @@ -52,7 +52,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_HeightSamplerData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/xm/afxXM_MountedImageNode.cpp b/Engine/source/afx/xm/afxXM_MountedImageNode.cpp index 115c76598..9ab4ea029 100644 --- a/Engine/source/afx/xm/afxXM_MountedImageNode.cpp +++ b/Engine/source/afx/xm/afxXM_MountedImageNode.cpp @@ -54,7 +54,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_MountedImageNodeData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/xm/afxXM_Offset.cpp b/Engine/source/afx/xm/afxXM_Offset.cpp index 745885709..2500c8d9d 100644 --- a/Engine/source/afx/xm/afxXM_Offset.cpp +++ b/Engine/source/afx/xm/afxXM_Offset.cpp @@ -59,7 +59,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_LocalOffsetData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// @@ -145,7 +144,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_WorldOffsetData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/xm/afxXM_Oscillate.cpp b/Engine/source/afx/xm/afxXM_Oscillate.cpp index 55d8dd7c2..7a4c89caf 100644 --- a/Engine/source/afx/xm/afxXM_Oscillate.cpp +++ b/Engine/source/afx/xm/afxXM_Oscillate.cpp @@ -61,7 +61,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_OscillateData); - DECLARE_CATEGORY("AFX"); }; class afxXM_Oscillate_rot : public afxXM_WeightedBase diff --git a/Engine/source/afx/xm/afxXM_OscillateZodiacColor.cpp b/Engine/source/afx/xm/afxXM_OscillateZodiacColor.cpp index fc91e5046..6897023e8 100644 --- a/Engine/source/afx/xm/afxXM_OscillateZodiacColor.cpp +++ b/Engine/source/afx/xm/afxXM_OscillateZodiacColor.cpp @@ -48,7 +48,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_OscillateZodiacColorData); - DECLARE_CATEGORY("AFX"); }; class afxXM_OscillateZodiacColor : public afxXM_WeightedBase diff --git a/Engine/source/afx/xm/afxXM_PathConform.cpp b/Engine/source/afx/xm/afxXM_PathConform.cpp index d2a92e7fd..365f5af92 100644 --- a/Engine/source/afx/xm/afxXM_PathConform.cpp +++ b/Engine/source/afx/xm/afxXM_PathConform.cpp @@ -67,7 +67,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_PathConformData); - DECLARE_CATEGORY("AFX"); }; class afxPath3D; diff --git a/Engine/source/afx/xm/afxXM_PivotNodeOffset.cpp b/Engine/source/afx/xm/afxXM_PivotNodeOffset.cpp index 7204b1ce3..5a274f162 100644 --- a/Engine/source/afx/xm/afxXM_PivotNodeOffset.cpp +++ b/Engine/source/afx/xm/afxXM_PivotNodeOffset.cpp @@ -55,7 +55,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_PivotNodeOffsetData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/xm/afxXM_RandomRot.cpp b/Engine/source/afx/xm/afxXM_RandomRot.cpp index c8278293f..1580e5f16 100644 --- a/Engine/source/afx/xm/afxXM_RandomRot.cpp +++ b/Engine/source/afx/xm/afxXM_RandomRot.cpp @@ -60,7 +60,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_RandomRotData); - DECLARE_CATEGORY("AFX"); }; class afxXM_RandomRot : public afxXM_Base diff --git a/Engine/source/afx/xm/afxXM_Scale.cpp b/Engine/source/afx/xm/afxXM_Scale.cpp index 4444ec537..5d4b7e528 100644 --- a/Engine/source/afx/xm/afxXM_Scale.cpp +++ b/Engine/source/afx/xm/afxXM_Scale.cpp @@ -57,7 +57,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_ScaleData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/xm/afxXM_Shockwave.cpp b/Engine/source/afx/xm/afxXM_Shockwave.cpp index 13e0c7c10..b4152c583 100644 --- a/Engine/source/afx/xm/afxXM_Shockwave.cpp +++ b/Engine/source/afx/xm/afxXM_Shockwave.cpp @@ -58,7 +58,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_ShockwaveData); - DECLARE_CATEGORY("AFX"); }; class afxConstraint; diff --git a/Engine/source/afx/xm/afxXM_Spin.cpp b/Engine/source/afx/xm/afxXM_Spin.cpp index a76de1e53..f87b9d970 100644 --- a/Engine/source/afx/xm/afxXM_Spin.cpp +++ b/Engine/source/afx/xm/afxXM_Spin.cpp @@ -62,7 +62,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_SpinData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/xm/afxXM_VelocityOffset.cpp b/Engine/source/afx/xm/afxXM_VelocityOffset.cpp index 3f4f75b4f..ca2ea29df 100644 --- a/Engine/source/afx/xm/afxXM_VelocityOffset.cpp +++ b/Engine/source/afx/xm/afxXM_VelocityOffset.cpp @@ -60,7 +60,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_VelocityOffsetData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/xm/afxXM_WaveBase.h b/Engine/source/afx/xm/afxXM_WaveBase.h index 2feb3b71a..0931b937b 100644 --- a/Engine/source/afx/xm/afxXM_WaveBase.h +++ b/Engine/source/afx/xm/afxXM_WaveBase.h @@ -172,7 +172,6 @@ public: static afxXM_Waveform* getWaveform(U32 waveform_type); DECLARE_CONOBJECT(afxXM_WaveBaseData); - DECLARE_CATEGORY("AFX"); }; typedef afxXM_WaveBaseData::WaveFormType afxXM_WaveFormType; @@ -210,7 +209,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxXM_WaveRiderBaseData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/xm/afxXM_WaveColor.cpp b/Engine/source/afx/xm/afxXM_WaveColor.cpp index bc2df445f..3584ca011 100644 --- a/Engine/source/afx/xm/afxXM_WaveColor.cpp +++ b/Engine/source/afx/xm/afxXM_WaveColor.cpp @@ -181,7 +181,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_WaveColorData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// @@ -297,7 +296,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_WaveRiderColorData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/xm/afxXM_WaveScalar.cpp b/Engine/source/afx/xm/afxXM_WaveScalar.cpp index 211382a76..8bebc8f1b 100644 --- a/Engine/source/afx/xm/afxXM_WaveScalar.cpp +++ b/Engine/source/afx/xm/afxXM_WaveScalar.cpp @@ -596,7 +596,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_WaveScalarData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// @@ -723,7 +722,6 @@ public: afxXM_Base* create(afxEffectWrapper* fx, bool on_server); DECLARE_CONOBJECT(afxXM_WaveRiderScalarData); - DECLARE_CATEGORY("AFX"); }; //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// diff --git a/Engine/source/afx/xm/afxXfmMod.h b/Engine/source/afx/xm/afxXfmMod.h index 7c7eaa41d..4a5e2911f 100644 --- a/Engine/source/afx/xm/afxXfmMod.h +++ b/Engine/source/afx/xm/afxXfmMod.h @@ -91,7 +91,6 @@ public: virtual afxXM_Base* create(afxEffectWrapper* fx, bool on_server) { return 0; } DECLARE_CONOBJECT(afxXM_BaseData); - DECLARE_CATEGORY("AFX"); }; class afxXM_Base : public afxXM_Defs @@ -147,7 +146,6 @@ public: static void initPersistFields(); DECLARE_CONOBJECT(afxXM_WeightedBaseData); - DECLARE_CATEGORY("AFX"); }; class afxXM_WeightedBase : public afxXM_Base diff --git a/Engine/source/console/consoleObject.cpp b/Engine/source/console/consoleObject.cpp index 96ea82c2f..75928b542 100644 --- a/Engine/source/console/consoleObject.cpp +++ b/Engine/source/console/consoleObject.cpp @@ -831,11 +831,19 @@ DefineEngineFunction( getCategoryOfClass, const char*, ( const char* className "@ingroup Console") { AbstractClassRep* rep = AbstractClassRep::findClassRep( className ); - if( rep ) - return rep->getCategory(); - Con::errorf( "getCategoryOfClass - no class called '%s'", className ); - return ""; + if (rep == NULL) + { + Con::errorf("getCategoryOfClass - no class called '%s'", className); + return ""; + } + while (rep && rep->getParentClass()) + { + if (dStrcmp(rep->getCategory(), "") != 0) + break; + rep = rep->getParentClass(); + } + return rep ? rep->getCategory() : ""; } DefineEngineFunction( enumerateConsoleClasses, const char*, ( const char* className ), ( "" ), diff --git a/Engine/source/environment/VolumetricFog.h b/Engine/source/environment/VolumetricFog.h index 83df0c184..c9206a5dc 100644 --- a/Engine/source/environment/VolumetricFog.h +++ b/Engine/source/environment/VolumetricFog.h @@ -253,6 +253,7 @@ class VolumetricFog : public SceneObject void onShapeChanged() {} DECLARE_CONOBJECT(VolumetricFog); + DECLARE_CATEGORY("Environment \t Weather"); DECLARE_CALLBACK(void, onEnterFog, (SimObjectId obj)); DECLARE_CALLBACK(void, onLeaveFog, (SimObjectId obj)); diff --git a/Engine/source/environment/VolumetricFogRTManager.h b/Engine/source/environment/VolumetricFogRTManager.h index 725f3b5db..d4b2bcbd4 100644 --- a/Engine/source/environment/VolumetricFogRTManager.h +++ b/Engine/source/environment/VolumetricFogRTManager.h @@ -83,8 +83,9 @@ class VolumetricFogRTManager : public SceneObject U32 DecFogObjects(); DECLARE_CONOBJECT(VolumetricFogRTManager); + DECLARE_CATEGORY("UNLISTED"); }; extern VolumetricFogRTManager* gVolumetricFogRTManager; -#endif \ No newline at end of file +#endif diff --git a/Engine/source/environment/basicClouds.h b/Engine/source/environment/basicClouds.h index a9f6f874e..2e7468de8 100644 --- a/Engine/source/environment/basicClouds.h +++ b/Engine/source/environment/basicClouds.h @@ -65,6 +65,7 @@ public: virtual ~BasicClouds() {} DECLARE_CONOBJECT( BasicClouds ); + DECLARE_CATEGORY("Environment \t Weather"); // ConsoleObject virtual bool onAdd(); diff --git a/Engine/source/environment/cloudLayer.h b/Engine/source/environment/cloudLayer.h index 8f7ffc667..3634dbfa8 100644 --- a/Engine/source/environment/cloudLayer.h +++ b/Engine/source/environment/cloudLayer.h @@ -68,6 +68,7 @@ public: virtual ~CloudLayer() {} DECLARE_CONOBJECT( CloudLayer ); + DECLARE_CATEGORY("Environment \t Weather"); // ConsoleObject virtual bool onAdd(); diff --git a/Engine/source/environment/decalRoad.h b/Engine/source/environment/decalRoad.h index 180b92d08..f7759b447 100644 --- a/Engine/source/environment/decalRoad.h +++ b/Engine/source/environment/decalRoad.h @@ -154,6 +154,7 @@ public: ~DecalRoad(); DECLARE_CONOBJECT(DecalRoad); + DECLARE_CATEGORY("Environment \t BackGround"); // ConsoleObject static void initPersistFields(); diff --git a/Engine/source/environment/meshRoad.h b/Engine/source/environment/meshRoad.h index 9fe7505b9..abc3074b0 100644 --- a/Engine/source/environment/meshRoad.h +++ b/Engine/source/environment/meshRoad.h @@ -509,6 +509,7 @@ public: ~MeshRoad(); DECLARE_CONOBJECT(MeshRoad); + DECLARE_CATEGORY("Environment \t BackGround"); // ConObject. static void initPersistFields(); diff --git a/Engine/source/environment/scatterSky.h b/Engine/source/environment/scatterSky.h index e544e64a5..7f71183bb 100644 --- a/Engine/source/environment/scatterSky.h +++ b/Engine/source/environment/scatterSky.h @@ -85,6 +85,7 @@ public: // ConsoleObject DECLARE_CONOBJECT(ScatterSky); + DECLARE_CATEGORY("Environment \t Background"); void inspectPostApply(); static void initPersistFields(); diff --git a/Engine/source/environment/skyBox.h b/Engine/source/environment/skyBox.h index 7f2ebe494..0daddb8a7 100644 --- a/Engine/source/environment/skyBox.h +++ b/Engine/source/environment/skyBox.h @@ -75,6 +75,7 @@ public: virtual ~SkyBox(); DECLARE_CONOBJECT( SkyBox ); + DECLARE_CATEGORY("Environment \t Background"); // SimObject void onStaticModified( const char *slotName, const char *newValue ); @@ -129,4 +130,4 @@ protected: BaseMatInstance* _getMaterialInstance(); }; -#endif // _SKYBOX_H_ \ No newline at end of file +#endif // _SKYBOX_H_ diff --git a/Engine/source/environment/skySphere.h b/Engine/source/environment/skySphere.h index b68ef07ce..9c50b9bfc 100644 --- a/Engine/source/environment/skySphere.h +++ b/Engine/source/environment/skySphere.h @@ -66,6 +66,7 @@ public: virtual ~SkySphere(); DECLARE_CONOBJECT(SkySphere); + DECLARE_CATEGORY("Environment \t Background"); // SimObject void onStaticModified(const char* slotName, const char* newValue); diff --git a/Engine/source/environment/sun.h b/Engine/source/environment/sun.h index f9d1674f8..b87611a25 100644 --- a/Engine/source/environment/sun.h +++ b/Engine/source/environment/sun.h @@ -116,7 +116,8 @@ public: virtual void onRemove(); // ConsoleObject - DECLARE_CONOBJECT(Sun); + DECLARE_CONOBJECT(Sun); + DECLARE_CATEGORY("Lighting \t Lights"); static void initPersistFields(); void inspectPostApply(); diff --git a/Engine/source/environment/timeOfDay.h b/Engine/source/environment/timeOfDay.h index 53aa9563d..94a48f14a 100644 --- a/Engine/source/environment/timeOfDay.h +++ b/Engine/source/environment/timeOfDay.h @@ -76,6 +76,7 @@ public: static void initPersistFields(); static void consoleInit(); DECLARE_CONOBJECT( TimeOfDay ); + DECLARE_CATEGORY("Environment \t Weather"); void inspectPostApply(); // SimObject @@ -209,4 +210,4 @@ protected: }; -#endif // _TIMEOFDAY_H_ \ No newline at end of file +#endif // _TIMEOFDAY_H_ diff --git a/Engine/source/environment/waterObject.h b/Engine/source/environment/waterObject.h index 7c6bb7df2..4bdfbe021 100644 --- a/Engine/source/environment/waterObject.h +++ b/Engine/source/environment/waterObject.h @@ -149,6 +149,7 @@ public: virtual ~WaterObject(); DECLARE_CONOBJECT( WaterObject ); + DECLARE_CATEGORY("Environment \t Water"); // ConsoleObject static void consoleInit(); diff --git a/Engine/source/forest/forest.h b/Engine/source/forest/forest.h index 42f026976..7fed73f4d 100644 --- a/Engine/source/forest/forest.h +++ b/Engine/source/forest/forest.h @@ -147,6 +147,7 @@ public: virtual ~Forest(); DECLARE_CONOBJECT(Forest); + DECLARE_CATEGORY("Environment \t BackGround"); static void consoleInit(); static void initPersistFields(); diff --git a/Engine/source/forest/forestWindEmitter.h b/Engine/source/forest/forestWindEmitter.h index baf30d07f..9c3c51859 100644 --- a/Engine/source/forest/forestWindEmitter.h +++ b/Engine/source/forest/forestWindEmitter.h @@ -214,6 +214,7 @@ public: // ConObject. static void initPersistFields(); DECLARE_CONOBJECT(ForestWindEmitter); + DECLARE_CATEGORY("Environment \t Weather"); }; -#endif // _FORESTWINDEMITTER_H_ \ No newline at end of file +#endif // _FORESTWINDEMITTER_H_ diff --git a/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.cpp b/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.cpp index 8acd5ef16..ed778d59c 100644 --- a/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.cpp +++ b/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.cpp @@ -1768,10 +1768,10 @@ void GuiConvexEditorCtrl::submitUndo( UndoType type, const Vector mIsDirty = true; } -bool GuiConvexEditorCtrl::_cursorCastCallback( RayInfo* ri ) +bool GuiConvexEditorCtrl::_cursorCastCallback(SceneObject* object) { // Reject anything that's not a ConvexShape. - return dynamic_cast< ConvexShape* >( ri->object ); + return dynamic_cast< ConvexShape* >( object ); } bool GuiConvexEditorCtrl::_cursorCast( const Gui3DMouseEvent &event, ConvexShape **hitShape, S32 *hitFace ) diff --git a/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.h b/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.h index 1600a4e50..94b0874ef 100644 --- a/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.h +++ b/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.h @@ -169,7 +169,7 @@ protected: void _renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *matInst ); bool _cursorCast( const Gui3DMouseEvent &event, ConvexShape **hitShape, S32 *hitFace ); - static bool _cursorCastCallback( RayInfo* ri ); + static bool _cursorCastCallback(SceneObject* object); protected: diff --git a/Engine/source/navigation/coverPoint.h b/Engine/source/navigation/coverPoint.h index 4950767a9..6f2e317c9 100644 --- a/Engine/source/navigation/coverPoint.h +++ b/Engine/source/navigation/coverPoint.h @@ -54,6 +54,7 @@ public: virtual ~CoverPoint(); DECLARE_CONOBJECT(CoverPoint); + DECLARE_CATEGORY("Navigation"); /// Amount of cover provided at this point. enum Size { diff --git a/Engine/source/navigation/navMesh.h b/Engine/source/navigation/navMesh.h index a4096cdfc..5ae227f83 100644 --- a/Engine/source/navigation/navMesh.h +++ b/Engine/source/navigation/navMesh.h @@ -239,6 +239,7 @@ public: NavMesh(); ~NavMesh(); DECLARE_CONOBJECT(NavMesh); + DECLARE_CATEGORY("Navigation"); /// Return the server-side NavMesh SimSet. static SimSet *getServerSet(); diff --git a/Engine/source/navigation/navPath.h b/Engine/source/navigation/navPath.h index ccc16386d..93ac0c4e7 100644 --- a/Engine/source/navigation/navPath.h +++ b/Engine/source/navigation/navPath.h @@ -115,6 +115,7 @@ public: void renderSimple(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat); DECLARE_CONOBJECT(NavPath); + DECLARE_CATEGORY("Navigation"); /// @} diff --git a/Engine/source/scene/sceneContainer.cpp b/Engine/source/scene/sceneContainer.cpp index 024d833aa..57bb9760b 100644 --- a/Engine/source/scene/sceneContainer.cpp +++ b/Engine/source/scene/sceneContainer.cpp @@ -318,10 +318,9 @@ struct SceneRayHelper xformedEnd.convolveInverse(ptr->mObjScale); RayInfo ri; - ri.object = ptr; ri.generateTexCoord = info->generateTexCoord; - if (mFunc && !mFunc(&ri)) + if (mFunc && !mFunc(ptr)) return false; bool result = false; diff --git a/Engine/source/scene/sceneContainer.h b/Engine/source/scene/sceneContainer.h index ce6f6a023..2bfe73492 100644 --- a/Engine/source/scene/sceneContainer.h +++ b/Engine/source/scene/sceneContainer.h @@ -668,7 +668,7 @@ class SceneContainer /// @name Line intersection /// @{ - typedef bool ( *CastRayCallback )( RayInfo* ri ); + typedef bool ( *CastRayCallback )(SceneObject* object); /// Test against collision geometry -- fast. bool castRay( const Point3F &start, const Point3F &end, U32 mask, RayInfo* info, CastRayCallback callback = NULL ); diff --git a/Engine/source/scene/sceneObject.h b/Engine/source/scene/sceneObject.h index 56dc76b2d..7b48addec 100644 --- a/Engine/source/scene/sceneObject.h +++ b/Engine/source/scene/sceneObject.h @@ -754,6 +754,7 @@ class SceneObject : public NetObject, public ProcessObject static bool _setGameObject(void* object, const char* index, const char* data); DECLARE_CONOBJECT( SceneObject ); + DECLARE_CATEGORY("MISC"); private: SceneObject( const SceneObject& ); ///< @deprecated disallowed diff --git a/Engine/source/scene/simPath.h b/Engine/source/scene/simPath.h index 153acf7b5..b4a6395d7 100644 --- a/Engine/source/scene/simPath.h +++ b/Engine/source/scene/simPath.h @@ -84,6 +84,7 @@ class Path : public GameBase U32 getPathIndex() const; DECLARE_CONOBJECT(Path); + DECLARE_CATEGORY("Cinematic"); static void initPersistFields(); DECLARE_CALLBACK(void, onAdd, (SimObjectId ID)); }; @@ -151,6 +152,7 @@ class Marker : public SceneObject ~Marker(); DECLARE_CONOBJECT(Marker); + DECLARE_CATEGORY("Cinematic"); static void initPersistFields(); void inspectPostApply(); diff --git a/Engine/source/terrain/terrData.h b/Engine/source/terrain/terrData.h index de6f90488..8eeb79d12 100644 --- a/Engine/source/terrain/terrData.h +++ b/Engine/source/terrain/terrData.h @@ -488,6 +488,7 @@ public: DECLARE_CONOBJECT(TerrainBlock); + DECLARE_CATEGORY("Environment \t BackGround"); static void initPersistFields(); U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); void unpackUpdate(NetConnection *conn, BitStream *stream); diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.tscript b/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.tscript index ccb4779ca..458e0a0c9 100644 --- a/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.tscript +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.tscript @@ -472,7 +472,10 @@ function EditorGui::buildMenus(%this) for(%c=0; %c < getFieldCount(%enumeratedClasses); %c++) { %class = getField(%enumeratedClasses, %c); - + //SceneObject itself is not directly spawnable + if (%class $= "SceneObject") + continue; + %category = getCategoryOfClass(%class); if(%category $= "") @@ -480,7 +483,13 @@ function EditorGui::buildMenus(%this) error("Attempted to fetch category of class " @ %class @ " but none were found."); continue; } - + //skip classes explicitly tagged not for listing (typically due to being unspawnable) + if(%category $= "UNLISTED") + continue; + + if(%category $= "MISC") + warn("unsorted class: "@ %class); + %parentMenu = %addMenu; //start at the top for(%cat=0; %cat < getFieldCount(%category); %cat++) { From 373508f6226a802b5affcbaf64e9a5f0558a98f3 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 9 Nov 2023 11:51:58 -0600 Subject: [PATCH 030/122] add a generic fallback for gamebaseObject spawning via the "add" menu that fills in a generalized datablkock dropdown popup based on class name if a specified one does not exist --- .../tools/worldEditor/gui/objectBuilderGui.ed.gui | 8 ++++++++ .../game/tools/worldEditor/scripts/menus.ed.tscript | 11 ++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/objectBuilderGui.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/objectBuilderGui.ed.gui index 4a6f94ddb..ae5072029 100644 --- a/Templates/BaseGame/game/tools/worldEditor/gui/objectBuilderGui.ed.gui +++ b/Templates/BaseGame/game/tools/worldEditor/gui/objectBuilderGui.ed.gui @@ -1079,6 +1079,14 @@ function ObjectBuilderGui::buildObject(%this, %className) %this.process(); } +function ObjectBuilderGui::buildGameBaseObject(%this, %className) +{ + %this.objectClassName = %className; + //assumes we fgollow the pattern of class is instance, classData is datablock + %this.addField("dataBlock", "TypeDataBlock", "Data block", %className @"Data"); + + %this.process(); +} //------------------------------------------------------------------------------ // Environment //------------------------------------------------------------------------------ diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.tscript b/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.tscript index 458e0a0c9..92cefad74 100644 --- a/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.tscript +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.tscript @@ -507,10 +507,15 @@ function EditorGui::buildMenus(%this) %class = %class; %method = "build" @ %buildfunc; if( !ObjectBuilderGui.isMethod( %method ) ) - %method = "build" @ %class; - + %method = "build" @ %class; + if( !ObjectBuilderGui.isMethod( %method ) ) - %cmd = "return new " @ %class @ "();"; + { + if (isMemberOfClass(%class,"gameBase")) + %cmd = "ObjectBuilderGui.buildGameBaseObject("@ %class @");"; + else + %cmd = "return new " @ %class @ "();"; + } else %cmd = "ObjectBuilderGui." @ %method @ "();"; From 4e93c0543eb290ec4ce50e861dc524848211b4bb Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 9 Nov 2023 16:09:27 -0600 Subject: [PATCH 031/122] hide scopealwaysshape from the "add" list, by consensus shive portals et al back to being tagged as volumes --- Engine/source/T3D/accumulationVolume.h | 2 +- Engine/source/T3D/occlusionVolume.h | 2 +- Engine/source/T3D/physicalZone.h | 2 +- Engine/source/T3D/portal.h | 2 +- Engine/source/T3D/scopeAlwaysShape.cpp | 1 + Engine/source/T3D/sfx/sfxSpace.h | 2 +- Engine/source/T3D/trigger.h | 2 +- Engine/source/T3D/vehicles/vehicleBlocker.h | 2 +- Engine/source/T3D/zone.h | 2 +- 9 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Engine/source/T3D/accumulationVolume.h b/Engine/source/T3D/accumulationVolume.h index 2a42315bf..85b78cb53 100644 --- a/Engine/source/T3D/accumulationVolume.h +++ b/Engine/source/T3D/accumulationVolume.h @@ -74,7 +74,7 @@ class AccumulationVolume : public ScenePolyhedralSpace // SimObject. DECLARE_CONOBJECT( AccumulationVolume ); DECLARE_DESCRIPTION( "Allows objects in an area to have accumulation effect applied." ); - DECLARE_CATEGORY( "Area" ); + DECLARE_CATEGORY("Volume"); virtual bool onAdd(); virtual void onRemove(); diff --git a/Engine/source/T3D/occlusionVolume.h b/Engine/source/T3D/occlusionVolume.h index 64945115d..4c1d6f75d 100644 --- a/Engine/source/T3D/occlusionVolume.h +++ b/Engine/source/T3D/occlusionVolume.h @@ -64,7 +64,7 @@ class OcclusionVolume : public ScenePolyhedralSpace // SimObject. DECLARE_CONOBJECT( OcclusionVolume ); DECLARE_DESCRIPTION( "A visibility blocking volume." ); - DECLARE_CATEGORY("Area"); + DECLARE_CATEGORY("Volume"); virtual bool onAdd(); diff --git a/Engine/source/T3D/physicalZone.h b/Engine/source/T3D/physicalZone.h index d265f0c87..33aec9ad4 100644 --- a/Engine/source/T3D/physicalZone.h +++ b/Engine/source/T3D/physicalZone.h @@ -77,7 +77,7 @@ class PhysicalZone : public SceneObject // SimObject DECLARE_CONOBJECT(PhysicalZone); - DECLARE_CATEGORY("Area"); + DECLARE_CATEGORY("Volume"); static void consoleInit(); static void initPersistFields(); bool onAdd(); diff --git a/Engine/source/T3D/portal.h b/Engine/source/T3D/portal.h index a9574b57f..7a13b1348 100644 --- a/Engine/source/T3D/portal.h +++ b/Engine/source/T3D/portal.h @@ -185,7 +185,7 @@ class Portal : public Zone // SimObject. DECLARE_CONOBJECT( Portal ); - DECLARE_CATEGORY("Area"); + DECLARE_CATEGORY("Volume"); static void initPersistFields(); static void consoleInit(); diff --git a/Engine/source/T3D/scopeAlwaysShape.cpp b/Engine/source/T3D/scopeAlwaysShape.cpp index ea804cf69..92b5c1ee7 100644 --- a/Engine/source/T3D/scopeAlwaysShape.cpp +++ b/Engine/source/T3D/scopeAlwaysShape.cpp @@ -30,6 +30,7 @@ class ScopeAlwaysShape : public StaticShape ScopeAlwaysShape(); static void initPersistFields(); DECLARE_CONOBJECT(ScopeAlwaysShape); + DECLARE_CATEGORY("UNLISTED"); }; ScopeAlwaysShape::ScopeAlwaysShape() diff --git a/Engine/source/T3D/sfx/sfxSpace.h b/Engine/source/T3D/sfx/sfxSpace.h index 354a91a11..d43339c30 100644 --- a/Engine/source/T3D/sfx/sfxSpace.h +++ b/Engine/source/T3D/sfx/sfxSpace.h @@ -55,7 +55,7 @@ class SFXSpace : public SceneAmbientSoundObject< ScenePolyhedralObject< SceneSpa // SimObject. DECLARE_CONOBJECT( SFXSpace ); DECLARE_DESCRIPTION( "A box volume that defines an ambient sound space." ); - DECLARE_CATEGORY("Area"); + DECLARE_CATEGORY("Volume"); static void consoleInit(); }; diff --git a/Engine/source/T3D/trigger.h b/Engine/source/T3D/trigger.h index 74ab150b8..a661c32bd 100644 --- a/Engine/source/T3D/trigger.h +++ b/Engine/source/T3D/trigger.h @@ -127,7 +127,7 @@ class Trigger : public GameBase // SimObject DECLARE_CONOBJECT(Trigger); - DECLARE_CATEGORY("Area"); + DECLARE_CATEGORY("Volume"); DECLARE_CALLBACK( void, onAdd, ( U32 objectId ) ); DECLARE_CALLBACK( void, onRemove, ( U32 objectId ) ); diff --git a/Engine/source/T3D/vehicles/vehicleBlocker.h b/Engine/source/T3D/vehicles/vehicleBlocker.h index d670da0a9..12441b044 100644 --- a/Engine/source/T3D/vehicles/vehicleBlocker.h +++ b/Engine/source/T3D/vehicles/vehicleBlocker.h @@ -52,7 +52,7 @@ class VehicleBlocker : public SceneObject ~VehicleBlocker(); DECLARE_CONOBJECT(VehicleBlocker); - DECLARE_CATEGORY("Area"); + DECLARE_CATEGORY("Volume"); static void initPersistFields(); U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); diff --git a/Engine/source/T3D/zone.h b/Engine/source/T3D/zone.h index 3aedcc93a..090764aea 100644 --- a/Engine/source/T3D/zone.h +++ b/Engine/source/T3D/zone.h @@ -68,7 +68,7 @@ class Zone : public SceneAmbientSoundObject< ScenePolyhedralZone > // SimObject DECLARE_CONOBJECT( Zone ); DECLARE_DESCRIPTION( "A volume that encloses objects for visibility culling." ); - DECLARE_CATEGORY( "Area" ); + DECLARE_CATEGORY("Volume"); static void consoleInit(); }; From b0aadfb6e60946004883c48a64648d0bff80bf7e Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Fri, 10 Nov 2023 02:31:58 -0600 Subject: [PATCH 032/122] fix getPrototypeSig for cases of no input values whatsoever for a given method --- Engine/source/console/consoleInternal.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Engine/source/console/consoleInternal.cpp b/Engine/source/console/consoleInternal.cpp index 65bcf3ae9..9652e460a 100644 --- a/Engine/source/console/consoleInternal.cpp +++ b/Engine/source/console/consoleInternal.cpp @@ -1567,6 +1567,7 @@ String Namespace::Entry::getPrototypeSig() const str.append(cb.mCallbackName); else str.append(mFunctionName); + str.append("("); if (mHeader) { Vector< String > argList; @@ -1574,7 +1575,7 @@ String Namespace::Entry::getPrototypeSig() const const U32 numArgs = argList.size(); - str.append("(%this"); + str.append("%this"); if (numArgs > 0) str.append(','); @@ -1591,9 +1592,8 @@ String Namespace::Entry::getPrototypeSig() const sGetArgNameAndType(argList[i], type, name); str.append(name); } - str.append(')'); } - + str.append(')'); return str.end(); } //----------------------------------------------------------------------------- From 99b024d0c655d4b070e4e5e2c112071978589cf1 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 11 Nov 2023 13:34:59 -0600 Subject: [PATCH 033/122] kill duplicate ToolsGuiPopupMenuProfile define --- Templates/BaseGame/game/tools/gui/profiles.ed.tscript | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Templates/BaseGame/game/tools/gui/profiles.ed.tscript b/Templates/BaseGame/game/tools/gui/profiles.ed.tscript index f34968591..770c2ad61 100644 --- a/Templates/BaseGame/game/tools/gui/profiles.ed.tscript +++ b/Templates/BaseGame/game/tools/gui/profiles.ed.tscript @@ -1395,12 +1395,6 @@ singleton GuiControlProfile( ToolsGuiMenuBarProfile ) fontType = $Gui::fontTypeRegular; }; -singleton GuiControlProfile( ToolsGuiPopupMenuProfile : ToolsGuiMenuBarProfile ) -{ - borderColor = EditorSettings.value("Theme/dividerLightColor"); - borderColorHL = EditorSettings.value("Theme/dividerMidColor"); -}; - singleton GuiControlProfile( ToolsMenubarProfile : ToolsGuiDefaultProfile ) { bitmap = "./menubar"; From 1cf754dbca7327ed6afdc396c129a90c14d773f1 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sun, 12 Nov 2023 16:33:17 -0600 Subject: [PATCH 034/122] asset load refactor genral load method, now returns loadedstate across the board --- Engine/source/T3D/assets/ImageAsset.cpp | 21 ++++++------------ Engine/source/T3D/assets/ImageAsset.h | 5 ++--- Engine/source/T3D/assets/MaterialAsset.cpp | 13 ++++++----- Engine/source/T3D/assets/MaterialAsset.h | 2 +- Engine/source/T3D/assets/ShapeAsset.cpp | 16 +++++++------- Engine/source/T3D/assets/ShapeAsset.h | 4 ++-- Engine/source/T3D/assets/SoundAsset.cpp | 22 +++++-------------- Engine/source/T3D/assets/SoundAsset.h | 4 ++-- Engine/source/T3D/assets/TerrainAsset.cpp | 12 +++++----- Engine/source/T3D/assets/TerrainAsset.h | 2 +- .../T3D/assets/TerrainMaterialAsset.cpp | 13 ++++++----- .../source/T3D/assets/TerrainMaterialAsset.h | 2 +- Engine/source/assets/assetBase.h | 2 +- 13 files changed, 50 insertions(+), 68 deletions(-) diff --git a/Engine/source/T3D/assets/ImageAsset.cpp b/Engine/source/T3D/assets/ImageAsset.cpp index c3a2db842..af03cfe2d 100644 --- a/Engine/source/T3D/assets/ImageAsset.cpp +++ b/Engine/source/T3D/assets/ImageAsset.cpp @@ -207,8 +207,7 @@ U32 ImageAsset::getAssetByFilename(StringTableEntry fileName, AssetPtrsetAssetId(query.mAssetList[0]); - (*imageAsset)->loadImage(); - return (*imageAsset)->mLoadedState; + return (*imageAsset)->load(); } } @@ -240,8 +239,7 @@ U32 ImageAsset::getAssetById(StringTableEntry assetId, AssetPtr* ima if (imageAsset->notNull()) { - (*imageAsset)->loadImage(); - return (*imageAsset)->mLoadedState; + return (*imageAsset)->load(); } else { @@ -253,7 +251,6 @@ U32 ImageAsset::getAssetById(StringTableEntry assetId, AssetPtr* ima //handle fallback not being loaded itself if ((*imageAsset)->mLoadedState == BadFileReference) { - (*imageAsset)->loadImage(); Con::warnf("ImageAsset::getAssetById - Finding of asset with id %s failed, and fallback asset reported error of Bad File Reference.", assetId); return AssetErrCode::BadFileReference; } @@ -272,25 +269,26 @@ void ImageAsset::copyTo(SimObject* object) Parent::copyTo(object); } -void ImageAsset::loadImage() +U32 ImageAsset::load() { - if (mLoadedState == AssetErrCode::Ok) return; + if (mLoadedState == AssetErrCode::Ok) return mLoadedState; if (mImagePath) { if (!Torque::FS::IsFile(mImagePath)) { Con::errorf("ImageAsset::initializeAsset: Attempted to load file %s but it was not valid!", mImageFileName); mLoadedState = BadFileReference; - return; + return mLoadedState; } mLoadedState = Ok; mIsValidImage = true; - return; + return mLoadedState; } mLoadedState = BadFileReference; mIsValidImage = false; + return mLoadedState; } void ImageAsset::initializeAsset() @@ -327,11 +325,6 @@ void ImageAsset::setImageFileName(const char* pScriptFile) refreshAsset(); } -const GBitmap& ImageAsset::getImage() -{ - return GBitmap(); //TODO fix this -} - GFXTexHandle ImageAsset::getTexture(GFXTextureProfile* requestedProfile) { if (mResourceMap.contains(requestedProfile)) diff --git a/Engine/source/T3D/assets/ImageAsset.h b/Engine/source/T3D/assets/ImageAsset.h index c4826a0ef..d5b3b0b48 100644 --- a/Engine/source/T3D/assets/ImageAsset.h +++ b/Engine/source/T3D/assets/ImageAsset.h @@ -136,7 +136,6 @@ public: bool isValid() { return mIsValidImage; } - const GBitmap& getImage(); GFXTexHandle getTexture(GFXTextureProfile* requestedProfile); StringTableEntry getImageInfo(); @@ -152,14 +151,14 @@ public: static U32 getAssetById(StringTableEntry assetId, AssetPtr* imageAsset); static U32 getAssetById(String assetId, AssetPtr* imageAsset) { return getAssetById(assetId.c_str(), imageAsset); }; + U32 load(); + protected: virtual void initializeAsset(void); virtual void onAssetRefresh(void); static bool setImageFileName(void* obj, StringTableEntry index, StringTableEntry data) { static_cast(obj)->setImageFileName(data); return false; } static StringTableEntry getImageFileName(void* obj, StringTableEntry data) { return static_cast(obj)->getImageFileName(); } - - void loadImage(); }; DefineConsoleType(TypeImageAssetPtr, ImageAsset) diff --git a/Engine/source/T3D/assets/MaterialAsset.cpp b/Engine/source/T3D/assets/MaterialAsset.cpp index 363d7bc30..02f0a163a 100644 --- a/Engine/source/T3D/assets/MaterialAsset.cpp +++ b/Engine/source/T3D/assets/MaterialAsset.cpp @@ -206,7 +206,7 @@ void MaterialAsset::initializeAsset() } } - loadMaterial(); + load(); } void MaterialAsset::onAssetRefresh() @@ -236,7 +236,7 @@ void MaterialAsset::onAssetRefresh() Con::setVariable("$Con::redefineBehavior", redefineBehaviorPrev.c_str()); } - loadMaterial(); + load(); } void MaterialAsset::setScriptFile(const char* pScriptFile) @@ -255,7 +255,7 @@ void MaterialAsset::setScriptFile(const char* pScriptFile) //------------------------------------------------------------------------------ -void MaterialAsset::loadMaterial() +U32 MaterialAsset::load() { if (mMaterialDefinition) { @@ -274,7 +274,7 @@ void MaterialAsset::loadMaterial() mLoadedState = Ok; mMaterialDefinition->setInternalName(getAssetId()); mMaterialDefinition->reload(); - return; + return mLoadedState; } } } @@ -286,7 +286,7 @@ void MaterialAsset::loadMaterial() { Con::errorf("MaterialAsset: Unable to find the Material %s", mMatDefinitionName); mLoadedState = BadFileReference; - return; + return mLoadedState; } mMaterialDefinition = matDef; @@ -294,10 +294,11 @@ void MaterialAsset::loadMaterial() mLoadedState = Ok; mMaterialDefinition->setInternalName(getAssetId()); mMaterialDefinition->reload(); - return; + return mLoadedState; } mLoadedState = Failed; + return mLoadedState; } //------------------------------------------------------------------------------ diff --git a/Engine/source/T3D/assets/MaterialAsset.h b/Engine/source/T3D/assets/MaterialAsset.h index d51f6dabb..4fa868ff7 100644 --- a/Engine/source/T3D/assets/MaterialAsset.h +++ b/Engine/source/T3D/assets/MaterialAsset.h @@ -104,7 +104,7 @@ public: static void initPersistFields(); virtual void copyTo(SimObject* object); - void loadMaterial(); + U32 load(); StringTableEntry getMaterialDefinitionName() { return mMatDefinitionName; } SimObjectPtr getMaterialDefinition() { return mMaterialDefinition; } diff --git a/Engine/source/T3D/assets/ShapeAsset.cpp b/Engine/source/T3D/assets/ShapeAsset.cpp index 7958212c7..4197b0100 100644 --- a/Engine/source/T3D/assets/ShapeAsset.cpp +++ b/Engine/source/T3D/assets/ShapeAsset.cpp @@ -308,9 +308,9 @@ void ShapeAsset::_onResourceChanged(const Torque::Path &path) onAssetRefresh(); } -bool ShapeAsset::loadShape() +U32 ShapeAsset::load() { - if (mLoadedState == AssetErrCode::Ok) return true; + if (mLoadedState == AssetErrCode::Ok) return mLoadedState; mMaterialAssets.clear(); mMaterialAssetIds.clear(); @@ -357,7 +357,7 @@ bool ShapeAsset::loadShape() { Con::errorf("ShapeAsset::loadShape : failed to load shape file %s (%s)!", getAssetName(), mFilePath); mLoadedState = BadFileReference; - return false; //if it failed to load, bail out + return mLoadedState; //if it failed to load, bail out } // Construct billboards if not done already if (GFXDevice::devicePresent()) @@ -379,7 +379,7 @@ bool ShapeAsset::loadShape() mAnimationAssets[i]->getStartFrame(), mAnimationAssets[i]->getEndFrame(), mAnimationAssets[i]->getPadRotation(), mAnimationAssets[i]->getPadTransforms())) { mLoadedState = MissingAnimatons; - return false; + return mLoadedState; } if (mAnimationAssets[i]->isBlend()) hasBlends = true; @@ -402,7 +402,7 @@ bool ShapeAsset::loadShape() Con::errorf("ShapeAsset::initializeAsset - Unable to acquire reference animation asset %s for asset %s to blend!", mAnimationAssets[i]->getBlendAnimationName(), mAnimationAssets[i]->getAssetName()); { mLoadedState = MissingAnimatons; - return false; + return mLoadedState; } } @@ -412,7 +412,7 @@ bool ShapeAsset::loadShape() Con::errorf("ShapeAnimationAsset::initializeAsset - Unable to set animation clip %s for asset %s to blend!", mAnimationAssets[i]->getAnimationName(), mAnimationAssets[i]->getAssetName()); { mLoadedState = MissingAnimatons; - return false; + return mLoadedState; } } } @@ -422,7 +422,7 @@ bool ShapeAsset::loadShape() mChangeSignal.trigger(); mLoadedState = Ok; - return true; + return mLoadedState; } //------------------------------------------------------------------------------ @@ -706,7 +706,7 @@ DefineEngineMethod(ShapeAsset, generateCachedPreviewImage, const char*, (S32 res "@param resolution Optional field for what resolution to bake the preview image at. Must be pow2\n" "@param overrideMaterialName Optional field for overriding the material used when rendering the shape for the bake.") { - object->loadShape(); + object->load(); return object->generateCachedPreviewImage(resolution, overrideMaterialName); } diff --git a/Engine/source/T3D/assets/ShapeAsset.h b/Engine/source/T3D/assets/ShapeAsset.h index abeb13c39..29a72e2bf 100644 --- a/Engine/source/T3D/assets/ShapeAsset.h +++ b/Engine/source/T3D/assets/ShapeAsset.h @@ -132,11 +132,11 @@ public: /// Declare Console Object. DECLARE_CONOBJECT(ShapeAsset); - bool loadShape(); + U32 load(); TSShape* getShape() { return mShape; } - Resource getShapeResource() { loadShape(); return mShape; } + Resource getShapeResource() { load(); return mShape; } void SplitSequencePathAndName(String& srcPath, String& srcName); StringTableEntry getShapeFileName() { return mFileName; } diff --git a/Engine/source/T3D/assets/SoundAsset.cpp b/Engine/source/T3D/assets/SoundAsset.cpp index 3f3a3bf25..d9aeb7f4a 100644 --- a/Engine/source/T3D/assets/SoundAsset.cpp +++ b/Engine/source/T3D/assets/SoundAsset.cpp @@ -322,10 +322,6 @@ void SoundAsset::initializeAsset(void) mSoundPath[i] = getOwned() ? expandAssetFilePath(mSoundFile[i]) : mSoundPath[i]; } - - //loadSound(slotCount); - //mSoundPath = getOwned() ? expandAssetFilePath(mSoundFile) : mSoundPath; - //loadSound(); } void SoundAsset::_onResourceChanged(const Torque::Path &path) @@ -337,9 +333,6 @@ void SoundAsset::_onResourceChanged(const Torque::Path &path) return; } refreshAsset(); - - //loadSound(slotCount); - //loadSound(); } void SoundAsset::onAssetRefresh(void) @@ -354,16 +347,11 @@ void SoundAsset::onAssetRefresh(void) mSoundPath[i] = getOwned() ? expandAssetFilePath(mSoundFile[i]) : mSoundPath[i]; } - - //loadSound(slotCount); - //Update - //mSoundPath = getOwned() ? expandAssetFilePath(mSoundFile) : mSoundPath; - //loadSound(); } -bool SoundAsset::loadSound() +U32 SoundAsset::load() { - if (mLoadedState == AssetErrCode::Ok) return true; + if (mLoadedState == AssetErrCode::Ok) return mLoadedState; // find out how many active slots we have. U32 numSlots = 0; @@ -394,7 +382,7 @@ bool SoundAsset::loadSound() mSFXProfile[i].setDescription(NULL); mSFXProfile[i].setSoundFileName(StringTable->insert(StringTable->EmptyString())); mSFXProfile[i].setPreload(false); - return false; + return mLoadedState; } else {// = new SFXProfile(mProfileDesc, mSoundFile, mPreload); @@ -427,7 +415,7 @@ bool SoundAsset::loadSound() mSFXProfile[0].setDescription(NULL); mSFXProfile[0].setSoundFileName(StringTable->insert(StringTable->EmptyString())); mSFXProfile[0].setPreload(false); - return false; + return mLoadedState; } else {// = new SFXProfile(mProfileDesc, mSoundFile, mPreload); @@ -446,7 +434,7 @@ bool SoundAsset::loadSound() mChangeSignal.trigger(); mLoadedState = Ok; - return true; + return mLoadedState; } StringTableEntry SoundAsset::getSoundFile(const char* pSoundFile, const U32 slotId) diff --git a/Engine/source/T3D/assets/SoundAsset.h b/Engine/source/T3D/assets/SoundAsset.h index ccd10627b..9aaf51224 100644 --- a/Engine/source/T3D/assets/SoundAsset.h +++ b/Engine/source/T3D/assets/SoundAsset.h @@ -150,13 +150,13 @@ public: virtual void copyTo(SimObject* object); //SFXResource* getSound() { return mSoundResource; } - Resource getSoundResource(const U32 slotId = 0) { loadSound(); return mSFXProfile[slotId].getResource(); } + Resource getSoundResource(const U32 slotId = 0) { load(); return mSFXProfile[slotId].getResource(); } /// Declare Console Object. DECLARE_CONOBJECT(SoundAsset); void setSoundFile(const char* pSoundFile, const U32 slotId = 0); - bool loadSound(); + U32 load(); StringTableEntry getSoundFile(const char* pSoundFile, const U32 slotId = 0); inline StringTableEntry getSoundPath(const U32 slotId = 0) const { return mSoundPath[slotId]; }; SFXProfile* getSfxProfile(const U32 slotId = 0) { return &mSFXProfile[slotId]; } diff --git a/Engine/source/T3D/assets/TerrainAsset.cpp b/Engine/source/T3D/assets/TerrainAsset.cpp index 4b295a95b..11dd64796 100644 --- a/Engine/source/T3D/assets/TerrainAsset.cpp +++ b/Engine/source/T3D/assets/TerrainAsset.cpp @@ -161,14 +161,14 @@ void TerrainAsset::initializeAsset() mTerrainFilePath = getOwned() ? expandAssetFilePath(mTerrainFileName) : mTerrainFilePath; - loadTerrain(); + load(); } void TerrainAsset::onAssetRefresh() { mTerrainFilePath = getOwned() ? expandAssetFilePath(mTerrainFileName) : mTerrainFilePath; - loadTerrain(); + load(); } void TerrainAsset::setTerrainFileName(const char* pScriptFile) @@ -189,10 +189,10 @@ void TerrainAsset::setTerrainFileName(const char* pScriptFile) refreshAsset(); } -bool TerrainAsset::loadTerrain() +U32 TerrainAsset::load() { if (!Torque::FS::IsFile(mTerrainFilePath)) - return false; + return BadFileReference; mTerrMaterialAssets.clear(); mTerrMaterialAssetIds.clear(); @@ -229,9 +229,9 @@ bool TerrainAsset::loadTerrain() mTerrainFile = ResourceManager::get().load(mTerrainFilePath); if (mTerrainFile) - return true; + return Ok; - return false; + return BadFileReference; } //------------------------------------------------------------------------------ diff --git a/Engine/source/T3D/assets/TerrainAsset.h b/Engine/source/T3D/assets/TerrainAsset.h index 1ae593c62..991bab461 100644 --- a/Engine/source/T3D/assets/TerrainAsset.h +++ b/Engine/source/T3D/assets/TerrainAsset.h @@ -82,7 +82,7 @@ public: inline Resource getTerrainResource(void) const { return mTerrainFile; }; - bool loadTerrain(); + U32 load(); static bool getAssetByFilename(StringTableEntry fileName, AssetPtr* shapeAsset); static StringTableEntry getAssetIdByFilename(StringTableEntry fileName); diff --git a/Engine/source/T3D/assets/TerrainMaterialAsset.cpp b/Engine/source/T3D/assets/TerrainMaterialAsset.cpp index 18d175f3d..e186e0e4e 100644 --- a/Engine/source/T3D/assets/TerrainMaterialAsset.cpp +++ b/Engine/source/T3D/assets/TerrainMaterialAsset.cpp @@ -202,7 +202,7 @@ void TerrainMaterialAsset::initializeAsset() } } - loadMaterial(); + load(); } void TerrainMaterialAsset::onAssetRefresh() @@ -232,7 +232,7 @@ void TerrainMaterialAsset::onAssetRefresh() Con::setVariable("$Con::redefineBehavior", redefineBehaviorPrev.c_str()); } - loadMaterial(); + load(); } void TerrainMaterialAsset::setScriptFile(const char* pScriptFile) @@ -251,7 +251,7 @@ void TerrainMaterialAsset::setScriptFile(const char* pScriptFile) //------------------------------------------------------------------------------ -void TerrainMaterialAsset::loadMaterial() +U32 TerrainMaterialAsset::load() { if (mMaterialDefinition) mMaterialDefinition->safeDeleteObject(); @@ -287,7 +287,7 @@ void TerrainMaterialAsset::loadMaterial() } if(mLoadedState == Ok) - return; + return mLoadedState; } else if ((mLoadedState == ScriptLoaded || mLoadedState == DefinitionAlreadyExists) && mMatDefinitionName != StringTable->EmptyString()) { @@ -296,17 +296,18 @@ void TerrainMaterialAsset::loadMaterial() { Con::errorf("TerrainMaterialAsset: Unable to find the Material %s", mMatDefinitionName); mLoadedState = BadFileReference; - return; + return mLoadedState; } mMaterialDefinition = matDef; mLoadedState = Ok; mMaterialDefinition->setInternalName(getAssetId()); - return; + return mLoadedState; } mLoadedState = Failed; + return mLoadedState; } //------------------------------------------------------------------------------ diff --git a/Engine/source/T3D/assets/TerrainMaterialAsset.h b/Engine/source/T3D/assets/TerrainMaterialAsset.h index e8e268255..107c6fc92 100644 --- a/Engine/source/T3D/assets/TerrainMaterialAsset.h +++ b/Engine/source/T3D/assets/TerrainMaterialAsset.h @@ -89,7 +89,7 @@ public: static void initPersistFields(); virtual void copyTo(SimObject* object); - void loadMaterial(); + U32 load(); StringTableEntry getMaterialDefinitionName() { return mMatDefinitionName; } SimObjectPtr getMaterialDefinition() { return mMaterialDefinition; } diff --git a/Engine/source/assets/assetBase.h b/Engine/source/assets/assetBase.h index bffc8d7ba..ad6d4e95b 100644 --- a/Engine/source/assets/assetBase.h +++ b/Engine/source/assets/assetBase.h @@ -91,7 +91,7 @@ public: return mErrCodeStrings[errCode]; }; U32 getStatus() { return mLoadedState; }; - + U32 load() { return NotLoaded; }; AssetBase(); virtual ~AssetBase(); From 7a9715654dc689078f73cb23135384eddb671829 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sun, 12 Nov 2023 19:49:45 -0600 Subject: [PATCH 035/122] fix bitmap button state display --- .../gui/buttons/guiBitmapButtonCtrl.cpp | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/Engine/source/gui/buttons/guiBitmapButtonCtrl.cpp b/Engine/source/gui/buttons/guiBitmapButtonCtrl.cpp index ca51045ad..e2ef9d344 100644 --- a/Engine/source/gui/buttons/guiBitmapButtonCtrl.cpp +++ b/Engine/source/gui/buttons/guiBitmapButtonCtrl.cpp @@ -318,10 +318,14 @@ void GuiBitmapButtonCtrl::setBitmap( StringTableEntry name ) mTextures[i].mTextureNormalAsset = mTextures[i].mTextureNormalAssetId; } - if (mTextures[i].mTextureNormalAsset.notNull() && mTextures[i].mTextureNormalAsset->getStatus() == AssetBase::Ok) + if (mTextures[i].mTextureNormalAsset.notNull()) { - mTextures[i].mTextureNormal = GFXTexHandle(mTextures[i].mTextureNormalAsset->getImagePath(), &GFXDefaultGUIProfile, avar("%s() - mTextureNormal (line %d)", __FUNCTION__, __LINE__)); - break; + mTextures[i].mTextureNormalAsset->load(); + if (mTextures[i].mTextureNormalAsset->getStatus() == AssetBase::Ok) + { + mTextures[i].mTextureNormal = GFXTexHandle(mTextures[i].mTextureNormalAsset->getImagePath(), &GFXDefaultGUIProfile, avar("%s() - mTextureDepressed (line %d)", __FUNCTION__, __LINE__)); + break; + } } } } @@ -336,10 +340,14 @@ void GuiBitmapButtonCtrl::setBitmap( StringTableEntry name ) mTextures[i].mTextureHilightAsset = mTextures[i].mTextureHilightAssetId; } - if (mTextures[i].mTextureHilightAsset.notNull() && mTextures[i].mTextureHilightAsset->getStatus() == AssetBase::Ok) + if (mTextures[i].mTextureHilightAsset.notNull()) { - mTextures[i].mTextureHilight = GFXTexHandle(mTextures[i].mTextureHilightAsset->getImagePath(), &GFXDefaultGUIProfile, avar("%s() - mTextureHighlight (line %d)", __FUNCTION__, __LINE__)); - break; + mTextures[i].mTextureHilightAsset->load(); + if (mTextures[i].mTextureHilightAsset->getStatus() == AssetBase::Ok) + { + mTextures[i].mTextureHilight = GFXTexHandle(mTextures[i].mTextureHilightAsset->getImagePath(), &GFXDefaultGUIProfile, avar("%s() - mTextureDepressed (line %d)", __FUNCTION__, __LINE__)); + break; + } } } @@ -356,10 +364,14 @@ void GuiBitmapButtonCtrl::setBitmap( StringTableEntry name ) mTextures[i].mTextureDepressedAsset = mTextures[i].mTextureDepressedAssetId; } - if (mTextures[i].mTextureDepressedAsset.notNull() && mTextures[i].mTextureDepressedAsset->getStatus() == AssetBase::Ok) + if (mTextures[i].mTextureDepressedAsset.notNull()) { - mTextures[i].mTextureDepressed = GFXTexHandle(mTextures[i].mTextureDepressedAsset->getImagePath(), &GFXDefaultGUIProfile, avar("%s() - mTextureDepressed (line %d)", __FUNCTION__, __LINE__)); - break; + mTextures[i].mTextureDepressedAsset->load(); + if (mTextures[i].mTextureDepressedAsset->getStatus() == AssetBase::Ok) + { + mTextures[i].mTextureDepressed = GFXTexHandle(mTextures[i].mTextureDepressedAsset->getImagePath(), &GFXDefaultGUIProfile, avar("%s() - mTextureDepressed (line %d)", __FUNCTION__, __LINE__)); + break; + } } } @@ -376,10 +388,14 @@ void GuiBitmapButtonCtrl::setBitmap( StringTableEntry name ) mTextures[i].mTextureInactiveAsset = mTextures[i].mTextureInactiveAssetId; } - if (mTextures[i].mTextureInactiveAsset.notNull() && mTextures[i].mTextureInactiveAsset->getStatus() == AssetBase::Ok) + if (mTextures[i].mTextureInactiveAsset.notNull()) { - mTextures[i].mTextureInactive = GFXTexHandle(mTextures[i].mTextureInactiveAsset->getImagePath(), &GFXDefaultGUIProfile, avar("%s() - mTextureInactive (line %d)", __FUNCTION__, __LINE__)); - break; + mTextures[i].mTextureInactiveAsset->load(); + if (mTextures[i].mTextureInactiveAsset->getStatus() == AssetBase::Ok) + { + mTextures[i].mTextureInactive = GFXTexHandle(mTextures[i].mTextureInactiveAsset->getImagePath(), &GFXDefaultGUIProfile, avar("%s() - mTextureDepressed (line %d)", __FUNCTION__, __LINE__)); + break; + } } } From 155477492dc5bd19a652f15ca33df0e9bf4b74e9 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sun, 12 Nov 2023 21:32:24 -0600 Subject: [PATCH 036/122] fix updateAudioState null crash --- Engine/source/T3D/shapeBase.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index e6656fb15..e82400446 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -2303,9 +2303,12 @@ void ShapeBase::updateAudioState(SoundThread& st) // if asset is valid, play if (st.asset->isAssetValid() ) { - st.sound = SFX->createSource( st.asset->getSFXTrack() , &getTransform() ); - if ( st.sound ) - st.sound->play(); + if (st.asset->load() == AssetBase::Ok) + { + st.sound = SFX->createSource(st.asset->getSFXTrack(), &getTransform()); + if (st.sound) + st.sound->play(); + } } else st.play = false; From 182fec8b1ae9b565b871fd5fd1aa366ef321491e Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 14 Nov 2023 12:09:32 -0600 Subject: [PATCH 037/122] revert b5d1d1a02c5c495ba3953eaf769969dffef3c01c turns out that caused lighting 'occlusion' artifacting --- Engine/source/lighting/advanced/advancedLightManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/lighting/advanced/advancedLightManager.cpp b/Engine/source/lighting/advanced/advancedLightManager.cpp index 2fc213015..47c8d3d36 100644 --- a/Engine/source/lighting/advanced/advancedLightManager.cpp +++ b/Engine/source/lighting/advanced/advancedLightManager.cpp @@ -607,7 +607,7 @@ GFXVertexBufferHandle AdvancedLightManager::g for (S32 i=1; i Date: Tue, 14 Nov 2023 13:11:01 -0600 Subject: [PATCH 038/122] hide particleemitter dupes if you have a selected particleemitternode when opening the particle editor, don't show the edit-copy one --- .../game/tools/particleEditor/particleEditor.ed.tscript | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Templates/BaseGame/game/tools/particleEditor/particleEditor.ed.tscript b/Templates/BaseGame/game/tools/particleEditor/particleEditor.ed.tscript index eb9cb23bc..99a9024fd 100644 --- a/Templates/BaseGame/game/tools/particleEditor/particleEditor.ed.tscript +++ b/Templates/BaseGame/game/tools/particleEditor/particleEditor.ed.tscript @@ -222,6 +222,13 @@ function ParticleEditor::resetEmitterNode( %this ) ParticleEditor.updateEmitterNode(); } + if (EWorldEditor.getSelectionSize()>0) + { + %obj = EWorldEditor.getSelectedObject(0); + if (%obj.isMemberOfClass("ParticleEmitterNode")) + $ParticleEditor::emitterNode.sethidden(true); + } + } //--------------------------------------------------------------------------------------------- From 59247bd9ca34f9f73c30fa827d8776e4b54dbf03 Mon Sep 17 00:00:00 2001 From: Areloch Date: Tue, 14 Nov 2023 18:58:58 -0600 Subject: [PATCH 039/122] Moves the world Editor guis to be based through a tabbed structure to make it easier to begin separating out editor/tool guis into their own tabs or elements for better separation. Currently establishes the MainScene tab and otherwise retains existing behavior directly --- .../source/gui/containers/guiTabBookCtrl.cpp | 9 +- Engine/source/gui/containers/guiTabBookCtrl.h | 1 + Engine/source/lighting/lightManager.cpp | 2 +- .../tools/VPathEditor/Scripts/Plugin.tscript | 2 +- .../game/tools/VerveEditor/main.tscript | 2 +- .../game/tools/convexEditor/main.tscript | 8 +- .../game/tools/datablockEditor/main.tscript | 4 +- .../game/tools/decalEditor/main.tscript | 6 +- .../game/tools/forestEditor/main.tscript | 6 +- .../game/tools/materialEditor/main.tscript | 8 +- .../game/tools/meshRoadEditor/main.tscript | 6 +- .../game/tools/missionAreaEditor/main.tscript | 6 +- .../game/tools/navEditor/main.tscript | 8 +- .../game/tools/particleEditor/main.tscript | 2 +- .../game/tools/riverEditor/main.tscript | 6 +- .../game/tools/roadEditor/main.tscript | 6 +- .../game/tools/shapeEditor/main.tscript | 12 +- .../tools/worldEditor/gui/EditorGui.ed.gui | 2874 +++++++++-------- .../tools/worldEditor/gui/ToolsToolbar.ed.gui | 2 +- .../worldEditor/scripts/EditorGui.ed.tscript | 4 +- 20 files changed, 1503 insertions(+), 1471 deletions(-) diff --git a/Engine/source/gui/containers/guiTabBookCtrl.cpp b/Engine/source/gui/containers/guiTabBookCtrl.cpp index 3a9c9c608..a9810654e 100644 --- a/Engine/source/gui/containers/guiTabBookCtrl.cpp +++ b/Engine/source/gui/containers/guiTabBookCtrl.cpp @@ -58,6 +58,10 @@ IMPLEMENT_CALLBACK( GuiTabBookCtrl, onTabSelected, void, ( const String& text, U "Called when a new tab page is selected.\n\n" "@param text Text of the page header for the tab that is being selected.\n" "@param index Index of the tab page being selected." ); +IMPLEMENT_CALLBACK(GuiTabBookCtrl, onTabUnSelected, void, (const String& text, U32 index), (text, index), + "Called when a new tab page is unselected.\n\n" + "@param text Text of the page header for the tab that is being unselected.\n" + "@param index Index of the tab page being unselected."); IMPLEMENT_CALLBACK( GuiTabBookCtrl, onTabRightClick, void, ( const String& text, U32 index ), ( text, index ), "Called when the user right-clicks on a tab page header.\n\n" "@param text Text of the page header for the tab that is being selected.\n" @@ -849,7 +853,10 @@ void GuiTabBookCtrl::selectPage( GuiTabPageCtrl *page ) onTabSelected_callback( tab->getText(), index ); } else - tab->setVisible( false ); + { + tab->setVisible(false); + onTabUnSelected_callback(tab->getText(), index); + } } setUpdateLayout( updateSelf ); } diff --git a/Engine/source/gui/containers/guiTabBookCtrl.h b/Engine/source/gui/containers/guiTabBookCtrl.h index 21b8c05da..38ae2c69d 100644 --- a/Engine/source/gui/containers/guiTabBookCtrl.h +++ b/Engine/source/gui/containers/guiTabBookCtrl.h @@ -116,6 +116,7 @@ class GuiTabBookCtrl : public GuiContainer /// @{ DECLARE_CALLBACK( void, onTabSelected, ( const String& text, U32 index ) ); + DECLARE_CALLBACK(void, onTabUnSelected, (const String& text, U32 index)); DECLARE_CALLBACK( void, onTabRightClick, ( const String& text, U32 index ) ); /// @} diff --git a/Engine/source/lighting/lightManager.cpp b/Engine/source/lighting/lightManager.cpp index 57a1936f5..d0652a62f 100644 --- a/Engine/source/lighting/lightManager.cpp +++ b/Engine/source/lighting/lightManager.cpp @@ -248,7 +248,7 @@ void LightManager::registerGlobalLights( const Frustum *frustum, bool staticLigh // the shape bounds and can often get culled. GameConnection *conn = GameConnection::getConnectionToServer(); - if (conn->getControlObject()) + if (conn && conn->getControlObject()) { GameBase *conObject = conn->getControlObject(); activeLights.push_back_unique(conObject); diff --git a/Templates/BaseGame/game/tools/VPathEditor/Scripts/Plugin.tscript b/Templates/BaseGame/game/tools/VPathEditor/Scripts/Plugin.tscript index 0705216ed..67ea3c5c6 100644 --- a/Templates/BaseGame/game/tools/VPathEditor/Scripts/Plugin.tscript +++ b/Templates/BaseGame/game/tools/VPathEditor/Scripts/Plugin.tscript @@ -18,7 +18,7 @@ function VPathEditorPlugin::onWorldEditorStartup( %this ) // //---------------------------------------------------------------------- - EditorGui.add( EVPathEditor ); + MainSceneTabPanel.add( EVPathEditor ); EVPathEditor.setVisible( false ); %this.EditorMap = new ActionMap(); diff --git a/Templates/BaseGame/game/tools/VerveEditor/main.tscript b/Templates/BaseGame/game/tools/VerveEditor/main.tscript index d65cd2651..b6b2006bc 100644 --- a/Templates/BaseGame/game/tools/VerveEditor/main.tscript +++ b/Templates/BaseGame/game/tools/VerveEditor/main.tscript @@ -90,7 +90,7 @@ function VerveEditor::LaunchEditor() { %mainScreen.add(VerveEditorGUI); VerveEditorGUI.position.y = VerveEditorGUI.Position.y + 20; - EditorGUI.add(%mainScreen); + MainSceneTabPanel.add(%mainScreen); } // Clear History. diff --git a/Templates/BaseGame/game/tools/convexEditor/main.tscript b/Templates/BaseGame/game/tools/convexEditor/main.tscript index 7d04d55c8..86e09c7f6 100644 --- a/Templates/BaseGame/game/tools/convexEditor/main.tscript +++ b/Templates/BaseGame/game/tools/convexEditor/main.tscript @@ -36,10 +36,10 @@ function initializeConvexEditor() ConvexEditorOptionsWindow.setVisible( false ); - EditorGui.add( ConvexEditorGui ); - EditorGui.add( ConvexEditorOptionsWindow ); - EditorGui.add( ConvexEditorTreeWindow ); - EditorGui.add( ConvexEditorOptionsWindow ); + MainSceneTabPanel.add( ConvexEditorGui ); + MainSceneTabPanel.add( ConvexEditorOptionsWindow ); + MainSceneTabPanel.add( ConvexEditorTreeWindow ); + MainSceneTabPanel.add( ConvexEditorOptionsWindow ); new ScriptObject( ConvexEditorPlugin ) { diff --git a/Templates/BaseGame/game/tools/datablockEditor/main.tscript b/Templates/BaseGame/game/tools/datablockEditor/main.tscript index f577263cf..7bae6fb03 100644 --- a/Templates/BaseGame/game/tools/datablockEditor/main.tscript +++ b/Templates/BaseGame/game/tools/datablockEditor/main.tscript @@ -37,8 +37,8 @@ function initializeDatablockEditor() DatablockEditorInspectorWindow.setVisible( false ); DatablockEditorTreeWindow.setVisible( false ); - EditorGui.add( DatablockEditorInspectorWindow ); - EditorGui.add( DatablockEditorTreeWindow ); + MainSceneTabPanel.add( DatablockEditorInspectorWindow ); + MainSceneTabPanel.add( DatablockEditorTreeWindow ); new ScriptObject( DatablockEditorPlugin ) { diff --git a/Templates/BaseGame/game/tools/decalEditor/main.tscript b/Templates/BaseGame/game/tools/decalEditor/main.tscript index 58b3201ec..27a34e665 100644 --- a/Templates/BaseGame/game/tools/decalEditor/main.tscript +++ b/Templates/BaseGame/game/tools/decalEditor/main.tscript @@ -35,9 +35,9 @@ function initializeDecalEditor() DecalEditorGui.setVisible( false ); DecalPreviewWindow.setVisible( false ); DecalEditorWindow.setVisible( false ); - EditorGui.add( DecalEditorGui ); - EditorGui.add( DecalEditorWindow ); - EditorGui.add( DecalPreviewWindow ); + MainSceneTabPanel.add( DecalEditorGui ); + MainSceneTabPanel.add( DecalEditorWindow ); + MainSceneTabPanel.add( DecalPreviewWindow ); DecalEditorTabBook.selectPage( 0 ); new ScriptObject( DecalEditorPlugin ) diff --git a/Templates/BaseGame/game/tools/forestEditor/main.tscript b/Templates/BaseGame/game/tools/forestEditor/main.tscript index faeb2b12a..15bb273af 100644 --- a/Templates/BaseGame/game/tools/forestEditor/main.tscript +++ b/Templates/BaseGame/game/tools/forestEditor/main.tscript @@ -35,9 +35,9 @@ function initializeForestEditor() ForestEditorPalleteWindow.setVisible( false ); ForestEditorPropertiesWindow.setVisible( false ); - EditorGui.add( ForestEditorGui ); - EditorGui.add( ForestEditorPalleteWindow ); - EditorGui.add( ForestEditorPropertiesWindow ); + MainSceneTabPanel.add( ForestEditorGui ); + MainSceneTabPanel.add( ForestEditorPalleteWindow ); + MainSceneTabPanel.add( ForestEditorPropertiesWindow ); new ScriptObject( ForestEditorPlugin ) { diff --git a/Templates/BaseGame/game/tools/materialEditor/main.tscript b/Templates/BaseGame/game/tools/materialEditor/main.tscript index 7f50b9329..9ff894266 100644 --- a/Templates/BaseGame/game/tools/materialEditor/main.tscript +++ b/Templates/BaseGame/game/tools/materialEditor/main.tscript @@ -46,10 +46,10 @@ function initializeMaterialEditor() matEd_addCubemapWindow.setVisible( false ); MaterialEditorPropertiesWindow.setVisible( false ); - EditorGui.add( MaterialEditorPreviewWindow ); - EditorGui.add( matEd_cubemapEditor ); - EditorGui.add( matEd_addCubemapWindow ); - EditorGui.add( MaterialEditorPropertiesWindow ); + MainSceneTabPanel.add( MaterialEditorPreviewWindow ); + MainSceneTabPanel.add( matEd_cubemapEditor ); + MainSceneTabPanel.add( matEd_addCubemapWindow ); + MainSceneTabPanel.add( MaterialEditorPropertiesWindow ); } function destroyMaterialEditor() diff --git a/Templates/BaseGame/game/tools/meshRoadEditor/main.tscript b/Templates/BaseGame/game/tools/meshRoadEditor/main.tscript index 1ff15ebde..4a17c29e7 100644 --- a/Templates/BaseGame/game/tools/meshRoadEditor/main.tscript +++ b/Templates/BaseGame/game/tools/meshRoadEditor/main.tscript @@ -33,9 +33,9 @@ function initializeMeshRoadEditor() MeshRoadEditorOptionsWindow.setVisible( false ); MeshRoadEditorTreeWindow.setVisible( false ); - EditorGui.add( MeshRoadEditorGui ); - EditorGui.add( MeshRoadEditorOptionsWindow ); - EditorGui.add( MeshRoadEditorTreeWindow ); + MainSceneTabPanel.add( MeshRoadEditorGui ); + MainSceneTabPanel.add( MeshRoadEditorOptionsWindow ); + MainSceneTabPanel.add( MeshRoadEditorTreeWindow ); new ScriptObject( MeshRoadEditorPlugin ) { diff --git a/Templates/BaseGame/game/tools/missionAreaEditor/main.tscript b/Templates/BaseGame/game/tools/missionAreaEditor/main.tscript index bb69b4b35..1877f425c 100644 --- a/Templates/BaseGame/game/tools/missionAreaEditor/main.tscript +++ b/Templates/BaseGame/game/tools/missionAreaEditor/main.tscript @@ -33,9 +33,9 @@ function initializeMissionAreaEditor() MissionAreaEditorTerrainWindow.setVisible( false ); MissionAreaEditorPropertiesWindow.setVisible( false ); - EditorGui.add( MissionAreaEditorGui ); - EditorGui.add( MissionAreaEditorTerrainWindow ); - EditorGui.add( MissionAreaEditorPropertiesWindow ); + MainSceneTabPanel.add( MissionAreaEditorGui ); + MainSceneTabPanel.add( MissionAreaEditorTerrainWindow ); + MainSceneTabPanel.add( MissionAreaEditorPropertiesWindow ); new ScriptObject( MissionAreaEditorPlugin ) { diff --git a/Templates/BaseGame/game/tools/navEditor/main.tscript b/Templates/BaseGame/game/tools/navEditor/main.tscript index 28303a576..73b97d58d 100644 --- a/Templates/BaseGame/game/tools/navEditor/main.tscript +++ b/Templates/BaseGame/game/tools/navEditor/main.tscript @@ -46,10 +46,10 @@ function initializeNavEditor() NavEditorTreeWindow.setVisible(false); NavEditorConsoleDlg.setVisible(false); - EditorGui.add(NavEditorGui); - EditorGui.add(NavEditorOptionsWindow); - EditorGui.add(NavEditorTreeWindow); - EditorGui.add(NavEditorConsoleDlg); + MainSceneTabPanel.add(NavEditorGui); + MainSceneTabPanel.add(NavEditorOptionsWindow); + MainSceneTabPanel.add(NavEditorTreeWindow); + MainSceneTabPanel.add(NavEditorConsoleDlg); new ScriptObject(NavEditorPlugin) { diff --git a/Templates/BaseGame/game/tools/particleEditor/main.tscript b/Templates/BaseGame/game/tools/particleEditor/main.tscript index f09f2f747..a0c350060 100644 --- a/Templates/BaseGame/game/tools/particleEditor/main.tscript +++ b/Templates/BaseGame/game/tools/particleEditor/main.tscript @@ -36,7 +36,7 @@ function initializeParticleEditor() exec( "./particleParticleEditor.ed." @ $TorqueScriptFileExtension ); PE_Window.setVisible( false ); - EditorGui.add( PE_Window ); + MainSceneTabPanel.add( PE_Window ); new ScriptObject( ParticleEditorPlugin ) { diff --git a/Templates/BaseGame/game/tools/riverEditor/main.tscript b/Templates/BaseGame/game/tools/riverEditor/main.tscript index 3911b048c..a6d59e8ea 100644 --- a/Templates/BaseGame/game/tools/riverEditor/main.tscript +++ b/Templates/BaseGame/game/tools/riverEditor/main.tscript @@ -34,9 +34,9 @@ function initializeRiverEditor() RiverEditorOptionsWindow.setVisible( false ); RiverEditorTreeWindow.setVisible( false ); - EditorGui.add( RiverEditorGui ); - EditorGui.add( RiverEditorOptionsWindow ); - EditorGui.add( RiverEditorTreeWindow ); + MainSceneTabPanel.add( RiverEditorGui ); + MainSceneTabPanel.add( RiverEditorOptionsWindow ); + MainSceneTabPanel.add( RiverEditorTreeWindow ); new ScriptObject( RiverEditorPlugin ) { diff --git a/Templates/BaseGame/game/tools/roadEditor/main.tscript b/Templates/BaseGame/game/tools/roadEditor/main.tscript index 2583ab305..38cfc9252 100644 --- a/Templates/BaseGame/game/tools/roadEditor/main.tscript +++ b/Templates/BaseGame/game/tools/roadEditor/main.tscript @@ -34,9 +34,9 @@ function initializeRoadEditor() RoadEditorOptionsWindow.setVisible( false ); RoadEditorTreeWindow.setVisible( false ); - EditorGui.add( RoadEditorGui ); - EditorGui.add( RoadEditorOptionsWindow ); - EditorGui.add( RoadEditorTreeWindow ); + MainSceneTabPanel.add( RoadEditorGui ); + MainSceneTabPanel.add( RoadEditorOptionsWindow ); + MainSceneTabPanel.add( RoadEditorTreeWindow ); new ScriptObject( RoadEditorPlugin ) { diff --git a/Templates/BaseGame/game/tools/shapeEditor/main.tscript b/Templates/BaseGame/game/tools/shapeEditor/main.tscript index ad64611cb..35969608d 100644 --- a/Templates/BaseGame/game/tools/shapeEditor/main.tscript +++ b/Templates/BaseGame/game/tools/shapeEditor/main.tscript @@ -48,12 +48,12 @@ function initializeShapeEditor() ShapeEdSelectWindow.setVisible(false); ShapeEdPropWindow.setVisible(false); - EditorGui.add(ShapeEdPreviewGui); - EditorGui.add(ShapeEdAnimWindow); - EditorGui.add(ShapeEdAdvancedWindow); + MainSceneTabPanel.add(ShapeEdPreviewGui); + MainSceneTabPanel.add(ShapeEdAnimWindow); + MainSceneTabPanel.add(ShapeEdAdvancedWindow); - EditorGui.add(ShapeEdSelectWindow); - EditorGui.add(ShapeEdPropWindow); + MainSceneTabPanel.add(ShapeEdSelectWindow); + MainSceneTabPanel.add(ShapeEdPropWindow); new ScriptObject(ShapeEditorPlugin) { @@ -186,7 +186,7 @@ function ShapeEditorPlugin::open(%this, %shapeAsset) ShapeEdPropWindow.setVisible(true); ShapeEdAnimWindow.setVisible(true); ShapeEdAdvancedWindow.setVisible(ShapeEditorToolbar-->showAdvanced.getValue()); - EditorGui.bringToFront(ShapeEdPreviewGui); + MainSceneTabPanel.bringToFront(ShapeEdPreviewGui); ToolsPaletteArray->WorldEditorMove.performClick(); %this.map.push(); diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/EditorGui.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/EditorGui.ed.gui index 4f5891b93..1a153dabb 100644 --- a/Templates/BaseGame/game/tools/worldEditor/gui/EditorGui.ed.gui +++ b/Templates/BaseGame/game/tools/worldEditor/gui/EditorGui.ed.gui @@ -19,363 +19,963 @@ $guiContent = new GuiContainer(EditorGui,EditorGuiGroup) { AnchorLeft = "1"; AnchorRight = "0"; - new GuiContainer(EditorGuiToolbar) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "1"; - Profile = "ToolsMenubarProfile"; - HorizSizing = "width"; - VertSizing = "bottom"; - Position = "0 6"; - Extent = "800 37"; - MinExtent = "8 8"; - Margin = "0 0 0 0"; - Padding = "0 0 0 0"; - canSave = "1"; - Visible = "1"; - hovertime = "1000"; - - new GuiBitmapButtonCtrl(EHLogo) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiButtonProfile"; - HorizSizing = "right"; - VertSizing = "center"; - Position = "8 0"; - Extent = "24 24"; - MinExtent = "24 24"; - canSave = "1"; - Visible = "1"; - Command = ""; - tooltipprofile = "ToolsGuiToolTipProfile"; - ToolTip = "Torque3D"; - hovertime = "1000"; - bitmapAsset = "ToolsModule:menu_logo"; - groupNum = "0"; - buttonType = "PushButton"; - useMouseEvents = "0"; - }; - - new GuiStackControl(EditorGuiToolbarStack) { - stackingType = "Horizontal"; - horizStacking = "Left to Right"; - vertStacking = "Top to Bottom"; - padding = "0"; - dynamicSize = "1"; - dynamicNonStackExtent = "0"; - dynamicPos = "0"; - changeChildSizeToFit = "0"; - changeChildPosition = "1"; - position = "36 4"; - extent = "885 36"; - minExtent = "16 16"; - horizSizing = "right"; - vertSizing = "top"; - profile = "GuiDefaultProfile"; - visible = "1"; - active = "1"; + new GuiTabBookCtrl(EditorGuiMainTabBook) { + tabHeight = "20"; + selectedPage = "0"; + position = "0 5"; + extent = "800 595"; + horizSizing = "width"; + vertSizing = "height"; + profile = "ToolsGuiTabBookProfile"; + tooltipProfile = "GuiToolTipProfile"; + + new GuiTabPageCtrl(MainSceneEditorTab) { + text = "Main Scene"; + position = 0 SPC $MainEditor::TabHeight; + extent = 800 SPC 580; + horizSizing = "width"; + vertSizing = "height"; + profile = "ToolsGuiTabPageProfile"; tooltipProfile = "GuiToolTipProfile"; - hovertime = "1000"; - isContainer = "1"; - canSave = "1"; - canSaveDynamicFields = "0"; - new GuiBitmapCtrl() { - Enabled = "1"; - Profile = "ToolsGuiDefaultProfile"; - position = "0 3"; - Extent = "9 26"; - MinExtent = "1 1"; - bitmapAsset = "ToolsModule:separator_xt_h_image"; - horizSizing = "right"; - vertSizing = "bottom"; - }; - - new GuiBitmapButtonCtrl(WorldSaveBtn) { + new GuiContainer(MainSceneTabPanel) { + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "800 580"; + + new WorldEditor(EWorldEditor) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "WorldEditorProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "800 580"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Docking = "None"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + cameraZRot = "0"; + forceFOV = "0"; + renderMissionArea = "0"; + missionAreaFillColor = "255 0 0 20"; + missionAreaFrameColor = "255 0 0 128"; + allowBorderMove = "0"; + borderMovePixelSize = "20"; + borderMoveSpeed = "0.1"; + consoleFrameColor = "255 0 0 255"; + consoleFillColor = "0 0 0 0"; + consoleSphereLevel = "1"; + consoleCircleSegments = "32"; + consoleLineWidth = "1"; + GizmoProfile = "GlobalGizmoProfile"; + isDirty = "0"; + stickToGround = "0"; + dropAtBounds = "1"; + dropBelowCameraOffset = "15"; + dropType = "screenCenter"; + boundingBoxCollision = "1"; + renderPopupBackground = "1"; + popupBackgroundColor = "100 100 100 255"; + popupTextColor = "255 255 0 255"; + objectTextColor = "255 255 255 255"; + objectsUseBoxCenter = "1"; + objSelectColor = "255 0 0 255"; + objMouseOverSelectColor = "0 0 255 255"; + objMouseOverColor = "0 255 0 255"; + showMousePopupInfo = "1"; + dragRectColor = "255 255 0 255"; + renderObjText = "1"; + renderObjHandle = "1"; + objTextFormat = "$name|class$"; + faceSelectColor = "0 0 100 100"; + renderSelectionBox = "1"; + selectionBoxColor = "255 255 0 255"; + selectionLocked = "0"; + toggleIgnoreList = "0"; + selectHandle = "ToolsModule:SelectHandle_image"; + defaultHandle = "ToolsModule:DefaultHandle_image"; + lockedHandleAsset = "ToolsModule:LockedHandle_image"; + }; + new TerrainEditor(ETerrainEditor) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "WorldEditorProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "800 580"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + Docking = "None"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + cameraZRot = "0"; + forceFOV = "0"; + renderMissionArea = "0"; + missionAreaFillColor = "0 0 0 20";//"255 0 0 20"; + missionAreaFrameColor = "0 0 0 128";//"255 0 0 128"; + allowBorderMove = "0"; + borderMovePixelSize = "20"; + borderMoveSpeed = "0.1"; + consoleFrameColor = "0 0 0 255"; + consoleFillColor = "0 0 0 0"; + consoleSphereLevel = "1"; + consoleCircleSegments = "32"; + consoleLineWidth = "1"; + GizmoProfile = "GlobalGizmoProfile"; + isDirty = "0"; + isMissionDirty = "0"; + renderBorder = "1"; + borderHeight = "10"; + borderFillColor = "0 255 0 20"; + borderFrameColor = "0 255 0 128"; + borderLineMode = "0"; + selectionHidden = "1"; + renderVertexSelection = "1"; + processUsesBrush = "0"; + maxBrushSize = "256 256"; + adjustHeightVal = "10"; + setHeightVal = "100"; + scaleVal = "1"; + smoothFactor = "0.1"; + materialGroup = "0"; + softSelectRadius = "50"; + softSelectFilter = "1.000000 0.833333 0.666667 0.500000 0.333333 0.166667 0.000000"; + softSelectDefaultFilter = "1.000000 0.833333 0.666667 0.500000 0.333333 0.166667 0.000000"; + adjustHeightMouseScale = "0.1"; + paintIndex = "-1"; + + new GuiTextCtrl(TESelectionInfo) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfile"; + HorizSizing = "right"; + VertSizing = "top"; + Position = "288 549"; + Extent = "120 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = " (Selection) #: 0 avg: 0"; + maxLength = "255"; + }; + new GuiTextCtrl(TEMouseBrushInfo) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfile"; + HorizSizing = "right"; + VertSizing = "top"; + Position = "40 549"; + Extent = "107 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = " (Mouse) #: 0 avg: 0"; + maxLength = "255"; + }; + new GuiTextCtrl(TESelectionInfo1) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfileWhite"; + HorizSizing = "right"; + VertSizing = "top"; + Position = "289 550"; + Extent = "120 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = " (Selection) #: 0 avg: 0"; + maxLength = "255"; + }; + new GuiTextCtrl(TEMouseBrushInfo1) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfileWhite"; + HorizSizing = "right"; + VertSizing = "top"; + Position = "41 550"; + Extent = "107 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = " (Mouse) #: 0 avg: 0"; + maxLength = "255"; + }; + }; + }; + + new GuiContainer(EditorGuiToolbar) { canSaveDynamicFields = "0"; Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiButtonProfile"; - HorizSizing = "right"; + isContainer = "1"; + Profile = "ToolsMenubarProfile"; + HorizSizing = "width"; VertSizing = "bottom"; - Position = "4 3"; - Extent = "29 27"; + Position = "0 0"; + Extent = "800 37"; MinExtent = "8 8"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; canSave = "1"; Visible = "1"; - Command = "EditorSaveMissionMenu();"; - tooltipprofile = "ToolsGuiToolTipProfile"; - ToolTip = "Save the Level"; hovertime = "1000"; - bitmapAsset = "ToolsModule:save_n_image"; - groupNum = "0"; - buttonType = "PushButton"; - useMouseEvents = "0"; - }; - - new GuiBitmapCtrl() { - Enabled = "1"; - Profile = "ToolsGuiDefaultProfile"; - position = "0 3"; - Extent = "2 26"; - MinExtent = "1 1"; - bitmapAsset = "ToolsModule:separator_xt_h_image"; - horizSizing = "right"; - vertSizing = "bottom"; - }; - - new GuiBitmapButtonCtrl(EHWorldEditor) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiButtonProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - Position = "4 3"; - Extent = "29 27"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - Command = ""; - tooltipprofile = "ToolsGuiToolTipProfile"; - ToolTip = "Open the WorldEditor"; - hovertime = "1000"; - bitmapAsset = "ToolsModule:world_n_image"; - groupNum = "0"; - buttonType = "RadioButton"; - useMouseEvents = "0"; - }; - - new GuiBitmapButtonCtrl(EHGuiEditor) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiButtonProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - Position = "34 3"; - Extent = "29 27"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - Command = "toggleGuiEditor(true); $GuiEditorBtnPressed = true;"; - tooltipprofile = "ToolsGuiToolTipProfile"; - ToolTip = "Open the GuiEditor"; - hovertime = "1000"; - bitmapAsset = "ToolsModule:gui_n_image"; - groupNum = "0"; - buttonType = "RadioButton"; - useMouseEvents = "0"; - }; - - new GuiBitmapCtrl() { - Enabled = "1"; - Profile = "ToolsGuiDefaultProfile"; - position = "0 0"; - Extent = "9 26"; - MinExtent = "1 1"; - bitmapAsset = "ToolsModule:separator_xt_h_image"; - horizSizing = "right"; - vertSizing = "bottom"; - }; - - new GuiBitmapButtonCtrl(AssetBrowserBtn) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiButtonProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - position = "0 0"; - Extent = "29 27"; - MinExtent = "8 2"; - canSave = "1"; - Visible = "1"; - active = "1"; - Command = "AssetBrowser.toggleDialog();"; - tooltipprofile = "ToolsGuiToolTipProfile"; - tooltip = "Open the Asset Browser"; - hovertime = "750"; - bitmapAsset = "ToolsModule:assetBrowser_n_image"; - buttonType = "PushButton"; - useStates = "1"; - groupNum = "0"; - useMouseEvents = "0"; - internalName = "AssetBrowserBtn"; - }; - - new GuiBitmapCtrl() { - Enabled = "1"; - Profile = "ToolsGuiDefaultProfile"; - position = "0 3"; - Extent = "9 26"; - MinExtent = "1 1"; - bitmapAsset = "ToolsModule:separator_xt_h_image"; - horizSizing = "right"; - vertSizing = "bottom"; - }; - - new GuiBitmapCtrl() { - Enabled = "1"; - Profile = "ToolsGuiDefaultProfile"; - position = "0 3"; - Extent = "2 26"; - MinExtent = "1 1"; - bitmapAsset = "ToolsModule:separator_xt_h_image"; - horizSizing = "right"; - vertSizing = "bottom"; - }; - - new GuiBitmapButtonCtrl() { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiButtonProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - Position = "0 3"; - Extent = "29 27"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - Command = "Editor.close(ProjectSettings.value(\"UI/playGUIName\"));"; - tooltipprofile = "ToolsGuiToolTipProfile"; - ToolTip = "Play Game"; - hovertime = "1000"; - bitmapAsset = "ToolsModule:playbutton_n_image"; - groupNum = "0"; - buttonType = "PushButton"; - useMouseEvents = "0"; - }; - - new GuiBitmapCtrl() { - Enabled = "1"; - Profile = "ToolsGuiDefaultProfile"; - position = "0 3"; - Extent = "9 26"; - MinExtent = "1 1"; - bitmapAsset = "ToolsModule:separator_xt_h_image"; - horizSizing = "right"; - vertSizing = "bottom"; - }; - - new GuiBitmapButtonCtrl() { - BitmapAsset = "ToolsModule:settings_n_image"; - autoFitExtents = "0"; - useModifiers = "0"; - useStates = "1"; - masked = "0"; - groupNum = "-1"; - buttonType = "PushButton"; - useMouseEvents = "0"; - position = "574 0"; - extent = "29 27"; - minExtent = "8 8"; - horizSizing = "right"; - VertSizing = "bottom"; - profile = "ToolsGuiButtonProfile"; - visible = "1"; - active = "1"; - command = "ESettingsWindow.toggleEditorSettings();"; - tooltipProfile = "ToolsGuiToolTipProfile"; - tooltip = "Open Editor Settings"; - hovertime = "1000"; - isContainer = "0"; - internalName = "editorSettingsBtn"; - canSave = "1"; - canSaveDynamicFields = "0"; - }; - - new GuiBitmapButtonCtrl(windowConsoleBtn) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiButtonProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - position = "0 0"; - Extent = "29 27"; - MinExtent = "8 2"; - canSave = "1"; - Visible = "1"; - active = "1"; - Command = "windowConsoleDlg.showWindow();"; - tooltipprofile = "ToolsGuiToolTipProfile"; - tooltip = "Open the Console in a window"; - hovertime = "750"; - bitmapAsset = "ToolsModule:console_n_image"; - buttonType = "PushButton"; - useStates = "1"; - groupNum = "0"; - useMouseEvents = "0"; - internalName = "windowConsoleBtn"; - }; - - new GuiBitmapCtrl() { - Enabled = "1"; - Profile = "ToolsGuiDefaultProfile"; - position = "0 3"; - Extent = "9 26"; - MinExtent = "1 1"; - bitmapAsset = "ToolsModule:separator_xt_h_image"; - horizSizing = "right"; - vertSizing = "bottom"; - }; - - new GuiBitmapButtonCtrl(EWorldEditorToggleCamera) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiButtonProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - Position = "102 3"; - Extent = "29 27"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - Command = "CameraTypesDropdownToggle();"; - tooltipprofile = "ToolsGuiToolTipProfile"; - ToolTip = "Toggle Camera Modes"; - hovertime = "1000"; - bitmapAsset = "ToolsModule:player_n_image"; - groupNum = "-1"; - buttonType = "ToggleButton"; - useMouseEvents = "0"; - + + new GuiBitmapButtonCtrl(EHLogo) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "center"; + Position = "8 0"; + Extent = "24 24"; + MinExtent = "24 24"; + canSave = "1"; + Visible = "1"; + Command = ""; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Torque3D"; + hovertime = "1000"; + bitmapAsset = "ToolsModule:menu_logo"; + groupNum = "0"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + new GuiStackControl(EditorGuiToolbarStack) { + stackingType = "Horizontal"; + horizStacking = "Left to Right"; + vertStacking = "Top to Bottom"; + padding = "0"; + dynamicSize = "1"; + dynamicNonStackExtent = "0"; + dynamicPos = "0"; + changeChildSizeToFit = "0"; + changeChildPosition = "1"; + position = "36 4"; + extent = "885 36"; + minExtent = "16 16"; + horizSizing = "right"; + vertSizing = "top"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "0 3"; + Extent = "9 26"; + MinExtent = "1 1"; + bitmapAsset = "ToolsModule:separator_xt_h_image"; + horizSizing = "right"; + vertSizing = "bottom"; + }; + + new GuiBitmapButtonCtrl(WorldSaveBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "4 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "EditorSaveMissionMenu();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Save the Level"; + hovertime = "1000"; + bitmapAsset = "ToolsModule:save_n_image"; + groupNum = "0"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "0 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmapAsset = "ToolsModule:separator_xt_h_image"; + horizSizing = "right"; + vertSizing = "bottom"; + }; + + new GuiBitmapButtonCtrl(EHWorldEditor) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "4 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = ""; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Open the WorldEditor"; + hovertime = "1000"; + bitmapAsset = "ToolsModule:world_n_image"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl(EHGuiEditor) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "34 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "toggleGuiEditor(true); $GuiEditorBtnPressed = true;"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Open the GuiEditor"; + hovertime = "1000"; + bitmapAsset = "ToolsModule:gui_n_image"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "0 0"; + Extent = "9 26"; + MinExtent = "1 1"; + bitmapAsset = "ToolsModule:separator_xt_h_image"; + horizSizing = "right"; + vertSizing = "bottom"; + }; + + new GuiBitmapButtonCtrl(AssetBrowserBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + active = "1"; + Command = "AssetBrowser.toggleDialog();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Open the Asset Browser"; + hovertime = "750"; + bitmapAsset = "ToolsModule:assetBrowser_n_image"; + buttonType = "PushButton"; + useStates = "1"; + groupNum = "0"; + useMouseEvents = "0"; + internalName = "AssetBrowserBtn"; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "0 3"; + Extent = "9 26"; + MinExtent = "1 1"; + bitmapAsset = "ToolsModule:separator_xt_h_image"; + horizSizing = "right"; + vertSizing = "bottom"; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "0 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmapAsset = "ToolsModule:separator_xt_h_image"; + horizSizing = "right"; + vertSizing = "bottom"; + }; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "Editor.close(ProjectSettings.value(\"UI/playGUIName\"));"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Play Game"; + hovertime = "1000"; + bitmapAsset = "ToolsModule:playbutton_n_image"; + groupNum = "0"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "0 3"; + Extent = "9 26"; + MinExtent = "1 1"; + bitmapAsset = "ToolsModule:separator_xt_h_image"; + horizSizing = "right"; + vertSizing = "bottom"; + }; + + new GuiBitmapButtonCtrl() { + BitmapAsset = "ToolsModule:settings_n_image"; + autoFitExtents = "0"; + useModifiers = "0"; + useStates = "1"; + masked = "0"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "574 0"; + extent = "29 27"; + minExtent = "8 8"; + horizSizing = "right"; + VertSizing = "bottom"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + command = "ESettingsWindow.toggleEditorSettings();"; + tooltipProfile = "ToolsGuiToolTipProfile"; + tooltip = "Open Editor Settings"; + hovertime = "1000"; + isContainer = "0"; + internalName = "editorSettingsBtn"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + + new GuiBitmapButtonCtrl(windowConsoleBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + active = "1"; + Command = "windowConsoleDlg.showWindow();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Open the Console in a window"; + hovertime = "750"; + bitmapAsset = "ToolsModule:console_n_image"; + buttonType = "PushButton"; + useStates = "1"; + groupNum = "0"; + useMouseEvents = "0"; + internalName = "windowConsoleBtn"; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "0 3"; + Extent = "9 26"; + MinExtent = "1 1"; + bitmapAsset = "ToolsModule:separator_xt_h_image"; + horizSizing = "right"; + vertSizing = "bottom"; + }; + + new GuiBitmapButtonCtrl(EWorldEditorToggleCamera) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "102 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "CameraTypesDropdownToggle();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle Camera Modes"; + hovertime = "1000"; + bitmapAsset = "ToolsModule:player_n_image"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + + new GuiBitmapCtrl() { + HorizSizing = "left"; + VertSizing = "top"; + Position = getWord(EWorldEditorToggleCamera.extent, 0)-6 SPC getWord(EWorldEditorToggleCamera.extent, 1)-6; + Extent = "4 4"; + MinExtent = "4 4"; + bitmapAsset = "ToolsModule:dropdown_button_arrow_image"; + }; + }; + + new GuiControl(CameraSpeedDropdownContainer, EditorGuiGroup) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "136 5"; + Extent = "136 27"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 6"; + Extent = "72 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Cam Speed: "; + maxLength = "1024"; + }; + + new GuiTextEditCtrl(EWorldEditorCameraSpeed) { + canSaveDynamicFields = "0"; + internalName = "textEdit"; + isContainer = "0"; + profile="ToolsGuiNumericDropSliderTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "78 2"; + Extent = "42 16"; + MinExtent = "8 16"; + canSave = "1"; + Visible = "1"; + Command = "EWorldEditorCameraSpeed.updateMenuBar( $ThisControl );"; + hovertime = "1000"; + text = "100"; + maxLength = "4"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + }; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "112 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog(CameraSpeedDropdownCtrlContainer);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes the Camera Speed"; + hovertime = "750"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmapAsset = "ToolsModule:dropslider_n_image"; + }; + }; + + /*new GuiPopUpMenuCtrl(EWorldEditorCameraSpeed) { + canSaveDynamicFields = "0"; + internalName = "CameraSpeedDropdown"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "136 7"; + Extent = "130 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + };*/ + + new GuiBitmapButtonCtrl(visibilityToggleBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "270 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "VisibilityDropdownToggle();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle Visibility Modes (ALT V)"; + hovertime = "1000"; + bitmapAsset = "ToolsModule:visibility_toggle_n_image"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + + new GuiBitmapCtrl(){ + HorizSizing = "left"; + VertSizing = "top"; + Position = getWord(visibilityToggleBtn.extent, 0)-6 SPC getWord(visibilityToggleBtn.extent, 1)-6; + Extent = "4 4"; + MinExtent = "4 4"; + bitmapAsset = "ToolsModule:dropdown_button_arrow_image"; + }; + }; + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "303 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmapAsset = "ToolsModule:separator_h_image"; + VertSizing = "bottom"; + }; + + new GuiPopUpMenuCtrl(EWorldEditorAlignPopup) { + canSaveDynamicFields = "0"; + internalName = ""; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "439 2"; + Extent = "70 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + }; + }; + }; + new GuiContainer(EditorGuiStatusBar) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsMenubarProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "0 578"; + Extent = "800 35"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Docking = "Bottom"; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "14 7"; + Extent = "20 20"; + MinExtent = "1 1"; + bitmapAsset = "ToolsModule:mission_file_image"; + }; + + new GuiMLTextCtrl(EWorldEditorStatusBarInfo) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiMLTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "40 10"; + Extent = "450 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Current Tool"; + maxLength = "255"; + allowColorChars = "1"; + + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; HorizSizing = "left"; - VertSizing = "top"; - Position = getWord(EWorldEditorToggleCamera.extent, 0)-6 SPC getWord(EWorldEditorToggleCamera.extent, 1)-6; - Extent = "4 4"; - MinExtent = "4 4"; - bitmapAsset = "ToolsModule:dropdown_button_arrow_image"; + VertSizing = "bottom"; + position = "465 8"; + Extent = "2 18"; + MinExtent = "1 1"; + bitmapAsset = "ToolsModule:separator_h_image"; + }; + + new GuiTextCtrl(EWorldEditorStatusBarSelection) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "485 8"; + Extent = "180 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Ready."; + maxLength = "255"; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "645 8"; + Extent = "2 18"; + MinExtent = "1 1"; + bitmapAsset = "ToolsModule:separator_h_image"; + }; + + new GuiPopUpMenuCtrl(EWorldEditorStatusBarCamera) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "665 8"; + Extent = "120 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "800 2"; + Extent = "2 18"; + MinExtent = "1 1"; + bitmapAsset = "ToolsModule:separator_h_image"; }; }; - new GuiControl(CameraSpeedDropdownContainer, EditorGuiGroup) { + new GuiControl(RelightStatus) { canSaveDynamicFields = "0"; + Enabled = "1"; isContainer = "1"; - Profile = "ToolsGuiTransparentProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - position = "136 5"; - Extent = "136 27"; + Profile = "ToolsGuiSolidDefaultProfile"; + HorizSizing = "center"; + VertSizing = "center"; + Position = "223 277"; + Extent = "353 45"; MinExtent = "8 2"; canSave = "1"; - Visible = "1"; + Visible = "0"; + hovertime = "1000"; + + + new GuiProgressBitmapCtrl(RelightProgress) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiRLProgressBitmapProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "5 0"; + Extent = "440 24"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + + }; + new GuiTextCtrl(RelightProgressTxt) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiProgressTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 20"; + Extent = "440 20"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Loading Mission"; + maxLength = "255"; + }; + }; + new GuiControl(RelightMessage) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiSolidDefaultProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "19 570"; + Extent = "583 23"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; hovertime = "1000"; new GuiTextCtrl() { canSaveDynamicFields = "0"; Enabled = "1"; isContainer = "0"; - Profile = "ToolsGuiTextRightProfile"; - HorizSizing = "right"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; VertSizing = "bottom"; - position = "0 6"; - Extent = "72 10"; + Position = "5 1"; + Extent = "449 18"; MinExtent = "8 2"; canSave = "1"; Visible = "1"; @@ -386,957 +986,74 @@ $guiContent = new GuiContainer(EditorGui,EditorGuiGroup) { AnchorBottom = "0"; AnchorLeft = "1"; AnchorRight = "0"; - text = "Cam Speed: "; - maxLength = "1024"; + text = "A lightmapped object has been altered; relight the scene!"; + maxLength = "255"; }; - - new GuiTextEditCtrl(EWorldEditorCameraSpeed) { - canSaveDynamicFields = "0"; - internalName = "textEdit"; - isContainer = "0"; - profile="ToolsGuiNumericDropSliderTextProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - position = "78 2"; - Extent = "42 16"; - MinExtent = "8 16"; - canSave = "1"; - Visible = "1"; - Command = "EWorldEditorCameraSpeed.updateMenuBar( $ThisControl );"; - hovertime = "1000"; - text = "100"; - maxLength = "4"; - historySize = "0"; - password = "0"; - tabComplete = "0"; - sinkAllKeyEvents = "0"; - }; - - new GuiBitmapButtonCtrl() { + new GuiButtonCtrl() { canSaveDynamicFields = "0"; Enabled = "1"; isContainer = "0"; - Profile = "ToolsGuiDefaultProfile"; - HorizSizing = "right"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; VertSizing = "bottom"; - Position = "112 2"; - Extent = "18 18"; + Position = "468 2"; + Extent = "75 18"; MinExtent = "8 2"; canSave = "1"; Visible = "1"; - Command = "Canvas.pushDialog(CameraSpeedDropdownCtrlContainer);"; - tooltipprofile = "ToolsGuiToolTipProfile"; - ToolTip = "Changes the Camera Speed"; - hovertime = "750"; + Command = "Editor.lightScene(\"\", forceAlways); RelightMessage.visible = false;"; + hovertime = "1000"; + text = "Relight Scene"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "548 2"; + Extent = "32 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "RelightMessage.visible = false;"; + hovertime = "1000"; + text = "Hide"; groupNum = "-1"; buttonType = "PushButton"; useMouseEvents = "0"; - bitmapAsset = "ToolsModule:dropslider_n_image"; }; }; - - /*new GuiPopUpMenuCtrl(EWorldEditorCameraSpeed) { + new GuiControl(PhysicsEditMessage) { canSaveDynamicFields = "0"; - internalName = "CameraSpeedDropdown"; Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiPopUpMenuProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - Position = "136 7"; - Extent = "130 18"; - MinExtent = "8 2"; - canSave = "1"; - Visible = "1"; - hovertime = "1000"; - Margin = "0 0 0 0"; - Padding = "0 0 0 0"; - AnchorTop = "1"; - AnchorBottom = "0"; - AnchorLeft = "1"; - AnchorRight = "0"; - maxLength = "1024"; - maxPopupHeight = "200"; - sbUsesNAColor = "0"; - reverseTextList = "0"; - bitmapBounds = "16 16"; - };*/ - - new GuiBitmapButtonCtrl(visibilityToggleBtn) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiButtonProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - Position = "270 3"; - Extent = "29 27"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - Command = "VisibilityDropdownToggle();"; - tooltipprofile = "ToolsGuiToolTipProfile"; - ToolTip = "Toggle Visibility Modes (ALT V)"; - hovertime = "1000"; - bitmapAsset = "ToolsModule:visibility_toggle_n_image"; - groupNum = "-1"; - buttonType = "PushButton"; - useMouseEvents = "0"; - - new GuiBitmapCtrl(){ - HorizSizing = "left"; + isContainer = "1"; + Profile = "ToolsGuiSolidDefaultProfile"; + HorizSizing = "center"; VertSizing = "top"; - Position = getWord(visibilityToggleBtn.extent, 0)-6 SPC getWord(visibilityToggleBtn.extent, 1)-6; - Extent = "4 4"; - MinExtent = "4 4"; - bitmapAsset = "ToolsModule:dropdown_button_arrow_image"; - }; - }; - - new GuiBitmapCtrl() { - Enabled = "1"; - Profile = "ToolsGuiDefaultProfile"; - position = "303 3"; - Extent = "2 26"; - MinExtent = "1 1"; - bitmapAsset = "ToolsModule:separator_h_image"; - VertSizing = "bottom"; - }; - - new GuiPopUpMenuCtrl(EWorldEditorAlignPopup) { - canSaveDynamicFields = "0"; - internalName = ""; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiPopUpMenuProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - Position = "439 2"; - Extent = "70 18"; - MinExtent = "8 2"; - canSave = "1"; - Visible = "0"; - hovertime = "1000"; - Margin = "0 0 0 0"; - Padding = "0 0 0 0"; - AnchorTop = "1"; - AnchorBottom = "0"; - AnchorLeft = "1"; - AnchorRight = "0"; - maxLength = "1024"; - maxPopupHeight = "200"; - sbUsesNAColor = "0"; - reverseTextList = "0"; - bitmapBounds = "16 16"; - }; - }; - }; - - new GuiContainer(EditorGuiStatusBar) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "1"; - Profile = "ToolsMenubarProfile"; - HorizSizing = "width"; - VertSizing = "top"; - Position = "0 578"; - Extent = "800 35"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - hovertime = "1000"; - Docking = "Bottom"; - - new GuiBitmapCtrl() { - Enabled = "1"; - Profile = "ToolsGuiDefaultProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - Position = "14 7"; - Extent = "20 20"; - MinExtent = "1 1"; - bitmapAsset = "ToolsModule:mission_file_image"; - }; - - new GuiMLTextCtrl(EWorldEditorStatusBarInfo) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiMLTextProfile"; - HorizSizing = "width"; - VertSizing = "bottom"; - Position = "40 10"; - Extent = "450 18"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - hovertime = "1000"; - AnchorTop = "1"; - AnchorBottom = "0"; - AnchorLeft = "1"; - AnchorRight = "0"; - text = "Current Tool"; - maxLength = "255"; - allowColorChars = "1"; - - }; - - new GuiBitmapCtrl() { - Enabled = "1"; - Profile = "ToolsGuiDefaultProfile"; - HorizSizing = "left"; - VertSizing = "bottom"; - position = "465 8"; - Extent = "2 18"; - MinExtent = "1 1"; - bitmapAsset = "ToolsModule:separator_h_image"; - }; - - new GuiTextCtrl(EWorldEditorStatusBarSelection) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiTextProfile"; - HorizSizing = "left"; - VertSizing = "bottom"; - Position = "485 8"; - Extent = "180 18"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - hovertime = "1000"; - Margin = "0 0 0 0"; - Padding = "0 0 0 0"; - AnchorTop = "1"; - AnchorBottom = "0"; - AnchorLeft = "1"; - AnchorRight = "0"; - text = "Ready."; - maxLength = "255"; - }; - - new GuiBitmapCtrl() { - Enabled = "1"; - Profile = "ToolsGuiDefaultProfile"; - HorizSizing = "left"; - VertSizing = "bottom"; - position = "645 8"; - Extent = "2 18"; - MinExtent = "1 1"; - bitmapAsset = "ToolsModule:separator_h_image"; - }; - - new GuiPopUpMenuCtrl(EWorldEditorStatusBarCamera) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiPopUpMenuProfile"; - HorizSizing = "left"; - VertSizing = "bottom"; - Position = "665 8"; - Extent = "120 18"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - hovertime = "1000"; - Margin = "0 0 0 0"; - Padding = "0 0 0 0"; - AnchorTop = "1"; - AnchorBottom = "0"; - AnchorLeft = "1"; - AnchorRight = "0"; - }; - - new GuiBitmapCtrl() { - Enabled = "1"; - Profile = "ToolsGuiDefaultProfile"; - HorizSizing = "left"; - VertSizing = "bottom"; - position = "800 2"; - Extent = "2 18"; - MinExtent = "1 1"; - bitmapAsset = "ToolsModule:separator_h_image"; - }; - }; - - new WorldEditor(EWorldEditor) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "WorldEditorProfile"; - HorizSizing = "width"; - VertSizing = "height"; - Position = "0 0"; - Extent = "800 600"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - hovertime = "1000"; - Docking = "None"; - Margin = "0 0 0 0"; - Padding = "0 0 0 0"; - AnchorTop = "0"; - AnchorBottom = "0"; - AnchorLeft = "0"; - AnchorRight = "0"; - cameraZRot = "0"; - forceFOV = "0"; - renderMissionArea = "0"; - missionAreaFillColor = "255 0 0 20"; - missionAreaFrameColor = "255 0 0 128"; - allowBorderMove = "0"; - borderMovePixelSize = "20"; - borderMoveSpeed = "0.1"; - consoleFrameColor = "255 0 0 255"; - consoleFillColor = "0 0 0 0"; - consoleSphereLevel = "1"; - consoleCircleSegments = "32"; - consoleLineWidth = "1"; - GizmoProfile = "GlobalGizmoProfile"; - isDirty = "0"; - stickToGround = "0"; - dropAtBounds = "1"; - dropBelowCameraOffset = "15"; - dropType = "screenCenter"; - boundingBoxCollision = "1"; - renderPopupBackground = "1"; - popupBackgroundColor = "100 100 100 255"; - popupTextColor = "255 255 0 255"; - objectTextColor = "255 255 255 255"; - objectsUseBoxCenter = "1"; - objSelectColor = "255 0 0 255"; - objMouseOverSelectColor = "0 0 255 255"; - objMouseOverColor = "0 255 0 255"; - showMousePopupInfo = "1"; - dragRectColor = "255 255 0 255"; - renderObjText = "1"; - renderObjHandle = "1"; - objTextFormat = "$name|class$"; - faceSelectColor = "0 0 100 100"; - renderSelectionBox = "1"; - selectionBoxColor = "255 255 0 255"; - selectionLocked = "0"; - toggleIgnoreList = "0"; - selectHandle = "ToolsModule:SelectHandle_image"; - defaultHandle = "ToolsModule:DefaultHandle_image"; - lockedHandleAsset = "ToolsModule:LockedHandle_image"; - }; - new TerrainEditor(ETerrainEditor) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "WorldEditorProfile"; - HorizSizing = "width"; - VertSizing = "height"; - Position = "0 0"; - Extent = "800 600"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "0"; - hovertime = "1000"; - Docking = "None"; - Margin = "0 0 0 0"; - Padding = "0 0 0 0"; - AnchorTop = "0"; - AnchorBottom = "0"; - AnchorLeft = "0"; - AnchorRight = "0"; - cameraZRot = "0"; - forceFOV = "0"; - renderMissionArea = "0"; - missionAreaFillColor = "0 0 0 20";//"255 0 0 20"; - missionAreaFrameColor = "0 0 0 128";//"255 0 0 128"; - allowBorderMove = "0"; - borderMovePixelSize = "20"; - borderMoveSpeed = "0.1"; - consoleFrameColor = "0 0 0 255"; - consoleFillColor = "0 0 0 0"; - consoleSphereLevel = "1"; - consoleCircleSegments = "32"; - consoleLineWidth = "1"; - GizmoProfile = "GlobalGizmoProfile"; - isDirty = "0"; - isMissionDirty = "0"; - renderBorder = "1"; - borderHeight = "10"; - borderFillColor = "0 255 0 20"; - borderFrameColor = "0 255 0 128"; - borderLineMode = "0"; - selectionHidden = "1"; - renderVertexSelection = "1"; - processUsesBrush = "0"; - maxBrushSize = "256 256"; - adjustHeightVal = "10"; - setHeightVal = "100"; - scaleVal = "1"; - smoothFactor = "0.1"; - materialGroup = "0"; - softSelectRadius = "50"; - softSelectFilter = "1.000000 0.833333 0.666667 0.500000 0.333333 0.166667 0.000000"; - softSelectDefaultFilter = "1.000000 0.833333 0.666667 0.500000 0.333333 0.166667 0.000000"; - adjustHeightMouseScale = "0.1"; - paintIndex = "-1"; - - new GuiTextCtrl(TESelectionInfo) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "EditorTextProfile"; - HorizSizing = "right"; - VertSizing = "top"; - Position = "288 549"; - Extent = "120 18"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - hovertime = "1000"; - Margin = "0 0 0 0"; - Padding = "0 0 0 0"; - AnchorTop = "1"; - AnchorBottom = "0"; - AnchorLeft = "1"; - AnchorRight = "0"; - text = " (Selection) #: 0 avg: 0"; - maxLength = "255"; - }; - new GuiTextCtrl(TEMouseBrushInfo) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "EditorTextProfile"; - HorizSizing = "right"; - VertSizing = "top"; - Position = "40 549"; - Extent = "107 18"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - hovertime = "1000"; - Margin = "0 0 0 0"; - Padding = "0 0 0 0"; - AnchorTop = "1"; - AnchorBottom = "0"; - AnchorLeft = "1"; - AnchorRight = "0"; - text = " (Mouse) #: 0 avg: 0"; - maxLength = "255"; - }; - new GuiTextCtrl(TESelectionInfo1) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "EditorTextProfileWhite"; - HorizSizing = "right"; - VertSizing = "top"; - Position = "289 550"; - Extent = "120 18"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - hovertime = "1000"; - Margin = "0 0 0 0"; - Padding = "0 0 0 0"; - AnchorTop = "1"; - AnchorBottom = "0"; - AnchorLeft = "1"; - AnchorRight = "0"; - text = " (Selection) #: 0 avg: 0"; - maxLength = "255"; - }; - new GuiTextCtrl(TEMouseBrushInfo1) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "EditorTextProfileWhite"; - HorizSizing = "right"; - VertSizing = "top"; - Position = "41 550"; - Extent = "107 18"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - hovertime = "1000"; - Margin = "0 0 0 0"; - Padding = "0 0 0 0"; - AnchorTop = "1"; - AnchorBottom = "0"; - AnchorLeft = "1"; - AnchorRight = "0"; - text = " (Mouse) #: 0 avg: 0"; - maxLength = "255"; - }; - }; - - new GuiControl(RelightStatus) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "1"; - Profile = "ToolsGuiSolidDefaultProfile"; - HorizSizing = "center"; - VertSizing = "center"; - Position = "223 277"; - Extent = "353 45"; - MinExtent = "8 2"; - canSave = "1"; - Visible = "0"; - hovertime = "1000"; - - - new GuiProgressBitmapCtrl(RelightProgress) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "1"; - Profile = "ToolsGuiRLProgressBitmapProfile"; - HorizSizing = "center"; - VertSizing = "center"; - position = "5 0"; - Extent = "440 24"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - hovertime = "1000"; - Margin = "0 0 0 0"; - Padding = "0 0 0 0"; - AnchorTop = "1"; - AnchorBottom = "0"; - AnchorLeft = "1"; - AnchorRight = "0"; - maxLength = "1024"; - - }; - new GuiTextCtrl(RelightProgressTxt) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiProgressTextProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - position = "5 20"; - Extent = "440 20"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - tooltipprofile = "ToolsGuiToolTipProfile"; - hovertime = "1000"; - Margin = "0 0 0 0"; - Padding = "0 0 0 0"; - AnchorTop = "1"; - AnchorBottom = "0"; - AnchorLeft = "1"; - AnchorRight = "0"; - text = "Loading Mission"; - maxLength = "255"; - }; - }; - new GuiControl(RelightMessage) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "1"; - Profile = "ToolsGuiSolidDefaultProfile"; - HorizSizing = "width"; - VertSizing = "top"; - Position = "19 570"; - Extent = "583 23"; - MinExtent = "8 2"; - canSave = "1"; - Visible = "0"; - hovertime = "1000"; - - new GuiTextCtrl() { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiDefaultProfile"; - HorizSizing = "width"; - VertSizing = "bottom"; - Position = "5 1"; - Extent = "449 18"; - MinExtent = "8 2"; - canSave = "1"; - Visible = "1"; - hovertime = "1000"; - Margin = "0 0 0 0"; - Padding = "0 0 0 0"; - AnchorTop = "1"; - AnchorBottom = "0"; - AnchorLeft = "1"; - AnchorRight = "0"; - text = "A lightmapped object has been altered; relight the scene!"; - maxLength = "255"; - }; - new GuiButtonCtrl() { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiButtonProfile"; - HorizSizing = "left"; - VertSizing = "bottom"; - Position = "468 2"; - Extent = "75 18"; - MinExtent = "8 2"; - canSave = "1"; - Visible = "1"; - Command = "Editor.lightScene(\"\", forceAlways); RelightMessage.visible = false;"; - hovertime = "1000"; - text = "Relight Scene"; - groupNum = "-1"; - buttonType = "PushButton"; - useMouseEvents = "0"; - }; - new GuiButtonCtrl() { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiButtonProfile"; - HorizSizing = "left"; - VertSizing = "bottom"; - Position = "548 2"; - Extent = "32 18"; - MinExtent = "8 2"; - canSave = "1"; - Visible = "1"; - Command = "RelightMessage.visible = false;"; - hovertime = "1000"; - text = "Hide"; - groupNum = "-1"; - buttonType = "PushButton"; - useMouseEvents = "0"; - }; - }; - new GuiControl(PhysicsEditMessage) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "1"; - Profile = "ToolsGuiSolidDefaultProfile"; - HorizSizing = "center"; - VertSizing = "top"; - Position = "180 560"; - Extent = "440 32"; - MinExtent = "8 2"; - canSave = "1"; - Visible = "0"; - hovertime = "1000"; - - new GuiTextCtrl() { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "EditorTextProfile"; - HorizSizing = "width"; - VertSizing = "center"; - Position = "5 0"; - Extent = "238 18"; - MinExtent = "8 2"; - canSave = "1"; - Visible = "1"; - hovertime = "1000"; - Margin = "0 0 0 0"; - Padding = "0 0 0 0"; - AnchorTop = "1"; - AnchorBottom = "0"; - AnchorLeft = "1"; - AnchorRight = "0"; - text = "PHYSICS SIMULATION PAUSED FOR EDITING..."; - maxLength = "255"; - }; - new GuiButtonCtrl() { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiButtonProfile"; - HorizSizing = "left"; - VertSizing = "bottom"; - Position = "337 3"; - Extent = "43 26"; - MinExtent = "8 2"; - canSave = "1"; - Visible = "1"; - Command = "physicsStart(); PhysicsEditMessage.visible = false;"; - hovertime = "1000"; - text = "Start"; - groupNum = "-1"; - buttonType = "PushButton"; - useMouseEvents = "0"; - }; - new GuiButtonCtrl() { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiButtonProfile"; - HorizSizing = "left"; - VertSizing = "bottom"; - Position = "392 3"; - Extent = "43 26"; - MinExtent = "8 2"; - canSave = "1"; - Visible = "1"; - Command = "PhysicsEditMessage.visible = false;"; - hovertime = "1000"; - text = "Hide"; - groupNum = "-1"; - buttonType = "PushButton"; - useMouseEvents = "0"; - }; - }; - new GuiContainer(CameraTypesDropdown){ - Profile = "IconDropdownProfile"; - Position = getWord(EWorldEditorToggleCamera.position, 0)-5 SPC getWord(EditorGuiToolbar.extent, 1)-1; - Extent = "137" SPC ((6*28)+6);//97"; - isContainer = "1"; - visible = "0"; - - new GuiDynamicCtrlArrayControl(cameraDropdownArray) { - canSaveDynamicFields = "0"; - Enabled = "1"; - isContainer = "1"; - Profile = "ToolsGuiSolidDefaultProfile"; - HorizSizing = "width"; - VertSizing = "height"; - Position = "5 5"; - Extent = "132" SPC getWord(CameraTypesDropdown.extent, 1)-5; - MinExtent = "8 2"; - canSave = "1"; - Visible = "1"; - tooltipprofile = "ToolsGuiToolTipProfile"; - hovertime = "1000"; - colCount = "1"; - colSize = "127"; - rowCount = "0"; - RowSize = "64"; - rowSpacing = "3"; - colSpacing = "3"; - autoCellSize = "1"; - fillRowFirst = "0"; - - new GuiIconButtonCtrl() { - canSaveDynamicFields = "0"; - internalName = "StandardCamera"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiIconButtonProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - Position = "5 5"; - Extent = "127 25"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"Standard Camera\");"; - tooltipprofile = "ToolsGuiToolTipProfile"; - ToolTip = "Free Camera"; - hovertime = "1000"; - bitmapAsset = "ToolsModule:camera_n_image"; - groupNum = "0"; - text="Free Camera"; - buttonMargin = "0 4"; - textMargin = "38"; - groupNum = "0"; - buttonType = "RadioButton"; - useMouseEvents = "0"; - }; - new GuiIconButtonCtrl() { - canSaveDynamicFields = "0"; - internalName = "OrbitCamera"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiIconButtonProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - Position = "5 32"; - Extent = "127 25"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"Orbit Camera\");"; - tooltipprofile = "ToolsGuiToolTipProfile"; - ToolTip = "Toggle Orbit Cam"; - hovertime = "1000"; - bitmapAsset = "ToolsModule:orbit_cam_n_image"; - groupNum = "0"; - text="Orbit Camera"; - buttonMargin = "0 4"; - textMargin = "38"; - groupNum = "0"; - buttonType = "RadioButton"; - useMouseEvents = "0"; - }; - - new GuiIconButtonCtrl() { - canSaveDynamicFields = "0"; - internalName = "PlayerCamera"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiIconButtonProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - Position = "5 5"; - Extent = "127 25"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"1st Person Camera\");"; - tooltipprofile = "ToolsGuiToolTipProfile"; - ToolTip = "Player Camera"; - hovertime = "1000"; - bitmapAsset = "ToolsModule:player_n_image"; - groupNum = "0"; - text="Player Camera"; - buttonMargin = "0 4"; - textMargin = "38"; - groupNum = "0"; - buttonType = "RadioButton"; - useMouseEvents = "0"; - }; - new GuiIconButtonCtrl() { - canSaveDynamicFields = "0"; - internalName = "trdPersonCamera"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiIconButtonProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - Position = "5 5"; - Extent = "127 25"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"3rd Person Camera\");"; - tooltipprofile = "ToolsGuiToolTipProfile"; - ToolTip = "3rd Person Camera"; - hovertime = "1000"; - bitmapAsset = "ToolsModule:3rd_person_camera_n_image"; - groupNum = "0"; - text="3rd Person Cam"; - buttonMargin = "0 4"; - textMargin = "38"; - groupNum = "0"; - buttonType = "RadioButton"; - useMouseEvents = "0"; - }; - new GuiIconButtonCtrl() { - canSaveDynamicFields = "0"; - internalName = "NewtonianCamera"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiIconButtonProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - Position = "5 64"; - Extent = "127 25"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"Smooth Camera\");"; - tooltipprofile = "ToolsGuiToolTipProfile"; - ToolTip = "Toggle Newtonian Cam"; - hovertime = "1000"; - bitmapAsset = "ToolsModule:smooth_cam_n_image"; - groupNum = "0"; - text="Smooth Camera"; - buttonMargin = "0 4"; - textMargin = "38"; - groupNum = "0"; - buttonType = "RadioButton"; - useMouseEvents = "0"; - }; - new GuiIconButtonCtrl() { - canSaveDynamicFields = "0"; - internalName = "NewtonianRotationCamera"; - Enabled = "1"; - isContainer = "0"; - Profile = "ToolsGuiIconButtonProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - Position = "5 64"; - Extent = "127 25"; - MinExtent = "8 8"; - canSave = "1"; - Visible = "1"; - Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"Smooth Rot Camera\");"; - tooltipprofile = "ToolsGuiToolTipProfile"; - ToolTip = "Toggle Smooth Camera with Smooth Rotation"; - hovertime = "1000"; - bitmapAsset = "ToolsModule:smooth_cam_rot_n_image"; - groupNum = "0"; - text="Smooth Rotate"; - buttonMargin = "0 4"; - textMargin = "38"; - groupNum = "0"; - buttonType = "RadioButton"; - useMouseEvents = "0"; - }; - }; - - new GuiDecoyCtrl(CameraTypesDropdownDecoy) { - profile = "ToolsGuiDefaultProfile"; - horizSizing = "right"; - vertSizing = "bottom"; - position = "0 0"; - extent = getWord(CameraTypesDropdown.extent, 0) SPC getWord(CameraTypesDropdown.extent, 1); - minExtent = "8 8"; - visible = "1"; - helpTag = "0"; - useMouseEvents = "1"; - isDecoy = "1"; - }; - - }; - new GuiContainer(VisibilityDropdown){ - Profile = "IconDropdownProfile"; - Position = getWord(visibilityToggleBtn.position, 0)-5 SPC getWord(EditorGuiToolbar.extent, 1)-1; - Extent = "159 261"; //SPC ((6*28)+6);//97"; - isContainer = "1"; - visible = "0"; - - new GuiTabBookCtrl() { - canSaveDynamicFields = "0"; - Profile = "ToolsGuiTabBookProfile"; - HorizSizing = "width"; - VertSizing = "height"; - Docking = "Client"; - Margin = "3 3 3 3"; - Position = "5 24"; - Extent = "170 226"; - MinExtent = "8 2"; - canSave = "1"; - Visible = "1"; - hovertime = "1000"; - TabPosition = "Top"; - TabHeight = "22"; - TabMargin = "7"; - MinTabWidth = "64"; - - new GuiTabPageCtrl() { - canSaveDynamicFields = "0"; - Profile = "ToolsGuiTabPageProfile"; - HorizSizing = "width"; - VertSizing = "height"; - Docking = "Client"; - Margin = "-1 0 0 0"; - Position = "0 14"; - Extent = "164 220"; + Position = "180 560"; + Extent = "440 32"; MinExtent = "8 2"; canSave = "1"; Visible = "0"; hovertime = "1000"; - text = "Viz Toggles"; - maxLength = "255"; - - new GuiScrollCtrl() { + + new GuiTextCtrl() { canSaveDynamicFields = "0"; Enabled = "1"; - isContainer = "1"; - Profile = "ToolsGuiScrollProfile"; + isContainer = "0"; + Profile = "EditorTextProfile"; HorizSizing = "width"; - VertSizing = "height"; - Docking = "Client"; - Position = "4 12"; - Extent = "156 190"; + VertSizing = "center"; + Position = "5 0"; + Extent = "238 18"; MinExtent = "8 2"; canSave = "1"; - isDecoy = "0"; Visible = "1"; - tooltipprofile = "ToolsGuiToolTipProfile"; hovertime = "1000"; Margin = "0 0 0 0"; Padding = "0 0 0 0"; @@ -1344,194 +1061,501 @@ $guiContent = new GuiContainer(EditorGui,EditorGuiGroup) { AnchorBottom = "0"; AnchorLeft = "1"; AnchorRight = "0"; - willFirstRespond = "1"; - hScrollBar = "alwaysOff"; - vScrollBar = "dynamic"; - lockHorizScroll = "true"; - lockVertScroll = "false"; - constantThumbHeight = "0"; - childMargin = "2 0"; - - new GuiStackControl() { - StackingType = "Vertical"; - HorizStacking = "Left to Right"; - VertStacking = "Top to Bottom"; - Padding = "-2"; - canSaveDynamicFields = "0"; - internalName = "theVisOptionsList"; - Enabled = "1"; - isContainer = "1"; - Profile = "ToolsGuiDefaultProfile"; - HorizSizing = "width"; - VertSizing = "bottom"; - Position = "1 0"; - Extent = "156 16"; - MinExtent = "16 16"; - canSave = "1"; - isDecoy = "0"; - Visible = "1"; - tooltipprofile = "ToolsGuiToolTipProfile"; - hovertime = "1000"; - }; + text = "PHYSICS SIMULATION PAUSED FOR EDITING..."; + maxLength = "255"; }; - }; - new GuiTabPageCtrl() { - canSaveDynamicFields = "0"; - Profile = "ToolsGuiTabPageProfile"; - HorizSizing = "width"; - VertSizing = "height"; - Docking = "Client"; - Margin = "-1 0 0 0"; - Position = "0 14"; - Extent = "164 220"; - MinExtent = "8 2"; - canSave = "1"; - Visible = "0"; - hovertime = "1000"; - text = "Class Toggles"; - maxLength = "255"; - - new GuiScrollCtrl() { + new GuiButtonCtrl() { canSaveDynamicFields = "0"; Enabled = "1"; - isContainer = "1"; - Profile = "ToolsGuiScrollProfile"; - HorizSizing = "width"; - VertSizing = "height"; - Docking = "Client"; - Position = "4 12"; - Extent = "156 190"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "337 3"; + Extent = "43 26"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "physicsStart(); PhysicsEditMessage.visible = false;"; + hovertime = "1000"; + text = "Start"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "392 3"; + Extent = "43 26"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PhysicsEditMessage.visible = false;"; + hovertime = "1000"; + text = "Hide"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + }; + new GuiContainer(CameraTypesDropdown){ + Profile = "IconDropdownProfile"; + Position = getWord(EWorldEditorToggleCamera.position, 0)-5 SPC getWord(EditorGuiToolbar.extent, 1)-1; + Extent = "137" SPC ((6*28)+6);//97"; + isContainer = "1"; + visible = "0"; + + new GuiDynamicCtrlArrayControl(cameraDropdownArray) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiSolidDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "5 5"; + Extent = "132" SPC getWord(CameraTypesDropdown.extent, 1)-5; MinExtent = "8 2"; canSave = "1"; - isDecoy = "0"; Visible = "1"; tooltipprofile = "ToolsGuiToolTipProfile"; hovertime = "1000"; - Margin = "0 0 0 0"; - Padding = "0 0 0 0"; - AnchorTop = "1"; - AnchorBottom = "0"; - AnchorLeft = "1"; - AnchorRight = "0"; - willFirstRespond = "1"; - hScrollBar = "alwaysOff"; - vScrollBar = "dynamic"; - lockHorizScroll = "true"; - lockVertScroll = "false"; - constantThumbHeight = "0"; - childMargin = "2 0"; - - new GuiStackControl() { - StackingType = "Vertical"; - HorizStacking = "Left to Right"; - VertStacking = "Top to Bottom"; - Padding = "-2"; + colCount = "1"; + colSize = "127"; + rowCount = "0"; + RowSize = "64"; + rowSpacing = "3"; + colSpacing = "3"; + autoCellSize = "1"; + fillRowFirst = "0"; + + new GuiIconButtonCtrl() { canSaveDynamicFields = "0"; - internalName = "theClassVisList"; + internalName = "StandardCamera"; Enabled = "1"; - isContainer = "1"; - Profile = "ToolsGuiDefaultProfile"; - HorizSizing = "width"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "right"; VertSizing = "bottom"; - Position = "1 0"; - Extent = "156 16"; - MinExtent = "16 16"; + Position = "5 5"; + Extent = "127 25"; + MinExtent = "8 8"; canSave = "1"; - isDecoy = "0"; Visible = "1"; + Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"Standard Camera\");"; tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Free Camera"; hovertime = "1000"; + bitmapAsset = "ToolsModule:camera_n_image"; + groupNum = "0"; + text="Free Camera"; + buttonMargin = "0 4"; + textMargin = "38"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "OrbitCamera"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 32"; + Extent = "127 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"Orbit Camera\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle Orbit Cam"; + hovertime = "1000"; + bitmapAsset = "ToolsModule:orbit_cam_n_image"; + groupNum = "0"; + text="Orbit Camera"; + buttonMargin = "0 4"; + textMargin = "38"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "PlayerCamera"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 5"; + Extent = "127 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"1st Person Camera\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Player Camera"; + hovertime = "1000"; + bitmapAsset = "ToolsModule:player_n_image"; + groupNum = "0"; + text="Player Camera"; + buttonMargin = "0 4"; + textMargin = "38"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "trdPersonCamera"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 5"; + Extent = "127 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"3rd Person Camera\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "3rd Person Camera"; + hovertime = "1000"; + bitmapAsset = "ToolsModule:3rd_person_camera_n_image"; + groupNum = "0"; + text="3rd Person Cam"; + buttonMargin = "0 4"; + textMargin = "38"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "NewtonianCamera"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 64"; + Extent = "127 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"Smooth Camera\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle Newtonian Cam"; + hovertime = "1000"; + bitmapAsset = "ToolsModule:smooth_cam_n_image"; + groupNum = "0"; + text="Smooth Camera"; + buttonMargin = "0 4"; + textMargin = "38"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "NewtonianRotationCamera"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 64"; + Extent = "127 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"Smooth Rot Camera\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle Smooth Camera with Smooth Rotation"; + hovertime = "1000"; + bitmapAsset = "ToolsModule:smooth_cam_rot_n_image"; + groupNum = "0"; + text="Smooth Rotate"; + buttonMargin = "0 4"; + textMargin = "38"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + }; + + new GuiDecoyCtrl(CameraTypesDropdownDecoy) { + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = getWord(CameraTypesDropdown.extent, 0) SPC getWord(CameraTypesDropdown.extent, 1); + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + useMouseEvents = "1"; + isDecoy = "1"; + }; + + }; + new GuiContainer(VisibilityDropdown){ + Profile = "IconDropdownProfile"; + Position = getWord(visibilityToggleBtn.position, 0)-5 SPC getWord(EditorGuiToolbar.extent, 1)-1; + Extent = "159 261"; //SPC ((6*28)+6);//97"; + isContainer = "1"; + visible = "0"; + + new GuiTabBookCtrl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiTabBookProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Margin = "3 3 3 3"; + Position = "5 24"; + Extent = "170 226"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + TabPosition = "Top"; + TabHeight = "22"; + TabMargin = "7"; + MinTabWidth = "64"; + + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Margin = "-1 0 0 0"; + Position = "0 14"; + Extent = "164 220"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + text = "Viz Toggles"; + maxLength = "255"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Position = "4 12"; + Extent = "156 190"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "2 0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "-2"; + canSaveDynamicFields = "0"; + internalName = "theVisOptionsList"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 0"; + Extent = "156 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + }; + }; + }; + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Margin = "-1 0 0 0"; + Position = "0 14"; + Extent = "164 220"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + text = "Class Toggles"; + maxLength = "255"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Position = "4 12"; + Extent = "156 190"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "2 0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "-2"; + canSaveDynamicFields = "0"; + internalName = "theClassVisList"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 0"; + Extent = "156 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + }; + }; }; }; }; - }; - }; -}; - - - - -new GuiMouseEventCtrl(CameraSpeedDropdownCtrlContainer, EditorGuiGroup) { - internalName = "AggregateControl"; - horizSizing = "right"; - vertSizing = "bottom"; - position = "0 0"; - extent = "1024 768"; - minExtent = "8 8"; - visible = "1"; - helpTag = "0"; - class = "EditorDropdownSliderContainer"; - - new GuiContainer(){ - position = firstWord(CameraSpeedDropdownContainer.position) + firstWord(EditorGuiToolbar.position) + -6 SPC - (getWord(CameraSpeedDropdownContainer, 1)) + 31; - extent = "146 39"; - isContainer = "1"; - Profile = "IconDropdownProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - new GuiBitmapCtrl(){ // Fast - position = "105 15"; - extent = "2 8"; - bitmapAsset = "ToolsModule:separator_h_image"; - Profile = "ToolsGuiDefaultProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - }; - new GuiBitmapCtrl(){ // normal - position = "73 15"; - extent = "2 8"; - bitmapAsset = "ToolsModule:separator_h_image"; - Profile = "ToolsGuiDefaultProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - }; - new GuiBitmapCtrl(){ // slow - position = "41 15"; - extent = "2 8"; - bitmapAsset = "ToolsModule:separator_h_image"; - Profile = "ToolsGuiDefaultProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - }; - - new GuiSliderCtrl(){ //camera speed slider - internalName = "slider"; - position = "9 17"; - extent = "129 15"; - bitmap = "tools/gui/images/separator-h.png"; - HorizSizing = "width"; - VertSizing = "bottom"; - range = "1 200"; - ticks = "0"; - value = "100"; - AltCommand = "EWorldEditorCameraSpeed.updateMenuBar( $ThisControl );"; + new GuiMouseEventCtrl(CameraSpeedDropdownCtrlContainer, EditorGuiGroup) { + internalName = "AggregateControl"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 60"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "0"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiContainer(){ + position = firstWord(CameraSpeedDropdownContainer.position) + firstWord(EditorGuiToolbar.position) + -6 SPC + (getWord(CameraSpeedDropdownContainer, 1)) + 31; + extent = "146 39"; + isContainer = "1"; + Profile = "IconDropdownProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + + new GuiBitmapCtrl(){ // Fast + position = "105 15"; + extent = "2 8"; + bitmapAsset = "ToolsModule:separator_h_image"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + }; + new GuiBitmapCtrl(){ // normal + position = "73 15"; + extent = "2 8"; + bitmapAsset = "ToolsModule:separator_h_image"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + }; + new GuiBitmapCtrl(){ // slow + position = "41 15"; + extent = "2 8"; + bitmapAsset = "ToolsModule:separator_h_image"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + }; + + new GuiSliderCtrl(){ //camera speed slider + internalName = "slider"; + position = "9 17"; + extent = "129 15"; + bitmap = "tools/gui/images/separator-h.png"; + HorizSizing = "width"; + VertSizing = "bottom"; + range = "1 200"; + ticks = "0"; + value = "100"; + AltCommand = "EWorldEditorCameraSpeed.updateMenuBar( $ThisControl );"; + }; + new GuiTextCtrl(){ // Normal + internalName = "text"; + position = "54 3"; + extent = "39 18"; + text = "Normal"; + Profile = "ToolsGuiTextCenterProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + }; + new GuiTextCtrl(){ // - + position = "11 2"; + extent = "39 18"; + text = "-"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + }; + new GuiTextCtrl(){ // + + position = "98 5"; + extent = "39 13"; + text = "+"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + }; + }; }; - new GuiTextCtrl(){ // Normal - internalName = "text"; - position = "54 3"; - extent = "39 18"; - text = "Normal"; - Profile = "ToolsGuiTextCenterProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - }; - new GuiTextCtrl(){ // - - position = "11 2"; - extent = "39 18"; - text = "-"; - Profile = "ToolsGuiTextProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; - }; - new GuiTextCtrl(){ // + - position = "98 5"; - extent = "39 13"; - text = "+"; - Profile = "ToolsGuiTextRightProfile"; - HorizSizing = "right"; - VertSizing = "bottom"; }; }; }; diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsToolbar.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsToolbar.ed.gui index 14e419edf..4b8acbbcf 100644 --- a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsToolbar.ed.gui +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsToolbar.ed.gui @@ -7,7 +7,7 @@ $guiContent = new GuiContainer(EWToolsToolbar) { Profile = "ToolsMenubarProfile"; HorizSizing = "right"; VertSizing = "bottom"; - Position = "0 41"; + Position = "0 38"; Extent = "0 33"; MinExtent = "8 2"; canSave = "1"; diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.tscript b/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.tscript index afe0e3991..8a33a55da 100644 --- a/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.tscript +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.tscript @@ -43,7 +43,7 @@ function EditorGui::init(%this) if( isObject( EWToolsPaletteWindow ) ) { - %this.add( EWToolsPaletteWindow ); + MainSceneTabPanel.add( EWToolsPaletteWindow ); EWToolsPaletteWindow.init(); EWToolsPaletteWindow.setVisible( false ); } @@ -124,7 +124,7 @@ function EditorGui::init(%this) exec("~/worldEditor/gui/ToolsToolbar.ed.gui"); if( isObject( EWToolsToolbar ) ) { - %this.add( EWToolsToolbar ); + MainSceneTabPanel.add( EWToolsToolbar ); EWToolsToolbar.setVisible( true ); } From 20e0fa8ec9d8aca3f14712662826f2d5deb4bc3b Mon Sep 17 00:00:00 2001 From: Areloch Date: Thu, 16 Nov 2023 18:55:18 -0600 Subject: [PATCH 040/122] Misc Tools UI fixes - Fixes the AB's select button not being properly offset to the right side - Fixes incorrect height offset for the main editor tabbook because of a no-longer-used variable - Fixes the bottom of the scene tree scroll being cut off --- .../BaseGame/game/tools/assetBrowser/guis/assetBrowser.gui | 2 +- Templates/BaseGame/game/tools/worldEditor/gui/EditorGui.ed.gui | 2 +- .../game/tools/worldEditor/gui/WorldEditorTreeWindow.ed.gui | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Templates/BaseGame/game/tools/assetBrowser/guis/assetBrowser.gui b/Templates/BaseGame/game/tools/assetBrowser/guis/assetBrowser.gui index 735275523..f64982991 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/guis/assetBrowser.gui +++ b/Templates/BaseGame/game/tools/assetBrowser/guis/assetBrowser.gui @@ -1105,7 +1105,7 @@ $guiContent = new GuiControl(AssetBrowser) { groupNum = "-1"; buttonType = "PushButton"; useMouseEvents = "0"; - position = "120 340"; + position = getWord($pref::Video::mode, 0) - 420 SPC 340; extent = "53 19"; minExtent = "8 2"; horizSizing = "left"; diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/EditorGui.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/EditorGui.ed.gui index 1a153dabb..4c8feacfa 100644 --- a/Templates/BaseGame/game/tools/worldEditor/gui/EditorGui.ed.gui +++ b/Templates/BaseGame/game/tools/worldEditor/gui/EditorGui.ed.gui @@ -31,7 +31,7 @@ $guiContent = new GuiContainer(EditorGui,EditorGuiGroup) { new GuiTabPageCtrl(MainSceneEditorTab) { text = "Main Scene"; - position = 0 SPC $MainEditor::TabHeight; + position = 0 SPC 20; extent = 800 SPC 580; horizSizing = "width"; vertSizing = "height"; diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/WorldEditorTreeWindow.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/WorldEditorTreeWindow.ed.gui index 51692048c..735b9b070 100644 --- a/Templates/BaseGame/game/tools/worldEditor/gui/WorldEditorTreeWindow.ed.gui +++ b/Templates/BaseGame/game/tools/worldEditor/gui/WorldEditorTreeWindow.ed.gui @@ -131,7 +131,7 @@ $guiContent = new GuiControl() { HorizSizing = "width"; VertSizing = "height"; Position = "5 29"; - Extent = "300 271"; + Extent = "290 240"; MinExtent = "8 8"; canSave = "1"; Visible = "1"; From 593d0ef9f0633ec0062679bb6964c77df92d4339 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 18 Nov 2023 19:05:12 -0600 Subject: [PATCH 041/122] lighting corrections: 1) fix spotlight shadow casting. we weren't properly accounting for reversedepthbuffer there 2) fix mismatched variables in forward lit spotlight param array 3) use disney diffuse in the generalized brdf, and apply it for spotlights after angular attenuation, not before 4) .provide a generailze luxTargMultiplier approach for spot and point lights (as well as future growth areas) so that range, angle ect can impact the brightness variable sent to a given light source shader without perpixel overhead 5) for spotlightParams in particular, use sizeof(point4f) for the alignedarray untill that's properly reviewed/revised 6) narrow attenuation slightly based on a given lights dot product to prevent tool vs outcome leakage, plus provide safeties for when a given spotslight's inner and outter angles match identially --- .../advanced/advancedLightBinManager.cpp | 11 ++- Engine/source/lighting/lightManager.cpp | 87 ++++++++++--------- .../shadowMap/singleLightShadowMap.cpp | 3 +- .../game/core/rendering/shaders/brdf.hlsl | 10 +++ .../game/core/rendering/shaders/gl/brdf.glsl | 11 +++ .../core/rendering/shaders/gl/lighting.glsl | 28 +++--- .../game/core/rendering/shaders/lighting.hlsl | 33 ++++--- .../lighting/advanced/gl/spotLightP.glsl | 6 +- .../shaders/lighting/advanced/spotLightP.hlsl | 6 +- 9 files changed, 117 insertions(+), 78 deletions(-) diff --git a/Engine/source/lighting/advanced/advancedLightBinManager.cpp b/Engine/source/lighting/advanced/advancedLightBinManager.cpp index fd8cd6dc4..c857933dd 100644 --- a/Engine/source/lighting/advanced/advancedLightBinManager.cpp +++ b/Engine/source/lighting/advanced/advancedLightBinManager.cpp @@ -790,7 +790,7 @@ void AdvancedLightBinManager::LightMaterialInfo::setLightParameters( const Light MaterialParameters *matParams = matInstance->getMaterialParameters(); matParams->setSafe( lightColor, lightInfo->getColor() ); - matParams->setSafe(lightBrightness, lightInfo->getBrightness() * lightInfo->getFadeAmount()); + F32 luxTargMultiplier = 1; switch( lightInfo->getType() ) { @@ -804,10 +804,10 @@ void AdvancedLightBinManager::LightMaterialInfo::setLightParameters( const Light case LightInfo::Spot: { const F32 outerCone = lightInfo->getOuterConeAngle(); - const F32 innerCone = getMin(lightInfo->getInnerConeAngle(), outerCone); + const F32 innerCone = getMin(lightInfo->getInnerConeAngle(), outerCone-0.0001f); const F32 outerCos = mCos(mDegToRad(outerCone / 2.0f)); const F32 innerCos = mCos(mDegToRad(innerCone / 2.0f)); - Point2F spotParams(outerCos,innerCos - outerCos); + Point2F spotParams(outerCos,mMax(innerCos - outerCos,0.001f)); matParams->setSafe( lightSpotParams, spotParams ); matParams->setSafe( lightDirection, lightInfo->getDirection()); @@ -817,6 +817,9 @@ void AdvancedLightBinManager::LightMaterialInfo::setLightParameters( const Light const F32 invSqrRadius = 1.0f / mSquared(radius); matParams->setSafe(lightRange, radius); matParams->setSafe(lightInvSqrRange, invSqrRadius); + + F32 concentration = 360.0f/ outerCone; + luxTargMultiplier = radius * concentration; } break; @@ -828,6 +831,7 @@ void AdvancedLightBinManager::LightMaterialInfo::setLightParameters( const Light const F32 invSqrRadius = 1.0f / (radius * radius); matParams->setSafe( lightRange, radius); matParams->setSafe( lightInvSqrRange, invSqrRadius); + luxTargMultiplier =radius; } break; @@ -835,6 +839,7 @@ void AdvancedLightBinManager::LightMaterialInfo::setLightParameters( const Light AssertFatal( false, "Bad light type!" ); break; } + matParams->setSafe(lightBrightness, lightInfo->getBrightness()* lightInfo->getFadeAmount() * luxTargMultiplier); } bool LightMatInstance::setupPass( SceneRenderState *state, const SceneData &sgData ) diff --git a/Engine/source/lighting/lightManager.cpp b/Engine/source/lighting/lightManager.cpp index d0652a62f..ac331ba4d 100644 --- a/Engine/source/lighting/lightManager.cpp +++ b/Engine/source/lighting/lightManager.cpp @@ -333,7 +333,7 @@ void LightManager::_update4LightConsts( const SceneData &sgData, static AlignedArray lightSpotDirs(MAX_FORWARD_LIGHTS, sizeof(Point4F)); static AlignedArray lightColors(MAX_FORWARD_LIGHTS, sizeof(Point4F)); static AlignedArray lightConfigData(MAX_FORWARD_LIGHTS, sizeof(Point4F)); //type, brightness, range, invSqrRange : rgba - static AlignedArray lightSpotParams(MAX_FORWARD_LIGHTS, sizeof(Point2F)); + static AlignedArray lightSpotParams(MAX_FORWARD_LIGHTS, sizeof(Point4F)); dMemset(lightPositions.getBuffer(), 0, lightPositions.getBufferSize()); dMemset(lightSpotDirs.getBuffer(), 0, lightSpotDirs.getBufferSize()); @@ -352,11 +352,12 @@ void LightManager::_update4LightConsts( const SceneData &sgData, vectorLightDirection = Point4F::Zero; vectorLightColor = Point4F::Zero; vectorLightAmbientColor = Point4F::Zero; - + F32 luxTargMultiplier[MAX_FORWARD_LIGHTS]; // Gather the data for the first 4 lights. const LightInfo* light; for (U32 i = 0; i < MAX_FORWARD_LIGHTS; i++) { + luxTargMultiplier[i] = 1.0; light = sgData.lights[i]; if (!light) break; @@ -371,48 +372,52 @@ void LightManager::_update4LightConsts( const SceneData &sgData, vectorLightColor = Point4F(light->getColor()); vectorLightAmbientColor = Point4F(light->getAmbient()); hasVectorLight = 1; - continue; } - - // The light positions and spot directions are - // in SoA order to make optimal use of the GPU. - const Point3F& lightPos = light->getPosition(); - lightPositions[i].x = lightPos.x; - lightPositions[i].y = lightPos.y; - lightPositions[i].z = lightPos.z; - lightPositions[i].w = 0; - - const VectorF& lightDir = light->getDirection(); - lightSpotDirs[i].x = lightDir.x; - lightSpotDirs[i].y = lightDir.y; - lightSpotDirs[i].z = lightDir.z; - lightSpotDirs[i].w = 0; - - lightColors[i] = Point4F(light->getColor()); - - if (light->getType() == LightInfo::Point) + else { - lightConfigData[i].x = 0; + // The light positions and spot directions are + // in SoA order to make optimal use of the GPU. + const Point3F& lightPos = light->getPosition(); + lightPositions[i].x = lightPos.x; + lightPositions[i].y = lightPos.y; + lightPositions[i].z = lightPos.z; + lightPositions[i].w = 0; + + lightColors[i] = Point4F(light->getColor()); + + F32 range = light->getRange().x; + lightConfigData[i].z = range; + + if (light->getType() == LightInfo::Point) + { + lightConfigData[i].x = 0; + luxTargMultiplier[i] = range; + } + else if (light->getType() == LightInfo::Spot) + { + const VectorF& lightDir = light->getDirection(); + lightSpotDirs[i].x = lightDir.x; + lightSpotDirs[i].y = lightDir.y; + lightSpotDirs[i].z = lightDir.z; + lightSpotDirs[i].w = 0; + + lightConfigData[i].x = 1; + + const F32 outerCone = light->getOuterConeAngle(); + const F32 innerCone = getMin(light->getInnerConeAngle(), outerCone - 0.0001f); + const F32 outerCos = mCos(mDegToRad(outerCone / 2.0f)); + const F32 innerCos = mCos(mDegToRad(innerCone / 2.0f)); + Point2F spotParams(outerCos, mMax(innerCos - outerCos, 0.001f)); + + lightSpotParams[i].x = spotParams.x; + lightSpotParams[i].y = spotParams.y; + F32 concentration = 360.0f / outerCone; + luxTargMultiplier[i] = range * concentration; + } + + lightConfigData[i].y = light->getBrightness() * luxTargMultiplier[i]; + lightConfigData[i].w = 1.0f / (range * range); } - else if (light->getType() == LightInfo::Spot) - { - lightConfigData[i].x = 1; - - const F32 outerCone = light->getOuterConeAngle(); - const F32 innerCone = getMin(light->getInnerConeAngle(), outerCone); - const F32 outerCos = mCos(mDegToRad(outerCone / 2.0f)); - const F32 innerCos = mCos(mDegToRad(innerCone / 2.0f)); - Point2F spotParams(outerCos, innerCos - outerCos); - - lightSpotParams[i].x = spotParams.x; - lightSpotParams[i].y = spotParams.y; - } - - lightConfigData[i].y = light->getBrightness(); - - F32 range = light->getRange().x; - lightConfigData[i].z = range; - lightConfigData[i].w = 1.0f / (range * range); } shaderConsts->setSafe(lightPositionSC, lightPositions); diff --git a/Engine/source/lighting/shadowMap/singleLightShadowMap.cpp b/Engine/source/lighting/shadowMap/singleLightShadowMap.cpp index cd4724838..4f246bec8 100644 --- a/Engine/source/lighting/shadowMap/singleLightShadowMap.cpp +++ b/Engine/source/lighting/shadowMap/singleLightShadowMap.cpp @@ -71,7 +71,8 @@ void SingleLightShadowMap::_render( RenderPassManager* renderPass, lightMatrix.inverse(); GFX->setWorldMatrix(lightMatrix); - const MatrixF& lightProj = GFX->getProjectionMatrix(); + MatrixF lightProj = GFX->getProjectionMatrix(); + lightProj.reverseProjection(); mWorldToLightProj = lightProj * lightMatrix; // Render the shadowmap! diff --git a/Templates/BaseGame/game/core/rendering/shaders/brdf.hlsl b/Templates/BaseGame/game/core/rendering/shaders/brdf.hlsl index e18d1d0be..e36762e6e 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/brdf.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/brdf.hlsl @@ -73,4 +73,14 @@ float D_GGX(float NdotH, float alphaRoughnessSq) return alphaRoughnessSq / (M_PI_F * f * f); } +float3 Fr_DisneyDiffuse(float3 F0, float NdotV, float NdotL, float LdotH, float linearRoughness) +{ + float energyBias = lerp (0 , 0.5 , linearRoughness ); + float energyFactor = lerp (1.0 , 1.0 / 1.51 , linearRoughness ); + float fd90 = energyBias + 2.0 * LdotH * LdotH * linearRoughness ; + float3 lightScatter = F_Schlick( F0 , fd90 , NdotL ); + float3 viewScatter = F_Schlick(F0 , fd90 , NdotV ); + + return lightScatter * viewScatter * energyFactor ; +} #endif diff --git a/Templates/BaseGame/game/core/rendering/shaders/gl/brdf.glsl b/Templates/BaseGame/game/core/rendering/shaders/gl/brdf.glsl index dbe4d50ac..9d76de406 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/gl/brdf.glsl +++ b/Templates/BaseGame/game/core/rendering/shaders/gl/brdf.glsl @@ -67,4 +67,15 @@ float D_GGX(float NdotH, float alphaRoughnessSq) return alphaRoughnessSq / (M_PI_F * f * f); } +vec3 Fr_DisneyDiffuse(vec3 F0, float NdotV, float NdotL, float LdotH, float linearRoughness) +{ + float energyBias = lerp(0 , 0.5 , linearRoughness ); + float energyFactor = lerp(1.0 , 1.0 / 1.51 , linearRoughness ); + float fd90 = energyBias + 2.0 * LdotH * LdotH * linearRoughness ; + vec3 lightScatter = F_Schlick( F0 , fd90 , NdotL ); + vec3 viewScatter = F_Schlick(F0 , fd90 , NdotV ); + + return lightScatter * viewScatter * energyFactor ; +} + #endif diff --git a/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl b/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl index a903c0896..cc66cb500 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl +++ b/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl @@ -216,8 +216,8 @@ float getDistanceAtt( vec3 unormalizedLightVector , float invSqrAttRadius ) float getSpotAngleAtt( vec3 normalizedLightVector , vec3 lightDir , vec2 lightSpotParams ) { - float cd = dot ( lightDir , normalizedLightVector ); - float attenuation = saturate ( ( cd - lightSpotParams.x ) / lightSpotParams.y ); + float cd = max(dot( lightDir , normalizedLightVector ),0.0); + float attenuation = saturate ( ( cd - lightSpotParams.x/(cd*1.001) ) / lightSpotParams.y ); // smooth the transition return sqr(attenuation); } @@ -225,7 +225,7 @@ float getDistanceAtt( vec3 unormalizedLightVector , float invSqrAttRadius ) vec3 evaluateStandardBRDF(Surface surface, SurfaceToLight surfaceToLight) { //lambert diffuse - vec3 Fd = surface.albedo.rgb * M_1OVER_PI_F; + vec3 Fd = Fr_DisneyDiffuse(surface.f0, surface.NdotV, surfaceToLight.NdotL, surfaceToLight.NdotH, surface.linearRoughness); //GGX specular vec3 F = F_Schlick(surface.f0, surface.f90, surfaceToLight.HdotV); @@ -236,7 +236,7 @@ vec3 evaluateStandardBRDF(Surface surface, SurfaceToLight surfaceToLight) #if CAPTURING == 1 return saturate(mix(Fd + Fr,surface.f0,surface.metalness)); #else - return saturate(Fd + Fr); + return Fd + Fr; #endif } @@ -254,6 +254,15 @@ vec3 getPunctualLight(Surface surface, SurfaceToLight surfaceToLight, vec3 light return evaluateStandardBRDF(surface,surfaceToLight) * factor; } +vec3 getSpotlight(Surface surface, SurfaceToLight surfaceToLight, vec3 lightColor, float lightIntensity, float radius, vec3 lightDir, vec2 lightSpotParams, float shadow) +{ + float attenuation = 1.0f; + attenuation *= getDistanceAtt(surfaceToLight.Lu, radius); + attenuation *= getSpotAngleAtt(-surfaceToLight.L, lightDir, lightSpotParams.xy); + vec3 factor = lightColor * max(surfaceToLight.NdotL* shadow * lightIntensity * attenuation, 0.0f) ; + return evaluateStandardBRDF(surface,surfaceToLight) * factor; +} + float computeSpecOcclusion( float NdotV , float AO , float roughness ) { return saturate (pow( abs(NdotV + AO) , exp2 ( -16.0f * roughness - 1.0f )) - 1.0f + AO ); @@ -270,7 +279,7 @@ vec4 compute4Lights( Surface surface, vec4 inLightConfigData[4], vec4 inLightColor[4], vec4 inLightSpotDir[4], - vec2 lightSpotParams[4], + vec2 inlightSpotParams[4], int hasVectorLight, vec4 vectorLightDirection, vec4 vectorLightingColor, @@ -305,13 +314,10 @@ vec4 compute4Lights( Surface surface, //get punctual light contribution lighting = getPunctualLight(surface, surfaceToLight, lightCol, lightBrightness, lightInvSqrRange, shadowed); } - else //spot + else if(inLightConfigData[i].x == 1) //spot { - - //get Punctual light contribution - lighting = getPunctualLight(surface, surfaceToLight, lightCol, lightBrightness, lightInvSqrRange, shadowed); - //get spot angle attenuation - lighting *= getSpotAngleAtt(-surfaceToLight.L, inLightSpotDir[i].xyz, lightSpotParams[i].xy ); + //get spot light contribution + lighting = getSpotlight(surface, surfaceToLight, lightCol, lightBrightness, lightInvSqrRange, inLightSpotDir[i].xyz, inlightSpotParams[i], shadowed); } } finalLighting += lighting; diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl index 0a0f1b967..9f5659ae1 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl @@ -217,16 +217,16 @@ float getDistanceAtt( float3 unormalizedLightVector , float invSqrAttRadius ) float getSpotAngleAtt( float3 normalizedLightVector , float3 lightDir , float2 lightSpotParams ) { - float cd = dot ( lightDir , normalizedLightVector ); - float attenuation = saturate ( ( cd - lightSpotParams.x ) / lightSpotParams.y ); + float cd = max(dot ( lightDir , normalizedLightVector ),0.0); + float attenuation = saturate(((cd - lightSpotParams.x/(cd*1.001))/lightSpotParams.y)); // smooth the transition return sqr(attenuation); } float3 evaluateStandardBRDF(Surface surface, SurfaceToLight surfaceToLight) { - //lambert diffuse - float3 Fd = surface.albedo.rgb * M_1OVER_PI_F; + //disney diffuse + float3 Fd = Fr_DisneyDiffuse(surface.f0, surface.NdotV, surfaceToLight.NdotL, surfaceToLight.NdotH, surface.linearRoughness); //GGX specular float3 F = F_Schlick(surface.f0, surface.f90, surfaceToLight.HdotV); @@ -237,7 +237,7 @@ float3 evaluateStandardBRDF(Surface surface, SurfaceToLight surfaceToLight) #if CAPTURING == 1 return saturate(lerp(Fd + Fr,surface.f0,surface.metalness)); #else - return saturate(Fd + Fr); + return Fd + Fr; #endif } @@ -255,6 +255,14 @@ float3 getPunctualLight(Surface surface, SurfaceToLight surfaceToLight, float3 l return evaluateStandardBRDF(surface,surfaceToLight) * factor; } +float3 getSpotlight(Surface surface, SurfaceToLight surfaceToLight, float3 lightColor, float lightIntensity, float radius, float3 lightDir, float2 lightSpotParams, float shadow) +{ + float attenuation = 1.0f; + attenuation *= getDistanceAtt(surfaceToLight.Lu, radius); + attenuation *= getSpotAngleAtt(-surfaceToLight.L, lightDir, lightSpotParams.xy); + float3 factor = lightColor * max(surfaceToLight.NdotL* shadow * lightIntensity * attenuation, 0.0f) ; + return evaluateStandardBRDF(surface,surfaceToLight) * factor; +} float computeSpecOcclusion( float NdotV , float AO , float roughness ) { return saturate (pow( abs(NdotV + AO) , exp2 ( -16.0f * roughness - 1.0f )) - 1.0f + AO ); @@ -271,7 +279,7 @@ float4 compute4Lights( Surface surface, float4 inLightConfigData[4], float4 inLightColor[4], float4 inLightSpotDir[4], - float2 lightSpotParams[4], + float2 inlightSpotParams[4], int hasVectorLight, float4 vectorLightDirection, float4 vectorLightingColor, @@ -296,7 +304,7 @@ float4 compute4Lights( Surface surface, float lightBrightness = inLightConfigData[i].y; float lightInvSqrRange= inLightConfigData[i].a; - float3 lighting = 0.0.xxx; + float3 lighting = float3(0.0,0.0,0.0); [branch] if(dist < lightRange) @@ -307,13 +315,10 @@ float4 compute4Lights( Surface surface, //get punctual light contribution lighting = getPunctualLight(surface, surfaceToLight, lightCol, lightBrightness, lightInvSqrRange, shadowed); } - else //spot + else if(inLightConfigData[i].x == 1) //spot { - - //get Punctual light contribution - lighting = getPunctualLight(surface, surfaceToLight, lightCol, lightBrightness, lightInvSqrRange, shadowed); - //get spot angle attenuation - lighting *= getSpotAngleAtt(-surfaceToLight.L, inLightSpotDir[i].xyz, lightSpotParams[i].xy ); + //get spot light contribution + lighting = getSpotlight(surface, surfaceToLight, lightCol, lightBrightness, lightInvSqrRange, inLightSpotDir[i].xyz, inlightSpotParams[i], shadowed); } } finalLighting += lighting; @@ -321,7 +326,7 @@ float4 compute4Lights( Surface surface, //Vector light [branch] - if(hasVectorLight) + if(hasVectorLight == 1) { SurfaceToLight surfaceToVecLight = createSurfaceToLight(surface, -vectorLightDirection.xyz); diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/gl/spotLightP.glsl b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/gl/spotLightP.glsl index 1a7c42822..42654c532 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/gl/spotLightP.glsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/gl/spotLightP.glsl @@ -154,10 +154,8 @@ void main() return; #endif - //get Punctual light contribution - lighting = getPunctualLight(surface, surfaceToLight, lightCol, lightBrightness, lightInvSqrRange, shadow); - //get spot angle attenuation - lighting *= getSpotAngleAtt(-surfaceToLight.L, lightDirection, lightSpotParams ); + //get spot light contribution + lighting = getSpotlight(surface, surfaceToLight, lightCol, lightBrightness, lightInvSqrRange, lightDirection, lightSpotParams, shadow); } OUT_col = vec4(lighting, 0); diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/spotLightP.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/spotLightP.hlsl index a78ed019b..d4ef36a36 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/spotLightP.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting/advanced/spotLightP.hlsl @@ -151,10 +151,8 @@ float4 main( ConvexConnectP IN ) : SV_TARGET return final; #endif - //get Punctual light contribution - lighting = getPunctualLight(surface, surfaceToLight, lightCol, lightBrightness, lightInvSqrRange, shadow); - //get spot angle attenuation - lighting *= getSpotAngleAtt(-surfaceToLight.L, lightDirection, lightSpotParams ); + //get spot light contribution + lighting = getSpotlight(surface, surfaceToLight, lightCol, lightBrightness, lightInvSqrRange, lightDirection, lightSpotParams, shadow); } return float4(lighting, 0); From 456334fc559a3911b4091b95d238eee9ec895132 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 20 Nov 2023 15:26:30 -0600 Subject: [PATCH 042/122] ambient scales ibl so default to 1,1,1, not 1/3rds --- Engine/source/environment/sun.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/environment/sun.cpp b/Engine/source/environment/sun.cpp index 4237a34f7..c7ffefa05 100644 --- a/Engine/source/environment/sun.cpp +++ b/Engine/source/environment/sun.cpp @@ -61,7 +61,7 @@ Sun::Sun() mTypeMask = EnvironmentObjectType | LightObjectType | StaticObjectType; mLightColor.set(0.7f, 0.7f, 0.7f); - mLightAmbient.set(0.3f, 0.3f, 0.3f); + mLightAmbient.set(1.0f, 1.0f, 1.0f); mBrightness = 1.0f; mSunAzimuth = 0.0f; mSunElevation = 35.0f; From 431cffac11a2f31609ef9d9be4b6df77d30b43a5 Mon Sep 17 00:00:00 2001 From: Areloch Date: Thu, 23 Nov 2023 12:46:55 -0600 Subject: [PATCH 043/122] Adds the D3DCompiler_47.dll to ensure it's packed and ready for projects when publishing --- .gitignore | 1 + Templates/BaseGame/CMakeLists.txt | 11 +++++++++-- Templates/BaseGame/game/D3DCompiler_47.dll | Bin 0 -> 3833344 bytes 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 Templates/BaseGame/game/D3DCompiler_47.dll diff --git a/.gitignore b/.gitignore index c968f47c7..e4c55cefb 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,7 @@ local.properties .builds *.dotCover *.dll +!d3dcompiler_47.dll *.lib *.exp *.exe diff --git a/Templates/BaseGame/CMakeLists.txt b/Templates/BaseGame/CMakeLists.txt index fb5fea3e1..67414265c 100644 --- a/Templates/BaseGame/CMakeLists.txt +++ b/Templates/BaseGame/CMakeLists.txt @@ -4,8 +4,15 @@ foreach(TEMPLATE_FILE ${TEMPLATE_FILES}) endforeach() # Perform installation minus scripts -file(COPY "game" "source" DESTINATION "${TORQUE_APP_ROOT_DIRECTORY}" PATTERN "*.tscript" EXCLUDE PATTERN - PATTERN "*.in" EXCLUDE PATTERN) +if(WIN32) + file(COPY "game" "source" DESTINATION "${TORQUE_APP_ROOT_DIRECTORY}" PATTERN "*.tscript" EXCLUDE PATTERN + PATTERN "*.in" EXCLUDE PATTERN) +else() + file(COPY "game" "source" DESTINATION "${TORQUE_APP_ROOT_DIRECTORY}" PATTERN "*.tscript" EXCLUDE PATTERN + PATTERN "*.in" EXCLUDE PATTERN + PATTERN "*.dll" EXLCUDE PATTERN) +endif(WIN32) + # Enumerate scripts and install with extension file(GLOB_RECURSE SCRIPT_FILES "game/*.tscript") foreach(ITEM ${SCRIPT_FILES}) diff --git a/Templates/BaseGame/game/D3DCompiler_47.dll b/Templates/BaseGame/game/D3DCompiler_47.dll new file mode 100644 index 0000000000000000000000000000000000000000..e61c5aea85f30489ab3d89ef9a884737e80b529e GIT binary patch literal 3833344 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%0DN>LbJ22&T<9;5!KH zHyX_!Mziz_m>gjf7u=e+!q_aCj}(oF;U)|CwLI{9);9YC&@-FBUb-(=8ehzGfhP(B zz8R4Evu4O(;PcM+F`v4-9u|&o+g}+tr|V}L@CNOT*@%BN(hq2RLON7mgI1~ulOSYE z+&37SgQB;f=uj5rO^nbqJ+NYcdiIBU-8VWsNZD2xS2t(XeWLc!7tBmdOSu|mef%)i zI_;M+t~>M!-sgZ`C}epK9NpFE$JCbB+QK`6P_KBQBt%!kAdtQC*nSABo8xwTwT?5G z3iEKF(Q6sn;`KDsJ?b^0VKw1hk9UO17Z1N#cARiG=G4_>zV+qq0bV}CFVjw~#XN?z zbGq?YgV*jla|D_zJBgzS#0leKW;~Dd^)su>lTci$H~2d#LIXD~X-ynW8AtQrsx^tKyo~k-7Nj@)p@sz;XilR(sC#HSdyi0!DCFfe3j0Kh0 zaTGml8Gutu1h&qYRX6^CSVR&;C0?3VKJ6zvzZq6UU=PdGS9e&Q0 z$#vvem?bWWp6Tq=w2*U(UmB&OY#S$!ez551h-fLA=1!deI?Q98_UksgFwCZwuc^zC zVqvnr;q0&&C=p8G`zs+^$YUf@4kyy@&~uwDy}dHQ-|m=DM_xy}fO^bVgaGm^i7VBi?c$W%e5(q`pTgcGxSh)3-dU;BBC70S|6 zp`zG_BEC~3ZzS(yW`D?Zf7`Of88&9+p$Nx@bG4r^>V0jX%cmoPY_x@6O^yw?m0|0( zev4Ik9xYs47UF)D>vDZVqlU%}IrLNwjc4nc5z5j$i3O?M1q=Z47%J5l(EF

7tdsKQ-vaMPi1K=QwA_A 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

l9W9-k}C1 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