diff --git a/.gitignore b/.gitignore index 333e6dfb634..cd98d4609c8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,74 +1,76 @@ -# Global rules # -################ - -# Build directories -build -com -webots_catkin_ws - -# Compiled source -*.exe -*.o -*.d -*.class -*.so -*.dylib -*.dll -*.lib -*.a -*.cof -*.hex -*.pyc -*.gch - -# Packages -*.7z -*.bz2 -*.dmg -*.gz -*.iso -*.jar -*.rar -*.tar -*.xz -*.zip - -# OS generated files -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -.directory -ehthumbs.db -Thumbs.db -*.swp - -# Text editor backup files -*~ - -# Log files -*.log - -# Blender cache -*.blend1 - -# Webots generated files -*.cache - -# IDE files -.vscode - -# Local rules # -############### - -/.clang-format -/msys64 -/webots -/webots-controller -/webots.lnk -/webots_debug_output.txt -/util - -# world thumbnail files -.*.jpg +# Global rules # +################ + +# Build directories +build +com +webots_catkin_ws + +# Compiled source +*.exe +*.o +*.d +*.class +*.so +*.dylib +*.dll +*.lib +*.a +*.cof +*.hex +*.pyc +*.gch + +# Packages +*.7z +*.bz2 +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.xz +*.zip + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +.directory +ehthumbs.db +Thumbs.db +*.swp + +# Text editor backup files +*~ + +# Log files +*.log + +# Blender cache +*.blend1 + +# Webots generated files +*.cache + +# IDE files +.vscode + +# Local rules # +############### + +/.clang-format +/msys64 +/webots +/webots-controller +/webots.lnk +/webots_debug_output.txt +/util + +# world thumbnail files +.*.jpg +.aider* +.env diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000000..c9541659d59 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,89 @@ +cmake_minimum_required(VERSION 3.10) +project(webots) + +# Handle dynamic loading library +if(WIN32) + find_package(dlfcn-win32 REQUIRED) + set(CMAKE_DL_LIBS dlfcn-win32::dl) +else() + set(CMAKE_DL_LIBS ${CMAKE_DL_LIBS}) +endif() + +# Installation configuration +if(NOT CMAKE_INSTALL_PREFIX) + # Check if we can write to /usr/local + execute_process(COMMAND test -w /usr/local RESULT_VARIABLE can_write_usr_local) + if(can_write_usr_local EQUAL 0) + set(CMAKE_INSTALL_PREFIX "/usr/local" CACHE PATH "Installation prefix" FORCE) + else() + # Fallback to ~/.local + set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/.local" CACHE PATH "Installation prefix" FORCE) + endif() +endif() + +message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}") + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) +# Set C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Find Qt6 components +find_package(Qt6 REQUIRED COMPONENTS + Core + Gui + OpenGL + Qml + QmlIntegration + PrintSupport + Widgets + Xml + OpenGLWidgets + WebSockets + Test + Network +) +file(READ ${CMAKE_SOURCE_DIR}/resources/version.txt WEBOTS_VERSION) # Path is correct - ${CMAKE_SOURCE_DIR} handles OS-specific path separators +string(STRIP ${WEBOTS_VERSION} WEBOTS_VERSION) +# Find other required packages +find_package(OpenAL REQUIRED) +find_package(OpenGL REQUIRED) +find_package(PkgConfig REQUIRED) +find_package(Freetype REQUIRED) +find_package(assimp REQUIRED) +pkg_check_modules(OPENVR REQUIRED IMPORTED_TARGET openvr) +# Include external dependencies +include(src/webots/cmake/ExternalDependencies.cmake) + +# Add subdirectories +# Installation rules +install(DIRECTORY resources/ + DESTINATION ${CMAKE_INSTALL_PREFIX}/share/webots/resources + PATTERN ".gitkeep" EXCLUDE + PATTERN "*.user" EXCLUDE + PATTERN "*.lock" EXCLUDE +) + +install(DIRECTORY projects/ + DESTINATION ${CMAKE_INSTALL_PREFIX}/share/webots/projects + PATTERN ".gitkeep" EXCLUDE + PATTERN "*.user" EXCLUDE + PATTERN "*.lock" EXCLUDE +) + +# Add subdirectories +add_subdirectory(src) + +# Install main executable +install(TARGETS webots + RUNTIME DESTINATION bin + BUNDLE DESTINATION . +) + +# Install libraries +install(TARGETS Controller CppController + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) diff --git a/projects/CMakeLists.txt b/projects/CMakeLists.txt new file mode 100644 index 00000000000..d4673bdac50 --- /dev/null +++ b/projects/CMakeLists.txt @@ -0,0 +1,28 @@ +# CMakeLists.txt for the 'projects' directory +# This file is part of the effort to port the Webots build system from Make to CMake. + +cmake_minimum_required(VERSION 3.10) + +# Assuming CMAKE_SOURCE_DIR points to the root of the Webots project. +# This variable can be used by sub-projects if they need to reference the Webots root. +set(WEBOTS_HOME ${CMAKE_SOURCE_DIR} CACHE INTERNAL "Path to Webots root directory") + +message(STATUS "Configuring Webots Projects from: ${CMAKE_CURRENT_SOURCE_DIR}") +message(STATUS "WEBOTS_HOME for projects set to: ${WEBOTS_HOME}") + +# Add subdirectories. Each of these will require its own CMakeLists.txt +# to define how its contents are built and installed. +add_subdirectory(languages) +add_subdirectory(default) +add_subdirectory(objects) +add_subdirectory(robots) +add_subdirectory(samples) +add_subdirectory(vehicles) +add_subdirectory(humans) + +# Install guided_tour.txt to the correct projects directory in the install location +install(FILES guided_tour.txt DESTINATION share/webots/projects) + +# The Makefile targets (release, profile, debug, clean, cleanse) +# are generally handled by CMake's built-in build type configurations (CMAKE_BUILD_TYPE) +# and the standard 'clean' target generated by CMake. \ No newline at end of file diff --git a/projects/default/CMakeLists.txt b/projects/default/CMakeLists.txt new file mode 100644 index 00000000000..c3376fd9ca3 --- /dev/null +++ b/projects/default/CMakeLists.txt @@ -0,0 +1,9 @@ +# Placeholder CMakeLists.txt for projects/default +# TODO: Convert the logic from projects/default/Makefile to CMake. + +cmake_minimum_required(VERSION 3.10) +project(webots_project_default) + +message(STATUS "Configuring projects/default") + +# Add build targets, sources, and installation rules here. \ No newline at end of file diff --git a/projects/humans/CMakeLists.txt b/projects/humans/CMakeLists.txt new file mode 100644 index 00000000000..be3a5b9163a --- /dev/null +++ b/projects/humans/CMakeLists.txt @@ -0,0 +1,9 @@ +# Placeholder CMakeLists.txt for projects/humans +# TODO: Convert the logic from projects/humans/Makefile to CMake. + +cmake_minimum_required(VERSION 3.10) +project(webots_project_humans) + +message(STATUS "Configuring projects/humans") + +# Add build targets, sources, and installation rules here. \ No newline at end of file diff --git a/projects/languages/CMakeLists.txt b/projects/languages/CMakeLists.txt new file mode 100644 index 00000000000..86e41c8dac1 --- /dev/null +++ b/projects/languages/CMakeLists.txt @@ -0,0 +1,13 @@ +# Placeholder CMakeLists.txt for projects/languages +# TODO: Convert the logic from projects/languages/Makefile to CMake. + +cmake_minimum_required(VERSION 3.10) +project(webots_project_languages) + +message(STATUS "Configuring projects/languages") + +# Add build targets, sources, and installation rules here. +# For example: +# add_library(my_language_library my_source.c) +# target_link_libraries(my_language_library PRIVATE ${WEBOTS_HOME}/lib/Controller.so) # Example dependency +# install(TARGETS my_language_library DESTINATION lib) \ No newline at end of file diff --git a/projects/objects/CMakeLists.txt b/projects/objects/CMakeLists.txt new file mode 100644 index 00000000000..e7db00b8084 --- /dev/null +++ b/projects/objects/CMakeLists.txt @@ -0,0 +1,9 @@ +# Placeholder CMakeLists.txt for projects/objects +# TODO: Convert the logic from projects/objects/Makefile to CMake. + +cmake_minimum_required(VERSION 3.10) +project(webots_project_objects) + +message(STATUS "Configuring projects/objects") + +# Add build targets, sources, and installation rules here. \ No newline at end of file diff --git a/projects/robots/CMakeLists.txt b/projects/robots/CMakeLists.txt new file mode 100644 index 00000000000..b65c60f09fd --- /dev/null +++ b/projects/robots/CMakeLists.txt @@ -0,0 +1,9 @@ +# Placeholder CMakeLists.txt for projects/robots +# TODO: Convert the logic from projects/robots/Makefile to CMake. + +cmake_minimum_required(VERSION 3.10) +project(webots_project_robots) + +message(STATUS "Configuring projects/robots") + +# Add build targets, sources, and installation rules here. \ No newline at end of file diff --git a/projects/samples/CMakeLists.txt b/projects/samples/CMakeLists.txt new file mode 100644 index 00000000000..d5da306163a --- /dev/null +++ b/projects/samples/CMakeLists.txt @@ -0,0 +1,9 @@ +# Placeholder CMakeLists.txt for projects/samples +# TODO: Convert the logic from projects/samples/Makefile to CMake. + +cmake_minimum_required(VERSION 3.10) +project(webots_project_samples) + +message(STATUS "Configuring projects/samples") + +# Add build targets, sources, and installation rules here. \ No newline at end of file diff --git a/projects/vehicles/CMakeLists.txt b/projects/vehicles/CMakeLists.txt new file mode 100644 index 00000000000..11456e653bd --- /dev/null +++ b/projects/vehicles/CMakeLists.txt @@ -0,0 +1,9 @@ +# Placeholder CMakeLists.txt for projects/vehicles +# TODO: Convert the logic from projects/vehicles/Makefile to CMake. + +cmake_minimum_required(VERSION 3.10) +project(webots_project_vehicles) + +message(STATUS "Configuring projects/vehicles") + +# Add build targets, sources, and installation rules here. \ No newline at end of file diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt new file mode 100644 index 00000000000..5835dc4e8f2 --- /dev/null +++ b/resources/CMakeLists.txt @@ -0,0 +1,25 @@ +# CMakeLists.txt for the 'resources' directory +# This file is part of the effort to port the Webots build system from Make to CMake. + +cmake_minimum_required(VERSION 3.10) + +message(STATUS "Configuring Webots Resources from: ${CMAKE_CURRENT_SOURCE_DIR}") + +# Add subdirectories. Each of these will require its own CMakeLists.txt +# to define how its contents are built/processed and installed. +add_subdirectory(translations) +add_subdirectory(projects) # This is resources/projects + +# Other files and directories in 'resources' (like fonts, icons, images, templates, etc.) +# are typically handled by 'install(DIRECTORY ...)' commands in the root CMakeLists.txt +# or directly here if specific processing is needed before installation. +# The existing root CMakeLists.txt already has: +# install(DIRECTORY resources/ DESTINATION ${CMAKE_INSTALL_PREFIX}/share/webots/resources ...) +# This will cover most static assets. If specific files from 'resources' need to be +# generated or processed by CMake, that logic would go here or in specific subdirectory CMakeLists.txt files. + +# The Makefile targets (release, debug, profile, clean, cleanse) +# are generally handled by CMake's built-in build type configurations (CMAKE_BUILD_TYPE) +# and the standard 'clean' target generated by CMake. +# Specific cleaning tasks like 'rm -rf sumo_exporter/*.pyc' would need +# to be translated into custom clean commands or handled by the build process of 'sumo_exporter' if it gets its own CMakeLists.txt. \ No newline at end of file diff --git a/resources/projects/CMakeLists.txt b/resources/projects/CMakeLists.txt new file mode 100644 index 00000000000..64d7ded4060 --- /dev/null +++ b/resources/projects/CMakeLists.txt @@ -0,0 +1,17 @@ +# Placeholder CMakeLists.txt for resources/projects +# TODO: Convert the logic from resources/projects/Makefile to CMake. +# This will involve adding subdirectories for controllers, libraries, plugins, worlds. + +cmake_minimum_required(VERSION 3.10) +project(webots_resources_projects) + +message(STATUS "Configuring resources/projects") + +# Add subdirectories. Each of these will require its own CMakeLists.txt. +add_subdirectory(controllers) +add_subdirectory(libraries) +add_subdirectory(plugins) +add_subdirectory(worlds) + +# Add build targets, sources, and installation rules here as needed +# for any direct content of resources/projects, if any. \ No newline at end of file diff --git a/resources/projects/controllers/CMakeLists.txt b/resources/projects/controllers/CMakeLists.txt new file mode 100644 index 00000000000..a3039340fa7 --- /dev/null +++ b/resources/projects/controllers/CMakeLists.txt @@ -0,0 +1,10 @@ +# Placeholder CMakeLists.txt for resources/projects/controllers +# TODO: Convert the logic from the corresponding Makefile section to CMake. + +cmake_minimum_required(VERSION 3.10) +project(webots_resources_projects_controllers) + +message(STATUS "Configuring resources/projects/controllers") + +# Add build targets, sources, and installation rules for controllers here. +# This will likely involve iterating over controller subdirectories if they exist. \ No newline at end of file diff --git a/resources/projects/libraries/CMakeLists.txt b/resources/projects/libraries/CMakeLists.txt new file mode 100644 index 00000000000..ad3a53d50b2 --- /dev/null +++ b/resources/projects/libraries/CMakeLists.txt @@ -0,0 +1,9 @@ +# Placeholder CMakeLists.txt for resources/projects/libraries +# TODO: Convert the logic from the corresponding Makefile section to CMake. + +cmake_minimum_required(VERSION 3.10) +project(webots_resources_projects_libraries) + +message(STATUS "Configuring resources/projects/libraries") + +# Add build targets, sources, and installation rules for libraries here. \ No newline at end of file diff --git a/resources/projects/plugins/CMakeLists.txt b/resources/projects/plugins/CMakeLists.txt new file mode 100644 index 00000000000..6e1c0a2dba5 --- /dev/null +++ b/resources/projects/plugins/CMakeLists.txt @@ -0,0 +1,10 @@ +# Placeholder CMakeLists.txt for resources/projects/plugins +# TODO: Convert the logic from the corresponding Makefile section to CMake. + +cmake_minimum_required(VERSION 3.10) +project(webots_resources_projects_plugins) + +message(STATUS "Configuring resources/projects/plugins") + +# Add build targets, sources, and installation rules for plugins here. +# This might involve subdirectories like 'physics', 'remote_controls', 'robot_windows'. \ No newline at end of file diff --git a/resources/projects/worlds/CMakeLists.txt b/resources/projects/worlds/CMakeLists.txt new file mode 100644 index 00000000000..5e5b723fff4 --- /dev/null +++ b/resources/projects/worlds/CMakeLists.txt @@ -0,0 +1,13 @@ +# Placeholder CMakeLists.txt for resources/projects/worlds +# TODO: Convert the logic from the corresponding Makefile section to CMake. + +cmake_minimum_required(VERSION 3.10) +project(webots_resources_projects_worlds) + +message(STATUS "Configuring resources/projects/worlds") + +# Worlds are typically assets. This file might be used for: +# - Custom commands to process world files. +# - Installing world files to the correct location. +# Example: +# install(DIRECTORY . DESTINATION share/webots/projects/worlds FILES_MATCHING PATTERN "*.wbt") \ No newline at end of file diff --git a/resources/translations/CMakeLists.txt b/resources/translations/CMakeLists.txt new file mode 100644 index 00000000000..25644a15831 --- /dev/null +++ b/resources/translations/CMakeLists.txt @@ -0,0 +1,24 @@ +# Placeholder CMakeLists.txt for resources/translations +# TODO: Convert the logic from resources/translations/Makefile to CMake. +# This will likely involve using Qt's CMake functions for translations (e.g., qt_add_translation). + +cmake_minimum_required(VERSION 3.10) +project(webots_resources_translations) + +message(STATUS "Configuring resources/translations") + +# Example for Qt translations: +# find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core LinguistTools) +# +# set(TS_FILES +# qt_de.ts +# qt_es.ts +# # ... other .ts files +# wb_de.ts +# wb_es.ts +# # ... other .ts files +# ) +# +# qt_add_translations(QM_FILES ${TS_FILES}) +# +# install(FILES ${QM_FILES} DESTINATION share/webots/translations) # Adjust destination as needed \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000000..a442eb40c9d --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,51 @@ +# Find required packages + +# Add glad library +add_library(glad glad/glad.c) +target_include_directories(glad PUBLIC ${CMAKE_SOURCE_DIR}/include) +target_compile_options(glad PUBLIC -fPIC) + +add_custom_target(stb + COMMAND git submodule update --init --recursive src/stb + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Initializing stb submodule" +) + +# --- Handle stb_image.h --- + +# Check for system installation of stb +find_path(STB_INCLUDE_DIR + NAMES stb_image.h + PATHS + /usr/include/stb + /usr/local/include/stb + $ENV{MSYSTEM_PREFIX}/include/stb + $ENV{MSYSTEM_PREFIX}/local/include/stb +) + +# If not found, use the stb submodule +if(NOT STB_INCLUDE_DIR) + message(STATUS "System installation of stb not found. Using stb submodule.") + + # Initialize and update the stb submodule + execute_process( + COMMAND ${CMAKE_COMMAND} --build . --target stb + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + + # Set STB_INCLUDE_DIR to the submodule's include path + set(STB_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/stb) + + # Cache the variable so it persists across CMake runs + set(STB_INCLUDE_DIR ${STB_INCLUDE_DIR} CACHE PATH "Path to stb include directory") +endif() + +message(STATUS "STB_INCLUDE_DIR: ${STB_INCLUDE_DIR}") + +# Make STB_INCLUDE_DIR available to other CMake files (e.g., in src/controller/c) +set(STB_INCLUDE_DIR ${STB_INCLUDE_DIR} CACHE PATH "Path to stb include directory" FORCE) + +add_subdirectory(ode) +add_subdirectory(wren) +add_subdirectory(webots) +add_subdirectory(controller) diff --git a/src/controller/CMakeLists.txt b/src/controller/CMakeLists.txt new file mode 100644 index 00000000000..c3c7a1eaab4 --- /dev/null +++ b/src/controller/CMakeLists.txt @@ -0,0 +1,4 @@ +# Add subdirectories for C and C++ controllers +add_subdirectory(c) +add_subdirectory(cpp) +add_subdirectory(java) diff --git a/src/controller/c/CMakeLists.txt b/src/controller/c/CMakeLists.txt new file mode 100644 index 00000000000..4822400b906 --- /dev/null +++ b/src/controller/c/CMakeLists.txt @@ -0,0 +1,150 @@ +# Name of the library to be created +set(CONTROLLER_LIBRARY_NAME Controller) + +# Source files for the controller library +set(CONTROLLER_SOURCES + abstract_camera.c + accelerometer.c + altimeter.c + ansi_codes.c + base64.c + brake.c + camera.c + compass.c + connector.c + console.c + default_robot_window.c + device.c + display.c + distance_sensor.c + dynamic_library.c + emitter.c + file.c + g_image.c + g_pipe.c + gps.c + gyro.c + html_robot_window.c + image.c + inertial_unit.c + joystick.c + keyboard.c + led.c + lidar.c + light_sensor.c + microphone.c + motion.c + motor.c + mouse.c + node.c + pen.c + percent.c + position_sensor.c + radar.c + radio.c + range_finder.c + receiver.c + remote_control.c + request.c + robot.c + robot_window.c + scheduler.c + sha1.c + skin.c + speaker.c + string.c + supervisor.c + system.c + tcp_client.c + touch_sensor.c + vacuum_gripper.c +) + +# Header files for internal use within the library +set(CONTROLLER_HEADERS + abstract_camera.h + base64.h + camera_private.h + default_robot_window_private.h + device_private.h + display_private.h + dynamic_library.h + file.h + g_image.h + g_pipe.h + html_robot_window_private.h + image_private.h + joystick_private.h + keyboard_private.h + messages.h + motion_private.h + mouse_private.h + percent.h + remote_control_private.h + request.h + robot_private.h + robot_window_private.h + scheduler.h + sha1.h + supervisor_private.h + tcp_client.h +) + +# Create the shared library +add_library(${CONTROLLER_LIBRARY_NAME} SHARED ${CONTROLLER_SOURCES} ${CONTROLLER_HEADERS}) + +# Include directories for the controller library + target_include_directories(${CONTROLLER_LIBRARY_NAME} + PUBLIC + $ + $ + $ + $ + $ + $ +) +# Find libraries on Windows (using MSYS2) +if (WIN32) + # Directly use MSYSTEM_PREFIX for library paths + set(winsock_LIBRARY_PATH "$ENV{MSYSTEM_PREFIX}/lib/libws2_32.a") + + # Check if winsock library exists and link it + if (EXISTS ${winsock_LIBRARY_PATH}) + message(STATUS "winsock library found: ${winsock_LIBRARY_PATH}") + else() + message(FATAL_ERROR "winsock library not found at ${winsock_LIBRARY_PATH}. Please ensure it is installed in your MSYS2 environment.") + endif() + + # Link libraries if found + target_link_libraries(Controller + PRIVATE + ${winsock_LIBRARY_PATH} + ) +endif() + +target_compile_definitions(${CONTROLLER_LIBRARY_NAME} + PUBLIC + "LIBCONTROLLER_VERSION=\"${WEBOTS_VERSION}\"" +) +# Link necessary libraries to the controller +target_link_libraries(${CONTROLLER_LIBRARY_NAME} + PUBLIC + # glad + # ${Freetype_LIBRARIES} +) + +# Set properties for the library (adjust as needed) +#set_target_properties(${CONTROLLER_LIBRARY_NAME} PROPERTIES +# VERSION 0.1 +# SOVERSION 0 +# PUBLIC_HEADER "${CONTROLLER_HEADERS}" +#) + +# Install rules +install(TARGETS ${CONTROLLER_LIBRARY_NAME} + DESTINATION lib +) + +install(FILES ${CONTROLLER_HEADERS} + DESTINATION include +) diff --git a/src/controller/cpp/CMakeLists.txt b/src/controller/cpp/CMakeLists.txt new file mode 100644 index 00000000000..864dbb97fec --- /dev/null +++ b/src/controller/cpp/CMakeLists.txt @@ -0,0 +1,81 @@ +# src/controller/cpp/CMakeLists.txt + +# Name of the C++ controller library +set(CONTROLLER_CPP_LIBRARY_NAME CppController) + +# Source files for the C++ controller library +set(CONTROLLER_CPP_SOURCES + Accelerometer.cpp + Altimeter.cpp + Brake.cpp + Camera.cpp + Compass.cpp + Connector.cpp + Device.cpp + Display.cpp + DistanceSensor.cpp + Emitter.cpp + Field.cpp + GPS.cpp + Gyro.cpp + InertialUnit.cpp + Joystick.cpp + Keyboard.cpp + LED.cpp + Lidar.cpp + LightSensor.cpp + Motion.cpp + Motor.cpp + Mouse.cpp + Node.cpp + Pen.cpp + PositionSensor.cpp + Proto.cpp + Radar.cpp + RangeFinder.cpp + Receiver.cpp + Robot.cpp + Skin.cpp + Speaker.cpp + Supervisor.cpp + TouchSensor.cpp + VacuumGripper.cpp +) + +# Create the shared library +add_library(${CONTROLLER_CPP_LIBRARY_NAME} SHARED ${CONTROLLER_CPP_SOURCES}) + +# Include directories for the C++ controller library +target_include_directories(${CONTROLLER_CPP_LIBRARY_NAME} + PUBLIC + $ + $ + $ # added because some include files are not in the cpp folder + $ # added because some include files are not in the cpp folder# + PRIVATE + $ + $ +) +target_compile_definitions(${CONTROLLER_CPP_LIBRARY_NAME} + PUBLIC + "LIBCONTROLLER_VERSION=\"${WEBOTS_VERSION}\"" +) +# Link necessary libraries to the C++ controller +target_link_libraries(${CONTROLLER_CPP_LIBRARY_NAME} + PUBLIC + Controller +) + +# Set properties for the library (adjust as needed) +#set_target_properties(${CONTROLLER_CPP_LIBRARY_NAME} PROPERTIES +# VERSION 0.1 +# SOVERSION 0 +# PUBLIC_HEADER "${CONTROLLER_CPP_HEADERS}" # No need for PUBLIC_HEADER with C++ +#) + +# Install rules +install(TARGETS ${CONTROLLER_CPP_LIBRARY_NAME} + DESTINATION lib +) + +# No need to install header files separately in C++, they are typically included via target_include_directories diff --git a/src/controller/java/CMakeLists.txt b/src/controller/java/CMakeLists.txt new file mode 100644 index 00000000000..479f9ec77c8 --- /dev/null +++ b/src/controller/java/CMakeLists.txt @@ -0,0 +1,327 @@ +# Find SWIG and setup Java option +option(WEBOTS_BUILD_JAVA "Build Java controller support" OFF) + +find_package(SWIG) + +# Check if SWIG was found and Java is enabled +if(SWIG_FOUND AND WEBOTS_BUILD_JAVA) + include(UseSWIG) + message(STATUS "SWIG found: ${SWIG_EXECUTABLE}") + + # Set the module name (should match %module in the .i file) + set(SWIG_MODULE_NAME wrapper) + + # Set the output directory for generated Java files + set(SWIG_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/SWIG_generated_files) + + # Set the package name for the generated Java code + set(SWIG_PACKAGE_NAME com.cyberbotics.webots.controller) + + # Set the input SWIG interface file + set(SWIG_INTERFACE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/controller.i) + + # Set include directories for C++ headers + set(SWIG_INCLUDE_DIRS + ${CMAKE_SOURCE_DIR}/include/controller/cpp + ${CMAKE_SOURCE_DIR}/include/controller/c + ) + + # Create the output directory if it doesn't exist + file(MAKE_DIRECTORY ${SWIG_OUTPUT_DIR}) + + # Configure SWIG + set_source_files_properties(${SWIG_INTERFACE_FILE} + PROPERTIES CPLUSPLUS ON) + set_source_files_properties(${SWIG_INTERFACE_FILE} + PROPERTIES SWIG_FLAGS "-c++;-package;${SWIG_PACKAGE_NAME};-outdir;${SWIG_OUTPUT_DIR}") + + # Generate the SWIG wrapper code + swig_add_library(${SWIG_MODULE_NAME} TYPE SHARED + LANGUAGE java + SOURCES ${SWIG_INTERFACE_FILE}) + set_property(TARGET ${SWIG_MODULE_NAME} PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE) + swig_link_libraries(${SWIG_MODULE_NAME} + PRIVATE + Controller + CppController + ) + + target_include_directories(${SWIG_MODULE_NAME} + PUBLIC + ${SWIG_INCLUDE_DIRS} + ) + + # Get the SWIG generated source file name + get_property(SWIG_WRAPPER_SOURCE TARGET ${SWIG_MODULE_NAME} PROPERTY SWIG_GENERATED_SOURCE_NAME_JAVA) + + # --- Compilation of Generated Java Code --- + # Create a list of Java source files (including generated ones) + file(GLOB_RECURSE JAVA_SOURCES LIST_DIRECTORIES false + "${SWIG_OUTPUT_DIR}/*.java" + "${CMAKE_CURRENT_SOURCE_DIR}/*.java") + + # Remove some of the auto-generated SWIG files which are not needed + list(FILTER JAVA_SOURCES EXCLUDE REGEX ".*(swigjni|Swig).*") + + # Compile Java source files into a JAR file + set(JAR_OUTPUT_PATH ${WEBOTS_CONTROLLER_LIB_PATH}/java/Controller.jar) + file(MAKE_DIRECTORY ${WEBOTS_CONTROLLER_LIB_PATH}/java) + + # Add custom command to compile Java files after SWIG generation + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${SWIG_PACKAGE_NAME} + COMMAND javac --release 11 ${JAVA_SOURCES} -d ${CMAKE_CURRENT_BINARY_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${SWIG_MODULE_NAME} + COMMENT "Compiling Java sources" + VERBATIM + ) + + # Add custom command to create JAR after Java compilation + add_custom_command( + OUTPUT ${JAR_OUTPUT_PATH} + COMMAND jar cfv ${JAR_OUTPUT_PATH} ${SWIG_PACKAGE_NAME}/*.class + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${SWIG_PACKAGE_NAME} + COMMENT "Creating JAR file" + VERBATIM + ) + + # Add a custom target to trigger the build of the java library + add_custom_target(java_controller + DEPENDS ${JAR_OUTPUT_PATH} + ) + # --- Compilation of Native Library --- + + # OS-specific settings + if(WIN32) + # Windows + set(JNI_LIBRARY_NAME JavaController) + set(JNI_LIBRARY_OUTPUT_PATH ${WEBOTS_CONTROLLER_LIB_PATH}/java/${JNI_LIBRARY_NAME}.dll) + set(JNI_LINK_FLAGS "-shared -mwindows -Wl,--add-stdcall-alias -Wl,--enable-auto-import -O") + + # Find Java include directories (prefer JAVA_HOME, then registry) + if(DEFINED ENV{JAVA_HOME}) + find_path(JAVA_INCLUDE_PATH jni.h + PATHS + "$ENV{JAVA_HOME}/include" + ) + + find_path(JAVA_MD_INCLUDE_PATH jni_md.h + PATHS + "$ENV{JAVA_HOME}/include/win32" + ) + else() + # Query the Windows Registry for Java installations + # 64-bit Java + file(READ "/HKEY_LOCAL_MACHINE/SOFTWARE/JavaSoft/Java Development Kit" JAVA_KEYS_64) + string(REPLACE "\n" ";" JAVA_KEYS_64 ${JAVA_KEYS_64}) + foreach(JAVA_KEY ${JAVA_KEYS_64}) + file(READ "/HKEY_LOCAL_MACHINE/SOFTWARE/JavaSoft/Java Development Kit/${JAVA_KEY}" JAVA_DATA_64) + string(REPLACE "\n" ";" JAVA_DATA_64 ${JAVA_DATA_64}) + list(FIND JAVA_DATA_64 "JavaHome" JAVA_HOME_INDEX_64) + if(NOT ${JAVA_HOME_INDEX_64} EQUAL -1) + math(EXPR JAVA_HOME_INDEX_64 "${JAVA_HOME_INDEX_64} + 1") + list(GET JAVA_DATA_64 ${JAVA_HOME_INDEX_64} JAVA_HOME_PATH_64) + string(REPLACE "\\" "/" JAVA_HOME_PATH_64 ${JAVA_HOME_PATH_64}) + find_path(JAVA_INCLUDE_PATH jni.h PATHS "${JAVA_HOME_PATH_64}/include") + find_path(JAVA_MD_INCLUDE_PATH jni_md.h PATHS "${JAVA_HOME_PATH_64}/include/win32") + if(JAVA_INCLUDE_PATH AND JAVA_MD_INCLUDE_PATH) + break() + endif() + endif() + endforeach() + + # 32-bit Java (on 64-bit Windows) + if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND (NOT JAVA_INCLUDE_PATH OR NOT JAVA_MD_INCLUDE_PATH)) + file(READ "/HKEY_LOCAL_MACHINE/SOFTWARE/Wow6432Node/JavaSoft/Java Development Kit" JAVA_KEYS_32) + string(REPLACE "\n" ";" JAVA_KEYS_32 ${JAVA_KEYS_32}) + foreach(JAVA_KEY ${JAVA_KEYS_32}) + file(READ "/HKEY_LOCAL_MACHINE/SOFTWARE/Wow6432Node/JavaSoft/Java Development Kit/${JAVA_KEY}" JAVA_DATA_32) + string(REPLACE "\n" ";" JAVA_DATA_32 ${JAVA_DATA_32}) + list(FIND JAVA_DATA_32 "JavaHome" JAVA_HOME_INDEX_32) + if(NOT ${JAVA_HOME_INDEX_32} EQUAL -1) + math(EXPR JAVA_HOME_INDEX_32 "${JAVA_HOME_INDEX_32} + 1") + list(GET JAVA_DATA_32 ${JAVA_HOME_INDEX_32} JAVA_HOME_PATH_32) + string(REPLACE "\\" "/" JAVA_HOME_PATH_32 ${JAVA_HOME_PATH_32}) + find_path(JAVA_INCLUDE_PATH jni.h PATHS "${JAVA_HOME_PATH_32}/include") + find_path(JAVA_MD_INCLUDE_PATH jni_md.h PATHS "${JAVA_HOME_PATH_32}/include/win32") + if(JAVA_INCLUDE_PATH AND JAVA_MD_INCLUDE_PATH) + break() + endif() + endif() + endforeach() + endif() + endif() + + elseif(APPLE) + # macOS + set(JNI_LIBRARY_NAME libJavaController.jnilib) + set(JNI_LIBRARY_OUTPUT_PATH ${WEBOTS_CONTROLLER_LIB_PATH}/java/${JNI_LIBRARY_NAME}) + set(JNI_LINK_FLAGS "-dynamiclib -install_name @rpath/Contents/lib/controller/java/libJavaController.jnilib -Wl,-rpath,@loader_path/../../../..") + + # Find Java include directories (prefer JAVA_HOME, then system paths) + if(DEFINED ENV{JAVA_HOME}) + find_path(JAVA_INCLUDE_PATH jni.h + PATHS + "$ENV{JAVA_HOME}/include" + ) + + find_path(JAVA_MD_INCLUDE_PATH jni_md.h + PATHS + "$ENV{JAVA_HOME}/include/darwin" + ) + else() + # Use 'which javac' to find the Java installation + execute_process( + COMMAND which javac + OUTPUT_VARIABLE JAVAC_PATH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + get_filename_component(JAVA_BIN_DIR ${JAVAC_PATH} DIRECTORY) + get_filename_component(JAVA_HOME_GUESS ${JAVA_BIN_DIR} DIRECTORY) + + find_path(JAVA_INCLUDE_PATH jni.h + PATHS + "${JAVA_HOME_GUESS}/include" + "/Library/Java/JavaVirtualMachines/*/Contents/Home/include" # Common macOS location + "/System/Library/Frameworks/JavaVM.framework/Headers" + ) + find_path(JAVA_MD_INCLUDE_PATH jni_md.h + PATHS + "${JAVA_HOME_GUESS}/include/darwin" + "/Library/Java/JavaVirtualMachines/*/Contents/Home/include/darwin" + "/System/Library/Frameworks/JavaVM.framework/Headers" + ) + endif() + + else() + # Linux + set(JNI_LIBRARY_NAME libJavaController.so) + set(JNI_LIBRARY_OUTPUT_PATH ${WEBOTS_CONTROLLER_LIB_PATH}/java/${JNI_LIBRARY_NAME}) + set(JNI_LINK_FLAGS "-shared -fPIC") + + # Find Java include directories (prefer JAVA_HOME, then system paths) + if(DEFINED ENV{JAVA_HOME}) + find_path(JAVA_INCLUDE_PATH jni.h + PATHS + "$ENV{JAVA_HOME}/include" + ) + + find_path(JAVA_MD_INCLUDE_PATH jni_md.h + PATHS + "$ENV{JAVA_HOME}/include/linux" + ) + endif() + endif() + + # Fallback: If JAVA_HOME is not defined or not found, use javac to find Java include paths + if(NOT JAVA_INCLUDE_PATH OR NOT JAVA_MD_INCLUDE_PATH) + # Use 'which javac' to find the Java installation + execute_process( + COMMAND which javac + OUTPUT_VARIABLE JAVAC_PATH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + if(JAVAC_PATH) + # Extract JAVA_HOME from javac path + get_filename_component(JAVA_BIN_DIR ${JAVAC_PATH} DIRECTORY) + get_filename_component(JAVA_HOME_GUESS ${JAVA_BIN_DIR} DIRECTORY) # Correctly go up one level for JAVA_HOME + + # Search for jni.h + find_path(JAVA_INCLUDE_PATH jni.h + PATHS + "${JAVA_HOME_GUESS}/include" + "/usr/lib/jvm/*/include" # Common Debian/Ubuntu locations + "/usr/java/*/include" # Common RedHat/Fedora locations + "/usr/include/java" + ) + + # OS-specific search for jni_md.h + if(WIN32) + find_path(JAVA_MD_INCLUDE_PATH jni_md.h + PATHS + "${JAVA_HOME_GUESS}/include/win32" + ) + elseif(APPLE) + find_path(JAVA_MD_INCLUDE_PATH jni_md.h + PATHS + "${JAVA_HOME_GUESS}/include/darwin" + "/Library/Java/JavaVirtualMachines/*/Contents/Home/include/darwin" + "/System/Library/Frameworks/JavaVM.framework/Headers" + ) + else() # Linux + find_path(JAVA_MD_INCLUDE_PATH jni_md.h + PATHS + "${JAVA_HOME_GUESS}/include/linux" + "/usr/lib/jvm/*/include/linux" + "/usr/java/*/include/linux" + "/usr/include/java/linux" + ) + endif() + else() + message(WARNING "Could not find 'javac'. Java include paths may not be found.") + endif() + endif() + + + target_include_directories(${SWIG_MODULE_NAME} + PUBLIC + ${JAVA_INCLUDE_PATH} # Add this line + ${JAVA_MD_INCLUDE_PATH} # Add this line + ) + + + # Check if Java include directories were found + if(NOT JAVA_INCLUDE_PATH OR NOT JAVA_MD_INCLUDE_PATH) + message(WARNING "Java include directories not found. Java controller support will be disabled.") + set(WEBOTS_BUILD_JAVA OFF CACHE BOOL "Build Java controller support" FORCE) + else() + message(STATUS "JAVA_INCLUDE_PATH: ${JAVA_INCLUDE_PATH}") + message(STATUS "JAVA_MD_INCLUDE_PATH: ${JAVA_MD_INCLUDE_PATH}") + endif() + + # Generate the JNI library in a separate target + add_library(${JNI_LIBRARY_NAME} SHARED ${SWIG_WRAPPER_SOURCE}) + add_dependencies(${JNI_LIBRARY_NAME} ${SWIG_MODULE_NAME}) + + target_include_directories(${JNI_LIBRARY_NAME} + PUBLIC + ${JAVA_INCLUDE_PATH} + ${JAVA_MD_INCLUDE_PATH} + ${SWIG_INCLUDE_DIRS} + ) + + target_link_libraries(${JNI_LIBRARY_NAME} + PRIVATE + Controller + CppController + ) + + set_target_properties(${JNI_LIBRARY_NAME} PROPERTIES + PREFIX "" + OUTPUT_NAME ${JNI_LIBRARY_NAME} + LIBRARY_OUTPUT_DIRECTORY ${JNI_LIBRARY_OUTPUT_PATH} + ) + + # Set linker flags for the JNI library + set_target_properties(${JNI_LIBRARY_NAME} PROPERTIES LINK_FLAGS ${JNI_LINK_FLAGS}) + + # Add dependency on the SWIG module + add_dependencies(java_controller ${JNI_LIBRARY_NAME}) + + # Create a symbolic link or copy the JNI library to the output directory + # This simplifies loading the library from Java code + add_custom_command(TARGET ${JNI_LIBRARY_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E create_symlink + ${JNI_LIBRARY_OUTPUT_PATH} + ${CMAKE_CURRENT_BINARY_DIR}/${JNI_LIBRARY_NAME} + COMMENT "Creating symbolic link for ${JNI_LIBRARY_NAME}" + ) + +elseif(SWIG_FOUND AND NOT WEBOTS_BUILD_JAVA) + message(STATUS "Java controller support explicitly disabled") +else() + message(WARNING "SWIG not found. Skipping Java API generation.") +endif() diff --git a/src/ode/CMakeLists.txt b/src/ode/CMakeLists.txt new file mode 100644 index 00000000000..7896c269941 --- /dev/null +++ b/src/ode/CMakeLists.txt @@ -0,0 +1,103 @@ +# ODE (Open Dynamics Engine) CMake configuration + +# ODE (Open Dynamics Engine) CMake configuration + +# Create ODE library target +add_library(ode STATIC) + +# Find all cpp files recursively in ode/src and its subdirectories +file(GLOB_RECURSE ODE_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/ode/src/*.cpp" +) + +# Find all c files recursively in ode/src and its subdirectories +file(GLOB_RECURSE ODE_C_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/ode/src/*.c" +) + +# Find all OPCODE collision detection library sources +file(GLOB_RECURSE OPCODE_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/OPCODE/*.cpp" +) + + +# Find all libccd collision detection library sources +target_sources(ode PRIVATE + libccd/src/alloc.c + libccd/src/ccd.c + libccd/src/mpr.c + libccd/src/polytope.c + libccd/src/support.c + libccd/src/vec3.c +) + +# Add source files +target_sources(ode PRIVATE + ${ODE_SOURCES} + ${ODE_C_SOURCES} + ${OPCODE_SOURCES} +) + +# Set include directories +target_include_directories(ode + PUBLIC + $ + $ + $ + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/ode/src + ${CMAKE_CURRENT_SOURCE_DIR}/OPCODE + ${CMAKE_CURRENT_SOURCE_DIR}/OPCODE/Ice + ${CMAKE_CURRENT_SOURCE_DIR}/libccd/src + ${CMAKE_CURRENT_SOURCE_DIR}/libccd/src/custom +) + +# Define compile definitions and options +target_compile_definitions(ode + PRIVATE + dTRIMESH_ENABLED + dTRIMESH_OPCODE +) + +target_compile_options(ode PRIVATE + -I${CMAKE_SOURCE_DIR}/include + -fPIC +) + +# Set properties +set_target_properties(ode PROPERTIES + VERSION 0.16.2 + SOVERSION 8 + PUBLIC_HEADER "${CMAKE_SOURCE_DIR}/include/ode/ode.h" +) + +# Install rules +include(GNUInstallDirs) +install(TARGETS ode + EXPORT odeTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + #PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ode + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +# Export targets +#install(EXPORT odeTargets +# FILE odeTargets.cmake +# NAMESPACE ode:: +# DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ode +#) +# +## Create and install config file +#include(CMakePackageConfigHelpers) +#configure_package_config_file( +# ${CMAKE_CURRENT_SOURCE_DIR}/odeConfig.cmake.in +# ${CMAKE_CURRENT_BINARY_DIR}/odeConfig.cmake +# INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ode +#) +# +#install(FILES +# ${CMAKE_CURRENT_BINARY_DIR}/odeConfig.cmake +# DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ode +#) diff --git a/src/ode/ode/src/util.cpp b/src/ode/ode/src/util.cpp index 39f99695240..cef34e6c01b 100644 --- a/src/ode/ode/src/util.cpp +++ b/src/ode/ode/src/util.cpp @@ -646,7 +646,7 @@ void dxWorldProcessMemArena::FreeMemArena (dxWorldProcessMemArena *arena) size_t dxWorldProcessMemArena::AdjustArenaSizeForReserveRequirements(size_t arenareq, float rsrvfactor, unsigned rsrvminimum) { float scaledarena = arenareq * rsrvfactor; - size_t adjustedarena = (scaledarena < SIZE_MAX) ? (size_t)scaledarena : SIZE_MAX; + size_t adjustedarena = (scaledarena < (float)SIZE_MAX) ? (size_t)scaledarena : SIZE_MAX; size_t boundedarena = (adjustedarena > rsrvminimum) ? adjustedarena : (size_t)rsrvminimum; return dEFFICIENT_SIZE(boundedarena); } diff --git a/src/ode/odeConfig.cmake.in b/src/ode/odeConfig.cmake.in new file mode 100644 index 00000000000..18e3ffd24a3 --- /dev/null +++ b/src/ode/odeConfig.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/odeTargets.cmake") +check_required_components(ode) diff --git a/src/webots/.gitignore b/src/webots/.gitignore index 45f62bb8cd3..d5ac7638866 100644 --- a/src/webots/.gitignore +++ b/src/webots/.gitignore @@ -1 +1,2 @@ -cppclean.output +.aider* +.env diff --git a/src/webots/CMakeLists.txt b/src/webots/CMakeLists.txt new file mode 100644 index 00000000000..de544fa2e5f --- /dev/null +++ b/src/webots/CMakeLists.txt @@ -0,0 +1,87 @@ +cmake_minimum_required(VERSION 3.10) +project(Webots VERSION 1.0) + +# Set C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Enable PIC globally +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +# Enable RTTI globally +add_compile_options(-frtti) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -frtti") +if(WIN32) + add_definitions(-DUNICODE -D_UNICODE) +endif() +# Set visibility settings globally +add_compile_options(-fvisibility=default) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + add_compile_options(-fno-gnu-unique) +endif() + +# Add cmake modules +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +include(FileUtils) + + +# Find OIS +find_package(PkgConfig REQUIRED) +pkg_check_modules(OIS REQUIRED IMPORTED_TARGET OIS) + + +# Add subdirectories +add_subdirectory(app) +add_subdirectory(control) +add_subdirectory(core) +add_subdirectory(editor) +add_subdirectory(engine) +add_subdirectory(gui) +add_subdirectory(maths) +add_subdirectory(nodes) +add_subdirectory(ode) +add_subdirectory(plugins) +add_subdirectory(scene_tree) +add_subdirectory(sound) +add_subdirectory(user_commands) +add_subdirectory(util) +add_subdirectory(vrml) +add_subdirectory(widgets) +add_subdirectory(wren) + +# Main executable +add_executable(webots gui/main.cpp) +if(MINGW) # Or if you want it for other compilers, adjust the condition + target_link_options(webots PRIVATE "-Wl,-Map,${CMAKE_CURRENT_BINARY_DIR}/webots.map") + # For MSVC, the flag would be /MAP:"${CMAKE_CURRENT_BINARY_DIR}/webots.map" + # For Clang/LLD, it might be -Wl,--cref or -Wl,-Map=webots.map depending on the linker +endif() +# Link main executable with all components +target_link_libraries(webots + PRIVATE + app_lib + gui_lib + scene_tree_lib + editor_lib + engine_lib + control_lib + plugins_lib + nodes_utils_lib + nodes_lib + ode_lib + wren_lib + sound_lib + user_commands_lib + widgets_lib + vrml_lib + core_lib + maths_lib + util_lib + Qt6::Core + Qt6::Gui + Qt6::Widgets + Qt6::Network + Qt6::OpenGL + Qt6::QmlIntegration + ${OPENGL_LIBRARIES} +) diff --git a/src/webots/app/CMakeLists.txt b/src/webots/app/CMakeLists.txt new file mode 100644 index 00000000000..a04f1da025e --- /dev/null +++ b/src/webots/app/CMakeLists.txt @@ -0,0 +1,31 @@ +# Enable Qt MOC +set(CMAKE_AUTOMOC ON) + +add_webots_library(app_lib) + + +# Set position independent code +set_property(TARGET app_lib PROPERTY POSITION_INDEPENDENT_CODE ON) + +# Ensure symbols are exported +set_target_properties(app_lib PROPERTIES + ENABLE_EXPORTS ON + WINDOWS_EXPORT_ALL_SYMBOLS ON + CXX_VISIBILITY_PRESET default + VISIBILITY_INLINES_HIDDEN OFF +) + +target_link_libraries(app_lib + PUBLIC + nodes_utils_lib + nodes_lib + Qt6::Core + Qt6::Gui + Qt6::Widgets + Qt6::Network + Qt6::OpenGL + Qt6::QmlIntegration + engine_lib + wren_lib + ode +) diff --git a/src/webots/app/WbApplication.cpp b/src/webots/app/WbApplication.cpp index 8e651e0528d..884ebb77892 100644 --- a/src/webots/app/WbApplication.cpp +++ b/src/webots/app/WbApplication.cpp @@ -17,6 +17,7 @@ #include "WbAnimationRecorder.hpp" #include "WbApplicationInfo.hpp" #include "WbBoundingSphere.hpp" +#include "WbConcreteNodeFactory.hpp" #include "WbControlledWorld.hpp" #include "WbDownloadManager.hpp" #include "WbLog.hpp" @@ -199,7 +200,11 @@ bool WbApplication::isValidWorldFileName(const QString &worldName) { return true; } +#include // Added for debugging + void WbApplication::loadWorld(QString worldName, bool reloading, bool isLoadingAfterDownload) { + qDebug() << "WbApplication::loadWorld called with worldName:" << worldName << "reloading:" << reloading + << "isLoadingAfterDownload:" << isLoadingAfterDownload; disconnect(WbProtoManager::instance(), &WbProtoManager::worldLoadCompleted, this, &WbApplication::loadWorld); bool isValidProject = true; const QString newProjectPath = WbProject::projectPathFromWorldFile(worldName, isValidProject); diff --git a/src/webots/app/WbPerspective.cpp b/src/webots/app/WbPerspective.cpp index 1152bebabbb..63826592632 100644 --- a/src/webots/app/WbPerspective.cpp +++ b/src/webots/app/WbPerspective.cpp @@ -284,12 +284,15 @@ bool WbPerspective::save() const { out << "renderingDevicePerspectives: " << it.key() << ";" << it.value().join(";") << "\n"; outputFile.close(); - #ifdef _WIN32 // set hidden attribute to WBPROJ file - const QByteArray nativePathByteArray = QDir::toNativeSeparators(fileName()).toUtf8(); - const LPCSTR nativePath = nativePathByteArray.constData(); - SetFileAttributes(nativePath, GetFileAttributes(nativePath) | FILE_ATTRIBUTE_HIDDEN); + const QString nativePath = QDir::toNativeSeparators(fileName()); +#ifdef UNICODE + SetFileAttributes((LPCWSTR)nativePath.utf16(), GetFileAttributes((LPCWSTR)nativePath.utf16()) | FILE_ATTRIBUTE_HIDDEN); +#else + SetFileAttributes((LPCSTR)nativePath.toLocal8Bit().constData(), + GetFileAttributes((LPCSTR)nativePath.toLocal8Bit().constData()) | FILE_ATTRIBUTE_HIDDEN); +#endif #endif return true; diff --git a/src/webots/cmake/ExternalDependencies.cmake b/src/webots/cmake/ExternalDependencies.cmake new file mode 100644 index 00000000000..659fe06025c --- /dev/null +++ b/src/webots/cmake/ExternalDependencies.cmake @@ -0,0 +1,74 @@ +include(ExternalProject) + +# Determine library suffix based on platform +if(WIN32) + set(PICO_LIB_NAME "libpico.dll") + set(PICO_IMPLIB_NAME "libpico.dll.a") +elseif(APPLE) + set(PICO_LIB_NAME "libpico.dylib") +else() + set(PICO_LIB_NAME "libpico.so") +endif() + +# Set paths +set(PICOTTS_PREFIX ${CMAKE_BINARY_DIR}/external/picotts) +set(PICOTTS_LIBRARY ${PICOTTS_PREFIX}/lib/${PICO_LIB_NAME}) +set(PICOTTS_INCLUDE_DIR ${PICOTTS_PREFIX}/include) + +if(WIN32) + set(PICOTTS_IMPLIB ${PICOTTS_PREFIX}/lib/${PICO_IMPLIB_NAME}) +endif() + +# Create necessary directories +file(MAKE_DIRECTORY ${PICOTTS_INCLUDE_DIR}) + +# PicoTTS +ExternalProject_Add( + picotts_external + GIT_REPOSITORY https://github.com/Kreijstal/picotts.git + GIT_TAG master + PREFIX ${PICOTTS_PREFIX} + SOURCE_SUBDIR pico + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=${PICOTTS_PREFIX} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + BUILD_BYPRODUCTS + ${PICOTTS_LIBRARY} + ${PICOTTS_IMPLIB} + # Copy header files after build + INSTALL_COMMAND + ${CMAKE_COMMAND} -E copy_directory /pico/lib ${PICOTTS_INCLUDE_DIR} + COMMAND + ${CMAKE_COMMAND} -E copy_directory /pico/compat ${PICOTTS_INCLUDE_DIR} + COMMAND + ${CMAKE_MAKE_PROGRAM} install +) + +# Create the imported library target +add_library(pico::pico SHARED IMPORTED GLOBAL) + +# Set properties on the target +if(WIN32) + set_target_properties(pico::pico PROPERTIES + IMPORTED_LOCATION "${PICOTTS_LIBRARY}" + IMPORTED_IMPLIB "${PICOTTS_IMPLIB}" + INTERFACE_INCLUDE_DIRECTORIES "${PICOTTS_INCLUDE_DIR}" + ) +else() + set_target_properties(pico::pico PROPERTIES + IMPORTED_LOCATION "${PICOTTS_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${PICOTTS_INCLUDE_DIR}" + ) +endif() + +# Create a custom target that depends on the external project +add_custom_target(picotts ALL DEPENDS picotts_external) +add_dependencies(pico::pico picotts) + +# Install the pico library +install(FILES ${PICOTTS_LIBRARY} + DESTINATION bin + COMPONENT runtime +) diff --git a/src/webots/cmake/FileUtils.cmake b/src/webots/cmake/FileUtils.cmake new file mode 100644 index 00000000000..d091aa918f5 --- /dev/null +++ b/src/webots/cmake/FileUtils.cmake @@ -0,0 +1,77 @@ +# Helper function to get source files +function(get_source_files DIR SOURCES_OUT) + file(GLOB SOURCES "${DIR}/*.cpp") + set(${SOURCES_OUT} ${SOURCES} PARENT_SCOPE) +endfunction() + +# Helper function to create a library with standard settings +function(add_webots_library LIB_NAME) + get_filename_component(DIR_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) + + # Get all cpp files + file(GLOB ALL_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") + + # Filter out Windows-specific files on non-Windows platforms + if(NOT WIN32) + list(FILTER ALL_SOURCES EXCLUDE REGEX "WbVirtualRealityHeadset\\.cpp$") + list(FILTER ALL_SOURCES EXCLUDE REGEX "WbMicrosoftTextToSpeech\\.cpp$") + list(FILTER ALL_SOURCES EXCLUDE REGEX "WbWindowsRegistry\\.cpp$") + endif() + if(WIN32) + list(FILTER ALL_SOURCES EXCLUDE REGEX "WbPosixMemoryMappedFile\\.cpp$") + endif() + + add_library(${DIR_NAME}_lib ${ALL_SOURCES}) + target_include_directories(${DIR_NAME}_lib PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/src/webots/core + ${CMAKE_SOURCE_DIR}/src/webots/vrml + ${CMAKE_SOURCE_DIR}/src/webots/util + ${CMAKE_SOURCE_DIR}/src/webots/widgets + ${CMAKE_SOURCE_DIR}/src/webots/external/compilation_timestamp + ${CMAKE_SOURCE_DIR}/src/webots/user_commands + ${CMAKE_SOURCE_DIR}/src/webots/maths + ${CMAKE_SOURCE_DIR}/src/webots/nodes/utils + ${CMAKE_SOURCE_DIR}/src/webots/wren + ${CMAKE_SOURCE_DIR}/src/webots/ode + ${CMAKE_SOURCE_DIR}/src/webots/nodes + ${CMAKE_SOURCE_DIR}/src/webots/app + ${CMAKE_SOURCE_DIR}/src/webots/plugins + ${CMAKE_SOURCE_DIR}/src/webots/engine + ${CMAKE_SOURCE_DIR}/src/webots/control + ${CMAKE_SOURCE_DIR}/src/webots/editor + ${CMAKE_SOURCE_DIR}/src/webots/scene_tree + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/include/ode + ${CMAKE_SOURCE_DIR}/src/webots/sound + ${CMAKE_SOURCE_DIR}/src/webots/external/siphash + ${CMAKE_SOURCE_DIR}/include/controller/c + ${PICOTTS_INCLUDE_DIR} + /usr/include/stb + /usr/local/include/stb + $ENV{MINGW_PREFIX}/include/stb + /usr/include + /usr/local/include + $ENV{MINGW_PREFIX}/include + ${FREETYPE_INCLUDE_DIRS} + ) + target_link_libraries(${DIR_NAME}_lib + PUBLIC + Qt6::Core + Qt6::Network + Qt6::QmlIntegration + Qt6::Gui + Qt6::Widgets + Qt6::OpenGL + ) + + # Set C files to be compiled as C and add needed flags + file(GLOB PICO_C_FILES "${CMAKE_BINARY_DIR}/external/picotts/src/picotts/pico/lib/*.c") + set_source_files_properties( + ${PICO_C_FILES} + PROPERTIES + COMPILE_FLAGS "-x c -fno-strict-aliasing" + LANGUAGE C + ) +endfunction() diff --git a/src/webots/control/CMakeLists.txt b/src/webots/control/CMakeLists.txt new file mode 100644 index 00000000000..87675a36610 --- /dev/null +++ b/src/webots/control/CMakeLists.txt @@ -0,0 +1,6 @@ +add_webots_library(control_lib) + +target_link_libraries(control_lib + PRIVATE + core_lib +) diff --git a/src/webots/core/CMakeLists.txt b/src/webots/core/CMakeLists.txt new file mode 100644 index 00000000000..81e51f72ae1 --- /dev/null +++ b/src/webots/core/CMakeLists.txt @@ -0,0 +1,37 @@ +add_webots_library(core_lib) + +# Find libraries on Windows (using MSYS2) +if (WIN32) + # Directly use MSYSTEM_PREFIX for library paths + set(D3D9_LIBRARY_PATH "$ENV{MSYSTEM_PREFIX}/lib/libd3d9.a") + set(IPHLPAPI_LIBRARY_PATH "$ENV{MSYSTEM_PREFIX}/lib/libiphlpapi.a") + + # Check if D3D9 library exists and link it + if (EXISTS ${D3D9_LIBRARY_PATH}) + message(STATUS "D3D9 library found: ${D3D9_LIBRARY_PATH}") + else() + message(FATAL_ERROR "D3D9 library not found at ${D3D9_LIBRARY_PATH}. Please ensure it is installed in your MSYS2 environment.") + endif() + + # Check if IPHLPAPI library exists and link it + if (EXISTS ${IPHLPAPI_LIBRARY_PATH}) + message(STATUS "IPHLPAPI library found: ${IPHLPAPI_LIBRARY_PATH}") + else() + message(FATAL_ERROR "IPHLPAPI library not found at ${IPHLPAPI_LIBRARY_PATH}. Please ensure it is installed in your MSYS2 environment.") + endif() + + # Link libraries if found + target_link_libraries(core_lib + PRIVATE + Qt6::Qml + Qt6::Test + ${D3D9_LIBRARY_PATH} + ${IPHLPAPI_LIBRARY_PATH} + ) +else() + target_link_libraries(core_lib + PRIVATE + Qt6::Qml + Qt6::Test + ) +endif() diff --git a/src/webots/core/WbQtObjectSpy.cpp b/src/webots/core/WbQtObjectSpy.cpp index f6f6f156bbb..f214c6ff737 100644 --- a/src/webots/core/WbQtObjectSpy.cpp +++ b/src/webots/core/WbQtObjectSpy.cpp @@ -35,7 +35,7 @@ void WbQtObjectSpy::beginSignalSpy() { for (int i = 0; i < mMetaObject->methodCount(); i++) { const QMetaMethod &method = mMetaObject->method(i); if (method.methodType() == QMetaMethod::Signal) { - QString signalSignature = QString("2%1").arg(method.signature()); + QString signalSignature = QString("2%1").arg(QString::fromLatin1(method.methodSignature())); mSignalSpies << new QSignalSpy(mObject, signalSignature.toLatin1().data()); } } @@ -45,7 +45,7 @@ void WbQtObjectSpy::endSignalSpy() { foreach (QSignalSpy *spy, mSignalSpies) { QString debugMessage; debugMessage += mMetaObject->className(); - debugMessage += QString("[%1]").arg((long)mObject); + debugMessage += QString("[%1]").arg((quintptr)mObject); debugMessage += "::"; debugMessage += spy->signal(); debugMessage += " was emitted"; diff --git a/src/webots/core/WbStandardPaths.cpp b/src/webots/core/WbStandardPaths.cpp index 52a1a15ba12..5abcae97ff1 100644 --- a/src/webots/core/WbStandardPaths.cpp +++ b/src/webots/core/WbStandardPaths.cpp @@ -30,7 +30,13 @@ #include #ifdef _WIN32 +#include #include "../../../include/controller/c/webots/utils/system.h" +#elif defined(__linux__) +#include +#include +#elif defined(__APPLE__) +#include #endif const QString &WbStandardPaths::webotsHomePath() { @@ -42,15 +48,69 @@ const QString &WbStandardPaths::webotsHomePath() { // on macOS, the webots binary is located in $WEBOTS_HOME/Contents/MacOS/webots const int depth = 2; #else - // on Windows, the webots binary is located in $WEBOTS_HOME/msys64/mingw64/bin/webots + // on Windows, the webots binary can be in either: + // - $WEBOTS_HOME/msys64/mingw64/bin/webots (depth=3) + // - install_dir/bin/webots.exe (relative to share/webots/resources/) const int depth = 3; #endif if (path.isEmpty()) { - QDir dir(QCoreApplication::applicationDirPath()); - for (int i = 0; i < depth; i++) - if (!dir.cdUp()) - assert(false); - path = dir.absolutePath() + "/"; + QString exePath; + +// Get absolute path to executable +#ifdef _WIN32 + wchar_t winPath[MAX_PATH]; + if (GetModuleFileNameW(NULL, winPath, MAX_PATH)) { + exePath = QDir::cleanPath(QString::fromWCharArray(winPath)); + // qDebug() << "Windows executable path:" << exePath; + } +#elif defined(__linux__) + char linuxPath[PATH_MAX]; + ssize_t count = readlink("/proc/self/exe", linuxPath, PATH_MAX); + if (count != -1) { + linuxPath[count] = '\0'; + exePath = QFileInfo(linuxPath).absoluteFilePath(); + // qDebug() << "Linux executable path:" << exePath; + } +#elif defined(__APPLE__) + char macPath[PATH_MAX]; + uint32_t size = sizeof(macPath); + if (_NSGetExecutablePath(macPath, &size) == 0) { + exePath = QFileInfo(macPath).absoluteFilePath(); + // qDebug() << "macOS executable path:" << exePath; + } +#endif + + if (exePath.isEmpty() && QCoreApplication::instance()) { + exePath = QCoreApplication::applicationFilePath(); + // qDebug() << "Fallback to QCoreApplication path:" << exePath; + } + + QFileInfo exeInfo(exePath); + QString currentPath = exeInfo.absolutePath(); + // qDebug() << "Executable directory:" << currentPath; + + QDir dir(currentPath); + + // First check if we're in install_dir/bin/ + QDir installDir(dir); + if (installDir.cd("../share/webots/resources") && installDir.exists()) { + path = installDir.absolutePath() + "/../"; // Go up to share/webots + // qDebug() << "Using install_dir path:" << path; + } + // Fall back to standard path calculation + else { + for (int i = 0; i < depth; i++) { + if (!dir.cdUp()) { + QString error = QString("Failed to find Webots home directory. Current path: %1\nExecutable path: %2") + .arg(currentPath) + .arg(exePath); + qCritical() << error; + assert(false && error.toUtf8().constData()); + } + } + path = dir.absolutePath() + "/"; + // qDebug() << "Using standard path:" << path; + } } return path; }; diff --git a/src/webots/core/WbWindowsRegistry.cpp b/src/webots/core/WbWindowsRegistry.cpp index 85d92307287..63d54bbec2b 100644 --- a/src/webots/core/WbWindowsRegistry.cpp +++ b/src/webots/core/WbWindowsRegistry.cpp @@ -42,7 +42,7 @@ WbWindowsRegistry::WbWindowsRegistry(const QString &key) { #ifndef NDEBUG LONG successCode = #endif - RegOpenKeyEx(baseKey, keys.join("\\").toStdString().c_str(), 0, KEY_READ, &mCurrentKey); + RegOpenKeyExW(baseKey, keys.join(L"\\").toStdWString().c_str(), 0, KEY_READ, &mCurrentKey); assert(successCode == ERROR_SUCCESS); } diff --git a/src/webots/editor/CMakeLists.txt b/src/webots/editor/CMakeLists.txt new file mode 100644 index 00000000000..5b131fd6ac9 --- /dev/null +++ b/src/webots/editor/CMakeLists.txt @@ -0,0 +1,10 @@ +add_webots_library(editor_lib) + + +target_link_libraries(editor_lib + PRIVATE + Qt6::Gui + Qt6::Widgets + Qt6::PrintSupport Qt6::Widgets + core_lib +) diff --git a/src/webots/engine/CMakeLists.txt b/src/webots/engine/CMakeLists.txt new file mode 100644 index 00000000000..7f2313d63bd --- /dev/null +++ b/src/webots/engine/CMakeLists.txt @@ -0,0 +1,24 @@ +# Enable Qt MOC +set(CMAKE_AUTOMOC ON) + +file(GLOB SOURCES "*.cpp") +add_webots_library(engine_lib ${SOURCES}) + +target_include_directories(engine_lib PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/src/webots/core + ${CMAKE_SOURCE_DIR}/src/webots/nodes + ${CMAKE_SOURCE_DIR}/src/webots/vrml + ${CMAKE_SOURCE_DIR}/include +) + +target_link_libraries(engine_lib + PUBLIC + Qt6::Core + Qt6::Gui + Qt6::Widgets + core_lib + nodes_lib + ode_lib + vrml_lib +) diff --git a/src/webots/gui/CMakeLists.txt b/src/webots/gui/CMakeLists.txt new file mode 100644 index 00000000000..7f888dcdeb5 --- /dev/null +++ b/src/webots/gui/CMakeLists.txt @@ -0,0 +1,12 @@ +add_webots_library(gui_lib) +target_link_libraries(gui_lib + PRIVATE + Qt6::Gui + Qt6::Widgets + Qt6::Network + Qt6::OpenGL + Qt6::WebSockets + Qt6::OpenGLWidgets + ${OPENGL_LIBRARIES} + app_lib +) diff --git a/src/webots/gui/WbSplashScreen.cpp b/src/webots/gui/WbSplashScreen.cpp index e1c982ce466..26ebc921794 100644 --- a/src/webots/gui/WbSplashScreen.cpp +++ b/src/webots/gui/WbSplashScreen.cpp @@ -20,16 +20,34 @@ #include #include +#include + WbSplashScreen::WbSplashScreen(const QStringList &screenshots, const QString &logoFileName) { QPixmap background(960, 580); background.fill(Qt::black); QSplashScreen::setPixmap(background); - // rand is already given a fixed seed so use millisecond-time since epoch for pseudorandomness - int randomImageIndex = QTime::currentTime().msecsSinceStartOfDay() % screenshots.size(); + // Handle empty screenshots list + if (screenshots.isEmpty()) { + QMessageBox::warning(nullptr, "Warning", "No splash screen images provided."); + } else { + // rand is already given a fixed seed so use millisecond-time since epoch for pseudorandomness + int randomImageIndex = QTime::currentTime().msecsSinceStartOfDay() % screenshots.size(); + + mScreenshot = QImage("images:splash_images/" + screenshots.at(randomImageIndex)); + if (mScreenshot.isNull()) { + QMessageBox::warning(nullptr, "Error", "Failed to load splash screen image: " + screenshots.at(randomImageIndex)); + // Optionally load a default image here or skip loading a screenshot + // mScreenshot = QImage("path/to/default/image.png"); + } + } - mScreenshot = QImage("images:splash_images/" + screenshots.at(randomImageIndex)); mWebotsLogo = QImage("images:" + logoFileName); + if (mWebotsLogo.isNull()) { + QMessageBox::warning(nullptr, "Error", "Failed to load Webots logo image: " + logoFileName); + // You might decide to exit the program here or continue without a logo + } + #ifdef _WIN32 setWindowModality(Qt::ApplicationModal); #endif diff --git a/src/webots/gui/main.cpp b/src/webots/gui/main.cpp index bd29ce7f7de..c0f584d362d 100644 --- a/src/webots/gui/main.cpp +++ b/src/webots/gui/main.cpp @@ -12,8 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "../../nodes/utils/WbConcreteNodeFactory.hpp" #include "WbApplication.hpp" #include "WbGuiApplication.hpp" +#include "WbNodeFactory.hpp" +#include "WbStandardPaths.hpp" #include #include @@ -73,9 +76,9 @@ static void catchMessageOutput(QtMsgType type, const QMessageLogContext &context case QtInfoMsg: fprintf(stderr, "Info: %s\n", message.toUtf8().constData()); break; - case QtDebugMsg: - fprintf(stderr, "Debug: %s\n", message.toUtf8().constData()); - break; + // case QtDebugMsg: + // fprintf(stderr, "Debug: %s\n", message.toUtf8().constData()); + // break; case QtWarningMsg: fprintf(stderr, "Warning: %s\n", message.toUtf8().constData()); break; @@ -93,6 +96,9 @@ static void quitApplication(int sig) { } int main(int argc, char *argv[]) { + // Initialize both factory singletons early + WbNodeFactory::instance(); + WbConcreteNodeFactory::getInstance(); #ifdef _WIN32 // on Windows, the webots binary is located in $WEBOTS_HOME/msys64/mingw64/bin/webots // we need to use GetModuleFileName as argv[0] doesn't always provide an absolute path @@ -148,9 +154,9 @@ int main(int argc, char *argv[]) { ); #ifdef __APPLE__ - QString qtFiltersFilePath = QDir::fromNativeSeparators(webotsDirPath + "/Contents/Resources/qt_warning_filters.conf"); + QString qtFiltersFilePath = QDir::fromNativeSeparators(WbStandardPaths::resourcesPath() + "qt_warning_filters.conf"); #else - QString qtFiltersFilePath = QDir::fromNativeSeparators(webotsDirPath + "/resources/qt_warning_filters.conf"); + QString qtFiltersFilePath = QDir::fromNativeSeparators(WbStandardPaths::resourcesPath() + "qt_warning_filters.conf"); #endif // load qt warning filters from file QFile qtFiltersFile(qtFiltersFilePath); diff --git a/src/webots/maths/CMakeLists.txt b/src/webots/maths/CMakeLists.txt new file mode 100644 index 00000000000..b72d9b3dcd3 --- /dev/null +++ b/src/webots/maths/CMakeLists.txt @@ -0,0 +1 @@ +add_webots_library(maths_lib) diff --git a/src/webots/nodes/CMakeLists.txt b/src/webots/nodes/CMakeLists.txt new file mode 100644 index 00000000000..76478755440 --- /dev/null +++ b/src/webots/nodes/CMakeLists.txt @@ -0,0 +1,56 @@ +add_subdirectory(utils) +add_webots_library(nodes_lib) + +# Enable RTTI and visibility +target_compile_options(nodes_lib + PUBLIC + -frtti + -fvisibility=default +) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + add_compile_options(-fno-gnu-unique) +endif() + +# Find STB headers +find_path(STB_INCLUDE_DIR + NAMES stb_image.h + PATHS + ${STB_ROOT}/include + ${STB_ROOT} + $ENV{MSYSTEM_PREFIX}/include/stb + $ENV{MSYSTEM_PREFIX}/local/include/stb + /usr/include/stb + /usr/local/include/stb + ${CMAKE_SOURCE_DIR}/external/stb + ${CMAKE_SOURCE_DIR}/src/webots/external/stb + ${CMAKE_SOURCE_DIR}/include/stb +) + +if(NOT STB_INCLUDE_DIR) + message(FATAL_ERROR "STB headers not found - please install stb package or set STB_ROOT") +else() + message(STATUS "Found STB headers at: ${STB_INCLUDE_DIR}") +endif() + +# Set position independent code +set_property(TARGET nodes_lib PROPERTY POSITION_INDEPENDENT_CODE ON) + +# Ensure symbols are exported +set_target_properties(nodes_lib PROPERTIES + ENABLE_EXPORTS ON + WINDOWS_EXPORT_ALL_SYMBOLS ON + CXX_VISIBILITY_PRESET default + VISIBILITY_INLINES_HIDDEN OFF +) + +target_include_directories(nodes_lib + PUBLIC + ${STB_INCLUDE_DIR} +) + +target_link_libraries(nodes_lib + PUBLIC + core_lib + vrml_lib + ${CMAKE_DL_LIBS} +) diff --git a/src/webots/nodes/utils/CMakeLists.txt b/src/webots/nodes/utils/CMakeLists.txt new file mode 100644 index 00000000000..de6c3e35022 --- /dev/null +++ b/src/webots/nodes/utils/CMakeLists.txt @@ -0,0 +1,63 @@ + +file(GLOB SOURCES "*.cpp") +list(REMOVE_ITEM SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/WbVirtualRealityHeadset.cpp") + +if(WIN32) + list(APPEND SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/WbVirtualRealityHeadset.cpp") +endif() + +add_library(nodes_utils_lib STATIC ${SOURCES}) + +# Enable RTTI and visibility +target_compile_options(nodes_utils_lib + PUBLIC + -frtti + -fvisibility=default +) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + add_compile_options(-fno-gnu-unique) +endif() + +# Set position independent code +set_property(TARGET nodes_utils_lib PROPERTY POSITION_INDEPENDENT_CODE ON) + +# Ensure symbols are exported +set_target_properties(nodes_utils_lib PROPERTIES + ENABLE_EXPORTS ON + WINDOWS_EXPORT_ALL_SYMBOLS ON + CXX_VISIBILITY_PRESET default + VISIBILITY_INLINES_HIDDEN OFF + BUILD_SHARED_LIBS ON +) + +target_include_directories(nodes_utils_lib PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/src/webots/nodes + ${CMAKE_SOURCE_DIR}/src/webots/core + ${CMAKE_SOURCE_DIR}/src/webots/vrml + ${CMAKE_SOURCE_DIR}/src/webots/util + ${CMAKE_SOURCE_DIR}/include + $ENV{MINGW_PREFIX}/include + $ENV{MINGW_PREFIX}/local/include + /usr/include + /usr/local/include +) + +target_link_libraries(nodes_utils_lib + PUBLIC vrml_lib + PUBLIC util_lib + PUBLIC core_lib + PUBLIC Qt6::Core + PUBLIC Qt6::Gui + PUBLIC Qt6::Widgets + PUBLIC ${OIS_LIBRARIES} + PUBLIC PkgConfig::OIS + PUBLIC PkgConfig::OPENVR + PUBLIC ode + wren_lib + nodes_lib + app_lib +) + +# Ensure this library is built with -fPIC +set_property(TARGET nodes_utils_lib PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/src/webots/nodes/utils/WbConcreteNodeFactory.cpp b/src/webots/nodes/utils/WbConcreteNodeFactory.cpp index bf7a6b31412..0597c209ba8 100644 --- a/src/webots/nodes/utils/WbConcreteNodeFactory.cpp +++ b/src/webots/nodes/utils/WbConcreteNodeFactory.cpp @@ -110,13 +110,47 @@ #include "WbWorldInfo.hpp" #include "WbZoom.hpp" +#include // For qDebug() #include +#include "WbToken.hpp" // Added for full WbToken definition -// this creates and destructs the global instance -WbConcreteNodeFactory WbConcreteNodeFactory::gFactory; +// Forward declaration for WbNodeFactory::instance() to be callable from constructor/destructor if needed for logging +// though it's not strictly necessary for these specific logs. +// class WbNodeFactory; + +WbConcreteNodeFactory *WbConcreteNodeFactory::getInstance() { + static WbConcreteNodeFactory instance; + return &instance; +} + +WbConcreteNodeFactory::WbConcreteNodeFactory() { + // qDebug() << "WbConcreteNodeFactory CONSTRUCTOR (this:" << this << "). Base WbNodeFactory constructor should have run."; + // At this point, WbNodeFactory::gInstance should be 'this' (as a WbNodeFactory*) + // We can't directly call WbNodeFactory::instance() here to check gInstance without risking recursion + // if instance() itself had complex logic, but we know WbNodeFactory's constructor sets it. +} + +WbConcreteNodeFactory::~WbConcreteNodeFactory() { + // qDebug() << "WbConcreteNodeFactory DESTRUCTOR (this:" << this << "). Base WbNodeFactory destructor will run."; +} WbNode *WbConcreteNodeFactory::createNode(const QString &modelName, WbTokenizer *tokenizer, WbNode *parentNode, const QString *protoUrl) { + // qDebug() << "WbConcreteNodeFactory::createNode CALLED with modelName:" << modelName + // << "parentNode:" << (parentNode ? parentNode->nodeModelName() : "NULL") + // << "protoUrl:" << (protoUrl ? *protoUrl : "NULL"); + if (tokenizer) { + const WbToken *currentToken = tokenizer->peekToken(); + if (currentToken) { + // qDebug() << "WbConcreteNodeFactory::createNode: Tokenizer current token:" << currentToken->word() << "at line:" << + // currentToken->line() << "column:" << currentToken->column() << "pos:" << tokenizer->pos(); + } else { + // qDebug() << "WbConcreteNodeFactory::createNode: Tokenizer: No current token available (peekToken() is NULL). Pos:" << + // tokenizer->pos(); + } + } else { + // qDebug() << "WbConcreteNodeFactory::createNode: tokenizer is NULL."; + } if (modelName == "Accelerometer") return new WbAccelerometer(tokenizer); if (modelName == "Altimeter") @@ -293,34 +327,68 @@ WbNode *WbConcreteNodeFactory::createNode(const QString &modelName, WbTokenizer return new WbVacuumGripper(tokenizer); if (modelName == "Viewpoint") return new WbViewpoint(tokenizer); - if (modelName == "WorldInfo") - return new WbWorldInfo(tokenizer); - if (modelName == "Zoom") - return new WbZoom(tokenizer); + if (modelName == "WorldInfo") { + // qDebug() << "WbConcreteNodeFactory::createNode: Creating WbWorldInfo"; + WbNode *node = new WbWorldInfo(tokenizer); + // qDebug() << "WbConcreteNodeFactory::createNode: Created WbWorldInfo:" << (node ? node->nodeModelName() : "NULL"); + return node; + } + if (modelName == "Zoom") { + // qDebug() << "WbConcreteNodeFactory::createNode: Creating WbZoom"; + WbNode *node = new WbZoom(tokenizer); + // qDebug() << "WbConcreteNodeFactory::createNode: Created WbZoom:" << (node ? node->nodeModelName() : "NULL"); + return node; + } + // ... (other built-in nodes will be here, ensure to add qDebug before and after their creation if needed for deeper + // debugging) + // qDebug() << "WbConcreteNodeFactory::createNode: ModelName" << modelName << "not a built-in, looking for PROTOs..."; // look for PROTOs WbProtoModel *model; const QString &worldPath = WbWorld::instance() ? WbWorld::instance()->fileName() : ""; + // qDebug() << "WbConcreteNodeFactory::createNode: worldPath for PROTO search:" << worldPath; if (protoUrl) { + // qDebug() << "WbConcreteNodeFactory::createNode: protoUrl provided:" << *protoUrl; const QString prefix = WbUrl::computePrefix(*protoUrl); + // qDebug() << "WbConcreteNodeFactory::createNode: prefix for protoUrl:" << prefix; model = WbProtoManager::instance()->readModel(*protoUrl, worldPath, prefix); + // qDebug() << "WbConcreteNodeFactory::createNode: WbProtoManager::readModel result:" << (model ? model->name() : "NULL"); } else { + // qDebug() << "WbConcreteNodeFactory::createNode: protoUrl NOT provided, finding model based on tokenizer file info."; const QString &parentFilePath = tokenizer->fileName().isEmpty() ? tokenizer->referralFile() : tokenizer->fileName(); + // qDebug() << "WbConcreteNodeFactory::createNode: parentFilePath for PROTO search:" << parentFilePath; model = WbProtoManager::instance()->findModel(modelName, worldPath, parentFilePath); + // qDebug() << "WbConcreteNodeFactory::createNode: WbProtoManager::findModel result:" << (model ? model->name() : "NULL"); } - if (!model) + if (!model) { + // qDebug() << "WbConcreteNodeFactory::createNode: PROTO model" << modelName << "not found. Returning NULL."; return NULL; + } + // qDebug() << "WbConcreteNodeFactory::createNode: Found PROTO model:" << model->name(); // reset global parent that could be changed while parsing the PROTO model - if (parentNode) + if (parentNode) { + // qDebug() << "WbConcreteNodeFactory::createNode: Setting global parent node to:" << parentNode->nodeModelName(); WbNode::setGlobalParentNode(parentNode); + } else { + // qDebug() << "WbConcreteNodeFactory::createNode: No parentNode, not resetting global parent."; + } + // qDebug() << "WbConcreteNodeFactory::createNode: Creating PROTO instance for" << model->name(); WbNode *protoInstance = WbNode::createProtoInstance(model, tokenizer, WbWorld::instance() ? WbWorld::instance()->fileName() : ""); - if (protoInstance) + // qDebug() << "WbConcreteNodeFactory::createNode: Created PROTO instance:" << (protoInstance ? protoInstance->nodeModelName() + // : "NULL"); + + if (protoInstance) { + // qDebug() << "WbConcreteNodeFactory::createNode: Subscribing PROTO instance to TemplateManager."; WbTemplateManager::instance()->subscribe(protoInstance, false); + } + // qDebug() << "WbConcreteNodeFactory::createNode: Fixing backward compatibility for PROTO instance (if any)."; WbNodeUtilities::fixBackwardCompatibility(protoInstance); + // qDebug() << "WbConcreteNodeFactory::createNode: Returning PROTO instance:" << (protoInstance ? + // protoInstance->nodeModelName() : "NULL"); return protoInstance; } diff --git a/src/webots/nodes/utils/WbConcreteNodeFactory.hpp b/src/webots/nodes/utils/WbConcreteNodeFactory.hpp index ad5d6542cbc..17b30ec92c3 100644 --- a/src/webots/nodes/utils/WbConcreteNodeFactory.hpp +++ b/src/webots/nodes/utils/WbConcreteNodeFactory.hpp @@ -36,10 +36,11 @@ class WbConcreteNodeFactory : public WbNodeFactory { bool validateExistingChildNode(const WbField *field, const WbNode *childNode, const WbNode *node, bool isInBoundingObject, QString &errorMessage) const override; + static WbConcreteNodeFactory *getInstance(); + private: - WbConcreteNodeFactory() {} - virtual ~WbConcreteNodeFactory() override {} - static WbConcreteNodeFactory gFactory; + WbConcreteNodeFactory(); // Declaration only + virtual ~WbConcreteNodeFactory() override; // Declaration only }; #endif diff --git a/src/webots/nodes/utils/WbJoystickInterface.cpp b/src/webots/nodes/utils/WbJoystickInterface.cpp index 822e37fcf18..67d392bae34 100644 --- a/src/webots/nodes/utils/WbJoystickInterface.cpp +++ b/src/webots/nodes/utils/WbJoystickInterface.cpp @@ -17,10 +17,10 @@ #include -#include -#include -#include -#include +#include +#include +#include +#include #define SIGMOID_LAMBDA 0.0001 diff --git a/src/webots/nodes/utils/WbJoystickListener.hpp b/src/webots/nodes/utils/WbJoystickListener.hpp index 13cb2d0809d..a5bf410f542 100644 --- a/src/webots/nodes/utils/WbJoystickListener.hpp +++ b/src/webots/nodes/utils/WbJoystickListener.hpp @@ -15,7 +15,7 @@ #ifndef WB_JOYSTICK_LISTENER_HPP #define WB_JOYSTICK_LISTENER_HPP -#include +#include #include class WbJoystickListener : public QObject, public OIS::JoyStickListener { diff --git a/src/webots/nodes/utils/WbVirtualRealityHeadset.cpp b/src/webots/nodes/utils/WbVirtualRealityHeadset.cpp index e7d23307a9b..9b61b7b9055 100644 --- a/src/webots/nodes/utils/WbVirtualRealityHeadset.cpp +++ b/src/webots/nodes/utils/WbVirtualRealityHeadset.cpp @@ -41,7 +41,7 @@ #include #include -#include +//#include #include #include diff --git a/src/webots/ode/CMakeLists.txt b/src/webots/ode/CMakeLists.txt new file mode 100644 index 00000000000..571893fa3ab --- /dev/null +++ b/src/webots/ode/CMakeLists.txt @@ -0,0 +1,6 @@ +add_webots_library(ode_lib) + +target_link_libraries(ode_lib + PRIVATE + maths_lib +) diff --git a/src/webots/plugins/CMakeLists.txt b/src/webots/plugins/CMakeLists.txt new file mode 100644 index 00000000000..6570ab611e7 --- /dev/null +++ b/src/webots/plugins/CMakeLists.txt @@ -0,0 +1,6 @@ +add_webots_library(plugins_lib) + +target_link_libraries(plugins_lib + PRIVATE + core_lib +) diff --git a/src/webots/scene_tree/CMakeLists.txt b/src/webots/scene_tree/CMakeLists.txt new file mode 100644 index 00000000000..522b60dc3dd --- /dev/null +++ b/src/webots/scene_tree/CMakeLists.txt @@ -0,0 +1,7 @@ +add_webots_library(scene_tree_lib) + +target_link_libraries(scene_tree_lib + PRIVATE + Qt6::Widgets + nodes_lib +) diff --git a/src/webots/sound/CMakeLists.txt b/src/webots/sound/CMakeLists.txt new file mode 100644 index 00000000000..bf562834d10 --- /dev/null +++ b/src/webots/sound/CMakeLists.txt @@ -0,0 +1,32 @@ +add_webots_library(sound_lib) +add_dependencies(sound_lib picotts) + +# Find SAPI (Speech API) +if (WIN32) + # Directly use MSYSTEM_PREFIX + set(SAPI_LIBRARY_PATH "$ENV{MSYSTEM_PREFIX}/lib/libsapi.a") + + # Check if SAPI library exists and link it + if (EXISTS ${SAPI_LIBRARY_PATH}) + target_link_libraries(sound_lib + PRIVATE + core_lib + OpenAL::OpenAL + pico::pico + Qt6::Xml + ${SAPI_LIBRARY_PATH} + ) + message(STATUS "SAPI library found and linked: ${SAPI_LIBRARY_PATH}") + else() + message(FATAL_ERROR "SAPI library not found at ${SAPI_LIBRARY_PATH}. Please ensure it is installed in your MSYS2 environment.") + endif() +else() + target_link_libraries(sound_lib + PRIVATE + core_lib + OpenAL::OpenAL + pico::pico + Qt6::Xml + ) +endif() + diff --git a/src/webots/sound/PicoTTSWrapper.h b/src/webots/sound/PicoTTSWrapper.h new file mode 100644 index 00000000000..f3584065999 --- /dev/null +++ b/src/webots/sound/PicoTTSWrapper.h @@ -0,0 +1,37 @@ +/* + * Copyright 1996-2025 Cyberbotics Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PICO_TTS_WRAPPER_H +#define PICO_TTS_WRAPPER_H + +// Rename C keyword 'this' to avoid conflicts +#define this pico_this + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#ifdef __cplusplus +} +#endif + +#undef this + +#endif // PICO_TTS_WRAPPER_H diff --git a/src/webots/sound/WbMicrosoftTextToSpeech.cpp b/src/webots/sound/WbMicrosoftTextToSpeech.cpp index 71674dbacfe..d0ecf7d13bd 100644 --- a/src/webots/sound/WbMicrosoftTextToSpeech.cpp +++ b/src/webots/sound/WbMicrosoftTextToSpeech.cpp @@ -19,9 +19,8 @@ #include #include -#include - #include +#include #include static QString gError = ""; diff --git a/src/webots/sound/WbPicoTextToSpeech.cpp b/src/webots/sound/WbPicoTextToSpeech.cpp index 93b83f6d5aa..eb54b7237d7 100644 --- a/src/webots/sound/WbPicoTextToSpeech.cpp +++ b/src/webots/sound/WbPicoTextToSpeech.cpp @@ -20,10 +20,8 @@ #include #include -#include -#include -#include #include +#include "PicoTTSWrapper.h" // adaptation layer defines #define PICO_MEM_SIZE 25000000 diff --git a/src/webots/user_commands/CMakeLists.txt b/src/webots/user_commands/CMakeLists.txt new file mode 100644 index 00000000000..e851bb57a35 --- /dev/null +++ b/src/webots/user_commands/CMakeLists.txt @@ -0,0 +1,7 @@ +add_webots_library(user_commands_lib) + +target_link_libraries(user_commands_lib + PRIVATE + Qt6::Widgets + editor_lib +) diff --git a/src/webots/util/CMakeLists.txt b/src/webots/util/CMakeLists.txt new file mode 100644 index 00000000000..358b23007a0 --- /dev/null +++ b/src/webots/util/CMakeLists.txt @@ -0,0 +1,7 @@ +add_webots_library(util_lib) + +target_link_libraries(util_lib + PRIVATE + Qt6::Network + Qt6::Widgets +) diff --git a/src/webots/vrml/CMakeLists.txt b/src/webots/vrml/CMakeLists.txt new file mode 100644 index 00000000000..f21391e3808 --- /dev/null +++ b/src/webots/vrml/CMakeLists.txt @@ -0,0 +1,20 @@ +# Enable Qt MOC +set(CMAKE_AUTOMOC ON) + +file(GLOB SOURCES "*.cpp") +add_webots_library(vrml_lib ${SOURCES}) + +target_include_directories(vrml_lib PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/src/webots/core + ${CMAKE_SOURCE_DIR}/src/webots/maths + ${CMAKE_SOURCE_DIR}/include +) + +target_link_libraries(vrml_lib + PUBLIC + Qt6::Core + Qt6::Gui + maths_lib + core_lib +) diff --git a/src/webots/vrml/WbNodeFactory.cpp b/src/webots/vrml/WbNodeFactory.cpp index 83412a388df..ef1972f9207 100644 --- a/src/webots/vrml/WbNodeFactory.cpp +++ b/src/webots/vrml/WbNodeFactory.cpp @@ -13,17 +13,22 @@ // limitations under the License. #include "WbNodeFactory.hpp" +#include // For qDebug static WbNodeFactory *gInstance = 0; WbNodeFactory *WbNodeFactory::instance() { + // qDebug() << "WbNodeFactory::instance() called, returning gInstance:" << gInstance; return gInstance; } WbNodeFactory::WbNodeFactory() { + // qDebug() << "WbNodeFactory CONSTRUCTOR (this:" << this << "). Setting gInstance."; gInstance = this; + // qDebug() << "WbNodeFactory CONSTRUCTOR: gInstance is now:" << gInstance; } WbNodeFactory::~WbNodeFactory() { - gInstance = 0; + if (gInstance == this) // Only nullify if it's us + gInstance = 0; } diff --git a/src/webots/vrml/WbNodeReader.cpp b/src/webots/vrml/WbNodeReader.cpp index 25f29960722..001fad0d29b 100644 --- a/src/webots/vrml/WbNodeReader.cpp +++ b/src/webots/vrml/WbNodeReader.cpp @@ -24,6 +24,7 @@ #include "WbTokenizer.hpp" #include "WbVrmlNodeUtilities.hpp" +#include // For qDebug() #include #include @@ -47,21 +48,71 @@ WbNodeReader::~WbNodeReader() { WbNode *WbNodeReader::createNode(const QString &modelName, WbTokenizer *tokenizer, const QString &worldPath, const QString &fileName) { - if (mMode == NORMAL) - return WbNodeFactory::instance()->createNode(WbNodeModel::compatibleNodeName(modelName), tokenizer); - - if (modelName == "Transform") - return WbVrmlNodeUtilities::transformBackwardCompatibility(tokenizer) ? new WbNode("Pose", worldPath, tokenizer) : - new WbNode("Transform", worldPath, tokenizer); - else { - if (WbNodeModel::findModel(modelName)) - return new WbNode(modelName, worldPath, tokenizer); + // qDebug() << "WbNodeReader::createNode called with modelName:" << modelName + // << "worldPath:" << worldPath << "fileName:" << fileName; + if (tokenizer) { + const WbToken *currentToken = tokenizer->peekToken(); + if (currentToken) { + // qDebug() << "Tokenizer current token:" << currentToken->word() << "at line:" << currentToken->line() << "column:" + // << currentToken->column() << "pos:" << tokenizer->pos(); + } else { + // qDebug() << "Tokenizer: No current token available (peekToken() is NULL). Pos:" << tokenizer->pos(); + } + } else { + // qDebug() << "WbNodeReader::createNode: tokenizer is NULL!"; + } + + if (mMode == NORMAL) { + /// qDebug() << "WbNodeReader::createNode: NORMAL mode, modelName:" << modelName; + // qDebug() << "WbNodeReader::createNode: NORMAL mode: Getting WbNodeFactory instance..."; + WbNodeFactory *factory = WbNodeFactory::instance(); + // qDebug() << "WbNodeReader::createNode: Value of 'factory' variable after call to WbNodeFactory::instance():" << + // factory; + if (!factory) { + // qDebug() << "WbNodeReader::createNode: NORMAL mode: WbNodeFactory::instance() returned NULL!"; + // Potentially handle error or return NULL, though a segfault elsewhere suggests it's not just returning null + return NULL; + } + // qDebug() << "WbNodeReader::createNode: NORMAL mode: WbNodeFactory instance obtained:" << factory; + // qDebug() << "WbNodeReader::createNode: NORMAL mode: Calling WbNodeModel::compatibleNodeName for" << modelName; + QString compatibleName = WbNodeModel::compatibleNodeName(modelName); + // qDebug() << "WbNodeReader::createNode: NORMAL mode: Compatible name:" << compatibleName; + /// qDebug() << "WbNodeReader::createNode: NORMAL mode: Calling factory->createNode for" << compatibleName; + WbNode *node = factory->createNode(compatibleName, tokenizer); + // qDebug() << "WbNodeReader::createNode: NORMAL mode, created node:" << (node ? node->nodeModelName() : "NULL"); + return node; } + + if (modelName == "Transform") { + // qDebug() << "WbNodeReader::createNode: Handling Transform backward compatibility"; + bool isPose = WbVrmlNodeUtilities::transformBackwardCompatibility(tokenizer); + // qDebug() << "WbNodeReader::createNode: isPose:" << isPose; + return isPose ? new WbNode("Pose", worldPath, tokenizer) : new WbNode("Transform", worldPath, tokenizer); + } else { + if (WbNodeModel::findModel(modelName)) { + // qDebug() << "WbNodeReader::createNode: Found model in WbNodeModel:" << modelName; + WbNode *node = new WbNode(modelName, worldPath, tokenizer); + // qDebug() << "WbNodeReader::createNode: Created WbNode for model:" << modelName << "node:" << (node ? + // node->nodeModelName() : "NULL"); + return node; + } + } + // qDebug() << "WbNodeReader::createNode: Attempting to find PROTO model:" << modelName << "in worldPath:" << worldPath << + // "fileName:" << fileName; WbProtoModel *const proto = WbProtoManager::instance()->findModel(modelName, worldPath, fileName); - if (proto) - return WbNode::createProtoInstance(proto, tokenizer, worldPath); + if (proto) { + // qDebug() << "WbNodeReader::createNode: Found PROTO model:" << modelName << "proto name:" << proto->name(); + WbNode *node = WbNode::createProtoInstance(proto, tokenizer, worldPath); + // qDebug() << "WbNodeReader::createNode: Created PROTO instance:" << (node ? node->nodeModelName() : "NULL"); + return node; + } - tokenizer->reportError(QObject::tr("Skipped unknown '%1' node or PROTO").arg(modelName)); + // qDebug() << "WbNodeReader::createNode: Skipped unknown node or PROTO:" << modelName; + if (tokenizer) + tokenizer->reportError(QObject::tr("Skipped unknown '%1' node or PROTO").arg(modelName)); + else + // qDebug() << "WbNodeReader::createNode: Cannot report error, tokenizer is NULL for unknown model:" << modelName; + return NULL; return NULL; } diff --git a/src/webots/vrml/WbParser.cpp b/src/webots/vrml/WbParser.cpp index 5ab9cdd2561..649bfcb26da 100644 --- a/src/webots/vrml/WbParser.cpp +++ b/src/webots/vrml/WbParser.cpp @@ -13,6 +13,7 @@ // limitations under the License. #include "WbParser.hpp" +#include // Added for debugging #include "WbApplicationInfo.hpp" #include "WbFieldModel.hpp" @@ -198,6 +199,7 @@ void WbParser::reportUnexpected(const QString &expected) const { } bool WbParser::parseWorld(const QString &worldPath, bool (*updateProgress)(int)) { + qDebug() << "WbParser::parseWorld called with worldPath:" << worldPath; mTokenizer->rewind(); try { while (!peekToken()->isEof()) { diff --git a/src/webots/vrml/WbTokenizer.cpp b/src/webots/vrml/WbTokenizer.cpp index 1ef183a0ea6..ced65e9eb56 100644 --- a/src/webots/vrml/WbTokenizer.cpp +++ b/src/webots/vrml/WbTokenizer.cpp @@ -13,6 +13,7 @@ // limitations under the License. #include "WbTokenizer.hpp" +#include // Added for debugging #include "WbApplicationInfo.hpp" #include "WbFileUtil.hpp" @@ -373,6 +374,7 @@ QString WbTokenizer::readWord() { } int WbTokenizer::tokenize(const QString &fileName, const QString &prefix) { + qDebug() << "WbTokenizer::tokenize called with fileName:" << fileName << "prefix:" << prefix; mFileName = fileName; mFileType = fileTypeFromFileName(fileName); mIndex = 0; diff --git a/src/webots/widgets/CMakeLists.txt b/src/webots/widgets/CMakeLists.txt new file mode 100644 index 00000000000..ee46b39e408 --- /dev/null +++ b/src/webots/widgets/CMakeLists.txt @@ -0,0 +1,8 @@ +add_webots_library(widgets_lib) + +target_link_libraries(widgets_lib + PRIVATE + Qt6::Gui + Qt6::Widgets + maths_lib +) diff --git a/src/webots/wren/CMakeLists.txt b/src/webots/wren/CMakeLists.txt new file mode 100644 index 00000000000..2169599f1d4 --- /dev/null +++ b/src/webots/wren/CMakeLists.txt @@ -0,0 +1,20 @@ +# Enable Qt MOC +set(CMAKE_AUTOMOC ON) + +file(GLOB SOURCES "*.cpp") +add_webots_library(wren_lib ${SOURCES}) + +target_include_directories(wren_lib PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/include +) + +target_link_libraries(wren_lib + PUBLIC + Qt6::Core + Qt6::Gui + Qt6::OpenGL + ${OPENGL_LIBRARIES} + maths_lib + wren +) diff --git a/src/wren/CMakeLists.txt b/src/wren/CMakeLists.txt new file mode 100644 index 00000000000..8516f3685a6 --- /dev/null +++ b/src/wren/CMakeLists.txt @@ -0,0 +1,51 @@ +# Wren library configuration + +# Source files +file(GLOB WREN_SOURCES + "*.cpp" +) + +# Header files +file(GLOB WREN_HEADERS + "*.hpp" +) + +# Create wren library +add_library(wren STATIC ${WREN_SOURCES} ${WREN_HEADERS}) +target_compile_options(wren PUBLIC -fPIC) + +# Find required packages +find_package(Freetype REQUIRED) +find_package(assimp REQUIRED) +find_package(glm REQUIRED) + +# Include directories +target_include_directories(wren PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/src/webots/external + ${CMAKE_SOURCE_DIR}/src/webots/external/siphash + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/src/webots/core + ${OPENGL_INCLUDE_DIR} + ${Qt6Core_INCLUDE_DIRS} + ${Qt6Gui_INCLUDE_DIRS} + ${Qt6OpenGL_INCLUDE_DIRS} + ${FREETYPE_INCLUDE_DIRS} + ${ASSIMP_INCLUDE_DIRS} + ${GLM_INCLUDE_DIRS} +) + +# Define GLM experimental features +add_definitions(-DGLM_ENABLE_EXPERIMENTAL) + +# Link libraries +target_link_libraries(wren PUBLIC + ${OPENGL_LIBRARIES} + Qt6::Core + Qt6::Gui + Qt6::OpenGL + ${FREETYPE_LIBRARIES} + assimp + glad +) +