commit 957fb82f0321bbc4978677610883f3eef568c72b Author: Relintai Date: Tue May 23 19:01:39 2023 +0200 Initial commit. Added https://github.com/godotengine/godot-cpp/tree/3.x 7c09b5484de21 as a base, but without the submodule. diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..1c26421 --- /dev/null +++ b/.clang-format @@ -0,0 +1,128 @@ +# Commented out parameters are those with the same value as base LLVM style +# We can uncomment them if we want to change their value, or enforce the +# chosen value in case the base style changes (last sync: Clang 6.0.1). +--- +### General config, applies to all languages ### +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: DontAlign +# AlignConsecutiveAssignments: false +# AlignConsecutiveDeclarations: false +# AlignEscapedNewlines: Right +# AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +# AllowShortBlocksOnASingleLine: false +# AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +# AllowShortIfStatementsOnASingleLine: false +# AllowShortLoopsOnASingleLine: false +# AlwaysBreakAfterDefinitionReturnType: None +# AlwaysBreakAfterReturnType: None +# AlwaysBreakBeforeMultilineStrings: false +# AlwaysBreakTemplateDeclarations: false +# BinPackArguments: true +# BinPackParameters: true +# BraceWrapping: +# AfterClass: false +# AfterControlStatement: false +# AfterEnum: false +# AfterFunction: false +# AfterNamespace: false +# AfterObjCDeclaration: false +# AfterStruct: false +# AfterUnion: false +# AfterExternBlock: false +# BeforeCatch: false +# BeforeElse: false +# IndentBraces: false +# SplitEmptyFunction: true +# SplitEmptyRecord: true +# SplitEmptyNamespace: true +# BreakBeforeBinaryOperators: None +# BreakBeforeBraces: Attach +# BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: false +# BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: AfterColon +# BreakStringLiterals: true +ColumnLimit: 0 +# CommentPragmas: '^ IWYU pragma:' +# CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +# DerivePointerAlignment: false +# DisableFormat: false +# ExperimentalAutoDetectBinPacking: false +# FixNamespaceComments: true +# ForEachMacros: +# - foreach +# - Q_FOREACH +# - BOOST_FOREACH +# IncludeBlocks: Preserve +IncludeCategories: + - Regex: '".*"' + Priority: 1 + - Regex: '^<.*\.h>' + Priority: 2 + - Regex: '^<.*' + Priority: 3 +# IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: true +# IndentPPDirectives: None +IndentWidth: 4 +# IndentWrappedFunctionNames: false +# JavaScriptQuotes: Leave +# JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +# MacroBlockBegin: '' +# MacroBlockEnd: '' +# MaxEmptyLinesToKeep: 1 +# NamespaceIndentation: None +# PenaltyBreakAssignment: 2 +# PenaltyBreakBeforeFirstCallParameter: 19 +# PenaltyBreakComment: 300 +# PenaltyBreakFirstLessLess: 120 +# PenaltyBreakString: 1000 +# PenaltyExcessCharacter: 1000000 +# PenaltyReturnTypeOnItsOwnLine: 60 +# PointerAlignment: Right +# RawStringFormats: +# - Delimiter: pb +# Language: TextProto +# BasedOnStyle: google +# ReflowComments: true +# SortIncludes: true +# SortUsingDeclarations: true +# SpaceAfterCStyleCast: false +# SpaceAfterTemplateKeyword: true +# SpaceBeforeAssignmentOperators: true +# SpaceBeforeParens: ControlStatements +# SpaceInEmptyParentheses: false +# SpacesBeforeTrailingComments: 1 +# SpacesInAngles: false +# SpacesInContainerLiterals: true +# SpacesInCStyleCastParentheses: false +# SpacesInParentheses: false +# SpacesInSquareBrackets: false +TabWidth: 4 +UseTab: Always +--- +### C++ specific config ### +Language: Cpp +Standard: Cpp11 +--- +### ObjC specific config ### +Language: ObjC +Standard: Cpp11 +ObjCBlockIndentWidth: 4 +# ObjCSpaceAfterProperty: false +# ObjCSpaceBeforeProtocolList: true +--- +### Java specific config ### +Language: Java +# BreakAfterJavaFieldAnnotations: false +JavaImportGroups: ['org.godotengine', 'android', 'androidx', 'com.android', 'com.google', 'java', 'javax'] +... diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9abe13e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +*.c eol=lf +*.cpp eol=lf +*.gd eol=lf +*.tscn eol=lf +*.cfg eol=lf +*.godot eol=lf diff --git a/.github/actions/godot-cache/action.yml b/.github/actions/godot-cache/action.yml new file mode 100644 index 0000000..2d7afc8 --- /dev/null +++ b/.github/actions/godot-cache/action.yml @@ -0,0 +1,22 @@ +name: Setup Godot build cache +description: Setup Godot build cache. +inputs: + cache-name: + description: The cache base name (job name by default). + default: "${{github.job}}" + scons-cache: + description: The scons cache path. + default: "${{github.workspace}}/.scons-cache/" +runs: + using: "composite" + steps: + # Upload cache on completion and check it out now + - name: Load .scons_cache directory + uses: actions/cache@v3 + with: + path: ${{inputs.scons-cache}} + key: ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}} + restore-keys: | + ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}} + ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}} + ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}} diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..1230149 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3e47228 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,158 @@ +name: Continuous integration +on: [push, pull_request] + +env: + # Only used for the cache key. Increment version to force clean build. + GODOT_BASE_BRANCH: 3.x + +concurrency: + group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}} + cancel-in-progress: true + +jobs: + build: + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - name: 🐧 Linux (GCC) + os: ubuntu-18.04 + platform: linux + artifact-name: godot-cpp-linux-glibc2.27-x86_64-release + artifact-path: bin/libgodot-cpp.linux.release.64.a + godot_zip: Godot_v3.5-stable_linux_server.64.zip + executable: Godot_v3.5-stable_linux_server.64 + cache-name: linux-x86_64 + + - name: 🏁 Windows (x86_64, MSVC) + os: windows-2019 + platform: windows + artifact-name: godot-cpp-windows-msvc2019-x86_64-release + artifact-path: bin/libgodot-cpp.windows.release.64.lib + cache-name: windows-x86_64-msvc + + - name: 🏁 Windows (x86_64, MinGW) + os: windows-2019 + platform: windows + artifact-name: godot-cpp-linux-mingw-x86_64-release + artifact-path: bin/libgodot-cpp.windows.release.64.a + flags: use_mingw=yes + cache-name: windows-x86_64-mingw + + - name: 🍎 macOS (universal) + os: macos-11 + platform: osx + artifact-name: godot-cpp-macos-universal-release + artifact-path: bin/libgodot-cpp.osx.release.64.a + flags: macos_arch=universal + godot_zip: Godot_v3.5-stable_osx.universal.zip + executable: Godot.app/Contents/MacOS/Godot + cache-name: macos-unversal + + - name: 🤖 Android (arm64) + os: ubuntu-18.04 + platform: android + artifact-name: godot-cpp-android-arm64-release + artifact-path: bin/libgodot-cpp.android.release.arm64v8.a + flags: ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME android_arch=arm64v8 + cache-name: android-arm64 + + - name: 🍏 iOS (arm64) + os: macos-11 + platform: ios + artifact-name: godot-cpp-ios-arm64-release + artifact-path: bin/libgodot-cpp.ios.release.arm64.a + cache-name: ios-arm64 + + env: + SCONS_CACHE: ${{ github.workspace }}/.scons-cache/ + + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Setup Godot build cache + uses: ./.github/actions/godot-cache + with: + cache-name: ${{ matrix.cache-name }} + continue-on-error: true + + - name: Set up Python (for SCons) + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Linux dependencies + if: ${{ matrix.platform == 'linux' }} + run: | + sudo apt-get update -qq + sudo apt-get install -qqq build-essential pkg-config + + - name: Install scons + run: | + python -m pip install scons + + - name: Windows dependency (MinGW) + if: ${{ matrix.platform == 'windows' }} + uses: egor-tensin/setup-mingw@v2 + + - name: Build godot-cpp (debug) + run: | + scons platform=${{ matrix.platform }} target=debug ${{ matrix.flags }} + + - name: Build test without rebuilding godot-cpp (debug) + run: | + cd test + scons platform=${{ matrix.platform }} target=debug ${{ matrix.flags }} build_library=no + + - name: Build test and godot-cpp (release) + run: | + cd test + scons platform=${{ matrix.platform }} target=release ${{ matrix.flags }} + + - name: Run test GDNative library + if: ${{ matrix.platform == 'linux' || matrix.platform == 'osx' }} + run: | + curl -LO https://downloads.tuxfamily.org/godotengine/3.5/${{ matrix.godot_zip }} + unzip ${{ matrix.godot_zip }} + ./${{ matrix.executable }} --path test -s script.gd + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.artifact-name }} + path: ${{ matrix.artifact-path }} + if-no-files-found: error + + static-checks: + name: 📊 Static Checks (clang-format) + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Make apt sources.list use the default Ubuntu repositories + run: | + sudo rm -f /etc/apt/sources.list.d/* + sudo cp -f misc/ci/sources.list /etc/apt/sources.list + sudo apt-get update + + - name: Install dependencies + run: | + sudo apt-get install -qq dos2unix recode clang-format-11 + sudo update-alternatives --remove-all clang-format + sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-11 100 + + - name: Style checks via clang-format + run: | + bash ./misc/scripts/clang_format.sh + + - name: Bindings generation checks (ensures get_file_list returns all generated files) + run: | + python ./misc/scripts/check_get_file_list.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b031b69 --- /dev/null +++ b/.gitignore @@ -0,0 +1,180 @@ +# Misc +gen/* +logs/* +*.log + +# The default cache directory +.scons_cache/ + +# Binaries +*.o +*.os +*.so +*.obj +*.bc +*.pyc +*.dblite +*.pdb +*.lib +bin +*.config +*.creator +*.creator.user +*.files +*.includes + +# Gprof output +gmon.out + +# Vim temp files +*.swo +*.swp + +# Qt project files +*.config +*.creator +*.creator.* +*.files +*.includes +*.cflags +*.cxxflags + +# Eclipse CDT files +.cproject +.settings/ + +# Geany/geany-plugins files +*.geany +.geanyprj + +# Misc +.DS_Store +logs/ + +# for projects that use SCons for building: http://http://www.scons.org/ +.sconf_temp +.sconsign.dblite +*.pyc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.opendb +*.VC.VC.opendb +enc_temp_folder/ + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# CodeLite project files +*.project +*.workspace +.codelite/ + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +__pycache__/ + +# KDE +.directory + +#Kdevelop project files +*.kdev4 + +# xCode +xcuserdata + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ +logo.h +*.autosave + +# https://github.com/github/gitignore/blob/master/Global/Tags.gitignore +# Ignore tags created by etags, ctags, gtags (GNU global) and cscope +TAGS +!TAGS/ +tags +*.tags +!tags/ +gtags.files +GTAGS +GRTAGS +GPATH +cscope.files +cscope.out +cscope.in.out +cscope.po.out +godot.creator.* + +# Visual Studio 2017 and Visual Studio Code workspace folder +/.vs +/.vscode + +# Visual Studio Code workspace file +*.code-workspace + +# Scons progress indicator +.scons_node_count + +# ccls cache (https://github.com/MaskRay/ccls) +.ccls-cache/ + +# compile commands (https://clang.llvm.org/docs/JSONCompilationDatabase.html) +compile_commands.json diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..36c9545 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "godot-headers"] + path = godot-headers + url = https://github.com/godotengine/godot-headers diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b91f0d2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,210 @@ +# cmake arguments +# CMAKE_BUILD_TYPE: Compilation target (Debug or Release defaults to Debug) +# +# godot-cpp cmake arguments +# GODOT_HEADERS_DIR: This is where the gdnative include folder is (godot_source/modules/gdnative/include) +# GODOT_CUSTOM_API_FILE: This is if you have another path for the godot_api.json +# +# Android cmake arguments +# CMAKE_TOOLCHAIN_FILE: The path to the android cmake toolchain ($ANDROID_NDK/build/cmake/android.toolchain.cmake) +# ANDROID_NDK: The path to the android ndk root folder +# ANDROID_TOOLCHAIN_NAME: The android toolchain (arm-linux-androideabi-4.9 or aarch64-linux-android-4.9 or x86-4.9 or x86_64-4.9) +# ANDROID_PLATFORM: The android platform version (android-23) +# More info here: https://godot.readthedocs.io/en/latest/development/compiling/compiling_for_android.html +# +# Examples +# +# Builds a debug version: +# cmake . +# cmake --build . +# +# Builds a release version with clang +# CC=/usr/bin/clang CXX=/usr/bin/clang++ cmake -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" . +# cmake --build . +# +# Builds an android armeabi-v7a debug version: +# cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_NDK=$ANDROID_NDK \ +# -DANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-4.9 -DANDROID_PLATFORM=android-23 -DCMAKE_BUILD_TYPE=Debug . +# cmake --build . +# +# Protip +# Generate the buildfiles in a sub directory to not clutter the root directory with build files: +# mkdir build && cd build && cmake -G "Unix Makefiles" .. && cmake --build . +# +# Todo +# Test build for Windows, Mac and mingw. + +project(godot-cpp) +cmake_minimum_required(VERSION 3.6) + +option(GENERATE_TEMPLATE_GET_NODE "Generate a template version of the Node class's get_node." ON) + +# Change the output directory to the bin directory +set(BUILD_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${BUILD_PATH}") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${BUILD_PATH}") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${BUILD_PATH}") +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${BUILD_PATH}") +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${BUILD_PATH}") +SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${BUILD_PATH}") +SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${BUILD_PATH}") +SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${BUILD_PATH}") +SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${BUILD_PATH}") + +# Default build type is Debug in the SConstruct +if("${CMAKE_BUILD_TYPE}" STREQUAL "") + set(CMAKE_BUILD_TYPE Debug) +endif() + +if(CMAKE_BUILD_TYPE MATCHES Debug) + add_definitions(-D_DEBUG) +else() + add_definitions(-DNDEBUG) +endif(CMAKE_BUILD_TYPE MATCHES Debug) + +# Set the c++ standard to c++14 +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +if(NOT DEFINED BITS) + set(BITS 32) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(BITS 64) + endif(CMAKE_SIZEOF_VOID_P EQUAL 8) +endif() + +# Input from user for godot headers and the api file +set(GODOT_HEADERS_DIR "godot-headers" CACHE STRING "") +set(GODOT_CUSTOM_API_FILE "godot-headers/api.json" CACHE STRING "") + +set(GODOT_COMPILE_FLAGS ) +set(GODOT_LINKER_FLAGS ) + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + # using Visual Studio C++ + set(GODOT_COMPILE_FLAGS "/EHsc /WX") # /GF /MP + + if(CMAKE_BUILD_TYPE MATCHES Debug) + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /MDd") # /Od /RTC1 /Zi + else() + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /MD /O2") # /Oy /GL /Gy + STRING(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) + endif(CMAKE_BUILD_TYPE MATCHES Debug) + + # Disable conversion warning, trunkation, unreferenced var, signed missmatch + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /wd4244 /wd4305 /wd4101 /wd4018 /wd4267") + + # Todo: Check if needed. + add_definitions(-DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_WARNINGS) + + # Unkomment for warning level 4 + #if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + # string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + #endif() + +else() # GCC/Clang + set(GODOT_LINKER_FLAGS "-static-libgcc -static-libstdc++ -Wl,-R,'$$ORIGIN'") + + if(NOT CMAKE_SYSTEM_NAME MATCHES "Windows") + set(GODOT_COMPILE_FLAGS "-fPIC") + endif() + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -g -Wwrite-strings") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wchar-subscripts -Wcomment -Wdisabled-optimization") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wformat -Wformat=2 -Wformat-security -Wformat-y2k") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wimport -Winit-self -Winline -Winvalid-pch -Werror") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wmissing-braces -Wmissing-format-attribute") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wmissing-include-dirs -Wmissing-noreturn -Wpacked -Wpointer-arith") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wredundant-decls -Wreturn-type -Wsequence-point") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wswitch -Wswitch-enum -Wtrigraphs") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wuninitialized -Wunknown-pragmas -Wunreachable-code -Wunused-label") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wunused-value -Wvariadic-macros -Wvolatile-register-var -Wno-error=attributes") + + # -Wshadow -Wextra -Wall -Weffc++ -Wfloat-equal -Wstack-protector -Wunused-parameter -Wsign-compare -Wunused-variable -Wcast-align + # -Wunused-function -Wstrict-aliasing -Wstrict-aliasing=2 -Wmissing-field-initializers + + if(NOT CMAKE_SYSTEM_NAME STREQUAL "Android") + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -Wno-ignored-attributes") + endif() + + if(CMAKE_BUILD_TYPE MATCHES Debug) + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-omit-frame-pointer -O0") + else() + set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -O3") + endif(CMAKE_BUILD_TYPE MATCHES Debug) +endif() + +# Generate source from the bindings file +find_package(Python3 3.4 REQUIRED) # pathlib should be present +if(GENERATE_TEMPLATE_GET_NODE) + set(GENERATE_BINDING_PARAMETERS "True") +else() + set(GENERATE_BINDING_PARAMETERS "False") +endif() + +message(STATUS "Generating Bindings") +execute_process(COMMAND "${Python3_EXECUTABLE}" "-c" "import binding_generator; binding_generator.print_file_list(\"${GODOT_CUSTOM_API_FILE}\", \"${CMAKE_CURRENT_BINARY_DIR}\", headers=True)" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + RESULT_VARIABLE HEADERS_FILE_LIST_RESULT + OUTPUT_VARIABLE HEADERS_FILE_LIST +) +set(HEADERS_FILE_LIST ${HEADERS_FILE_LIST}) + +execute_process(COMMAND "${Python3_EXECUTABLE}" "-c" "import binding_generator; binding_generator.print_file_list(\"${GODOT_CUSTOM_API_FILE}\", \"${CMAKE_CURRENT_BINARY_DIR}\", sources=True)" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + RESULT_VARIABLE SOURCES_FILE_LIST_RESULT + OUTPUT_VARIABLE SOURCES_FILE_LIST +) +set(SOURCES_FILE_LIST ${SOURCES_FILE_LIST}) + +add_custom_command(OUTPUT ${HEADERS_FILE_LIST} ${SOURCES_FILE_LIST} + COMMAND "${Python3_EXECUTABLE}" "-c" "import binding_generator; binding_generator.generate_bindings(\"${GODOT_CUSTOM_API_FILE}\", \"${GENERATE_BINDING_PARAMETERS}\", \"${CMAKE_CURRENT_BINARY_DIR}\")" + VERBATIM + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + MAIN_DEPENDENCY ${GODOT_CUSTOM_API_FILE} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/binding_generator.py + COMMENT Generating Bindings +) + +# Get Sources +file(GLOB_RECURSE SOURCES src/*.c**) +file(GLOB_RECURSE HEADERS include/*.h**) + +# Define our godot-cpp library +add_library(${PROJECT_NAME} + ${SOURCES} + ${SOURCES_FILE_LIST} + ${HEADERS} + ${HEADERS_FILE_LIST} +) +target_include_directories(${PROJECT_NAME} + PUBLIC + include + include/core + ${CMAKE_CURRENT_BINARY_DIR}/include/gen/ +) + +# Put godot headers as SYSTEM PUBLIC to exclude warnings from irrelevant headers +target_include_directories(${PROJECT_NAME} + SYSTEM PUBLIC + ${GODOT_HEADERS_DIR} +) + +# Add the compile flags +set_property(TARGET ${PROJECT_NAME} APPEND_STRING PROPERTY COMPILE_FLAGS ${GODOT_COMPILE_FLAGS}) +set_property(TARGET ${PROJECT_NAME} APPEND_STRING PROPERTY LINK_FLAGS ${GODOT_LINKER_FLAGS}) + +# Create the correct name (godot.os.build_type.system_bits) + +string(TOLOWER "${CMAKE_SYSTEM_NAME}" SYSTEM_NAME) +string(TOLOWER "${CMAKE_BUILD_TYPE}" BUILD_TYPE) + +if(ANDROID) + # Added the android abi after system name + set(SYSTEM_NAME ${SYSTEM_NAME}.${ANDROID_ABI}) + # Android does not have the bits at the end if you look at the main godot repo build + set_property(TARGET ${PROJECT_NAME} PROPERTY OUTPUT_NAME "godot-cpp.${SYSTEM_NAME}.${BUILD_TYPE}") +else() + set_property(TARGET ${PROJECT_NAME} PROPERTY OUTPUT_NAME "godot-cpp.${SYSTEM_NAME}.${BUILD_TYPE}.${BITS}") +endif() diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..654253a --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# MIT License + +Copyright (c) 2017-2022 Godot Engine contributors. + +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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7429058 --- /dev/null +++ b/Makefile @@ -0,0 +1,47 @@ +GENERATE_BINDINGS = no +HEADERS = godot-headers +TARGET = debug +USE_CLANG = no + +BASE = scons use_llvm=$(USE_CLANG) generate_bindings=$(GENERATE_BINDINGS) target=$(TARGET) headers=$(HEADERS) +LINUX = $(BASE) platform=linux +WINDOWS = $(BASE) platform=windows +OSX = $(BASE) platform=osx + + +all: + make linux + make windows + + +linux: + make linux32 + make linux64 + +linux32: SConstruct + $(LINUX) bits=32 + +linux64: SConstruct + $(LINUX) bits=64 + + +windows: + make windows32 + make windows64 + +windows32: SConstruct + $(WINDOWS) bits=32 + +windows64: SConstruct + $(WINDOWS) bits=64 + + +osx: + make osx32 + make osx64 + +osx32: SConstruct + $(OSX) bits=32 + +osx64: SConstruct + $(OSX) bits=64 diff --git a/README.md b/README.md new file mode 100644 index 0000000..a55cd04 --- /dev/null +++ b/README.md @@ -0,0 +1,372 @@ +# godot-cpp + +This repository contains the *C++ bindings* for the [**Godot Engine**](https://github.com/godotengine/godot)'s GDNative API. + +- [**Versioning**](#versioning) +- [**Contributing**](#contributing) +- [**Getting Started**](#getting-started) +- [**Creating a simple class**](#creating-a-simple-class) + +## Versioning + +This repositories follows the same branch versioning as the main [Godot Engine +repository](https://github.com/godotengine/godot): + +- `master` tracks the current development branch. +- `3.x` tracks the development of the next 3.x minor release. +- Other versioned branches (e.g. `3.3`, `3.2`) track the latest stable release + in the corresponding branch. + +Stable releases are also tagged on this repository: +[**Tags**](https://github.com/godotengine/godot-cpp/tags). + +**For any project built against a stable release of Godot, we recommend using +this repository as a Git submodule, checking out the specific tag matching your +Godot version.** + +> As the `master` and `3.x` branches are constantly getting updates, if you are +> using `godot-cpp` against a more current version of Godot, see the instructions +> in [**godot-headers**](https://github.com/godotengine/godot-headers) for +> updating the relevant files. + +## Contributing + +We greatly appreciate help in maintaining and extending this project. If you +wish to help out, ensure you have an account on GitHub and create a "fork" of +this repository. Rémi "Akien" Verschelde wrote an excellent bit of documentation +for the main Godot project on this: +[Pull request workflow](https://docs.godotengine.org/en/stable/community/contributing/pr_workflow.html) + +Please install clang-format and copy the files in `misc/hooks` into `.git/hooks` +so formatting is done before your changes are submitted. + +## Getting Started + +| **Build latest version of Godot** | [**GitHub**](https://github.com/godotengine/godot) | [**Docs**](https://godot.readthedocs.io/en/latest/development/compiling/index.html) | +| --- | --- | --- | + +### Setting up a new project + +We recommend using Git for managing your project. The instructions below assume +you're using Git. Alternatively, you can download the source code directly from +GitHub. In this case, you need to download both +[godot-cpp](https://github.com/godotengine/godot-cpp) and +[godot-headers](https://github.com/godotengine/godot-headers). + +```bash +mkdir SimpleLibrary +cd SimpleLibrary +mkdir bin +mkdir src +git clone --recursive https://github.com/godotengine/godot-cpp +``` + +If you wish to use a specific branch, add the -b option to the clone command: + +```bash +git clone --recursive https://github.com/godotengine/godot-cpp -b 3.0 +``` + +If your project is an existing repository, use a Git submodule instead: + +```bash +git submodule add https://github.com/godotengine/godot-cpp +git submodule update --init --recursive +``` + +Right now, our directory structure should look like this: + +```text +SimpleLibrary/ +├─godot-cpp/ +| └─godot-headers/ +├─bin/ +└─src/ +``` + +### Updating the `api.json` file + +Our `api.json` file contains metadata for all the classes that are part of the +Godot core. This metadata is required to generate the C++ binding classes for +use in GDNative modules. + +This file is supplied with our +[godot-headers](https://github.com/godotengine/godot-headers) repository +for your convenience. However, if you're running a custom build of Godot and +need access to classes that have recent changes, you must generate a new +`api.json` file. You do this by starting your Godot executable with the +following parameters: + +```bash +godot --gdnative-generate-json-api api.json +``` + +Now copy the `api.json` file into your folder structure to make it easier to +access. + +See the remark below for the extra ```custom_api_file``` SCons argument, which +is required to tell SCons where to find your file. + +### Compiling the C++ bindings library + +The final step is to compile our C++ bindings library: + +```bash +cd godot-cpp +scons platform= generate_bindings=yes +cd .. +``` + +Replace `` with either `windows`, `linux`, `osx` or `android`. If +you leave out `platform`, the target platform will automatically be detected +from the host platform. + +The resulting library will be created in `godot-cpp/bin/`, take note of its name +as it'll differ depending on the target platform. + +#### Compiling for Android + +Download the latest [Android NDK](https://developer.android.com/ndk/downloads) +and set the NDK path. + +```bash +scons platform=android generate_bindings=yes ANDROID_NDK_ROOT="/PATH-TO-ANDROID-NDK/" android_arch= +``` + +The value of `android_arch` can be `armv7, arm64v8, x86, x86_64`. Most Android +devices in use nowadays use an ARM architecture, so compiling for `armv7` and +`arm64v8` is often enough when distributing an application. + +`ANDROID_NDK_ROOT` can also be set in the environment variables of your PC if +you don't want to include it in your SCons call. + +#### Compilation options + +You can optionally add the following options to the SCons command line: + +- When targeting Linux, add `use_llvm=yes` to use Clang instead of GCC. +- When targeting Windows, add `use_mingw=yes` to use MinGW instead of MSVC. +- When targeting Windows, include `target=runtime` to build a runtime build. +- To use an alternative `api.json` file, add `use_custom_api_file=yes + custom_api_file=../api.json`. Be sure to specify the correct location where + you placed your file (it can be a relative or absolute path). + +## Creating a simple class + +Create `init.cpp` under `SimpleLibrary/src/` and add the following code: + +```cpp +#include +#include + +using namespace godot; + +class SimpleClass : public Reference { + GODOT_CLASS(SimpleClass, Reference); +public: + SimpleClass() { } + + /** `_init` must exist as it is called by Godot. */ + void _init() { } + + void test_void_method() { + Godot::print("This is test"); + } + + Variant method(Variant arg) { + Variant ret; + ret = arg; + + return ret; + } + + static void _register_methods() { + register_method("method", &SimpleClass::method); + + /** + * The line below is equivalent to the following GDScript export: + * export var _name = "SimpleClass" + **/ + register_property("base/name", &SimpleClass::_name, String("SimpleClass")); + + /** Alternatively, with getter and setter methods: */ + register_property("base/value", &SimpleClass::set_value, &SimpleClass::get_value, 0); + + /** Registering a signal: **/ + // register_signal("signal_name"); + // register_signal("signal_name", "string_argument", GODOT_VARIANT_TYPE_STRING) + } + + String _name; + int _value; + + void set_value(int p_value) { + _value = p_value; + } + + int get_value() const { + return _value; + } +}; + +/** GDNative Initialize **/ +extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) { + godot::Godot::gdnative_init(o); +} + +/** GDNative Terminate **/ +extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) { + godot::Godot::gdnative_terminate(o); +} + +/** NativeScript Initialize **/ +extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) { + godot::Godot::nativescript_init(handle); + + godot::register_class(); +} +``` + +### Compiling the GDNative library + +Once you've compiled the GDNative C++ bindings (see above), you can compile the GDNative library we've just created. + +#### Linux + +```bash +cd SimpleLibrary +clang++ -fPIC -o src/init.o -c src/init.cpp -g -O3 -std=c++14 -Igodot-cpp/include -Igodot-cpp/include/core -Igodot-cpp/include/gen -Igodot-cpp/godot-headers +clang++ -o bin/libtest.so -shared src/init.o -Lgodot-cpp/bin -l +``` + +You'll need to replace `` with the file that was created in [**Compiling the cpp bindings library**](#compiling-the-cpp-bindings-library). + +This creates the file `libtest.so` in your `SimpleLibrary/bin` directory. + +#### Windows + +```bash +cd SimpleLibrary +cl /Fosrc/init.obj /c src/init.cpp /nologo -EHsc -DNDEBUG /MDd /Igodot-cpp\include /Igodot-cpp\include\core /Igodot-cpp\include\gen /Igodot-cpp\godot-headers +link /nologo /dll /out:bin\libtest.dll /implib:bin\libsimple.lib src\init.obj godot-cpp\bin\ +``` + +You'll need to replace `` with the file that was created +in [**Compiling the cpp bindingslibrary**](#compiling-the-cpp-bindings-library). +Replace `/MDd` with `/MD` to create a release build, which will run faster and +be smaller. + +This creates the file `libtest.dll` in your `SimpleLibrary/bin` directory. + +#### macOS + +For macOS, you'll need to find out which compiler flags need to be used. These +are likely similar to Linux when using Clang, but may not be identical. + +If you find suitable compiler flags for this example library, feel free to +submit a pull request :slightly_smiling_face: + +#### Android + +```bash +cd SimpleLibrary +aarch64-linux-android29-clang++ -fPIC -o src/init.o -c src/init.cpp -g -O3 -std=c++14 -Igodot-cpp/include -Igodot-cpp/include/core -Igodot-cpp/include/gen -Igodot-cpp/godot-headers +aarch64-linux-android29-clang++ -o bin/libtest.so -shared src/init.o -Lgodot-cpp/bin -l +``` + +You'll need to replace `` with the file that was created in [**Compiling the cpp bindings library**](#compiling-the-cpp-bindings-library). The command above targets `arm64v8`. To target `armv7`, use `armv7a-linux-androideabi29-clang++` instead of `aarch64-linux-android29-clang++`. + +This creates the file `libtest.so` in your `SimpleLibrary/bin` directory. + +#### iOS + +GDNative isn't supported on iOS yet. This is because iOS only allows linking +static libraries, not dynamic libraries. In theory, it would be possible to link +a GDNative library statically, but some of GDNative's convenience would be lost +in the process as one would have to recompile the engine on every change. See +[issue #30](https://github.com/godotengine/godot-headers/issues/30) in the +Godot headers repository for more information. + +#### HTML5 + +GDNative is supported on [specific exports](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_web.html#export-options) for the HTML5 platform since Godot `3.3`. Linking webassembly modules is currently underspecified in the standard, but [emscripten](https://emscripten.org/), which Godot uses to build the HTML5 version, implements its own linking system. + +To build GDNative libraries, you will need a recent version of [Emscripten](https://emscripten.org/). + +```bash +cd SimpleLibrary +emcc -o bin/libtest.wasm -g -O3 -s SIDE_MODULE=1 src/init.cpp godot-cpp/bin/ -Igodot-cpp/include -Igodot-cpp/include/core -Igodot-cpp/include/gen -Igodot-cpp/godot-headers +``` + +You'll need to replace `` with the file that was created in [**Compiling the cpp bindings library**](#compiling-the-cpp-bindings-library). + +This creates the file `libtest.so` in your `SimpleLibrary/bin` directory. + +### Creating `.gdnlib` and `.gdns` files + +Follow the instructions in +[godot-headers/README.md](https://github.com/godotengine/godot-headers/blob/master/README.md#how-do-i-use-native-scripts-from-the-editor) +to create the `.gdns` file. This file contains paths to GDNative libraries for +various platforms. This makes the library usable from Godot in a +platform-independent manner. + +### Implementing with GDScript + +Once your GDNative library is compiled and referenced in a `.gdns` file, you can use it in GDScript or C#. Here's an example with GDScript: + +```gdscript +var simpleclass = load("res://simpleclass.gdns").new() +simpleclass.method("Test argument") +``` + +### Using Godot classes in C++ + +Godot expects you to manage its classes the same way the engine does. These rules apply to all Godot classes, including your NativeScripts, but not to any normal C++ classes used in your library. + +- Instantiate Objects using `_new()`, not C++'s `new` operator. + +```cpp +Sprite *sprite = Sprite::_new(); +``` + +- Destroy Nodes using `queue_free()`, not C++'s `delete` operator. + +```cpp +some_old_node->queue_free(); +``` + +- Wrap References in `Ref` instead of passing around raw pointers. They are reference-counted and don't need to be freed manually. + +```cpp +Ref texture = resource_loader->load("res://icon.png"); +``` + +- Pass core types that do *not* inherit Object by value. The containers (Array, Dictionary, PoolArray, String) manage their own memory and do not need to be explicitly initialized or freed. + +```cpp +Array ints; +ints.append(123); +return ints; +``` + +- Initialize your NativeScript classes in their `_init()` method, not their constructor. The constructor can't access the base class's methods. + +- Cast objects using `Object::cast_to`, not unsafe C-style casts or `static_cast`. + +```cpp +MeshInstance *m = Object::cast_to(get_node("ChildNode")); +// `m` will be null if it's not a MeshInstance +if (m) { ... } +``` + +- **Never** use Godot types in static or global variables. The Godot API isn't loaded until after their constructors are called. + +```cpp +String s; // crashes +class SomeClass { + static Dictionary d; // crashes + + static Node *node_a = NULL; // fine, it's just a pointer + static Node *node_b = Node::_new(); // crashes +}; +``` diff --git a/SConstruct b/SConstruct new file mode 100644 index 0000000..f653d54 --- /dev/null +++ b/SConstruct @@ -0,0 +1,525 @@ +#!/usr/bin/env python + +import os +import sys +import subprocess +from binding_generator import scons_generate_bindings, scons_emit_files + +if sys.version_info < (3,): + + def decode_utf8(x): + return x + +else: + import codecs + + def decode_utf8(x): + return codecs.utf_8_decode(x)[0] + + +# Workaround for MinGW. See: +# http://www.scons.org/wiki/LongCmdLinesOnWin32 +if os.name == "nt": + import subprocess + + def mySubProcess(cmdline, env): + # print "SPAWNED : " + cmdline + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + proc = subprocess.Popen( + cmdline, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + startupinfo=startupinfo, + shell=False, + env=env, + ) + data, err = proc.communicate() + rv = proc.wait() + if rv: + print("=====") + print(err.decode("utf-8")) + print("=====") + return rv + + def mySpawn(sh, escape, cmd, args, env): + + newargs = " ".join(args[1:]) + cmdline = cmd + " " + newargs + + rv = 0 + if len(cmdline) > 32000 and cmd.endswith("ar"): + cmdline = cmd + " " + args[1] + " " + args[2] + " " + for i in range(3, len(args)): + rv = mySubProcess(cmdline + args[i], env) + if rv: + break + else: + rv = mySubProcess(cmdline, env) + + return rv + + +def add_sources(sources, dir, extension): + for f in os.listdir(dir): + if f.endswith("." + extension): + sources.append(dir + "/" + f) + + +# Try to detect the host platform automatically. +# This is used if no `platform` argument is passed +if sys.platform.startswith("linux"): + host_platform = "linux" +elif sys.platform.startswith("freebsd"): + host_platform = "freebsd" +elif sys.platform == "darwin": + host_platform = "osx" +elif sys.platform == "win32" or sys.platform == "msys": + host_platform = "windows" +else: + raise ValueError("Could not detect platform automatically, please specify with platform=") + +env = Environment(ENV=os.environ) + +# Default num_jobs to local cpu count if not user specified. +# SCons has a peculiarity where user-specified options won't be overridden +# by SetOption, so we can rely on this to know if we should use our default. +initial_num_jobs = env.GetOption("num_jobs") +altered_num_jobs = initial_num_jobs + 1 +env.SetOption("num_jobs", altered_num_jobs) +# os.cpu_count() requires Python 3.4+. +if hasattr(os, "cpu_count") and env.GetOption("num_jobs") == altered_num_jobs: + cpu_count = os.cpu_count() + if cpu_count is None: + print("Couldn't auto-detect CPU count to configure build parallelism. Specify it with the -j argument.") + else: + safer_cpu_count = cpu_count if cpu_count <= 4 else cpu_count - 1 + print( + "Auto-detected %d CPU cores available for build parallelism. Using %d cores by default. You can override it with the -j argument." + % (cpu_count, safer_cpu_count) + ) + env.SetOption("num_jobs", safer_cpu_count) + +is64 = sys.maxsize > 2 ** 32 +if ( + env["TARGET_ARCH"] == "amd64" + or env["TARGET_ARCH"] == "emt64" + or env["TARGET_ARCH"] == "x86_64" + or env["TARGET_ARCH"] == "arm64-v8a" +): + is64 = True + +opts = Variables([], ARGUMENTS) +opts.Add( + EnumVariable( + "platform", + "Target platform", + host_platform, + allowed_values=("linux", "freebsd", "osx", "windows", "android", "ios", "javascript"), + ignorecase=2, + ) +) +opts.Add(EnumVariable("bits", "Target platform bits", "64" if is64 else "32", ("32", "64"))) +opts.Add(BoolVariable("use_llvm", "Use the LLVM compiler - only effective when targeting Linux or FreeBSD", False)) +opts.Add(BoolVariable("use_mingw", "Use the MinGW compiler instead of MSVC - only effective on Windows", False)) +# Must be the same setting as used for cpp_bindings +opts.Add(EnumVariable("target", "Compilation target", "debug", allowed_values=("debug", "release"), ignorecase=2)) +opts.Add( + PathVariable( + "headers_dir", + "Path to the directory containing Godot headers", + "godot-headers", + PathVariable.PathIsDir, + ) +) +opts.Add(PathVariable("custom_api_file", "Path to a custom JSON API file", None, PathVariable.PathIsFile)) +opts.Add(BoolVariable("generate_bindings", "Force GDNative API bindings generation.", False)) +opts.Add( + EnumVariable( + "android_arch", + "Target Android architecture", + "armv7", + ["armv7", "arm64v8", "x86", "x86_64"], + ) +) +opts.Add("macos_deployment_target", "macOS deployment target", "default") +opts.Add("macos_sdk_path", "macOS SDK path", "") +opts.Add(EnumVariable("macos_arch", "Target macOS architecture", "universal", ["universal", "x86_64", "arm64"])) +opts.Add(EnumVariable("ios_arch", "Target iOS architecture", "arm64", ["armv7", "arm64", "x86_64"])) +opts.Add(BoolVariable("ios_simulator", "Target iOS Simulator", False)) +opts.Add( + "IPHONEPATH", + "Path to iPhone toolchain", + "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain", +) +opts.Add( + "android_api_level", + "Target Android API level", + "18" if ARGUMENTS.get("android_arch", "armv7") in ["armv7", "x86"] else "21", +) +opts.Add( + "ANDROID_NDK_ROOT", + "Path to your Android NDK installation. By default, uses ANDROID_NDK_ROOT from your defined environment variables.", + os.environ.get("ANDROID_NDK_ROOT", None), +) +opts.Add( + BoolVariable( + "generate_template_get_node", + "Generate a template version of the Node class's get_node.", + True, + ) +) + +opts.Add(BoolVariable("build_library", "Build the godot-cpp library.", True)) + +opts.Update(env) +Help(opts.GenerateHelpText(env)) + +# Detect and print a warning listing unknown SCons variables to ease troubleshooting. +unknown = opts.UnknownVariables() +if unknown: + print("WARNING: Unknown SCons variables were passed and will be ignored:") + for item in unknown.items(): + print(" " + item[0] + "=" + item[1]) + +# This makes sure to keep the session environment variables on Windows. +# This way, you can run SCons in a Visual Studio 2017 prompt and it will find +# all the required tools +if host_platform == "windows" and env["platform"] != "android": + if env["bits"] == "64": + env = Environment(TARGET_ARCH="amd64") + elif env["bits"] == "32": + env = Environment(TARGET_ARCH="x86") + + opts.Update(env) + +# Require C++14 +if host_platform == "windows" and env["platform"] == "windows" and not env["use_mingw"]: + # MSVC + env.Append(CCFLAGS=["/std:c++14"]) +else: + env.Append(CCFLAGS=["-std=c++14"]) + +if env["platform"] == "linux" or env["platform"] == "freebsd": + if env["use_llvm"]: + env["CXX"] = "clang++" + + env.Append(CCFLAGS=["-fPIC", "-Wwrite-strings"]) + env.Append(LINKFLAGS=["-Wl,-R,'$$ORIGIN'"]) + + if env["target"] == "debug": + env.Append(CCFLAGS=["-Og", "-g"]) + elif env["target"] == "release": + env.Append(CCFLAGS=["-O3"]) + + if env["bits"] == "64": + env.Append(CCFLAGS=["-m64"]) + env.Append(LINKFLAGS=["-m64"]) + elif env["bits"] == "32": + env.Append(CCFLAGS=["-m32"]) + env.Append(LINKFLAGS=["-m32"]) + +elif env["platform"] == "osx": + # Use Clang on macOS by default + env["CXX"] = "clang++" + + if env["bits"] == "32": + raise ValueError("Only 64-bit builds are supported for the macOS target.") + + if env["macos_arch"] == "universal": + env.Append(LINKFLAGS=["-arch", "x86_64", "-arch", "arm64"]) + env.Append(CCFLAGS=["-arch", "x86_64", "-arch", "arm64"]) + else: + env.Append(LINKFLAGS=["-arch", env["macos_arch"]]) + env.Append(CCFLAGS=["-arch", env["macos_arch"]]) + + if env["macos_deployment_target"] != "default": + env.Append(CCFLAGS=["-mmacosx-version-min=" + env["macos_deployment_target"]]) + env.Append(LINKFLAGS=["-mmacosx-version-min=" + env["macos_deployment_target"]]) + + if env["macos_sdk_path"]: + env.Append(CCFLAGS=["-isysroot", env["macos_sdk_path"]]) + env.Append(LINKFLAGS=["-isysroot", env["macos_sdk_path"]]) + + env.Append(LINKFLAGS=["-Wl,-undefined,dynamic_lookup"]) + + if env["target"] == "debug": + env.Append(CCFLAGS=["-Og", "-g"]) + elif env["target"] == "release": + env.Append(CCFLAGS=["-O3"]) + +elif env["platform"] == "ios": + if env["ios_simulator"]: + sdk_name = "iphonesimulator" + env.Append(CCFLAGS=["-mios-simulator-version-min=10.0"]) + else: + sdk_name = "iphoneos" + env.Append(CCFLAGS=["-miphoneos-version-min=10.0"]) + + try: + sdk_path = decode_utf8(subprocess.check_output(["xcrun", "--sdk", sdk_name, "--show-sdk-path"]).strip()) + except (subprocess.CalledProcessError, OSError): + raise ValueError("Failed to find SDK path while running xcrun --sdk {} --show-sdk-path.".format(sdk_name)) + + compiler_path = env["IPHONEPATH"] + "/usr/bin/" + env["ENV"]["PATH"] = env["IPHONEPATH"] + "/Developer/usr/bin/:" + env["ENV"]["PATH"] + + env["CC"] = compiler_path + "clang" + env["CXX"] = compiler_path + "clang++" + env["AR"] = compiler_path + "ar" + env["RANLIB"] = compiler_path + "ranlib" + env["SHLIBSUFFIX"] = ".dylib" + + env.Append(CCFLAGS=["-arch", env["ios_arch"], "-isysroot", sdk_path]) + env.Append( + LINKFLAGS=[ + "-arch", + env["ios_arch"], + "-Wl,-undefined,dynamic_lookup", + "-isysroot", + sdk_path, + "-F" + sdk_path, + ] + ) + + if env["target"] == "debug": + env.Append(CCFLAGS=["-Og", "-g"]) + elif env["target"] == "release": + env.Append(CCFLAGS=["-O3"]) + +elif env["platform"] == "windows": + if host_platform == "windows" and not env["use_mingw"]: + # MSVC + env.Append(LINKFLAGS=["/WX"]) + if env["target"] == "debug": + env.Append(CCFLAGS=["/Z7", "/Od", "/EHsc", "/D_DEBUG", "/MDd"]) + elif env["target"] == "release": + env.Append(CCFLAGS=["/O2", "/EHsc", "/DNDEBUG", "/MD"]) + + elif host_platform == "linux" or host_platform == "freebsd" or host_platform == "osx": + # Cross-compilation using MinGW + if env["bits"] == "64": + env["CXX"] = "x86_64-w64-mingw32-g++" + env["AR"] = "x86_64-w64-mingw32-ar" + env["RANLIB"] = "x86_64-w64-mingw32-ranlib" + env["LINK"] = "x86_64-w64-mingw32-g++" + elif env["bits"] == "32": + env["CXX"] = "i686-w64-mingw32-g++" + env["AR"] = "i686-w64-mingw32-ar" + env["RANLIB"] = "i686-w64-mingw32-ranlib" + env["LINK"] = "i686-w64-mingw32-g++" + + elif host_platform == "windows" and env["use_mingw"]: + # Don't Clone the environment. Because otherwise, SCons will pick up msvc stuff. + env = Environment(ENV=os.environ, tools=["mingw"]) + opts.Update(env) + + # Still need to use C++14. + env.Append(CCFLAGS=["-std=c++14"]) + # Don't want lib prefixes + env["IMPLIBPREFIX"] = "" + env["SHLIBPREFIX"] = "" + + env["SPAWN"] = mySpawn + env.Replace(ARFLAGS=["q"]) + + # Native or cross-compilation using MinGW + if host_platform == "linux" or host_platform == "freebsd" or host_platform == "osx" or env["use_mingw"]: + # These options are for a release build even using target=debug + env.Append(CCFLAGS=["-O3", "-Wwrite-strings"]) + env.Append( + LINKFLAGS=[ + "--static", + "-Wl,--no-undefined", + "-static-libgcc", + "-static-libstdc++", + ] + ) + +elif env["platform"] == "android": + if host_platform == "windows": + # Don't Clone the environment. Because otherwise, SCons will pick up msvc stuff. + env = Environment(ENV=os.environ, tools=["mingw"]) + opts.Update(env) + + # Long line hack. Use custom spawn, quick AR append (to avoid files with the same names to override each other). + env["SPAWN"] = mySpawn + env.Replace(ARFLAGS=["q"]) + + # Verify NDK root + if not "ANDROID_NDK_ROOT" in env: + raise ValueError( + "To build for Android, ANDROID_NDK_ROOT must be defined. Please set ANDROID_NDK_ROOT to the root folder of your Android NDK installation." + ) + + # Validate API level + api_level = int(env["android_api_level"]) + if env["android_arch"] in ["x86_64", "arm64v8"] and api_level < 21: + print("WARN: 64-bit Android architectures require an API level of at least 21; setting android_api_level=21") + env["android_api_level"] = "21" + api_level = 21 + + # Setup toolchain + toolchain = env["ANDROID_NDK_ROOT"] + "/toolchains/llvm/prebuilt/" + if host_platform == "windows": + toolchain += "windows" + import platform as pltfm + + if pltfm.machine().endswith("64"): + toolchain += "-x86_64" + elif host_platform == "linux": + toolchain += "linux-x86_64" + elif host_platform == "osx": + toolchain += "darwin-x86_64" + env.PrependENVPath("PATH", toolchain + "/bin") # This does nothing half of the time, but we'll put it here anyways + + # Get architecture info + arch_info_table = { + "armv7": { + "march": "armv7-a", + "target": "armv7a-linux-androideabi", + "tool_path": "arm-linux-androideabi", + "compiler_path": "armv7a-linux-androideabi", + "ccflags": ["-mfpu=neon"], + }, + "arm64v8": { + "march": "armv8-a", + "target": "aarch64-linux-android", + "tool_path": "aarch64-linux-android", + "compiler_path": "aarch64-linux-android", + "ccflags": [], + }, + "x86": { + "march": "i686", + "target": "i686-linux-android", + "tool_path": "i686-linux-android", + "compiler_path": "i686-linux-android", + "ccflags": ["-mstackrealign"], + }, + "x86_64": { + "march": "x86-64", + "target": "x86_64-linux-android", + "tool_path": "x86_64-linux-android", + "compiler_path": "x86_64-linux-android", + "ccflags": [], + }, + } + arch_info = arch_info_table[env["android_arch"]] + + # Setup tools + env["CC"] = toolchain + "/bin/clang" + env["CXX"] = toolchain + "/bin/clang++" + env["AR"] = toolchain + "/bin/llvm-ar" + env["AS"] = toolchain + "/bin/llvm-as" + env["LD"] = toolchain + "/bin/llvm-ld" + env["STRIP"] = toolchain + "/bin/llvm-strip" + env["RANLIB"] = toolchain + "/bin/llvm-ranlib" + env["SHLIBSUFFIX"] = ".so" + + env.Append( + CCFLAGS=[ + "--target=" + arch_info["target"] + env["android_api_level"], + "-march=" + arch_info["march"], + "-fPIC", + ] + ) + env.Append(CCFLAGS=arch_info["ccflags"]) + env.Append(LINKFLAGS=["--target=" + arch_info["target"] + env["android_api_level"], "-march=" + arch_info["march"]]) + + if env["target"] == "debug": + env.Append(CCFLAGS=["-Og", "-g"]) + elif env["target"] == "release": + env.Append(CCFLAGS=["-O3"]) + +elif env["platform"] == "javascript": + env["ENV"] = os.environ + env["CC"] = "emcc" + env["CXX"] = "em++" + env["AR"] = "emar" + env["RANLIB"] = "emranlib" + env.Append(CPPFLAGS=["-s", "SIDE_MODULE=1"]) + env.Append(LINKFLAGS=["-s", "SIDE_MODULE=1"]) + env["SHOBJSUFFIX"] = ".bc" + env["SHLIBSUFFIX"] = ".wasm" + # Use TempFileMunge since some AR invocations are too long for cmd.exe. + # Use POSIX-style paths, required with TempFileMunge. + env["ARCOM_POSIX"] = env["ARCOM"].replace("$TARGET", "$TARGET.posix").replace("$SOURCES", "$SOURCES.posix") + env["ARCOM"] = "${TEMPFILE(ARCOM_POSIX)}" + + # All intermediate files are just LLVM bitcode. + env["OBJPREFIX"] = "" + env["OBJSUFFIX"] = ".bc" + env["PROGPREFIX"] = "" + # Program() output consists of multiple files, so specify suffixes manually at builder. + env["PROGSUFFIX"] = "" + env["LIBPREFIX"] = "lib" + env["LIBSUFFIX"] = ".a" + env["LIBPREFIXES"] = ["$LIBPREFIX"] + env["LIBSUFFIXES"] = ["$LIBSUFFIX"] + env.Replace(SHLINKFLAGS="$LINKFLAGS") + env.Replace(SHLINKFLAGS="$LINKFLAGS") + + if env["target"] == "debug": + env.Append(CCFLAGS=["-O0", "-g"]) + elif env["target"] == "release": + env.Append(CCFLAGS=["-O3"]) + +# Cache +scons_cache_path = os.environ.get("SCONS_CACHE") +if scons_cache_path is not None: + CacheDir(scons_cache_path) + Decider("MD5") + +# Generate bindings +env.Append(BUILDERS={"GenerateBindings": Builder(action=scons_generate_bindings, emitter=scons_emit_files)}) +json_api_file = "" + +if "custom_api_file" in env: + json_api_file = env["custom_api_file"] +else: + json_api_file = os.path.join(os.getcwd(), env["headers_dir"], "api.json") + +bindings = env.GenerateBindings( + env.Dir("."), [json_api_file, "binding_generator.py"] +) + +# Forces bindings regeneration. +if env["generate_bindings"]: + AlwaysBuild(bindings) + NoCache(bindings) + +# Includes +env.Append(CPPPATH=[[env.Dir(d) for d in [".", env["headers_dir"], "include", "include/gen", "include/core"]]]) + +# Sources to compile +sources = [] +add_sources(sources, "src/core", "cpp") +sources.extend(f for f in bindings if str(f).endswith(".cpp")) + +arch_suffix = env["bits"] +if env["platform"] == "android": + arch_suffix = env["android_arch"] +elif env["platform"] == "ios": + arch_suffix = env["ios_arch"] + if env["ios_simulator"]: + arch_suffix += ".simulator" +elif env["platform"] == "osx": + if env["macos_arch"] != "universal": + arch_suffix = env["macos_arch"] +elif env["platform"] == "javascript": + arch_suffix = "wasm" +# Expose it to projects that import this env. +env["arch_suffix"] = arch_suffix + +library = None +env["OBJSUFFIX"] = ".{}.{}.{}{}".format(env["platform"], env["target"], arch_suffix, env["OBJSUFFIX"]) +library_name = "libgodot-cpp.{}.{}.{}{}".format(env["platform"], env["target"], arch_suffix, env["LIBSUFFIX"]) + +if env["build_library"]: + library = env.StaticLibrary(target=env.File("bin/%s" % library_name), source=sources) + Default(library) + +env.Append(LIBPATH=[env.Dir("bin")]) +env.Append(LIBS=library_name) +Return("env") diff --git a/binding_generator.py b/binding_generator.py new file mode 100644 index 0000000..da4bb61 --- /dev/null +++ b/binding_generator.py @@ -0,0 +1,955 @@ +#!/usr/bin/env python +from __future__ import print_function +import json +import os +import errno +from pathlib import Path + +# Convenience function for using template get_node +def correct_method_name(method_list): + for method in method_list: + if method["name"] == "get_node": + method["name"] = "get_node_internal" + + +classes = [] + + +def get_file_list(api_filepath, output_dir, headers=False, sources=False): + global classes + files = [] + with open(api_filepath) as api_file: + classes = json.load(api_file) + include_gen_folder = Path(output_dir) / "include" / "gen" + source_gen_folder = Path(output_dir) / "src" / "gen" + for _class in classes: + header_filename = include_gen_folder / (strip_name(_class["name"]) + ".hpp") + source_filename = source_gen_folder / (strip_name(_class["name"]) + ".cpp") + if headers: + files.append(str(header_filename.as_posix())) + if sources: + files.append(str(source_filename.as_posix())) + icall_header_filename = include_gen_folder / "__icalls.hpp" + register_types_filename = source_gen_folder / "__register_types.cpp" + init_method_bindings_filename = source_gen_folder / "__init_method_bindings.cpp" + if headers: + files.append(str(icall_header_filename.as_posix())) + if sources: + files.append(str(register_types_filename.as_posix())) + files.append(str(init_method_bindings_filename.as_posix())) + return files + + +def print_file_list(api_filepath, output_dir, headers=False, sources=False): + for f in get_file_list(api_filepath, output_dir, headers, sources): + print(f, end=";") + + +def scons_emit_files(target, source, env): + files = [env.File(f) for f in get_file_list(str(source[0]), target[0].abspath, True, True)] + env.Clean(target, files) + env["godot_cpp_gen_dir"] = target[0].abspath + return files, source + + +def scons_generate_bindings(target, source, env): + generate_bindings(str(source[0]), env["generate_template_get_node"], env["godot_cpp_gen_dir"]) + return None + + +def generate_bindings(api_filepath, use_template_get_node, output_dir="."): + global classes + with open(api_filepath) as api_file: + classes = json.load(api_file) + + icalls = set() + include_gen_folder = Path(output_dir) / "include" / "gen" + source_gen_folder = Path(output_dir) / "src" / "gen" + + include_gen_folder.mkdir(parents=True, exist_ok=True) + source_gen_folder.mkdir(parents=True, exist_ok=True) + + for c in classes: + # print(c['name']) + used_classes = sorted(get_used_classes(c)) + if use_template_get_node and c["name"] == "Node": + correct_method_name(c["methods"]) + + header = generate_class_header(used_classes, c, use_template_get_node) + + impl = generate_class_implementation(icalls, used_classes, c, use_template_get_node) + + header_filename = include_gen_folder / (strip_name(c["name"]) + ".hpp") + with header_filename.open("w+") as header_file: + header_file.write(header) + + source_filename = source_gen_folder / (strip_name(c["name"]) + ".cpp") + with source_filename.open("w+") as source_file: + source_file.write(impl) + + icall_header_filename = include_gen_folder / "__icalls.hpp" + with icall_header_filename.open("w+") as icall_header_file: + icall_header_file.write(generate_icall_header(icalls)) + + register_types_filename = source_gen_folder / "__register_types.cpp" + with register_types_filename.open("w+") as register_types_file: + register_types_file.write(generate_type_registry(classes)) + + init_method_bindings_filename = source_gen_folder / "__init_method_bindings.cpp" + with init_method_bindings_filename.open("w+") as init_method_bindings_file: + init_method_bindings_file.write(generate_init_method_bindings(classes)) + + +def is_reference_type(t): + for c in classes: + if c["name"] != t: + continue + if c["is_reference"]: + return True + return False + + +def make_gdnative_type(t, ref_allowed): + if is_enum(t): + return remove_enum_prefix(t) + " " + elif is_class_type(t): + if is_reference_type(t) and ref_allowed: + return "Ref<" + strip_name(t) + "> " + else: + return strip_name(t) + " *" + else: + if t == "int": + return "int64_t " + if t == "float" or t == "real": + return "real_t " + return strip_name(t) + " " + + +def generate_class_header(used_classes, c, use_template_get_node): + + source = [] + source.append("#ifndef GODOT_CPP_" + strip_name(c["name"]).upper() + "_HPP") + source.append("#define GODOT_CPP_" + strip_name(c["name"]).upper() + "_HPP") + source.append("") + source.append("") + + source.append("#include ") + source.append("#include ") + source.append("") + + source.append("#include ") + + class_name = strip_name(c["name"]) + + # Ref is not included in object.h in Godot either, + # so don't include it here because it's not needed + if class_name != "Object" and class_name != "Reference": + source.append("#include ") + ref_allowed = True + else: + source.append("#include ") + ref_allowed = False + + included = [] + + for used_class in used_classes: + if is_enum(used_class) and is_nested_type(used_class): + used_class_name = remove_enum_prefix(extract_nested_type(used_class)) + if used_class_name not in included: + included.append(used_class_name) + source.append('#include "' + used_class_name + '.hpp"') + elif is_enum(used_class) and is_nested_type(used_class) and not is_nested_type(used_class, class_name): + used_class_name = remove_enum_prefix(used_class) + if used_class_name not in included: + included.append(used_class_name) + source.append('#include "' + used_class_name + '.hpp"') + + source.append("") + + if c["base_class"] != "": + source.append('#include "' + strip_name(c["base_class"]) + '.hpp"') + + source.append("namespace godot {") + source.append("") + + for used_type in used_classes: + if is_enum(used_type) or is_nested_type(used_type, class_name): + continue + else: + source.append("class " + strip_name(used_type) + ";") + + source.append("") + + vararg_templates = "" + + # generate the class definition here + source.append( + "class " + + class_name + + (" : public _Wrapped" if c["base_class"] == "" else (" : public " + strip_name(c["base_class"]))) + + " {" + ) + + if c["base_class"] == "": + source.append("public: enum { ___CLASS_IS_SCRIPT = 0, };") + source.append("") + source.append("private:") + + if c["singleton"]: + source.append("\tstatic " + class_name + " *_singleton;") + source.append("") + source.append("\t" + class_name + "();") + source.append("") + + # Generate method table + source.append("\tstruct ___method_bindings {") + + for method in c["methods"]: + source.append("\t\tgodot_method_bind *mb_" + method["name"] + ";") + + source.append("\t};") + source.append("\tstatic ___method_bindings ___mb;") + source.append("\tstatic void *_detail_class_tag;") + source.append("") + source.append("public:") + source.append("\tstatic void ___init_method_bindings();") + + # class id from core engine for casting + source.append("\tinline static size_t ___get_id() { return (size_t)_detail_class_tag; }") + + source.append("") + + if c["singleton"]: + source.append("\tstatic inline " + class_name + " *get_singleton()") + source.append("\t{") + source.append("\t\tif (!" + class_name + "::_singleton) {") + source.append("\t\t\t" + class_name + "::_singleton = new " + class_name + ";") + source.append("\t\t}") + source.append("\t\treturn " + class_name + "::_singleton;") + source.append("\t}") + source.append("") + + # godot::api->godot_global_get_singleton((char *) \"" + strip_name(c["name"]) + "\");" + + # class name: + # Two versions needed needed because when the user implements a custom class, + # we want to override `___get_class_name` while `___get_godot_class_name` can keep returning the base name + source.append( + '\tstatic inline const char *___get_class_name() { return (const char *) "' + strip_name(c["name"]) + '"; }' + ) + source.append( + '\tstatic inline const char *___get_godot_class_name() { return (const char *) "' + + strip_name(c["name"]) + + '"; }' + ) + + source.append( + "\tstatic inline Object *___get_from_variant(Variant a) { godot_object *o = (godot_object*) a; return (o) ? (Object *) godot::nativescript_1_1_api->godot_nativescript_get_instance_binding_data(godot::_RegisterState::language_index, o) : nullptr; }" + ) + + enum_values = [] + + source.append("\n\t// enums") + for enum in c["enums"]: + source.append("\tenum " + strip_name(enum["name"]) + " {") + for value in enum["values"]: + source.append("\t\t" + remove_nested_type_prefix(value) + " = " + str(enum["values"][value]) + ",") + enum_values.append(value) + source.append("\t};") + + source.append("\n\t// constants") + + for name in c["constants"]: + if name not in enum_values: + source.append("\tconst static int " + name + " = " + str(c["constants"][name]) + ";") + + if c["instanciable"]: + source.append("") + source.append("") + source.append("\tstatic " + class_name + " *_new();") + + source.append("\n\t// methods") + + if class_name == "Object": + source.append("#ifndef GODOT_CPP_NO_OBJECT_CAST") + source.append("\ttemplate") + source.append("\tstatic T *cast_to(const Object *obj);") + source.append("#endif") + source.append("") + + for method in c["methods"]: + method_signature = "" + + # TODO decide what to do about virtual methods + # method_signature += "virtual " if method["is_virtual"] else "" + method_signature += make_gdnative_type(method["return_type"], ref_allowed) + method_name = escape_cpp(method["name"]) + method_signature += method_name + "(" + + has_default_argument = False + method_arguments = "" + + for i, argument in enumerate(method["arguments"]): + method_signature += "const " + make_gdnative_type(argument["type"], ref_allowed) + argument_name = escape_cpp(argument["name"]) + method_signature += argument_name + method_arguments += argument_name + + # default arguments + def escape_default_arg(_type, default_value): + if _type == "Color": + return "Color(" + default_value + ")" + if _type == "bool" or _type == "int": + return default_value.lower() + if _type == "Array": + return "Array()" + if _type in [ + "PoolVector2Array", + "PoolStringArray", + "PoolVector3Array", + "PoolColorArray", + "PoolIntArray", + "PoolRealArray", + "PoolByteArray", + ]: + return _type + "()" + if _type == "Vector2": + return "Vector2" + default_value + if _type == "Vector3": + return "Vector3" + default_value + if _type == "Transform": + return "Transform()" + if _type == "Transform2D": + return "Transform2D()" + if _type == "Rect2": + return "Rect2" + default_value + if _type == "Variant": + return "Variant()" if default_value == "Null" else default_value + if _type == "String" or _type == "NodePath": + return '"' + default_value + '"' + if _type == "RID": + return "RID()" + + if default_value == "Null" or default_value == "[Object:null]": + return "nullptr" + + return default_value + + if argument["has_default_value"] or has_default_argument: + method_signature += " = " + escape_default_arg(argument["type"], argument["default_value"]) + has_default_argument = True + + if i != len(method["arguments"]) - 1: + method_signature += ", " + method_arguments += "," + + if method["has_varargs"]: + if len(method["arguments"]) > 0: + method_signature += ", " + method_arguments += ", " + vararg_templates += ( + "\ttemplate " + + method_signature + + "Args... args){\n\t\treturn " + + method_name + + "(" + + method_arguments + + "Array::make(args...));\n\t}\n" + "" + ) + method_signature += "const Array& __var_args = Array()" + + method_signature += ")" + (" const" if method["is_const"] else "") + + source.append("\t" + method_signature + ";") + + source.append(vararg_templates) + + if use_template_get_node and class_name == "Node": + # Extra definition for template get_node that calls the renamed get_node_internal; has a default template parameter for backwards compatibility. + source.append("\ttemplate ") + source.append("\tT *get_node(const NodePath path) const {") + source.append("\t\treturn Object::cast_to(get_node_internal(path));") + source.append("\t}") + + source.append("};") + source.append("") + + # ...And a specialized version so we don't unnecessarily cast when using the default. + source.append("template <>") + source.append("inline Node *Node::get_node(const NodePath path) const {") + source.append("\treturn get_node_internal(path);") + source.append("}") + source.append("") + else: + source.append("};") + source.append("") + + source.append("}") + source.append("") + + source.append("#endif") + + return "\n".join(source) + + +def generate_class_implementation(icalls, used_classes, c, use_template_get_node): + class_name = strip_name(c["name"]) + + ref_allowed = class_name != "Object" and class_name != "Reference" + + source = [] + source.append('#include "' + class_name + '.hpp"') + source.append("") + source.append("") + + source.append("#include ") + source.append("#include ") + source.append("#include ") + + source.append("#include ") + source.append("") + + source.append('#include "__icalls.hpp"') + source.append("") + source.append("") + + for used_class in used_classes: + if is_enum(used_class): + continue + else: + source.append('#include "' + strip_name(used_class) + '.hpp"') + + source.append("") + source.append("") + + source.append("namespace godot {") + + core_object_name = "this" + + source.append("") + source.append("") + + if c["singleton"]: + source.append("" + class_name + " *" + class_name + "::_singleton = NULL;") + source.append("") + source.append("") + + # FIXME Test if inlining has a huge impact on binary size + source.append(class_name + "::" + class_name + "() {") + source.append('\t_owner = godot::api->godot_global_get_singleton((char *) "' + strip_name(c["name"]) + '");') + source.append("}") + + source.append("") + source.append("") + + # Method table initialization + source.append(class_name + "::___method_bindings " + class_name + "::___mb = {};") + source.append("") + + source.append("void *" + class_name + "::_detail_class_tag = nullptr;") + source.append("") + + source.append("void " + class_name + "::___init_method_bindings() {") + + for method in c["methods"]: + source.append( + "\t___mb.mb_" + + method["name"] + + ' = godot::api->godot_method_bind_get_method("' + + c["name"] + + '", "' + + ("get_node" if use_template_get_node and method["name"] == "get_node_internal" else method["name"]) + + '");' + ) + + source.append("\tgodot_string_name class_name;") + source.append('\tgodot::api->godot_string_name_new_data(&class_name, "' + c["name"] + '");') + source.append("\t_detail_class_tag = godot::core_1_2_api->godot_get_class_tag(&class_name);") + source.append("\tgodot::api->godot_string_name_destroy(&class_name);") + + source.append("}") + source.append("") + + if c["instanciable"]: + source.append(class_name + " *" + strip_name(c["name"]) + "::_new()") + source.append("{") + source.append( + "\treturn (" + + class_name + + ' *) godot::nativescript_1_1_api->godot_nativescript_get_instance_binding_data(godot::_RegisterState::language_index, godot::api->godot_get_class_constructor((char *)"' + + c["name"] + + '")());' + ) + source.append("}") + + for method in c["methods"]: + method_signature = "" + + method_signature += make_gdnative_type(method["return_type"], ref_allowed) + method_signature += strip_name(c["name"]) + "::" + escape_cpp(method["name"]) + "(" + + for i, argument in enumerate(method["arguments"]): + method_signature += "const " + make_gdnative_type(argument["type"], ref_allowed) + method_signature += escape_cpp(argument["name"]) + + if i != len(method["arguments"]) - 1: + method_signature += ", " + + if method["has_varargs"]: + if len(method["arguments"]) > 0: + method_signature += ", " + method_signature += "const Array& __var_args" + + method_signature += ")" + (" const" if method["is_const"] else "") + + source.append(method_signature + " {") + + if method["name"] == "free": + # dirty hack because Object::free is marked virtual but doesn't actually exist... + source.append("\tgodot::api->godot_object_destroy(_owner);") + source.append("}") + source.append("") + continue + + return_statement = "" + return_type_is_ref = is_reference_type(method["return_type"]) and ref_allowed + + if method["return_type"] != "void": + if is_class_type(method["return_type"]): + if is_enum(method["return_type"]): + return_statement += "return (" + remove_enum_prefix(method["return_type"]) + ") " + elif return_type_is_ref: + return_statement += "return Ref<" + strip_name(method["return_type"]) + ">::__internal_constructor(" + else: + return_statement += "return " + ( + "(" + strip_name(method["return_type"]) + " *) " if is_class_type(method["return_type"]) else "" + ) + else: + return_statement += "return " + + def get_icall_type_name(name): + if is_enum(name): + return "int" + if is_class_type(name): + return "Object" + return name + + if method["has_varargs"]: + + if len(method["arguments"]) != 0: + source.append("\tVariant __given_args[" + str(len(method["arguments"])) + "];") + + for i, argument in enumerate(method["arguments"]): + source.append("\tgodot::api->godot_variant_new_nil((godot_variant *) &__given_args[" + str(i) + "]);") + + source.append("") + + for i, argument in enumerate(method["arguments"]): + source.append("\t__given_args[" + str(i) + "] = " + escape_cpp(argument["name"]) + ";") + + source.append("") + + size = "" + if method["has_varargs"]: + size = "(__var_args.size() + " + str(len(method["arguments"])) + ")" + else: + size = "(" + str(len(method["arguments"])) + ")" + + source.append( + "\tgodot_variant **__args = (godot_variant **) alloca(sizeof(godot_variant *) * " + size + ");" + ) + + source.append("") + + for i, argument in enumerate(method["arguments"]): + source.append("\t__args[" + str(i) + "] = (godot_variant *) &__given_args[" + str(i) + "];") + + source.append("") + + if method["has_varargs"]: + source.append("\tfor (int i = 0; i < __var_args.size(); i++) {") + source.append( + "\t\t__args[i + " + + str(len(method["arguments"])) + + "] = (godot_variant *) &((Array &) __var_args)[i];" + ) + source.append("\t}") + + source.append("") + + source.append("\tVariant __result;") + source.append( + "\t*(godot_variant *) &__result = godot::api->godot_method_bind_call(___mb.mb_" + + method["name"] + + ", ((const Object *) " + + core_object_name + + ")->_owner, (const godot_variant **) __args, " + + size + + ", nullptr);" + ) + + source.append("") + + if is_class_type(method["return_type"]): + source.append("\tObject *obj = Object::___get_from_variant(__result);") + source.append('\tif (obj->has_method("reference"))') + source.append('\t\tobj->callv("reference", Array());') + + source.append("") + + for i, argument in enumerate(method["arguments"]): + source.append("\tgodot::api->godot_variant_destroy((godot_variant *) &__given_args[" + str(i) + "]);") + + source.append("") + + if method["return_type"] != "void": + cast = "" + if is_class_type(method["return_type"]): + if return_type_is_ref: + cast += "Ref<" + strip_name(method["return_type"]) + ">::__internal_constructor(__result);" + else: + cast += ( + "(" + + strip_name(method["return_type"]) + + " *) " + + strip_name(method["return_type"] + "::___get_from_variant(") + + "__result);" + ) + else: + cast += "__result;" + source.append("\treturn " + cast) + + else: + + args = [] + for arg in method["arguments"]: + args.append(get_icall_type_name(arg["type"])) + + icall_ret_type = get_icall_type_name(method["return_type"]) + + icall_sig = tuple((icall_ret_type, tuple(args))) + + icalls.add(icall_sig) + + icall_name = get_icall_name(icall_sig) + + return_statement += icall_name + "(___mb.mb_" + method["name"] + ", (const Object *) " + core_object_name + + for arg in method["arguments"]: + arg_is_ref = is_reference_type(arg["type"]) and ref_allowed + return_statement += ", " + escape_cpp(arg["name"]) + (".ptr()" if arg_is_ref else "") + + return_statement += ")" + + if return_type_is_ref: + return_statement += ")" + + source.append("\t" + return_statement + ";") + + source.append("}") + source.append("") + + source.append("}") + + return "\n".join(source) + + +def generate_icall_header(icalls): + + source = [] + source.append("#ifndef GODOT_CPP__ICALLS_HPP") + source.append("#define GODOT_CPP__ICALLS_HPP") + + source.append("") + + source.append("#include ") + source.append("#include ") + source.append("") + + source.append("#include ") + source.append("#include ") + source.append('#include "Object.hpp"') + source.append("") + source.append("") + + source.append("namespace godot {") + source.append("") + + for icall in icalls: + ret_type = icall[0] + args = icall[1] + + method_signature = "static inline " + + method_signature += ( + get_icall_return_type(ret_type) + get_icall_name(icall) + "(godot_method_bind *mb, const Object *inst" + ) + + for i, arg in enumerate(args): + method_signature += ", const " + + if is_core_type(arg): + method_signature += arg + "&" + elif arg == "int": + method_signature += "int64_t " + elif arg == "float": + method_signature += "double " + elif is_primitive(arg): + method_signature += arg + " " + else: + method_signature += "Object *" + + method_signature += "arg" + str(i) + + method_signature += ")" + + source.append(method_signature + " {") + + if ret_type != "void": + source.append( + "\t" + ("godot_object *" if is_class_type(ret_type) else get_icall_return_type(ret_type)) + "ret;" + ) + if is_class_type(ret_type): + source.append("\tret = nullptr;") + + source.append("\tconst void *args[" + ("1" if len(args) == 0 else "") + "] = {") + + for i, arg in enumerate(args): + + wrapped_argument = "\t\t" + if is_primitive(arg) or is_core_type(arg): + wrapped_argument += "(void *) &arg" + str(i) + else: + wrapped_argument += "(void *) (arg" + str(i) + ") ? arg" + str(i) + "->_owner : nullptr" + + wrapped_argument += "," + source.append(wrapped_argument) + + source.append("\t};") + source.append("") + + source.append( + "\tgodot::api->godot_method_bind_ptrcall(mb, inst->_owner, args, " + + ("nullptr" if ret_type == "void" else "&ret") + + ");" + ) + + if ret_type != "void": + if is_class_type(ret_type): + source.append("\tif (ret) {") + source.append( + "\t\treturn (Object *) godot::nativescript_1_1_api->godot_nativescript_get_instance_binding_data(godot::_RegisterState::language_index, ret);" + ) + source.append("\t}") + source.append("") + source.append("\treturn (Object *) ret;") + else: + source.append("\treturn ret;") + + source.append("}") + source.append("") + + source.append("}") + source.append("") + + source.append("#endif") + + return "\n".join(source) + + +def generate_type_registry(classes): + source = [] + + source.append('#include "TagDB.hpp"') + source.append("#include ") + source.append("\n") + + for c in classes: + source.append("#include <" + strip_name(c["name"]) + ".hpp>") + + source.append("") + source.append("") + + source.append("namespace godot {") + + source.append("void ___register_types()") + source.append("{") + + for c in classes: + class_name = strip_name(c["name"]) + base_class_name = strip_name(c["base_class"]) + + class_type_hash = "typeid(" + class_name + ").hash_code()" + + base_class_type_hash = "typeid(" + base_class_name + ").hash_code()" + + if base_class_name == "": + base_class_type_hash = "0" + + source.append( + '\tgodot::_TagDB::register_global_type("' + + c["name"] + + '", ' + + class_type_hash + + ", " + + base_class_type_hash + + ");" + ) + + source.append("}") + + source.append("") + source.append("}") + + return "\n".join(source) + + +def generate_init_method_bindings(classes): + source = [] + + for c in classes: + source.append("#include <" + strip_name(c["name"]) + ".hpp>") + + source.append("") + source.append("") + + source.append("namespace godot {") + + source.append("void ___init_method_bindings()") + source.append("{") + + for c in classes: + source.append("\t" + strip_name(c["name"]) + "::___init_method_bindings();") + + source.append("}") + + source.append("") + source.append("}") + + return "\n".join(source) + + +def get_icall_return_type(t): + if is_class_type(t): + return "Object *" + if t == "int": + return "int64_t " + if t == "float" or t == "real": + return "double " + return t + " " + + +def get_icall_name(sig): + ret_type = sig[0] + args = sig[1] + + name = "___godot_icall_" + name += strip_name(ret_type) + for arg in args: + name += "_" + strip_name(arg) + + return name + + +def get_used_classes(c): + classes = [] + for method in c["methods"]: + if is_class_type(method["return_type"]) and not (method["return_type"] in classes): + classes.append(method["return_type"]) + + for arg in method["arguments"]: + if is_class_type(arg["type"]) and not (arg["type"] in classes): + classes.append(arg["type"]) + return classes + + +def strip_name(name): + if len(name) == 0: + return name + if name[0] == "_": + return name[1:] + return name + + +def extract_nested_type(nested_type): + return strip_name(nested_type[: nested_type.find("::")]) + + +def remove_nested_type_prefix(name): + return name if name.find("::") == -1 else strip_name(name[name.find("::") + 2 :]) + + +def remove_enum_prefix(name): + return strip_name(name[name.find("enum.") + 5 :]) + + +def is_nested_type(name, type=""): + return name.find(type + "::") != -1 + + +def is_enum(name): + return name.find("enum.") == 0 + + +def is_class_type(name): + return not is_core_type(name) and not is_primitive(name) + + +def is_core_type(name): + core_types = [ + "Array", + "Basis", + "Color", + "Dictionary", + "Error", + "NodePath", + "Plane", + "PoolByteArray", + "PoolIntArray", + "PoolRealArray", + "PoolStringArray", + "PoolVector2Array", + "PoolVector3Array", + "PoolColorArray", + "PoolIntArray", + "PoolRealArray", + "Quat", + "Rect2", + "AABB", + "RID", + "String", + "Transform", + "Transform2D", + "Variant", + "Vector2", + "Vector3", + ] + return name in core_types + + +def is_primitive(name): + core_types = ["int", "bool", "real", "float", "void"] + return name in core_types + + +def escape_cpp(name): + escapes = { + "class": "_class", + "enum": "_enum", + "char": "_char", + "short": "_short", + "bool": "_bool", + "int": "_int", + "default": "_default", + "case": "_case", + "switch": "_switch", + "export": "_export", + "template": "_template", + "new": "new_", + "operator": "_operator", + "typename": "_typename", + } + if name in escapes: + return escapes[name] + return name diff --git a/include/core/AABB.hpp b/include/core/AABB.hpp new file mode 100644 index 0000000..5d2f921 --- /dev/null +++ b/include/core/AABB.hpp @@ -0,0 +1,110 @@ +/*************************************************************************/ +/* AABB.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef AABB_H +#define AABB_H + +#include "Vector3.hpp" + +#include "Plane.hpp" + +#include + +namespace godot { + +class AABB { +public: + Vector3 position; + Vector3 size; + + real_t get_area() const; /// get area + inline bool has_no_area() const { + return (size.x <= CMP_EPSILON || size.y <= CMP_EPSILON || size.z <= CMP_EPSILON); + } + + inline bool has_no_surface() const { + return (size.x <= CMP_EPSILON && size.y <= CMP_EPSILON && size.z <= CMP_EPSILON); + } + + inline const Vector3 &get_position() const { return position; } + inline void set_position(const Vector3 &p_position) { position = p_position; } + inline const Vector3 &get_size() const { return size; } + inline void set_size(const Vector3 &p_size) { size = p_size; } + + bool operator==(const AABB &p_rval) const; + bool operator!=(const AABB &p_rval) const; + + bool intersects(const AABB &p_aabb) const; /// Both AABBs overlap + bool intersects_inclusive(const AABB &p_aabb) const; /// Both AABBs (or their faces) overlap + bool encloses(const AABB &p_aabb) const; /// p_aabb is completely inside this + + AABB merge(const AABB &p_with) const; + void merge_with(const AABB &p_aabb); ///merge with another AABB + AABB intersection(const AABB &p_aabb) const; ///get box where two intersect, empty if no intersection occurs + bool intersects_segment(const Vector3 &p_from, const Vector3 &p_to, Vector3 *r_clip = nullptr, Vector3 *r_normal = nullptr) const; + bool intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *r_clip = nullptr, Vector3 *r_normal = nullptr) const; + bool smits_intersect_ray(const Vector3 &from, const Vector3 &p_dir, real_t t0, real_t t1) const; + + bool intersects_convex_shape(const Plane *p_plane, int p_plane_count) const; + bool intersects_plane(const Plane &p_plane) const; + + bool has_point(const Vector3 &p_point) const; + Vector3 get_support(const Vector3 &p_normal) const; + + Vector3 get_longest_axis() const; + int get_longest_axis_index() const; + real_t get_longest_axis_size() const; + + Vector3 get_shortest_axis() const; + int get_shortest_axis_index() const; + real_t get_shortest_axis_size() const; + + AABB grow(real_t p_by) const; + void grow_by(real_t p_amount); + + void get_edge(int p_edge, Vector3 &r_from, Vector3 &r_to) const; + Vector3 get_endpoint(int p_point) const; + + AABB expand(const Vector3 &p_vector) const; + void project_range_in_plane(const Plane &p_plane, real_t &r_min, real_t &r_max) const; + void expand_to(const Vector3 &p_vector); /** expand to contain a point if necesary */ + + operator String() const; + + inline AABB() {} + inline AABB(const Vector3 &p_pos, const Vector3 &p_size) { + position = p_pos; + size = p_size; + } +}; + +} // namespace godot + +#endif // RECT3_H diff --git a/include/core/Array.hpp b/include/core/Array.hpp new file mode 100644 index 0000000..06e70de --- /dev/null +++ b/include/core/Array.hpp @@ -0,0 +1,191 @@ +/*************************************************************************/ +/* Array.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef ARRAY_H +#define ARRAY_H + +#include + +#include "String.hpp" + +namespace godot { + +namespace helpers { +template +T append_all(T appendable, ValueT value) { + appendable.append(value); + return appendable; +} + +template +T append_all(T appendable, ValueT value, Args... args) { + appendable.append(value); + return append_all(appendable, args...); +} + +template +T append_all(T appendable) { + return appendable; +} + +template +KV add_all(KV kv, KeyT key, ValueT value) { + kv[key] = value; + return kv; +} + +template +KV add_all(KV kv, KeyT key, ValueT value, Args... args) { + kv[key] = value; + return add_all(kv, args...); +} + +template +KV add_all(KV kv) { + return kv; +} +} // namespace helpers + +class Variant; +class PoolByteArray; +class PoolIntArray; +class PoolRealArray; +class PoolStringArray; +class PoolVector2Array; +class PoolVector3Array; +class PoolColorArray; + +class Object; + +class Array { + godot_array _godot_array; + + friend class Variant; + friend class Dictionary; + friend class String; + inline explicit Array(const godot_array &other) { + _godot_array = other; + } + +public: + Array(); + Array(const Array &other); + Array &operator=(const Array &other); + + Array(const PoolByteArray &a); + + Array(const PoolIntArray &a); + + Array(const PoolRealArray &a); + + Array(const PoolStringArray &a); + + Array(const PoolVector2Array &a); + + Array(const PoolVector3Array &a); + + Array(const PoolColorArray &a); + + template + static Array make(Args... args) { + return helpers::append_all(Array(), args...); + } + + Variant &operator[](const int idx); + + const Variant &operator[](const int idx) const; + + void append(const Variant &v); + + void clear(); + + int count(const Variant &v); + + bool empty() const; + + void erase(const Variant &v); + + Variant front() const; + + Variant back() const; + + int find(const Variant &what, const int from = 0) const; + + int find_last(const Variant &what) const; + + bool has(const Variant &what) const; + + uint32_t hash() const; + + void insert(const int pos, const Variant &value); + + void invert(); + + bool is_shared() const; + + Variant pop_back(); + + Variant pop_front(); + + void push_back(const Variant &v); + + void push_front(const Variant &v); + + void remove(const int idx); + + int size() const; + + void resize(const int size); + + int rfind(const Variant &what, const int from = -1) const; + + void sort(); + + void sort_custom(Object *obj, const String &func); + + int bsearch(const Variant &value, const bool before = true); + + int bsearch_custom(const Variant &value, const Object *obj, + const String &func, const bool before = true); + + Array duplicate(const bool deep = false) const; + + Variant max() const; + + Variant min() const; + + void shuffle(); + + ~Array(); +}; + +} // namespace godot + +#endif // ARRAY_H diff --git a/include/core/Basis.hpp b/include/core/Basis.hpp new file mode 100644 index 0000000..719a223 --- /dev/null +++ b/include/core/Basis.hpp @@ -0,0 +1,458 @@ +/*************************************************************************/ +/* Basis.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef BASIS_H +#define BASIS_H + +#include + +#include "Defs.hpp" + +#include "Vector3.hpp" + +namespace godot { + +class Quat; + +class Basis { +private: + static const Basis IDENTITY; + static const Basis FLIP_X; + static const Basis FLIP_Y; + static const Basis FLIP_Z; + + // This helper template is for mimicking the behavior difference between the engine + // and script interfaces that logically script sees matrices as column major, while + // the engine stores them in row major to efficiently take advantage of SIMD + // instructions in case of matrix-vector multiplications. + // With this helper template native scripts see the data as if it was column major + // without actually transposing the basis matrix at the script-engine boundary. + template + class ColumnVector3 { + private: + template + class ColumnVectorComponent { + private: + Vector3 elements[3]; + + protected: + inline ColumnVectorComponent &operator=(const ColumnVectorComponent &p_value) { + return *this = real_t(p_value); + } + + inline ColumnVectorComponent(const ColumnVectorComponent &p_value) { + *this = real_t(p_value); + } + + inline ColumnVectorComponent &operator=(const real_t &p_value) { + elements[component][column1] = p_value; + return *this; + } + + inline operator real_t() const { + return elements[component][column1]; + } + }; + + public: + enum Axis { + AXIS_X, + AXIS_Y, + AXIS_Z, + }; + + union { + ColumnVectorComponent x; + ColumnVectorComponent y; + ColumnVectorComponent z; + + Vector3 elements[3]; // Not for direct access, use [] operator instead + }; + + inline ColumnVector3 &operator=(const ColumnVector3 &p_value) { + return *this = Vector3(p_value); + } + + inline ColumnVector3(const ColumnVector3 &p_value) { + *this = Vector3(p_value); + } + + inline ColumnVector3 &operator=(const Vector3 &p_value) { + elements[0][column] = p_value.x; + elements[1][column] = p_value.y; + elements[2][column] = p_value.z; + return *this; + } + + inline operator Vector3() const { + return Vector3(elements[0][column], elements[1][column], elements[2][column]); + } + + // Unfortunately, we also need to replicate the other interfaces of Vector3 in + // order for being able to directly operate on these "meta-Vector3" objects without + // an explicit cast or an intermediate assignment to a real Vector3 object. + + inline const real_t &operator[](int p_axis) const { + return elements[p_axis][column]; + } + + inline real_t &operator[](int p_axis) { + return elements[p_axis][column]; + } + + inline ColumnVector3 &operator+=(const Vector3 &p_v) { + return *this = *this + p_v; + } + + inline Vector3 operator+(const Vector3 &p_v) const { + return Vector3(*this) + p_v; + } + + inline ColumnVector3 &operator-=(const Vector3 &p_v) { + return *this = *this - p_v; + } + + inline Vector3 operator-(const Vector3 &p_v) const { + return Vector3(*this) - p_v; + } + + inline ColumnVector3 &operator*=(const Vector3 &p_v) { + return *this = *this * p_v; + } + + inline Vector3 operator*(const Vector3 &p_v) const { + return Vector3(*this) * p_v; + } + + inline ColumnVector3 &operator/=(const Vector3 &p_v) { + return *this = *this / p_v; + } + + inline Vector3 operator/(const Vector3 &p_v) const { + return Vector3(*this) / p_v; + } + + inline ColumnVector3 &operator*=(real_t p_scalar) { + return *this = *this * p_scalar; + } + + inline Vector3 operator*(real_t p_scalar) const { + return Vector3(*this) * p_scalar; + } + + inline ColumnVector3 &operator/=(real_t p_scalar) { + return *this = *this / p_scalar; + } + + inline Vector3 operator/(real_t p_scalar) const { + return Vector3(*this) / p_scalar; + } + + inline Vector3 operator-() const { + return -Vector3(*this); + } + + inline bool operator==(const Vector3 &p_v) const { + return Vector3(*this) == p_v; + } + + inline bool operator!=(const Vector3 &p_v) const { + return Vector3(*this) != p_v; + } + + inline bool operator<(const Vector3 &p_v) const { + return Vector3(*this) < p_v; + } + + inline bool operator<=(const Vector3 &p_v) const { + return Vector3(*this) <= p_v; + } + + inline Vector3 abs() const { + return Vector3(*this).abs(); + } + + inline Vector3 ceil() const { + return Vector3(*this).ceil(); + } + + inline Vector3 cross(const Vector3 &b) const { + return Vector3(*this).cross(b); + } + + inline Vector3 linear_interpolate(const Vector3 &p_b, real_t p_t) const { + return Vector3(*this).linear_interpolate(p_b, p_t); + } + + inline Vector3 cubic_interpolate(const Vector3 &b, const Vector3 &pre_a, const Vector3 &post_b, const real_t t) const { + return Vector3(*this).cubic_interpolate(b, pre_a, post_b, t); + } + + inline Vector3 bounce(const Vector3 &p_normal) const { + return Vector3(*this).bounce(p_normal); + } + + inline real_t length() const { + return Vector3(*this).length(); + } + + inline real_t length_squared() const { + return Vector3(*this).length_squared(); + } + + inline real_t distance_squared_to(const Vector3 &b) const { + return Vector3(*this).distance_squared_to(b); + } + + inline real_t distance_to(const Vector3 &b) const { + return Vector3(*this).distance_to(b); + } + + inline real_t dot(const Vector3 &b) const { + return Vector3(*this).dot(b); + } + + inline real_t angle_to(const Vector3 &b) const { + return Vector3(*this).angle_to(b); + } + + inline Vector3 floor() const { + return Vector3(*this).floor(); + } + + inline Vector3 inverse() const { + return Vector3(*this).inverse(); + } + + inline bool is_normalized() const { + return Vector3(*this).is_normalized(); + } + + inline Basis outer(const Vector3 &b) const { + return Vector3(*this).outer(b); + } + + inline int max_axis() const { + return Vector3(*this).max_axis(); + } + + inline int min_axis() const { + return Vector3(*this).min_axis(); + } + + inline void normalize() { + Vector3 v = *this; + v.normalize(); + *this = v; + } + + inline Vector3 normalized() const { + return Vector3(*this).normalized(); + } + + inline Vector3 reflect(const Vector3 &by) const { + return Vector3(*this).reflect(by); + } + + inline Vector3 rotated(const Vector3 &axis, const real_t phi) const { + return Vector3(*this).rotated(axis, phi); + } + + inline void rotate(const Vector3 &p_axis, real_t p_phi) { + Vector3 v = *this; + v.rotate(p_axis, p_phi); + *this = v; + } + + inline Vector3 slide(const Vector3 &by) const { + return Vector3(*this).slide(by); + } + + inline void snap(real_t p_val) { + Vector3 v = *this; + v.snap(p_val); + *this = v; + } + + inline Vector3 snapped(const float by) { + return Vector3(*this).snapped(by); + } + + inline operator String() const { + return String(Vector3(*this)); + } + }; + +public: + union { + ColumnVector3<0> x; + ColumnVector3<1> y; + ColumnVector3<2> z; + + Vector3 elements[3]; // Not for direct access, use [] operator instead + }; + + inline Basis(const Basis &p_basis) { + elements[0] = p_basis.elements[0]; + elements[1] = p_basis.elements[1]; + elements[2] = p_basis.elements[2]; + } + + inline Basis &operator=(const Basis &p_basis) { + elements[0] = p_basis.elements[0]; + elements[1] = p_basis.elements[1]; + elements[2] = p_basis.elements[2]; + return *this; + } + + Basis(const Quat &p_quat); // euler + Basis(const Vector3 &p_euler); // euler + Basis(const Vector3 &p_axis, real_t p_phi); + + Basis(const Vector3 &row0, const Vector3 &row1, const Vector3 &row2); + + Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz); + + Basis(); + + const Vector3 operator[](int axis) const { + return get_axis(axis); + } + + ColumnVector3<0> &operator[](int axis) { + // We need to do a little pointer magic to get this to work, because the + // ColumnVector3 template takes the axis as a template parameter. + // Don't touch this unless you're sure what you're doing! + return (reinterpret_cast(reinterpret_cast(this) + axis))->x; + } + + void invert(); + + bool isequal_approx(const Basis &a, const Basis &b) const; + + bool is_orthogonal() const; + + bool is_rotation() const; + + void transpose(); + + Basis inverse() const; + + Basis transposed() const; + + real_t determinant() const; + + Vector3 get_axis(int p_axis) const; + + void set_axis(int p_axis, const Vector3 &p_value); + + void rotate(const Vector3 &p_axis, real_t p_phi); + + Basis rotated(const Vector3 &p_axis, real_t p_phi) const; + + void scale(const Vector3 &p_scale); + + Basis scaled(const Vector3 &p_scale) const; + + Vector3 get_scale() const; + + Basis slerp(Basis b, float t) const; + + Vector3 get_euler_xyz() const; + void set_euler_xyz(const Vector3 &p_euler); + Vector3 get_euler_yxz() const; + void set_euler_yxz(const Vector3 &p_euler); + + inline Vector3 get_euler() const { return get_euler_yxz(); } + inline void set_euler(const Vector3 &p_euler) { set_euler_yxz(p_euler); } + + // transposed dot products + real_t tdotx(const Vector3 &v) const; + real_t tdoty(const Vector3 &v) const; + real_t tdotz(const Vector3 &v) const; + + bool operator==(const Basis &p_matrix) const; + + bool operator!=(const Basis &p_matrix) const; + + Vector3 xform(const Vector3 &p_vector) const; + + Vector3 xform_inv(const Vector3 &p_vector) const; + void operator*=(const Basis &p_matrix); + + Basis operator*(const Basis &p_matrix) const; + + void operator+=(const Basis &p_matrix); + + Basis operator+(const Basis &p_matrix) const; + + void operator-=(const Basis &p_matrix); + + Basis operator-(const Basis &p_matrix) const; + + void operator*=(real_t p_val); + + Basis operator*(real_t p_val) const; + + int get_orthogonal_index() const; // down below + + void set_orthogonal_index(int p_index); // down below + + operator String() const; + + void get_axis_and_angle(Vector3 &r_axis, real_t &r_angle) const; + + /* create / set */ + + void set(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz); + + Vector3 get_column(int i) const; + + Vector3 get_row(int i) const; + Vector3 get_main_diagonal() const; + + void set_row(int i, const Vector3 &p_row); + + Basis transpose_xform(const Basis &m) const; + + void orthonormalize(); + + Basis orthonormalized() const; + + bool is_symmetric() const; + + Basis diagonalize(); + + operator Quat() const; +}; + +} // namespace godot + +#endif // BASIS_H diff --git a/include/core/CameraMatrix.hpp b/include/core/CameraMatrix.hpp new file mode 100644 index 0000000..035f53b --- /dev/null +++ b/include/core/CameraMatrix.hpp @@ -0,0 +1,124 @@ +/*************************************************************************/ +/* CameraMatrix.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef CAMERA_MATRIX_H +#define CAMERA_MATRIX_H + +#include "Defs.hpp" +#include "Math.hpp" +#include "Plane.hpp" +#include "Rect2.hpp" +#include "Transform.hpp" + +#include + +namespace { +using namespace godot; +} // namespace + +struct CameraMatrix { + enum Planes { + PLANE_NEAR, + PLANE_FAR, + PLANE_LEFT, + PLANE_TOP, + PLANE_RIGHT, + PLANE_BOTTOM + }; + + real_t matrix[4][4]; + + void set_identity(); + void set_zero(); + void set_light_bias(); + void set_light_atlas_rect(const Rect2 &p_rect); + void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov = false); + void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist); + void set_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far); + void set_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar); + void set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov = false); + void set_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far); + void set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov = false); + + static real_t get_fovy(real_t p_fovx, real_t p_aspect) { + return Math::rad2deg(atan(p_aspect * tan(Math::deg2rad(p_fovx) * 0.5)) * 2.0); + } + + static inline double absd(double g) { + union { + double d; + uint64_t i; + } u; + u.d = g; + u.i &= (uint64_t)9223372036854775807ll; + return u.d; + } + + real_t get_z_far() const; + real_t get_z_near() const; + real_t get_aspect() const; + real_t get_fov() const; + bool is_orthogonal() const; + + std::vector get_projection_planes(const Transform &p_transform) const; + + bool get_endpoints(const Transform &p_transform, Vector3 *p_8points) const; + Vector2 get_viewport_half_extents() const; + + void invert(); + CameraMatrix inverse() const; + + CameraMatrix operator*(const CameraMatrix &p_matrix) const; + + Plane xform4(const Plane &p_vec4) const; + inline Vector3 xform(const Vector3 &p_vec3) const; + + operator String() const; + + void scale_translate_to_fit(const AABB &p_aabb); + void make_scale(const Vector3 &p_scale); + int get_pixels_per_meter(int p_for_pixel_width) const; + operator Transform() const; + + CameraMatrix(); + CameraMatrix(const Transform &p_transform); + ~CameraMatrix(); +}; + +Vector3 CameraMatrix::xform(const Vector3 &p_vec3) const { + Vector3 ret; + ret.x = matrix[0][0] * p_vec3.x + matrix[1][0] * p_vec3.y + matrix[2][0] * p_vec3.z + matrix[3][0]; + ret.y = matrix[0][1] * p_vec3.x + matrix[1][1] * p_vec3.y + matrix[2][1] * p_vec3.z + matrix[3][1]; + ret.z = matrix[0][2] * p_vec3.x + matrix[1][2] * p_vec3.y + matrix[2][2] * p_vec3.z + matrix[3][2]; + real_t w = matrix[0][3] * p_vec3.x + matrix[1][3] * p_vec3.y + matrix[2][3] * p_vec3.z + matrix[3][3]; + return ret / w; +} + +#endif diff --git a/include/core/Color.hpp b/include/core/Color.hpp new file mode 100644 index 0000000..0432275 --- /dev/null +++ b/include/core/Color.hpp @@ -0,0 +1,171 @@ +/*************************************************************************/ +/* Color.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef COLOR_H +#define COLOR_H + +#include + +#include + +#include "Defs.hpp" +#include "String.hpp" + +namespace godot { + +struct Color { +private: + // static float _parse_col(const String& p_str, int p_ofs); +public: + union { + struct { + float r; + float g; + float b; + float a; + }; + float components[4]; + }; + + inline bool operator==(const Color &p_color) const { return (r == p_color.r && g == p_color.g && b == p_color.b && a == p_color.a); } + inline bool operator!=(const Color &p_color) const { return (r != p_color.r || g != p_color.g || b != p_color.b || a != p_color.a); } + + uint32_t to_32() const; + + uint32_t to_ARGB32() const; + + uint32_t to_ABGR32() const; + + uint64_t to_ABGR64() const; + + uint64_t to_ARGB64() const; + + uint32_t to_RGBA32() const; + + uint64_t to_RGBA64() const; + + float gray() const; + + uint8_t get_r8() const; + + uint8_t get_g8() const; + + uint8_t get_b8() const; + + uint8_t get_a8() const; + + float get_h() const; + + float get_s() const; + + float get_v() const; + + void set_hsv(float p_h, float p_s, float p_v, float p_alpha = 1.0); + + Color darkened(const float amount) const; + + Color lightened(const float amount) const; + + Color from_hsv(float p_h, float p_s, float p_v, float p_a = 1.0) const; + + inline float &operator[](int idx) { + return components[idx]; + } + inline const float &operator[](int idx) const { + return components[idx]; + } + + Color operator+(const Color &p_color) const; + void operator+=(const Color &p_color); + + Color operator-() const; + Color operator-(const Color &p_color) const; + void operator-=(const Color &p_color); + + Color operator*(const Color &p_color) const; + Color operator*(const real_t &rvalue) const; + void operator*=(const Color &p_color); + void operator*=(const real_t &rvalue); + + Color operator/(const Color &p_color) const; + Color operator/(const real_t &rvalue) const; + void operator/=(const Color &p_color); + void operator/=(const real_t &rvalue); + + void invert(); + + void contrast(); + + Color inverted() const; + + Color contrasted() const; + + Color linear_interpolate(const Color &p_b, float p_t) const; + + Color blend(const Color &p_over) const; + + Color to_linear() const; + + static Color hex(uint32_t p_hex); + + static Color html(const String &p_color); + + static bool html_is_valid(const String &p_color); + + String to_html(bool p_alpha = true) const; + + bool operator<(const Color &p_color) const; //used in set keys + + operator String() const; + + /** + * No construct parameters, r=0, g=0, b=0. a=255 + */ + inline Color() { + r = 0; + g = 0; + b = 0; + a = 1.0; + } + + /** + * RGB / RGBA construct parameters. Alpha is optional, but defaults to 1.0 + */ + inline Color(float p_r, float p_g, float p_b, float p_a = 1.0) { + r = p_r; + g = p_g; + b = p_b; + a = p_a; + } +}; + +} // namespace godot + +#endif // COLOR_H diff --git a/include/core/CoreTypes.hpp b/include/core/CoreTypes.hpp new file mode 100644 index 0000000..165cef2 --- /dev/null +++ b/include/core/CoreTypes.hpp @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* CoreTypes.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef CORETYPES_H +#define CORETYPES_H + +#include "Defs.hpp" + +#include "AABB.hpp" +#include "Array.hpp" +#include "Basis.hpp" +#include "Color.hpp" +#include "Dictionary.hpp" +#include "NodePath.hpp" +#include "Plane.hpp" +#include "PoolArrays.hpp" +#include "Quat.hpp" +#include "RID.hpp" +#include "Rect2.hpp" +#include "String.hpp" +#include "Transform.hpp" +#include "Transform2D.hpp" +#include "Variant.hpp" +#include "Vector2.hpp" +#include "Vector3.hpp" + +#include "Wrapped.hpp" + +#endif // CORETYPES_H diff --git a/include/core/Defs.hpp b/include/core/Defs.hpp new file mode 100644 index 0000000..a70e615 --- /dev/null +++ b/include/core/Defs.hpp @@ -0,0 +1,298 @@ +/*************************************************************************/ +/* Defs.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef DEFS_H +#define DEFS_H + +namespace godot { + +enum class Error { + OK, + FAILED, ///< Generic fail error + ERR_UNAVAILABLE, ///< What is requested is unsupported/unavailable + ERR_UNCONFIGURED, ///< The object being used hasnt been properly set up yet + ERR_UNAUTHORIZED, ///< Missing credentials for requested resource + ERR_PARAMETER_RANGE_ERROR, ///< Parameter given out of range (5) + ERR_OUT_OF_MEMORY, ///< Out of memory + ERR_FILE_NOT_FOUND, + ERR_FILE_BAD_DRIVE, + ERR_FILE_BAD_PATH, + ERR_FILE_NO_PERMISSION, // (10) + ERR_FILE_ALREADY_IN_USE, + ERR_FILE_CANT_OPEN, + ERR_FILE_CANT_WRITE, + ERR_FILE_CANT_READ, + ERR_FILE_UNRECOGNIZED, // (15) + ERR_FILE_CORRUPT, + ERR_FILE_MISSING_DEPENDENCIES, + ERR_FILE_EOF, + ERR_CANT_OPEN, ///< Can't open a resource/socket/file + ERR_CANT_CREATE, // (20) + ERR_QUERY_FAILED, + ERR_ALREADY_IN_USE, + ERR_LOCKED, ///< resource is locked + ERR_TIMEOUT, + ERR_CANT_CONNECT, // (25) + ERR_CANT_RESOLVE, + ERR_CONNECTION_ERROR, + ERR_CANT_AQUIRE_RESOURCE, + ERR_CANT_FORK, + ERR_INVALID_DATA, ///< Data passed is invalid (30) + ERR_INVALID_PARAMETER, ///< Parameter passed is invalid + ERR_ALREADY_EXISTS, ///< When adding, item already exists + ERR_DOES_NOT_EXIST, ///< When retrieving/erasing, it item does not exist + ERR_DATABASE_CANT_READ, ///< database is full + ERR_DATABASE_CANT_WRITE, ///< database is full (35) + ERR_COMPILATION_FAILED, + ERR_METHOD_NOT_FOUND, + ERR_LINK_FAILED, + ERR_SCRIPT_FAILED, + ERR_CYCLIC_LINK, // (40) + ERR_INVALID_DECLARATION, + ERR_DUPLICATE_SYMBOL, + ERR_PARSE_ERROR, + ERR_BUSY, + ERR_SKIP, // (45) + ERR_HELP, ///< user requested help!! + ERR_BUG, ///< a bug in the software certainly happened, due to a double check failing or unexpected behavior. + ERR_PRINTER_ON_FIRE, /// the parallel port printer is engulfed in flames +}; + +} // namespace godot + +#include + +// alloca() is non-standard. When using MSVC, it's in malloc.h. +#if defined(__linux__) || defined(__APPLE__) +#include +#else +#include +#endif + +typedef float real_t; + +// This epsilon should match the one used by Godot for consistency. +// Using `f` when `real_t` is float. +#define CMP_EPSILON 0.00001f +#define CMP_EPSILON2 (CMP_EPSILON * CMP_EPSILON) + +#define Math_PI 3.1415926535897932384626433833 +#define Math_TAU 6.2831853071795864769252867666 + +#define _PLANE_EQ_DOT_EPSILON 0.999 +#define _PLANE_EQ_D_EPSILON 0.0001 + +#ifdef __GNUC__ +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#else +#define likely(x) x +#define unlikely(x) x +#endif + +// Don't use this directly; instead, use any of the CRASH_* macros +#ifdef _MSC_VER +#define GENERATE_TRAP \ + __debugbreak(); \ + /* Avoid warning about control paths */ \ + for (;;) { \ + } +#else +#define GENERATE_TRAP __builtin_trap(); +#endif + +// ERR/WARN macros +#ifndef WARN_PRINT +#define WARN_PRINT(msg) godot::Godot::print_warning(msg, __func__, __FILE__, __LINE__) +#endif + +#ifndef WARN_PRINTS +#define WARN_PRINTS(msg) WARN_PRINT((msg).utf8().get_data()) +#endif + +#ifndef ERR_PRINT +#define ERR_PRINT(msg) godot::Godot::print_error(msg, __func__, __FILE__, __LINE__) +#endif + +#ifndef ERR_PRINTS +#define ERR_PRINTS(msg) ERR_PRINT((msg).utf8().get_data()) +#endif + +#ifndef FATAL_PRINT +#define FATAL_PRINT(msg) ERR_PRINT(godot::String("FATAL: ") + (msg)) +#endif + +#ifndef ERR_MSG_INDEX +#define ERR_MSG_INDEX(index, size) (godot::String("Index ") + #index + "=" + godot::String::num_int64(index) + " out of size (" + #size + "=" + godot::String::num_int64(size) + ")") +#endif + +#ifndef ERR_MSG_NULL +#define ERR_MSG_NULL(param) (godot::String("Parameter '") + #param + "' is null.") +#endif + +#ifndef ERR_MSG_COND +#define ERR_MSG_COND(cond) (godot::String("Condition '") + #cond + "' is true.") +#endif + +#ifndef ERR_FAIL_INDEX +#define ERR_FAIL_INDEX(index, size) \ + do { \ + if (unlikely((index) < 0 || (index) >= (size))) { \ + ERR_PRINT(ERR_MSG_INDEX(index, size)); \ + return; \ + } \ + } while (0) +#endif + +#ifndef ERR_FAIL_INDEX_V +#define ERR_FAIL_INDEX_V(index, size, ret) \ + do { \ + if (unlikely((index) < 0 || (index) >= (size))) { \ + ERR_PRINT(ERR_MSG_INDEX(index, size)); \ + return ret; \ + } \ + } while (0) +#endif + +#ifndef ERR_FAIL_UNSIGNED_INDEX_V +#define ERR_FAIL_UNSIGNED_INDEX_V(index, size, ret) \ + do { \ + if (unlikely((index) >= (size))) { \ + ERR_PRINT(ERR_MSG_INDEX(index, size)); \ + return ret; \ + } \ + } while (0) +#endif + +#ifndef CRASH_BAD_INDEX +#define CRASH_BAD_INDEX(index, size) \ + do { \ + if (unlikely((index) < 0 || (index) >= (size))) { \ + FATAL_PRINT(ERR_MSG_INDEX(index, size)); \ + GENERATE_TRAP; \ + } \ + } while (0) +#endif + +#ifndef ERR_FAIL_NULL +#define ERR_FAIL_NULL(param) \ + do { \ + if (unlikely(!param)) { \ + ERR_PRINT(ERR_MSG_NULL(param)); \ + return; \ + } \ + } while (0) +#endif + +#ifndef ERR_FAIL_NULL_V +#define ERR_FAIL_NULL_V(param, ret) \ + do { \ + if (unlikely(!param)) { \ + ERR_PRINT(ERR_MSG_NULL(param)); \ + return ret; \ + } \ + } while (0) +#endif + +#ifndef ERR_FAIL_COND +#define ERR_FAIL_COND(cond) \ + do { \ + if (unlikely(cond)) { \ + ERR_PRINT(ERR_MSG_COND(cond)); \ + return; \ + } \ + } while (0) +#endif + +#ifndef CRASH_COND +#define CRASH_COND(cond) \ + do { \ + if (unlikely(cond)) { \ + FATAL_PRINT(ERR_MSG_COND(cond)); \ + GENERATE_TRAP; \ + } \ + } while (0) +#endif + +#ifndef ERR_FAIL_COND_V +#define ERR_FAIL_COND_V(cond, ret) \ + do { \ + if (unlikely(cond)) { \ + ERR_PRINT(ERR_MSG_COND(cond)); \ + return ret; \ + } \ + } while (0) +#endif + +#ifndef ERR_CONTINUE +#define ERR_CONTINUE(cond) \ + { \ + if (unlikely(cond)) { \ + ERR_PRINT(ERR_MSG_COND(cond)); \ + continue; \ + } \ + } +#endif + +#ifndef ERR_BREAK +#define ERR_BREAK(cond) \ + { \ + if (unlikely(cond)) { \ + ERR_PRINT(ERR_MSG_COND(cond)); \ + break; \ + } \ + } +#endif + +#ifndef ERR_FAIL +#define ERR_FAIL() \ + do { \ + ERR_PRINT("Method/Function Failed."); \ + return; \ + } while (0) +#endif + +#ifndef ERR_FAIL_V +#define ERR_FAIL_V(ret) \ + do { \ + ERR_PRINT("Method/Function Failed."); \ + return ret; \ + } while (0) +#endif + +#ifndef CRASH_NOW +#define CRASH_NOW() \ + do { \ + FATAL_PRINT("Method/Function Failed."); \ + GENERATE_TRAP; \ + } while (0) +#endif + +#endif // DEFS_H diff --git a/include/core/Dictionary.hpp b/include/core/Dictionary.hpp new file mode 100644 index 0000000..7f57587 --- /dev/null +++ b/include/core/Dictionary.hpp @@ -0,0 +1,89 @@ +/*************************************************************************/ +/* Dictionary.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef DICTIONARY_H +#define DICTIONARY_H + +#include "Variant.hpp" + +#include "Array.hpp" + +#include + +namespace godot { + +class Dictionary { + godot_dictionary _godot_dictionary; + + friend Variant::operator Dictionary() const; + inline explicit Dictionary(const godot_dictionary &other) { + _godot_dictionary = other; + } + +public: + Dictionary(); + Dictionary(const Dictionary &other); + Dictionary &operator=(const Dictionary &other); + + template + static Dictionary make(Args... args) { + return helpers::add_all(Dictionary(), args...); + } + + void clear(); + + bool empty() const; + + void erase(const Variant &key); + + bool has(const Variant &key) const; + + bool has_all(const Array &keys) const; + + uint32_t hash() const; + + Array keys() const; + + Variant &operator[](const Variant &key); + + const Variant &operator[](const Variant &key) const; + + int size() const; + + String to_json() const; + + Array values() const; + + ~Dictionary(); +}; + +} // namespace godot + +#endif // DICTIONARY_H diff --git a/include/core/Godot.hpp b/include/core/Godot.hpp new file mode 100644 index 0000000..34d297a --- /dev/null +++ b/include/core/Godot.hpp @@ -0,0 +1,619 @@ +/*************************************************************************/ +/* Godot.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef GODOT_HPP +#define GODOT_HPP + +#include +#include + +#include +#include +#include + +#include "CoreTypes.hpp" +#include "Ref.hpp" +#include "TagDB.hpp" +#include "Variant.hpp" + +#include "Object.hpp" + +#include "GodotGlobal.hpp" + +#include + +namespace godot { +namespace detail { + +// Godot classes are wrapped by heap-allocated instances mimicking them through the C API. +// They all inherit `_Wrapped`. +template +T *get_wrapper(godot_object *obj) { + return (T *)godot::nativescript_1_1_api->godot_nativescript_get_instance_binding_data(godot::_RegisterState::language_index, obj); +} + +// Custom class instances are not obtainable by just casting the pointer to the base class they inherit, +// partly because in Godot, scripts are not instances of the classes themselves, they are only attached to them. +// Yet we want to "fake" it as if they were the same entity. +template +T *get_custom_class_instance(const Object *obj) { + return (obj) ? (T *)godot::nativescript_api->godot_nativescript_get_userdata(obj->_owner) : nullptr; +} + +template +inline T *create_custom_class_instance() { + // Usually, script instances hold a reference to their NativeScript resource. + // that resource is obtained from a `.gdns` file, which in turn exists because + // of the resource system of Godot. We can't cleanly hardcode that here, + // so the easiest for now (though not really clean) is to create new resource instances, + // individually attached to the script instances. + + // We cannot use wrappers because of https://github.com/godotengine/godot/issues/39181 + // godot::NativeScript *script = godot::NativeScript::_new(); + // script->set_library(get_wrapper((godot_object *)godot::gdnlib)); + // script->set_class_name(T::___get_class_name()); + + static_assert(T::___CLASS_IS_SCRIPT, "This function must only be used on custom classes"); + + // So we use the C API directly. + static godot_class_constructor script_constructor = godot::api->godot_get_class_constructor("NativeScript"); + static godot_method_bind *mb_set_library = godot::api->godot_method_bind_get_method("NativeScript", "set_library"); + static godot_method_bind *mb_set_class_name = godot::api->godot_method_bind_get_method("NativeScript", "set_class_name"); + godot_object *script = script_constructor(); + { + const void *args[] = { godot::gdnlib }; + godot::api->godot_method_bind_ptrcall(mb_set_library, script, args, nullptr); + } + { + const String class_name = T::___get_class_name(); + const void *args[] = { &class_name }; + godot::api->godot_method_bind_ptrcall(mb_set_class_name, script, args, nullptr); + } + + // Now to instanciate T, we initially did this, however in case of Reference it returns a variant with refcount + // already initialized, which woud cause inconsistent behavior compared to other classes (we still have to return a pointer). + //Variant instance_variant = script->new_(); + //T *instance = godot::get_custom_class_instance(instance_variant); + + // So we should do this instead, however while convenient, it uses unnecessary wrapper objects. + // Object *base_obj = T::___new_godot_base(); + // base_obj->set_script(script); + // return get_custom_class_instance(base_obj); + + // Again using the C API to do exactly what we have to do. + static godot_class_constructor base_constructor = godot::api->godot_get_class_constructor(T::___get_godot_class_name()); + static godot_method_bind *mb_set_script = godot::api->godot_method_bind_get_method("Object", "set_script"); + godot_object *base_obj = base_constructor(); + { + const void *args[] = { script }; + godot::api->godot_method_bind_ptrcall(mb_set_script, base_obj, args, nullptr); + } + + return (T *)godot::nativescript_api->godot_nativescript_get_userdata(base_obj); +} + +} // namespace detail + +// Used in the definition of a custom class. +// +// Name: Name of your class, without namespace +// Base: Name of the direct base class, with namespace if necessary +// +// ___get_class_name: Name of the class +// ___get_godot_class_name: Name of the Godot base class this class inherits from (i.e not direct) +// _new: Creates a new instance of the class +// ___get_id: Gets the unique ID of the class. Godot and custom classes are both within that set. +// ___get_base_id: Gets the ID of the direct base class, as returned by ___get_id +// ___get_base_class_name: Name of the direct base class +// ___get_from_variant: Converts a Variant into an Object*. Will be non-null if the class matches. +#define GODOT_CLASS(Name, Base) \ + \ +public: \ + inline static const char *___get_class_name() { return #Name; } \ + enum { ___CLASS_IS_SCRIPT = 1 }; \ + inline static const char *___get_godot_class_name() { \ + return Base::___get_godot_class_name(); \ + } \ + inline static Name *_new() { \ + return godot::detail::create_custom_class_instance(); \ + } \ + inline static size_t ___get_id() { return typeid(Name).hash_code(); } \ + inline static size_t ___get_base_id() { return Base::___get_id(); } \ + inline static const char *___get_base_class_name() { return Base::___get_class_name(); } \ + inline static godot::Object *___get_from_variant(godot::Variant a) { \ + return (godot::Object *)godot::detail::get_custom_class_instance( \ + godot::Object::___get_from_variant(a)); \ + } \ + \ +private: + +// Legacy compatibility +#define GODOT_SUBCLASS(Name, Base) GODOT_CLASS(Name, Base) + +template +struct _ArgCast { + static T _arg_cast(Variant a) { + return a; + } +}; + +template +struct _ArgCast { + static T *_arg_cast(Variant a) { + return (T *)T::___get_from_variant(a); + } +}; + +template <> +struct _ArgCast { + static Variant _arg_cast(Variant a) { + return a; + } +}; + +// instance and destroy funcs + +template +void *_godot_class_instance_func(godot_object *p, void * /*method_data*/) { + T *d = new T(); + d->_owner = p; + d->_type_tag = typeid(T).hash_code(); + d->_init(); + return d; +} + +template +void _godot_class_destroy_func(godot_object * /*p*/, void * /*method_data*/, void *data) { + T *d = (T *)data; + delete d; +} + +template +void register_class() { + static_assert(T::___CLASS_IS_SCRIPT, "This function must only be used on custom classes"); + + godot_instance_create_func create = {}; + create.create_func = _godot_class_instance_func; + + godot_instance_destroy_func destroy = {}; + destroy.destroy_func = _godot_class_destroy_func; + + _TagDB::register_type(T::___get_id(), T::___get_base_id()); + + godot::nativescript_api->godot_nativescript_register_class(godot::_RegisterState::nativescript_handle, + T::___get_class_name(), T::___get_base_class_name(), create, destroy); + + godot::nativescript_1_1_api->godot_nativescript_set_type_tag(godot::_RegisterState::nativescript_handle, + T::___get_class_name(), (const void *)T::___get_id()); + + T::_register_methods(); +} + +template +void register_tool_class() { + static_assert(T::___CLASS_IS_SCRIPT, "This function must only be used on custom classes"); + + godot_instance_create_func create = {}; + create.create_func = _godot_class_instance_func; + + godot_instance_destroy_func destroy = {}; + destroy.destroy_func = _godot_class_destroy_func; + + _TagDB::register_type(T::___get_id(), T::___get_base_id()); + + godot::nativescript_api->godot_nativescript_register_tool_class(godot::_RegisterState::nativescript_handle, + T::___get_class_name(), T::___get_base_class_name(), create, destroy); + + godot::nativescript_1_1_api->godot_nativescript_set_type_tag(godot::_RegisterState::nativescript_handle, + T::___get_class_name(), (const void *)T::___get_id()); + + T::_register_methods(); +} + +// method registering + +typedef godot_variant (*__godot_wrapper_method)(godot_object *, void *, void *, int, godot_variant **); + +template +const char *___get_method_class_name(R (T::*p)(args... a)) { + static_assert(T::___CLASS_IS_SCRIPT, "This function must only be used on custom classes"); + (void)p; // To avoid "unused parameter" warnings. `p` is required for template matching. + return T::___get_class_name(); +} + +// This second version is also required to match constant functions +template +const char *___get_method_class_name(R (T::*p)(args... a) const) { + static_assert(T::___CLASS_IS_SCRIPT, "This function must only be used on custom classes"); + (void)p; // To avoid "unused parameter" warnings. `p` is required for template matching. + return T::___get_class_name(); +} + +// Okay, time for some template magic. +// Many thanks to manpat from the GDL Discord Server. + +// This is stuff that's available in C++14 I think, but whatever. + +template +struct __Sequence {}; + +template +struct __construct_sequence { + using type = typename __construct_sequence::type; +}; + +template +struct __construct_sequence<0, I...> { + using type = __Sequence; +}; + +// Now the wrapping part. +template +struct _WrappedMethod { + R(T::*f) + (As...); + + template + void apply(Variant *ret, T *obj, Variant **args, __Sequence) { + *ret = (obj->*f)(_ArgCast::_arg_cast(*args[I])...); + } +}; + +template +struct _WrappedMethod { + void (T::*f)(As...); + + template + void apply(Variant * /*ret*/, T *obj, Variant **args, __Sequence) { + (obj->*f)(_ArgCast::_arg_cast(*args[I])...); + } +}; + +template +godot_variant __wrapped_method(godot_object *, void *method_data, void *user_data, int /*num_args*/, godot_variant **args) { + godot_variant v; + godot::api->godot_variant_new_nil(&v); + + T *obj = (T *)user_data; + _WrappedMethod *method = (_WrappedMethod *)method_data; + + Variant *var = (Variant *)&v; + Variant **arg = (Variant **)args; + + method->apply(var, obj, arg, typename __construct_sequence::type{}); + + return v; +} + +template +void *___make_wrapper_function(R (T::*f)(As...)) { + using MethodType = _WrappedMethod; + MethodType *p = (MethodType *)godot::api->godot_alloc(sizeof(MethodType)); + p->f = f; + return (void *)p; +} + +template +__godot_wrapper_method ___get_wrapper_function(R (T::* /*f*/)(As...)) { + return (__godot_wrapper_method)&__wrapped_method; +} + +template +void *___make_wrapper_function(R (T::*f)(A...) const) { + return ___make_wrapper_function((R(T::*)(A...))f); +} + +template +__godot_wrapper_method ___get_wrapper_function(R (T::*f)(A...) const) { + return ___get_wrapper_function((R(T::*)(A...))f); +} + +template +void register_method(const char *name, M method_ptr, godot_method_rpc_mode rpc_type = GODOT_METHOD_RPC_MODE_DISABLED) { + godot_instance_method method = {}; + method.method_data = ___make_wrapper_function(method_ptr); + method.free_func = godot::api->godot_free; + method.method = (__godot_wrapper_method)___get_wrapper_function(method_ptr); + + godot_method_attributes attr = {}; + attr.rpc_type = rpc_type; + + godot::nativescript_api->godot_nativescript_register_method(godot::_RegisterState::nativescript_handle, + ___get_method_class_name(method_ptr), name, attr, method); +} + +// User can specify a derived class D to register the method for, instead of it being inferred. +template +void register_method_explicit(const char *name, R (B::*method_ptr)(As...), + godot_method_rpc_mode rpc_type = GODOT_METHOD_RPC_MODE_DISABLED) { + static_assert(std::is_base_of::value, "Explicit class must derive from method class"); + register_method(name, static_cast(method_ptr), rpc_type); +} + +template +struct _PropertySetFunc { + void (T::*f)(P); + static void _wrapped_setter(godot_object * /*object*/, void *method_data, void *user_data, godot_variant *value) { + _PropertySetFunc *set_func = (_PropertySetFunc *)method_data; + T *obj = (T *)user_data; + + Variant *v = (Variant *)value; + + (obj->*(set_func->f))(_ArgCast

::_arg_cast(*v)); + } +}; + +template +struct _PropertyGetFunc { + P(T::*f) + (); + static godot_variant _wrapped_getter(godot_object * /*object*/, void *method_data, void *user_data) { + _PropertyGetFunc *get_func = (_PropertyGetFunc *)method_data; + T *obj = (T *)user_data; + + godot_variant var; + godot::api->godot_variant_new_nil(&var); + + Variant *v = (Variant *)&var; + + *v = (obj->*(get_func->f))(); + + return var; + } +}; + +template +struct _PropertyDefaultSetFunc { + P(T::*f); + static void _wrapped_setter(godot_object * /*object*/, void *method_data, void *user_data, godot_variant *value) { + _PropertyDefaultSetFunc *set_func = (_PropertyDefaultSetFunc *)method_data; + T *obj = (T *)user_data; + + Variant *v = (Variant *)value; + + (obj->*(set_func->f)) = _ArgCast

::_arg_cast(*v); + } +}; + +template +struct _PropertyDefaultGetFunc { + P(T::*f); + static godot_variant _wrapped_getter(godot_object * /*object*/, void *method_data, void *user_data) { + _PropertyDefaultGetFunc *get_func = (_PropertyDefaultGetFunc *)method_data; + T *obj = (T *)user_data; + + godot_variant var; + godot::api->godot_variant_new_nil(&var); + + Variant *v = (Variant *)&var; + + *v = (obj->*(get_func->f)); + + return var; + } +}; + +template +void register_property(const char *name, P(T::*var), P default_value, + godot_method_rpc_mode rpc_mode = GODOT_METHOD_RPC_MODE_DISABLED, + godot_property_usage_flags usage = GODOT_PROPERTY_USAGE_DEFAULT, + godot_property_hint hint = GODOT_PROPERTY_HINT_NONE, String hint_string = "") { + static_assert(T::___CLASS_IS_SCRIPT, "This function must only be used on custom classes"); + + Variant def_val = default_value; + + usage = (godot_property_usage_flags)((int)usage | GODOT_PROPERTY_USAGE_SCRIPT_VARIABLE); + + if (def_val.get_type() == Variant::OBJECT) { + Object *o = detail::get_wrapper(def_val.operator godot_object *()); + if (o && o->is_class("Resource")) { + hint = (godot_property_hint)((int)hint | GODOT_PROPERTY_HINT_RESOURCE_TYPE); + hint_string = o->get_class(); + } + } + + godot_string *_hint_string = (godot_string *)&hint_string; + + godot_property_attributes attr = {}; + if (def_val.get_type() == Variant::NIL) { + attr.type = Variant::OBJECT; + } else { + attr.type = def_val.get_type(); + attr.default_value = *(godot_variant *)&def_val; + } + + attr.hint = hint; + attr.rset_type = rpc_mode; + attr.usage = usage; + attr.hint_string = *_hint_string; + + _PropertyDefaultSetFunc *wrapped_set = + (_PropertyDefaultSetFunc *)godot::api->godot_alloc(sizeof(_PropertyDefaultSetFunc)); + wrapped_set->f = var; + + _PropertyDefaultGetFunc *wrapped_get = + (_PropertyDefaultGetFunc *)godot::api->godot_alloc(sizeof(_PropertyDefaultGetFunc)); + wrapped_get->f = var; + + godot_property_set_func set_func = {}; + set_func.method_data = (void *)wrapped_set; + set_func.free_func = godot::api->godot_free; + set_func.set_func = &_PropertyDefaultSetFunc::_wrapped_setter; + + godot_property_get_func get_func = {}; + get_func.method_data = (void *)wrapped_get; + get_func.free_func = godot::api->godot_free; + get_func.get_func = &_PropertyDefaultGetFunc::_wrapped_getter; + + godot::nativescript_api->godot_nativescript_register_property(godot::_RegisterState::nativescript_handle, + T::___get_class_name(), name, &attr, set_func, get_func); +} + +template +void register_property(const char *name, void (T::*setter)(P), P (T::*getter)(), P default_value, + godot_method_rpc_mode rpc_mode = GODOT_METHOD_RPC_MODE_DISABLED, + godot_property_usage_flags usage = GODOT_PROPERTY_USAGE_DEFAULT, + godot_property_hint hint = GODOT_PROPERTY_HINT_NONE, String hint_string = "") { + static_assert(T::___CLASS_IS_SCRIPT, "This function must only be used on custom classes"); + + Variant def_val = default_value; + + godot_string *_hint_string = (godot_string *)&hint_string; + + godot_property_attributes attr = {}; + if (def_val.get_type() == Variant::NIL) { + attr.type = Variant::OBJECT; + } else { + attr.type = def_val.get_type(); + attr.default_value = *(godot_variant *)&def_val; + } + attr.hint = hint; + attr.rset_type = rpc_mode; + attr.usage = usage; + attr.hint_string = *_hint_string; + + _PropertySetFunc *wrapped_set = (_PropertySetFunc *)godot::api->godot_alloc(sizeof(_PropertySetFunc)); + wrapped_set->f = setter; + + _PropertyGetFunc *wrapped_get = (_PropertyGetFunc *)godot::api->godot_alloc(sizeof(_PropertyGetFunc)); + wrapped_get->f = getter; + + godot_property_set_func set_func = {}; + set_func.method_data = (void *)wrapped_set; + set_func.free_func = godot::api->godot_free; + set_func.set_func = &_PropertySetFunc::_wrapped_setter; + + godot_property_get_func get_func = {}; + get_func.method_data = (void *)wrapped_get; + get_func.free_func = godot::api->godot_free; + get_func.get_func = &_PropertyGetFunc::_wrapped_getter; + + godot::nativescript_api->godot_nativescript_register_property(godot::_RegisterState::nativescript_handle, + T::___get_class_name(), name, &attr, set_func, get_func); +} + +template +void register_property(const char *name, void (T::*setter)(P), P (T::*getter)() const, P default_value, + godot_method_rpc_mode rpc_mode = GODOT_METHOD_RPC_MODE_DISABLED, + godot_property_usage_flags usage = GODOT_PROPERTY_USAGE_DEFAULT, + godot_property_hint hint = GODOT_PROPERTY_HINT_NONE, String hint_string = "") { + register_property(name, setter, (P(T::*)())getter, default_value, rpc_mode, usage, hint, hint_string); +} + +template +void register_signal(String name, Dictionary args) { + static_assert(T::___CLASS_IS_SCRIPT, "This function must only be used on custom classes"); + + godot_signal signal = {}; + signal.name = *(godot_string *)&name; + signal.num_args = args.size(); + signal.num_default_args = 0; + + // Need to check because malloc(0) is platform-dependent. Zero arguments will leave args to nullptr. + if (signal.num_args != 0) { + signal.args = (godot_signal_argument *)godot::api->godot_alloc(sizeof(godot_signal_argument) * signal.num_args); + memset((void *)signal.args, 0, sizeof(godot_signal_argument) * signal.num_args); + } + + for (int i = 0; i < signal.num_args; i++) { + // Array entry = args[i]; + // String name = entry[0]; + String name = args.keys()[i]; + godot_string *_key = (godot_string *)&name; + godot::api->godot_string_new_copy(&signal.args[i].name, _key); + + // if (entry.size() > 1) { + // signal.args[i].type = entry[1]; + // } + signal.args[i].type = args.values()[i]; + } + + godot::nativescript_api->godot_nativescript_register_signal(godot::_RegisterState::nativescript_handle, + T::___get_class_name(), &signal); + + for (int i = 0; i < signal.num_args; i++) { + godot::api->godot_string_destroy(&signal.args[i].name); + } + + if (signal.args) { + godot::api->godot_free(signal.args); + } +} + +template +void register_signal(String name, Args... varargs) { + register_signal(name, Dictionary::make(varargs...)); +} + +template +void register_signal(String name) { + static_assert(T::___CLASS_IS_SCRIPT, "This function must only be used on custom classes"); + + godot_signal signal = {}; + signal.name = *(godot_string *)&name; + + godot::nativescript_api->godot_nativescript_register_signal(godot::_RegisterState::nativescript_handle, + T::___get_class_name(), &signal); +} + +#ifndef GODOT_CPP_NO_OBJECT_CAST +template +T *Object::cast_to(const Object *obj) { + if (!obj) + return nullptr; + + if (T::___CLASS_IS_SCRIPT) { + size_t have_tag = (size_t)godot::nativescript_1_1_api->godot_nativescript_get_type_tag(obj->_owner); + if (have_tag) { + if (!godot::_TagDB::is_type_known((size_t)have_tag)) { + have_tag = 0; + } + } + + if (!have_tag) { + have_tag = obj->_type_tag; + } + + if (godot::_TagDB::is_type_compatible(T::___get_id(), have_tag)) { + return detail::get_custom_class_instance(obj); + } + } else { + if (godot::core_1_2_api->godot_object_cast_to(obj->_owner, (void *)T::___get_id())) { + return (T *)obj; + } + } + + return nullptr; +} +#endif + +} // namespace godot + +#endif // GODOT_HPP diff --git a/include/core/GodotGlobal.hpp b/include/core/GodotGlobal.hpp new file mode 100644 index 0000000..847bf32 --- /dev/null +++ b/include/core/GodotGlobal.hpp @@ -0,0 +1,81 @@ +/*************************************************************************/ +/* GodotGlobal.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef GODOT_GLOBAL_HPP +#define GODOT_GLOBAL_HPP + +#include "Array.hpp" +#include "String.hpp" +#include + +namespace godot { + +extern "C" const godot_gdnative_core_api_struct *api; +extern "C" const godot_gdnative_core_1_1_api_struct *core_1_1_api; +extern "C" const godot_gdnative_core_1_2_api_struct *core_1_2_api; + +extern "C" const godot_gdnative_ext_nativescript_api_struct *nativescript_api; +extern "C" const godot_gdnative_ext_nativescript_1_1_api_struct *nativescript_1_1_api; +extern "C" const godot_gdnative_ext_pluginscript_api_struct *pluginscript_api; +extern "C" const godot_gdnative_ext_android_api_struct *android_api; +extern "C" const godot_gdnative_ext_arvr_api_struct *arvr_api; +extern "C" const godot_gdnative_ext_videodecoder_api_struct *videodecoder_api; +extern "C" const godot_gdnative_ext_net_api_struct *net_api; +extern "C" const godot_gdnative_ext_net_3_2_api_struct *net_3_2_api; + +extern "C" const void *gdnlib; + +class Godot { +public: + static void print(const String &message); + static void print_warning(const String &description, const String &function, const String &file, int line); + static void print_error(const String &description, const String &function, const String &file, int line); + + static void gdnative_init(godot_gdnative_init_options *o); + static void gdnative_terminate(godot_gdnative_terminate_options *o); + static void nativescript_init(void *handle); + static void nativescript_terminate(void *handle); + + static void gdnative_profiling_add_data(const char *p_signature, uint64_t p_time); + + template + static void print(const String &fmt, Args... values) { + print(fmt.format(Array::make(values...))); + } +}; + +struct _RegisterState { + static void *nativescript_handle; + static int language_index; +}; + +} // namespace godot + +#endif diff --git a/include/core/GodotProfiling.hpp b/include/core/GodotProfiling.hpp new file mode 100644 index 0000000..124a830 --- /dev/null +++ b/include/core/GodotProfiling.hpp @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* GodotProfiling.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef GODOT_PROFILING_HPP +#define GODOT_PROFILING_HPP + +#include "OS.hpp" + +namespace godot { + +class FunctionProfiling { + char signature[1024]; + uint64_t ticks; + +public: + FunctionProfiling(const char *p_function, const int p_line) { + snprintf(signature, 1024, "::%d::%s", p_line, p_function); + ticks = OS::get_singleton()->get_ticks_usec(); + } + ~FunctionProfiling() { + uint64_t t = OS::get_singleton()->get_ticks_usec() - ticks; + if (t > 0) { + Godot::gdnative_profiling_add_data(signature, t); + } + } +}; + +} // namespace godot + +#ifdef DEBUG_ENABLED +#define GODOT_PROFILING_FUNCTION FunctionProfiling __function_profiling(__FUNCTION__, __LINE__); +#else +#define GODOT_PROFILING_FUNCTION +#endif + +#endif diff --git a/include/core/Math.hpp b/include/core/Math.hpp new file mode 100644 index 0000000..f2468d3 --- /dev/null +++ b/include/core/Math.hpp @@ -0,0 +1,302 @@ +/*************************************************************************/ +/* Math.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef GODOT_MATH_H +#define GODOT_MATH_H + +#include "Defs.hpp" +#include + +namespace godot { +namespace Math { + +// Functions reproduced as in Godot's source code `math_funcs.h`. +// Some are overloads to automatically support changing real_t into either double or float in the way Godot does. + +inline double fmod(double p_x, double p_y) { + return ::fmod(p_x, p_y); +} +inline float fmod(float p_x, float p_y) { + return ::fmodf(p_x, p_y); +} + +inline double floor(double p_x) { + return ::floor(p_x); +} +inline float floor(float p_x) { + return ::floorf(p_x); +} + +inline double exp(double p_x) { + return ::exp(p_x); +} +inline float exp(float p_x) { + return ::expf(p_x); +} + +inline double sin(double p_x) { + return ::sin(p_x); +} +inline float sin(float p_x) { + return ::sinf(p_x); +} + +inline double cos(double p_x) { + return ::cos(p_x); +} +inline float cos(float p_x) { + return ::cosf(p_x); +} + +inline double tan(double p_x) { + return ::tan(p_x); +} +inline float tan(float p_x) { + return ::tanf(p_x); +} + +inline double asin(double p_x) { + return ::asin(p_x); +} +inline float asin(float p_x) { + return ::asinf(p_x); +} + +inline double acos(double p_x) { + return ::acos(p_x); +} +inline float acos(float p_x) { + return ::acosf(p_x); +} + +inline double atan(double p_x) { + return ::atan(p_x); +} +inline float atan(float p_x) { + return ::atanf(p_x); +} + +inline double atan2(double p_y, double p_x) { + return ::atan2(p_y, p_x); +} +inline float atan2(float p_y, float p_x) { + return ::atan2f(p_y, p_x); +} + +inline double sqrt(double p_x) { + return ::sqrt(p_x); +} +inline float sqrt(float p_x) { + return ::sqrtf(p_x); +} + +inline float lerp(float minv, float maxv, float t) { + return minv + t * (maxv - minv); +} +inline double lerp(double minv, double maxv, double t) { + return minv + t * (maxv - minv); +} + +inline double lerp_angle(double p_from, double p_to, double p_weight) { + double difference = fmod(p_to - p_from, Math_TAU); + double distance = fmod(2.0 * difference, Math_TAU) - difference; + return p_from + distance * p_weight; +} +inline float lerp_angle(float p_from, float p_to, float p_weight) { + float difference = fmod(p_to - p_from, (float)Math_TAU); + float distance = fmod(2.0f * difference, (float)Math_TAU) - difference; + return p_from + distance * p_weight; +} + +template +inline T clamp(T x, T minv, T maxv) { + if (x < minv) { + return minv; + } + if (x > maxv) { + return maxv; + } + return x; +} + +template +inline T min(T a, T b) { + return a < b ? a : b; +} + +template +inline T max(T a, T b) { + return a > b ? a : b; +} + +template +inline T sign(T x) { + return static_cast(x < 0 ? -1 : 1); +} + +inline double deg2rad(double p_y) { + return p_y * Math_PI / 180.0; +} +inline float deg2rad(float p_y) { + return p_y * static_cast(Math_PI) / 180.f; +} + +inline double rad2deg(double p_y) { + return p_y * 180.0 / Math_PI; +} +inline float rad2deg(float p_y) { + return p_y * 180.f / static_cast(Math_PI); +} + +inline double inverse_lerp(double p_from, double p_to, double p_value) { + return (p_value - p_from) / (p_to - p_from); +} +inline float inverse_lerp(float p_from, float p_to, float p_value) { + return (p_value - p_from) / (p_to - p_from); +} + +inline double range_lerp(double p_value, double p_istart, double p_istop, double p_ostart, double p_ostop) { + return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); +} +inline float range_lerp(float p_value, float p_istart, float p_istop, float p_ostart, float p_ostop) { + return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); +} + +inline bool is_equal_approx(real_t a, real_t b) { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) { + return true; + } + // Then check for approximate equality. + real_t tolerance = CMP_EPSILON * std::abs(a); + if (tolerance < CMP_EPSILON) { + tolerance = CMP_EPSILON; + } + return std::abs(a - b) < tolerance; +} + +inline bool is_equal_approx(real_t a, real_t b, real_t tolerance) { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) { + return true; + } + // Then check for approximate equality. + return std::abs(a - b) < tolerance; +} + +inline bool is_zero_approx(real_t s) { + return std::abs(s) < CMP_EPSILON; +} + +inline double smoothstep(double p_from, double p_to, double p_weight) { + if (is_equal_approx(static_cast(p_from), static_cast(p_to))) { + return p_from; + } + double x = clamp((p_weight - p_from) / (p_to - p_from), 0.0, 1.0); + return x * x * (3.0 - 2.0 * x); +} +inline float smoothstep(float p_from, float p_to, float p_weight) { + if (is_equal_approx(p_from, p_to)) { + return p_from; + } + float x = clamp((p_weight - p_from) / (p_to - p_from), 0.0f, 1.0f); + return x * x * (3.0f - 2.0f * x); +} + +inline double move_toward(double p_from, double p_to, double p_delta) { + return std::abs(p_to - p_from) <= p_delta ? p_to : p_from + sign(p_to - p_from) * p_delta; +} + +inline float move_toward(float p_from, float p_to, float p_delta) { + return std::abs(p_to - p_from) <= p_delta ? p_to : p_from + sign(p_to - p_from) * p_delta; +} + +inline double linear2db(double p_linear) { + return log(p_linear) * 8.6858896380650365530225783783321; +} +inline float linear2db(float p_linear) { + return log(p_linear) * 8.6858896380650365530225783783321f; +} + +inline double db2linear(double p_db) { + return exp(p_db * 0.11512925464970228420089957273422); +} +inline float db2linear(float p_db) { + return exp(p_db * 0.11512925464970228420089957273422f); +} + +inline double round(double p_val) { + return (p_val >= 0) ? floor(p_val + 0.5) : -floor(-p_val + 0.5); +} +inline float round(float p_val) { + return (p_val >= 0) ? floor(p_val + 0.5f) : -floor(-p_val + 0.5f); +} + +inline int64_t wrapi(int64_t value, int64_t min, int64_t max) { + int64_t range = max - min; + return range == 0 ? min : min + ((((value - min) % range) + range) % range); +} + +inline float wrapf(real_t value, real_t min, real_t max) { + const real_t range = max - min; + return is_zero_approx(range) ? min : value - (range * floor((value - min) / range)); +} + +inline float stepify(float p_value, float p_step) { + if (p_step != 0) { + p_value = floor(p_value / p_step + 0.5f) * p_step; + } + return p_value; +} +inline double stepify(double p_value, double p_step) { + if (p_step != 0) { + p_value = floor(p_value / p_step + 0.5) * p_step; + } + return p_value; +} + +inline unsigned int next_power_of_2(unsigned int x) { + if (x == 0) + return 0; + + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + + return ++x; +} + +} // namespace Math +} // namespace godot + +#endif // GODOT_MATH_H diff --git a/include/core/NodePath.hpp b/include/core/NodePath.hpp new file mode 100644 index 0000000..417320a --- /dev/null +++ b/include/core/NodePath.hpp @@ -0,0 +1,84 @@ +/*************************************************************************/ +/* NodePath.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef NODEPATH_H +#define NODEPATH_H + +#include "String.hpp" + +#include + +namespace godot { + +class NodePath { + godot_node_path _node_path; + + friend class Variant; + inline explicit NodePath(godot_node_path node_path) { + _node_path = node_path; + } + +public: + NodePath(); + + NodePath(const NodePath &other); + + NodePath(const String &from); + + NodePath(const char *contents); + + String get_name(const int idx) const; + + int get_name_count() const; + + String get_subname(const int idx) const; + + int get_subname_count() const; + + bool is_absolute() const; + + bool is_empty() const; + + NodePath get_as_property_path() const; + + String get_concatenated_subnames() const; + + operator String() const; + + void operator=(const NodePath &other); + + bool operator==(const NodePath &other); + + ~NodePath(); +}; + +} // namespace godot + +#endif // NODEPATH_H diff --git a/include/core/Plane.hpp b/include/core/Plane.hpp new file mode 100644 index 0000000..a3c55db --- /dev/null +++ b/include/core/Plane.hpp @@ -0,0 +1,98 @@ +/*************************************************************************/ +/* Plane.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef PLANE_H +#define PLANE_H + +#include "Vector3.hpp" + +#include + +namespace godot { + +enum ClockDirection { + + CLOCKWISE, + COUNTERCLOCKWISE +}; + +class Plane { +public: + Vector3 normal; + real_t d; + + void set_normal(const Vector3 &p_normal); + + inline Vector3 get_normal() const { return normal; } ///Point is coplanar, CMP_EPSILON for precision + + void normalize(); + + Plane normalized() const; + + /* Plane-Point operations */ + + inline Vector3 center() const { return normal * d; } + Vector3 get_any_point() const; + Vector3 get_any_perpendicular_normal() const; + + bool is_point_over(const Vector3 &p_point) const; ///< Point is over plane + real_t distance_to(const Vector3 &p_point) const; + bool has_point(const Vector3 &p_point, real_t _epsilon = CMP_EPSILON) const; + + /* intersections */ + + bool intersect_3(const Plane &p_plane1, const Plane &p_plane2, Vector3 *r_result = 0) const; + bool intersects_ray(Vector3 p_from, Vector3 p_dir, Vector3 *p_intersection) const; + bool intersects_segment(Vector3 p_begin, Vector3 p_end, Vector3 *p_intersection) const; + + Vector3 project(const Vector3 &p_point) const; + + /* misc */ + + inline Plane operator-() const { return Plane(-normal, -d); } + bool is_almost_like(const Plane &p_plane) const; + + bool operator==(const Plane &p_plane) const; + bool operator!=(const Plane &p_plane) const; + operator String() const; + + inline Plane() { d = 0; } + inline Plane(real_t p_a, real_t p_b, real_t p_c, real_t p_d) : + normal(p_a, p_b, p_c), + d(p_d) {} + + Plane(const Vector3 &p_normal, real_t p_d); + Plane(const Vector3 &p_point, const Vector3 &p_normal); + Plane(const Vector3 &p_point1, const Vector3 &p_point2, const Vector3 &p_point3, ClockDirection p_dir = CLOCKWISE); +}; + +} // namespace godot + +#endif // PLANE_H diff --git a/include/core/PoolArrays.hpp b/include/core/PoolArrays.hpp new file mode 100644 index 0000000..dff9540 --- /dev/null +++ b/include/core/PoolArrays.hpp @@ -0,0 +1,766 @@ +/*************************************************************************/ +/* PoolArrays.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef POOLARRAYS_H +#define POOLARRAYS_H + +#include "Defs.hpp" + +#include "Color.hpp" +#include "GodotGlobal.hpp" +#include "String.hpp" +#include "Vector2.hpp" +#include "Vector3.hpp" + +#include + +namespace godot { + +class Array; + +class PoolByteArray { + godot_pool_byte_array _godot_array; + + friend class String; + friend class Variant; + inline explicit PoolByteArray(godot_pool_byte_array a) { + _godot_array = a; + } + +public: + class Read { + friend class PoolByteArray; + godot_pool_byte_array_read_access *_read_access; + + public: + inline Read() { + _read_access = nullptr; + } + + inline Read(const Read &p_other) { + _read_access = godot::api->godot_pool_byte_array_read_access_copy(p_other._read_access); + } + + inline ~Read() { + godot::api->godot_pool_byte_array_read_access_destroy(_read_access); + } + + inline const uint8_t *ptr() const { + return godot::api->godot_pool_byte_array_read_access_ptr(_read_access); + } + + inline const uint8_t &operator[](int p_idx) const { + return ptr()[p_idx]; + } + + inline void operator=(const Read &p_other) { + godot::api->godot_pool_byte_array_read_access_operator_assign(_read_access, p_other._read_access); + } + }; + + class Write { + friend class PoolByteArray; + godot_pool_byte_array_write_access *_write_access; + + public: + inline Write() { + _write_access = nullptr; + } + + inline Write(const Write &p_other) { + _write_access = godot::api->godot_pool_byte_array_write_access_copy(p_other._write_access); + } + + inline ~Write() { + godot::api->godot_pool_byte_array_write_access_destroy(_write_access); + } + + inline uint8_t *ptr() const { + return godot::api->godot_pool_byte_array_write_access_ptr(_write_access); + } + + inline uint8_t &operator[](int p_idx) const { + return ptr()[p_idx]; + } + + inline void operator=(const Write &p_other) { + godot::api->godot_pool_byte_array_write_access_operator_assign(_write_access, p_other._write_access); + } + }; + + PoolByteArray(); + PoolByteArray(const PoolByteArray &p_other); + PoolByteArray &operator=(const PoolByteArray &p_other); + + PoolByteArray(const Array &array); + + Read read() const; + + Write write(); + + void append(const uint8_t data); + + void append_array(const PoolByteArray &array); + + int insert(const int idx, const uint8_t data); + + void invert(); + + void push_back(const uint8_t data); + + void remove(const int idx); + + void resize(const int size); + + void set(const int idx, const uint8_t data); + + uint8_t operator[](const int idx); + + int size() const; + + ~PoolByteArray(); +}; + +class PoolIntArray { + godot_pool_int_array _godot_array; + + friend class Variant; + explicit inline PoolIntArray(godot_pool_int_array a) { + _godot_array = a; + } + +public: + class Read { + friend class PoolIntArray; + godot_pool_int_array_read_access *_read_access; + + public: + inline Read() { + _read_access = nullptr; + } + + inline Read(const Read &p_other) { + _read_access = godot::api->godot_pool_int_array_read_access_copy(p_other._read_access); + } + + inline ~Read() { + godot::api->godot_pool_int_array_read_access_destroy(_read_access); + } + + inline const int *ptr() const { + return godot::api->godot_pool_int_array_read_access_ptr(_read_access); + } + + inline const int &operator[](int p_idx) const { + return ptr()[p_idx]; + } + + inline void operator=(const Read &p_other) { + godot::api->godot_pool_int_array_read_access_operator_assign(_read_access, p_other._read_access); + } + }; + + class Write { + friend class PoolIntArray; + godot_pool_int_array_write_access *_write_access; + + public: + inline Write() { + _write_access = nullptr; + } + + inline Write(const Write &p_other) { + _write_access = godot::api->godot_pool_int_array_write_access_copy(p_other._write_access); + } + + inline ~Write() { + godot::api->godot_pool_int_array_write_access_destroy(_write_access); + } + + inline int *ptr() const { + return godot::api->godot_pool_int_array_write_access_ptr(_write_access); + } + + inline int &operator[](int p_idx) const { + return ptr()[p_idx]; + } + + inline void operator=(const Write &p_other) { + godot::api->godot_pool_int_array_write_access_operator_assign(_write_access, p_other._write_access); + } + }; + + PoolIntArray(); + PoolIntArray(const PoolIntArray &p_other); + PoolIntArray &operator=(const PoolIntArray &p_other); + + PoolIntArray(const Array &array); + + Read read() const; + + Write write(); + + void append(const int data); + + void append_array(const PoolIntArray &array); + + int insert(const int idx, const int data); + + void invert(); + + void push_back(const int data); + + void remove(const int idx); + + void resize(const int size); + + void set(const int idx, const int data); + + int operator[](const int idx); + + int size() const; + + ~PoolIntArray(); +}; + +class PoolRealArray { + godot_pool_real_array _godot_array; + + friend class Variant; + explicit inline PoolRealArray(godot_pool_real_array a) { + _godot_array = a; + } + +public: + class Read { + friend class PoolRealArray; + godot_pool_real_array_read_access *_read_access; + + public: + inline Read() { + _read_access = nullptr; + } + + inline Read(const Read &p_other) { + _read_access = godot::api->godot_pool_real_array_read_access_copy(p_other._read_access); + } + + inline ~Read() { + godot::api->godot_pool_real_array_read_access_destroy(_read_access); + } + + inline const real_t *ptr() const { + return godot::api->godot_pool_real_array_read_access_ptr(_read_access); + } + + inline const real_t &operator[](int p_idx) const { + return ptr()[p_idx]; + } + + inline void operator=(const Read &p_other) { + godot::api->godot_pool_real_array_read_access_operator_assign(_read_access, p_other._read_access); + } + }; + + class Write { + friend class PoolRealArray; + godot_pool_real_array_write_access *_write_access; + + public: + inline Write() { + _write_access = nullptr; + } + + inline Write(const Write &p_other) { + _write_access = godot::api->godot_pool_real_array_write_access_copy(p_other._write_access); + } + + inline ~Write() { + godot::api->godot_pool_real_array_write_access_destroy(_write_access); + } + + inline real_t *ptr() const { + return godot::api->godot_pool_real_array_write_access_ptr(_write_access); + } + + inline real_t &operator[](int p_idx) const { + return ptr()[p_idx]; + } + + inline void operator=(const Write &p_other) { + godot::api->godot_pool_real_array_write_access_operator_assign(_write_access, p_other._write_access); + } + }; + + PoolRealArray(); + PoolRealArray(const PoolRealArray &p_other); + PoolRealArray &operator=(const PoolRealArray &p_other); + + PoolRealArray(const Array &array); + + Read read() const; + + Write write(); + + void append(const real_t data); + + void append_array(const PoolRealArray &array); + + int insert(const int idx, const real_t data); + + void invert(); + + void push_back(const real_t data); + + void remove(const int idx); + + void resize(const int size); + + void set(const int idx, const real_t data); + + real_t operator[](const int idx); + + int size() const; + + ~PoolRealArray(); +}; + +class PoolStringArray { + godot_pool_string_array _godot_array; + + friend class String; + friend class Variant; + explicit inline PoolStringArray(godot_pool_string_array a) { + _godot_array = a; + } + +public: + class Read { + friend class PoolStringArray; + godot_pool_string_array_read_access *_read_access; + + public: + inline Read() { + _read_access = nullptr; + } + + inline Read(const Read &p_other) { + _read_access = godot::api->godot_pool_string_array_read_access_copy(p_other._read_access); + } + + inline ~Read() { + godot::api->godot_pool_string_array_read_access_destroy(_read_access); + } + + inline const String *ptr() const { + return (const String *)godot::api->godot_pool_string_array_read_access_ptr(_read_access); + } + + inline const String &operator[](int p_idx) const { + return ptr()[p_idx]; + } + + inline void operator=(const Read &p_other) { + godot::api->godot_pool_string_array_read_access_operator_assign(_read_access, p_other._read_access); + } + }; + + class Write { + friend class PoolStringArray; + godot_pool_string_array_write_access *_write_access; + + public: + inline Write() { + _write_access = nullptr; + } + + inline Write(const Write &p_other) { + _write_access = godot::api->godot_pool_string_array_write_access_copy(p_other._write_access); + } + + inline ~Write() { + godot::api->godot_pool_string_array_write_access_destroy(_write_access); + } + + inline String *ptr() const { + return (String *)godot::api->godot_pool_string_array_write_access_ptr(_write_access); + } + + inline String &operator[](int p_idx) const { + return ptr()[p_idx]; + } + + inline void operator=(const Write &p_other) { + godot::api->godot_pool_string_array_write_access_operator_assign(_write_access, p_other._write_access); + } + }; + + PoolStringArray(); + PoolStringArray(const PoolStringArray &p_other); + PoolStringArray &operator=(const PoolStringArray &p_other); + + PoolStringArray(const Array &array); + + Read read() const; + + Write write(); + + void append(const String &data); + + void append_array(const PoolStringArray &array); + + int insert(const int idx, const String &data); + + void invert(); + + void push_back(const String &data); + + void remove(const int idx); + + void resize(const int size); + + void set(const int idx, const String &data); + + const String operator[](const int idx); + + int size() const; + + ~PoolStringArray(); +}; + +class PoolVector2Array { + godot_pool_vector2_array _godot_array; + + friend class Variant; + explicit inline PoolVector2Array(godot_pool_vector2_array a) { + _godot_array = a; + } + +public: + class Read { + friend class PoolVector2Array; + godot_pool_vector2_array_read_access *_read_access; + + public: + inline Read() { + _read_access = nullptr; + } + + inline Read(const Read &p_other) { + _read_access = godot::api->godot_pool_vector2_array_read_access_copy(p_other._read_access); + } + + inline ~Read() { + godot::api->godot_pool_vector2_array_read_access_destroy(_read_access); + } + + inline const Vector2 *ptr() const { + return (const Vector2 *)godot::api->godot_pool_vector2_array_read_access_ptr(_read_access); + } + + inline const Vector2 &operator[](int p_idx) const { + return ptr()[p_idx]; + } + + inline void operator=(const Read &p_other) { + godot::api->godot_pool_vector2_array_read_access_operator_assign(_read_access, p_other._read_access); + } + }; + + class Write { + friend class PoolVector2Array; + godot_pool_vector2_array_write_access *_write_access; + + public: + inline Write() { + _write_access = nullptr; + } + + inline Write(const Write &p_other) { + _write_access = godot::api->godot_pool_vector2_array_write_access_copy(p_other._write_access); + } + + inline ~Write() { + godot::api->godot_pool_vector2_array_write_access_destroy(_write_access); + } + + inline Vector2 *ptr() const { + return (Vector2 *)godot::api->godot_pool_vector2_array_write_access_ptr(_write_access); + } + + inline Vector2 &operator[](int p_idx) const { + return ptr()[p_idx]; + } + + inline void operator=(const Write &p_other) { + godot::api->godot_pool_vector2_array_write_access_operator_assign(_write_access, p_other._write_access); + } + }; + + PoolVector2Array(); + PoolVector2Array(const PoolVector2Array &p_other); + PoolVector2Array &operator=(const PoolVector2Array &p_other); + + PoolVector2Array(const Array &array); + + Read read() const; + + Write write(); + + void append(const Vector2 &data); + + void append_array(const PoolVector2Array &array); + + int insert(const int idx, const Vector2 &data); + + void invert(); + + void push_back(const Vector2 &data); + + void remove(const int idx); + + void resize(const int size); + + void set(const int idx, const Vector2 &data); + + const Vector2 operator[](const int idx); + + int size() const; + + ~PoolVector2Array(); +}; + +class PoolVector3Array { + godot_pool_vector3_array _godot_array; + + friend class Variant; + explicit inline PoolVector3Array(godot_pool_vector3_array a) { + _godot_array = a; + } + +public: + class Read { + friend class PoolVector3Array; + godot_pool_vector3_array_read_access *_read_access; + + public: + inline Read() { + _read_access = nullptr; + } + + inline Read(const Read &p_other) { + _read_access = godot::api->godot_pool_vector3_array_read_access_copy(p_other._read_access); + } + + inline ~Read() { + godot::api->godot_pool_vector3_array_read_access_destroy(_read_access); + } + + inline const Vector3 *ptr() const { + return (const Vector3 *)godot::api->godot_pool_vector3_array_read_access_ptr(_read_access); + } + + inline const Vector3 &operator[](int p_idx) const { + return ptr()[p_idx]; + } + + inline void operator=(const Read &p_other) { + godot::api->godot_pool_vector3_array_read_access_operator_assign(_read_access, p_other._read_access); + } + }; + + class Write { + friend class PoolVector3Array; + godot_pool_vector3_array_write_access *_write_access; + + public: + inline Write() { + _write_access = nullptr; + } + + inline Write(const Write &p_other) { + _write_access = godot::api->godot_pool_vector3_array_write_access_copy(p_other._write_access); + } + + inline ~Write() { + godot::api->godot_pool_vector3_array_write_access_destroy(_write_access); + } + + inline Vector3 *ptr() const { + return (Vector3 *)godot::api->godot_pool_vector3_array_write_access_ptr(_write_access); + } + + inline Vector3 &operator[](int p_idx) const { + return ptr()[p_idx]; + } + + inline void operator=(const Write &p_other) { + godot::api->godot_pool_vector3_array_write_access_operator_assign(_write_access, p_other._write_access); + } + }; + + PoolVector3Array(); + PoolVector3Array(const PoolVector3Array &p_other); + PoolVector3Array &operator=(const PoolVector3Array &p_other); + + PoolVector3Array(const Array &array); + + Read read() const; + + Write write(); + + void append(const Vector3 &data); + + void append_array(const PoolVector3Array &array); + + int insert(const int idx, const Vector3 &data); + + void invert(); + + void push_back(const Vector3 &data); + + void remove(const int idx); + + void resize(const int size); + + void set(const int idx, const Vector3 &data); + + const Vector3 operator[](const int idx); + + int size() const; + + ~PoolVector3Array(); +}; + +class PoolColorArray { + godot_pool_color_array _godot_array; + + friend class Variant; + explicit inline PoolColorArray(godot_pool_color_array a) { + _godot_array = a; + } + +public: + class Read { + friend class PoolColorArray; + godot_pool_color_array_read_access *_read_access; + + public: + inline Read() { + _read_access = nullptr; + } + + inline Read(const Read &p_other) { + _read_access = godot::api->godot_pool_color_array_read_access_copy(p_other._read_access); + } + + inline ~Read() { + godot::api->godot_pool_color_array_read_access_destroy(_read_access); + } + + inline const Color *ptr() const { + return (const Color *)godot::api->godot_pool_color_array_read_access_ptr(_read_access); + } + + inline const Color &operator[](int p_idx) const { + return ptr()[p_idx]; + } + + inline void operator=(const Read &p_other) { + godot::api->godot_pool_color_array_read_access_operator_assign(_read_access, p_other._read_access); + } + }; + + class Write { + friend class PoolColorArray; + godot_pool_color_array_write_access *_write_access; + + public: + inline Write() { + _write_access = nullptr; + } + + inline Write(const Write &p_other) { + _write_access = godot::api->godot_pool_color_array_write_access_copy(p_other._write_access); + } + + inline ~Write() { + godot::api->godot_pool_color_array_write_access_destroy(_write_access); + } + + inline Color *ptr() const { + return (Color *)godot::api->godot_pool_color_array_write_access_ptr(_write_access); + } + + inline Color &operator[](int p_idx) const { + return ptr()[p_idx]; + } + + inline void operator=(const Write &p_other) { + godot::api->godot_pool_color_array_write_access_operator_assign(_write_access, p_other._write_access); + } + }; + + PoolColorArray(); + PoolColorArray(const PoolColorArray &p_other); + PoolColorArray &operator=(const PoolColorArray &p_other); + + PoolColorArray(const Array &array); + + Read read() const; + + Write write(); + + void append(const Color &data); + + void append_array(const PoolColorArray &array); + + int insert(const int idx, const Color &data); + + void invert(); + + void push_back(const Color &data); + + void remove(const int idx); + + void resize(const int size); + + void set(const int idx, const Color &data); + + const Color operator[](const int idx); + + int size() const; + + ~PoolColorArray(); +}; + +} // namespace godot + +#endif // POOLARRAYS_H diff --git a/include/core/Quat.hpp b/include/core/Quat.hpp new file mode 100644 index 0000000..e14d5a6 --- /dev/null +++ b/include/core/Quat.hpp @@ -0,0 +1,125 @@ +/*************************************************************************/ +/* Quat.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef QUAT_H +#define QUAT_H + +#include + +#include "Vector3.hpp" + +// #include "Basis.h" + +namespace godot { + +class Quat { +public: + static const Quat IDENTITY; + + real_t x, y, z, w; + + real_t length_squared() const; + real_t length() const; + + void normalize(); + + Quat normalized() const; + + bool is_normalized() const; + + Quat inverse() const; + + void set_euler_xyz(const Vector3 &p_euler); + Vector3 get_euler_xyz() const; + void set_euler_yxz(const Vector3 &p_euler); + Vector3 get_euler_yxz() const; + + inline void set_euler(const Vector3 &p_euler) { set_euler_yxz(p_euler); } + inline Vector3 get_euler() const { return get_euler_yxz(); } + + real_t dot(const Quat &q) const; + + Quat slerp(const Quat &q, const real_t &t) const; + + Quat slerpni(const Quat &q, const real_t &t) const; + + Quat cubic_slerp(const Quat &q, const Quat &prep, const Quat &postq, const real_t &t) const; + + void get_axis_and_angle(Vector3 &r_axis, real_t &r_angle) const; + + void set_axis_angle(const Vector3 &axis, const float angle); + + void operator*=(const Quat &q); + Quat operator*(const Quat &q) const; + + Quat operator*(const Vector3 &v) const; + + Vector3 xform(const Vector3 &v) const; + + void operator+=(const Quat &q); + void operator-=(const Quat &q); + void operator*=(const real_t &s); + void operator/=(const real_t &s); + Quat operator+(const Quat &q2) const; + Quat operator-(const Quat &q2) const; + Quat operator-() const; + Quat operator*(const real_t &s) const; + Quat operator/(const real_t &s) const; + + bool operator==(const Quat &p_quat) const; + bool operator!=(const Quat &p_quat) const; + + operator String() const; + + inline void set(real_t p_x, real_t p_y, real_t p_z, real_t p_w) { + x = p_x; + y = p_y; + z = p_z; + w = p_w; + } + inline Quat(real_t p_x, real_t p_y, real_t p_z, real_t p_w) { + x = p_x; + y = p_y; + z = p_z; + w = p_w; + } + Quat(const Vector3 &axis, const real_t &angle); + + Quat(const Vector3 &v0, const Vector3 &v1); + + inline Quat() { + x = y = z = 0; + w = 1; + } +}; + +} // namespace godot + +#endif // QUAT_H diff --git a/include/core/RID.hpp b/include/core/RID.hpp new file mode 100644 index 0000000..04bee9c --- /dev/null +++ b/include/core/RID.hpp @@ -0,0 +1,67 @@ +/*************************************************************************/ +/* RID.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef RID_H +#define RID_H + +#include + +namespace godot { + +class Object; + +class RID { + godot_rid _godot_rid; + +public: + RID(); + + RID(Object *p); + + godot_rid _get_godot_rid() const; + + int32_t get_id() const; + + inline bool is_valid() const { + // is_valid() is not available in the C API... + return *this != RID(); + } + + bool operator==(const RID &p_other) const; + bool operator!=(const RID &p_other) const; + bool operator<(const RID &p_other) const; + bool operator>(const RID &p_other) const; + bool operator<=(const RID &p_other) const; + bool operator>=(const RID &p_other) const; +}; + +} // namespace godot + +#endif // RID_H diff --git a/include/core/Rect2.hpp b/include/core/Rect2.hpp new file mode 100644 index 0000000..be088cf --- /dev/null +++ b/include/core/Rect2.hpp @@ -0,0 +1,160 @@ +/*************************************************************************/ +/* Rect2.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef RECT2_H +#define RECT2_H + +#include "Vector2.hpp" + +#include + +#include + +namespace godot { + +class String; + +typedef Vector2 Size2; +typedef Vector2 Point2; + +struct Transform2D; + +struct Rect2 { + Point2 position; + Size2 size; + + inline const Vector2 &get_position() const { return position; } + inline void set_position(const Vector2 &p_position) { position = p_position; } + inline const Vector2 &get_size() const { return size; } + inline void set_size(const Vector2 &p_size) { size = p_size; } + + inline real_t get_area() const { return size.width * size.height; } + + inline bool intersects(const Rect2 &p_rect) const { + if (position.x >= (p_rect.position.x + p_rect.size.width)) + return false; + if ((position.x + size.width) <= p_rect.position.x) + return false; + if (position.y >= (p_rect.position.y + p_rect.size.height)) + return false; + if ((position.y + size.height) <= p_rect.position.y) + return false; + + return true; + } + + real_t distance_to(const Vector2 &p_point) const; + + bool intersects_transformed(const Transform2D &p_xform, const Rect2 &p_rect) const; + + bool intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_position = nullptr, Point2 *r_normal = nullptr) const; + + inline bool encloses(const Rect2 &p_rect) const { + return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && + ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && + ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); + } + + inline bool has_no_area() const { + return (size.x <= 0 || size.y <= 0); + } + Rect2 clip(const Rect2 &p_rect) const; + + Rect2 merge(const Rect2 &p_rect) const; + + inline bool has_point(const Point2 &p_point) const { + if (p_point.x < position.x) + return false; + if (p_point.y < position.y) + return false; + + if (p_point.x >= (position.x + size.x)) + return false; + if (p_point.y >= (position.y + size.y)) + return false; + + return true; + } + + inline bool no_area() const { return (size.width <= 0 || size.height <= 0); } + + inline bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; } + inline bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; } + + inline Rect2 grow(real_t p_by) const { + Rect2 g = *this; + g.position.x -= p_by; + g.position.y -= p_by; + g.size.width += p_by * 2; + g.size.height += p_by * 2; + return g; + } + + inline Rect2 expand(const Vector2 &p_vector) const { + Rect2 r = *this; + r.expand_to(p_vector); + return r; + } + + inline void expand_to(const Vector2 &p_vector) { //in place function for speed + + Vector2 begin = position; + Vector2 end = position + size; + + if (p_vector.x < begin.x) + begin.x = p_vector.x; + if (p_vector.y < begin.y) + begin.y = p_vector.y; + + if (p_vector.x > end.x) + end.x = p_vector.x; + if (p_vector.y > end.y) + end.y = p_vector.y; + + position = begin; + size = end - begin; + } + + operator String() const; + + inline Rect2() {} + inline Rect2(real_t p_x, real_t p_y, real_t p_width, real_t p_height) { + position = Point2(p_x, p_y); + size = Size2(p_width, p_height); + } + inline Rect2(const Point2 &p_position, const Size2 &p_size) { + position = p_position; + size = p_size; + } +}; + +} // namespace godot + +#endif // RECT2_H diff --git a/include/core/Ref.hpp b/include/core/Ref.hpp new file mode 100644 index 0000000..adcb2fb --- /dev/null +++ b/include/core/Ref.hpp @@ -0,0 +1,216 @@ +/*************************************************************************/ +/* Ref.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef REF_H +#define REF_H + +#include "GodotGlobal.hpp" +#include "Reference.hpp" +#include "Variant.hpp" + +namespace godot { + +// Replicates Godot's Ref behavior +// Rewritten from f5234e70be7dec4930c2d5a0e829ff480d044b1d. +template +class Ref { + // TODO For this nice check to work, each class must actually #include Reference classes mentionned in its methods, + // which might be annoying for coders who prefer to forward-declare to reduce compile times + // static_assert(std::is_base_of::value, + // "Ref can only be used with classes deriving from Reference"); + + T *reference = nullptr; + + void ref(const Ref &p_from) { + if (p_from.reference == reference) + return; + + unref(); + + reference = p_from.reference; + if (reference) + reference->reference(); + } + + void ref_pointer(T *p_ref) { + ERR_FAIL_COND(p_ref == nullptr); + + if (p_ref->init_ref()) + reference = p_ref; + } + +public: + inline bool operator<(const Ref &p_r) const { + return reference < p_r.reference; + } + inline bool operator==(const Ref &p_r) const { + return reference == p_r.reference; + } + inline bool operator!=(const Ref &p_r) const { + return reference != p_r.reference; + } + + inline T *operator->() { + return reference; + } + + inline T *operator*() { + return reference; + } + + inline const T *operator->() const { + return reference; + } + + inline const T *ptr() const { + return reference; + } + inline T *ptr() { + return reference; + } + + inline const T *operator*() const { + return reference; + } + + operator Variant() const { + // Note: the C API handles the cases where the object is a Reference, + // so the Variant will be correctly constructed with a RefPtr engine-side + return Variant((Object *)reference); + } + + void operator=(const Ref &p_from) { + ref(p_from); + } + + template + void operator=(const Ref &p_from) { + Reference *refb = const_cast(static_cast(p_from.ptr())); + if (refb == nullptr) { + unref(); + return; + } + Ref r; + r.reference = Object::cast_to(refb); + ref(r); + r.reference = nullptr; + } + + void operator=(const Variant &p_variant) { + Object *refb = T::___get_from_variant(p_variant); + if (refb == nullptr) { + unref(); + return; + } + Ref r; + r.reference = Object::cast_to(refb); + ref(r); + r.reference = nullptr; + } + + Ref(const Ref &p_from) { + reference = nullptr; + ref(p_from); + } + + template + Ref(const Ref &p_from) { + reference = nullptr; + Reference *refb = const_cast(static_cast(p_from.ptr())); + if (refb == nullptr) { + unref(); + return; + } + Ref r; + r.reference = Object::cast_to(refb); + ref(r); + r.reference = nullptr; + } + + Ref(T *p_reference) { + if (p_reference) + ref_pointer(p_reference); + else + reference = nullptr; + } + + Ref(const Variant &p_variant) { + reference = nullptr; + Object *refb = T::___get_from_variant(p_variant); + if (refb == nullptr) { + unref(); + return; + } + Ref r; + r.reference = Object::cast_to(refb); + ref(r); + r.reference = nullptr; + } + + inline bool is_valid() const { return reference != nullptr; } + inline bool is_null() const { return reference == nullptr; } + + void unref() { + //TODO this should be moved to mutexes, since this engine does not really + // do a lot of referencing on references and stuff + // mutexes will avoid more crashes? + + if (reference && reference->unreference()) { + //memdelete(reference); + reference->free(); + } + reference = nullptr; + } + + void instance() { + //ref(memnew(T)); + ref(T::_new()); + } + + Ref() { + reference = nullptr; + } + + ~Ref() { + unref(); + } + + // Used exclusively in the bindings to recreate the Ref Godot encapsulates in return values, + // without adding to the refcount. + inline static Ref __internal_constructor(Object *obj) { + Ref r; + r.reference = (T *)obj; + return r; + } +}; + +} // namespace godot + +#endif diff --git a/include/core/String.hpp b/include/core/String.hpp new file mode 100644 index 0000000..acee823 --- /dev/null +++ b/include/core/String.hpp @@ -0,0 +1,184 @@ +/*************************************************************************/ +/* String.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef STRING_H +#define STRING_H + +#include + +namespace godot { + +class NodePath; +class Variant; +class PoolByteArray; +class PoolIntArray; +class PoolRealArray; +class PoolStringArray; +class String; + +class CharString { + friend class String; + + godot_char_string _char_string; + +public: + ~CharString(); + + int length() const; + const char *get_data() const; +}; + +class String { + godot_string _godot_string; + + friend class Dictionary; + friend class NodePath; + friend class Variant; + explicit inline String(godot_string contents) : + _godot_string(contents) {} + +public: + String(); + String(const char *contents); + String(const wchar_t *contents); + String(const wchar_t c); + String(const String &other); + String(String &&other); + + ~String(); + + static String num(double p_num, int p_decimals = -1); + static String num_scientific(double p_num); + static String num_real(double p_num); + static String num_int64(int64_t p_num, int base = 10, bool capitalize_hex = false); + static String chr(godot_char_type p_char); + static String md5(const uint8_t *p_md5); + static String hex_encode_buffer(const uint8_t *p_buffer, int p_len); + + wchar_t &operator[](const int idx); + wchar_t operator[](const int idx) const; + + void operator=(const String &s); + void operator=(String &&s); + bool operator==(const String &s) const; + bool operator!=(const String &s) const; + String operator+(const String &s) const; + void operator+=(const String &s); + void operator+=(const wchar_t c); + bool operator<(const String &s) const; + bool operator<=(const String &s) const; + bool operator>(const String &s) const; + bool operator>=(const String &s) const; + + operator NodePath() const; + + int length() const; + const wchar_t *unicode_str() const; + char *alloc_c_string() const; + CharString utf8() const; + CharString ascii(bool p_extended = false) const; + + bool begins_with(const String &s) const; + bool begins_with_char_array(const char *p_char_array) const; + PoolStringArray bigrams() const; + String c_escape() const; + String c_unescape() const; + String capitalize() const; + bool empty() const; + bool ends_with(const String &text) const; + void erase(int position, int chars); + int find(String what, int from = 0) const; + int find_last(String what) const; + int findn(String what, int from = 0) const; + String format(Variant values) const; + String format(Variant values, String placeholder) const; + String get_base_dir() const; + String get_basename() const; + String get_extension() const; + String get_file() const; + int hash() const; + int hex_to_int() const; + String insert(int position, String what) const; + bool is_abs_path() const; + bool is_rel_path() const; + bool is_subsequence_of(String text) const; + bool is_subsequence_ofi(String text) const; + bool is_valid_float() const; + bool is_valid_html_color() const; + bool is_valid_identifier() const; + bool is_valid_integer() const; + bool is_valid_ip_address() const; + String json_escape() const; + String left(int position) const; + bool match(String expr) const; + bool matchn(String expr) const; + PoolByteArray md5_buffer() const; + String md5_text() const; + int ord_at(int at) const; + String pad_decimals(int digits) const; + String pad_zeros(int digits) const; + String percent_decode() const; + String percent_encode() const; + String plus_file(String file) const; + String replace(String what, String forwhat) const; + String replacen(String what, String forwhat) const; + int rfind(String what, int from = -1) const; + int rfindn(String what, int from = -1) const; + String right(int position) const; + PoolByteArray sha256_buffer() const; + String sha256_text() const; + float similarity(String text) const; + PoolStringArray split(String divisor, bool allow_empty = true) const; + PoolIntArray split_ints(String divisor, bool allow_empty = true) const; + PoolRealArray split_floats(String divisor, bool allow_empty = true) const; + String strip_edges(bool left = true, bool right = true) const; + String substr(int from, int len) const; + float to_float() const; + int64_t to_int() const; + String to_lower() const; + String to_upper() const; + String xml_escape() const; + String xml_unescape() const; + signed char casecmp_to(String p_str) const; + signed char nocasecmp_to(String p_str) const; + signed char naturalnocasecmp_to(String p_str) const; + String dedent() const; + PoolStringArray rsplit(const String &divisor, const bool allow_empty = true, const int maxsplit = 0) const; + String rstrip(const String &chars) const; + String trim_prefix(const String &prefix) const; + String trim_suffix(const String &suffix) const; +}; + +String operator+(const char *a, const String &b); +String operator+(const wchar_t *a, const String &b); + +} // namespace godot + +#endif // STRING_H diff --git a/include/core/TagDB.hpp b/include/core/TagDB.hpp new file mode 100644 index 0000000..b470055 --- /dev/null +++ b/include/core/TagDB.hpp @@ -0,0 +1,49 @@ +/*************************************************************************/ +/* TagDB.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef TAGDB_HPP +#define TAGDB_HPP + +#include + +namespace godot { + +namespace _TagDB { + +void register_type(size_t type_tag, size_t base_type_tag); +bool is_type_known(size_t type_tag); +void register_global_type(const char *name, size_t type_tag, size_t base_type_tag); +bool is_type_compatible(size_t type_tag, size_t base_type_tag); + +} // namespace _TagDB + +} // namespace godot + +#endif // TAGDB_HPP diff --git a/include/core/Transform.hpp b/include/core/Transform.hpp new file mode 100644 index 0000000..853808e --- /dev/null +++ b/include/core/Transform.hpp @@ -0,0 +1,121 @@ +/*************************************************************************/ +/* Transform.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef TRANSFORM_H +#define TRANSFORM_H + +#include "Basis.hpp" + +#include "AABB.hpp" +#include "Plane.hpp" + +namespace godot { + +class Transform { +public: + static const Transform IDENTITY; + static const Transform FLIP_X; + static const Transform FLIP_Y; + static const Transform FLIP_Z; + + Basis basis; + Vector3 origin; + + void invert(); + Transform inverse() const; + + void affine_invert(); + Transform affine_inverse() const; + + Transform rotated(const Vector3 &p_axis, real_t p_phi) const; + + void rotate(const Vector3 &p_axis, real_t p_phi); + void rotate_basis(const Vector3 &p_axis, real_t p_phi); + + void set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up); + Transform looking_at(const Vector3 &p_target, const Vector3 &p_up) const; + + void scale(const Vector3 &p_scale); + Transform scaled(const Vector3 &p_scale) const; + void scale_basis(const Vector3 &p_scale); + void translate(real_t p_tx, real_t p_ty, real_t p_tz); + void translate(const Vector3 &p_translation); + Transform translated(const Vector3 &p_translation) const; + + inline const Basis &get_basis() const { return basis; } + inline void set_basis(const Basis &p_basis) { basis = p_basis; } + + inline const Vector3 &get_origin() const { return origin; } + inline void set_origin(const Vector3 &p_origin) { origin = p_origin; } + + void orthonormalize(); + Transform orthonormalized() const; + + bool operator==(const Transform &p_transform) const; + bool operator!=(const Transform &p_transform) const; + + Vector3 xform(const Vector3 &p_vector) const; + Vector3 xform_inv(const Vector3 &p_vector) const; + + Plane xform(const Plane &p_plane) const; + Plane xform_inv(const Plane &p_plane) const; + + AABB xform(const AABB &p_aabb) const; + AABB xform_inv(const AABB &p_aabb) const; + + void operator*=(const Transform &p_transform); + Transform operator*(const Transform &p_transform) const; + + inline Vector3 operator*(const Vector3 &p_vector) const { + return Vector3( + basis.elements[0].dot(p_vector) + origin.x, + basis.elements[1].dot(p_vector) + origin.y, + basis.elements[2].dot(p_vector) + origin.z); + } + + Transform interpolate_with(const Transform &p_transform, real_t p_c) const; + + Transform inverse_xform(const Transform &t) const; + + void set(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t tx, real_t ty, real_t tz); + + operator String() const; + + inline Transform(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t tx, real_t ty, real_t tz) { + set(xx, xy, xz, yx, yy, yz, zx, zy, zz, tx, ty, tz); + } + + Transform(const Basis &p_basis, const Vector3 &p_origin = Vector3()); + inline Transform() {} +}; + +} // namespace godot + +#endif // TRANSFORM_H diff --git a/include/core/Transform2D.hpp b/include/core/Transform2D.hpp new file mode 100644 index 0000000..eb92c28 --- /dev/null +++ b/include/core/Transform2D.hpp @@ -0,0 +1,136 @@ +/*************************************************************************/ +/* Transform2D.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef TRANSFORM2D_H +#define TRANSFORM2D_H + +#include "Vector2.hpp" + +namespace godot { + +typedef Vector2 Size2; + +struct Rect2; + +struct Transform2D { + static const Transform2D IDENTITY; + static const Transform2D FLIP_X; + static const Transform2D FLIP_Y; + + // Warning #1: basis of Transform2D is stored differently from Basis. In terms of elements array, the basis matrix looks like "on paper": + // M = (elements[0][0] elements[1][0]) + // (elements[0][1] elements[1][1]) + // This is such that the columns, which can be interpreted as basis vectors of the coordinate system "painted" on the object, can be accessed as elements[i]. + // Note that this is the opposite of the indices in mathematical texts, meaning: $M_{12}$ in a math book corresponds to elements[1][0] here. + // This requires additional care when working with explicit indices. + // See https://en.wikipedia.org/wiki/Row-_and_column-major_order for further reading. + + // Warning #2: 2D be aware that unlike 3D code, 2D code uses a left-handed coordinate system: Y-axis points down, + // and angle is measure from +X to +Y in a clockwise-fashion. + + Vector2 elements[3]; + + inline real_t tdotx(const Vector2 &v) const { return elements[0][0] * v.x + elements[1][0] * v.y; } + inline real_t tdoty(const Vector2 &v) const { return elements[0][1] * v.x + elements[1][1] * v.y; } + + inline const Vector2 &operator[](int p_idx) const { return elements[p_idx]; } + inline Vector2 &operator[](int p_idx) { return elements[p_idx]; } + + inline Vector2 get_axis(int p_axis) const { + ERR_FAIL_INDEX_V(p_axis, 3, Vector2()); + return elements[p_axis]; + } + inline void set_axis(int p_axis, const Vector2 &p_vec) { + ERR_FAIL_INDEX(p_axis, 3); + elements[p_axis] = p_vec; + } + + void invert(); + Transform2D inverse() const; + + void affine_invert(); + Transform2D affine_inverse() const; + + void set_rotation(real_t p_phi); + real_t get_rotation() const; + void set_rotation_and_scale(real_t p_phi, const Size2 &p_scale); + void rotate(real_t p_phi); + + void scale(const Size2 &p_scale); + void scale_basis(const Size2 &p_scale); + void translate(real_t p_tx, real_t p_ty); + void translate(const Vector2 &p_translation); + + real_t basis_determinant() const; + + Size2 get_scale() const; + + inline const Vector2 &get_origin() const { return elements[2]; } + inline void set_origin(const Vector2 &p_origin) { elements[2] = p_origin; } + + Transform2D scaled(const Size2 &p_scale) const; + Transform2D basis_scaled(const Size2 &p_scale) const; + Transform2D translated(const Vector2 &p_offset) const; + Transform2D rotated(real_t p_phi) const; + + Transform2D untranslated() const; + + void orthonormalize(); + Transform2D orthonormalized() const; + + bool operator==(const Transform2D &p_transform) const; + bool operator!=(const Transform2D &p_transform) const; + + void operator*=(const Transform2D &p_transform); + Transform2D operator*(const Transform2D &p_transform) const; + + Transform2D interpolate_with(const Transform2D &p_transform, real_t p_c) const; + + Vector2 basis_xform(const Vector2 &p_vec) const; + Vector2 basis_xform_inv(const Vector2 &p_vec) const; + Vector2 xform(const Vector2 &p_vec) const; + Vector2 xform_inv(const Vector2 &p_vec) const; + Rect2 xform(const Rect2 &p_vec) const; + Rect2 xform_inv(const Rect2 &p_vec) const; + + operator String() const; + + Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy); + + Transform2D(real_t p_rot, const Vector2 &p_pos); + inline Transform2D() { + elements[0][0] = 1.0; + elements[1][1] = 1.0; + } +}; + +} // namespace godot + +#endif // TRANSFORM2D_H diff --git a/include/core/Variant.hpp b/include/core/Variant.hpp new file mode 100644 index 0000000..127ec4d --- /dev/null +++ b/include/core/Variant.hpp @@ -0,0 +1,304 @@ +/*************************************************************************/ +/* Variant.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef VARIANT_H +#define VARIANT_H + +#include + +#include "Defs.hpp" + +#include "AABB.hpp" +#include "Basis.hpp" +#include "Color.hpp" +#include "NodePath.hpp" +#include "Plane.hpp" +#include "PoolArrays.hpp" +#include "Quat.hpp" +#include "RID.hpp" +#include "Rect2.hpp" +#include "String.hpp" +#include "Transform.hpp" +#include "Transform2D.hpp" +#include "Vector2.hpp" +#include "Vector3.hpp" + +namespace godot { + +class Dictionary; + +class Array; + +class Variant { + godot_variant _godot_variant; + + friend class Array; + inline explicit Variant(godot_variant v) { + _godot_variant = v; + } + +public: + enum Type { + + NIL, + + // atomic types + BOOL, + INT, + REAL, + STRING, + + // math types + + VECTOR2, // 5 + RECT2, + VECTOR3, + TRANSFORM2D, + PLANE, + QUAT, // 10 + RECT3, //sorry naming convention fail :( not like it's used often + BASIS, + TRANSFORM, + + // misc types + COLOR, + NODE_PATH, // 15 + _RID, + OBJECT, + DICTIONARY, + ARRAY, + + // arrays + POOL_BYTE_ARRAY, // 20 + POOL_INT_ARRAY, + POOL_REAL_ARRAY, + POOL_STRING_ARRAY, + POOL_VECTOR2_ARRAY, + POOL_VECTOR3_ARRAY, // 25 + POOL_COLOR_ARRAY, + + VARIANT_MAX + + }; + + enum Operator { + + //comparation + OP_EQUAL, + OP_NOT_EQUAL, + OP_LESS, + OP_LESS_EQUAL, + OP_GREATER, + OP_GREATER_EQUAL, + + //mathematic + OP_ADD, + OP_SUBSTRACT, + OP_MULTIPLY, + OP_DIVIDE, + OP_NEGATE, + OP_POSITIVE, + OP_MODULE, + OP_STRING_CONCAT, + + //bitwise + OP_SHIFT_LEFT, + OP_SHIFT_RIGHT, + OP_BIT_AND, + OP_BIT_OR, + OP_BIT_XOR, + OP_BIT_NEGATE, + + //logic + OP_AND, + OP_OR, + OP_XOR, + OP_NOT, + + //containment + OP_IN, + OP_MAX + + }; + + Variant(); + + Variant(const Variant &v); + + Variant(bool p_bool); + + Variant(signed int p_int); + + Variant(unsigned int p_int); + + Variant(signed short p_short); + + inline Variant(unsigned short p_short) : + Variant((unsigned int)p_short) {} + + inline Variant(signed char p_char) : + Variant((signed int)p_char) {} + + inline Variant(unsigned char p_char) : + Variant((unsigned int)p_char) {} + Variant(int64_t p_char); + + Variant(uint64_t p_char); + + Variant(float p_float); + + Variant(double p_double); + + Variant(const String &p_string); + + Variant(const char *const p_cstring); + + Variant(const wchar_t *p_wstring); + + Variant(const Vector2 &p_vector2); + + Variant(const Rect2 &p_rect2); + + Variant(const Vector3 &p_vector3); + + Variant(const Plane &p_plane); + + Variant(const AABB &p_aabb); + + Variant(const Quat &p_quat); + + Variant(const Basis &p_transform); + + Variant(const Transform2D &p_transform); + + Variant(const Transform &p_transform); + + Variant(const Color &p_color); + + Variant(const NodePath &p_path); + + Variant(const RID &p_rid); + + Variant(const Object *p_object); + + Variant(const Dictionary &p_dictionary); + + Variant(const Array &p_array); + + Variant(const PoolByteArray &p_raw_array); + + Variant(const PoolIntArray &p_int_array); + + Variant(const PoolRealArray &p_real_array); + + Variant(const PoolStringArray &p_string_array); + + Variant(const PoolVector2Array &p_vector2_array); + + Variant(const PoolVector3Array &p_vector3_array); + + Variant(const PoolColorArray &p_color_array); + + Variant &operator=(const Variant &v); + + operator bool() const; + operator signed int() const; + operator unsigned int() const; + operator signed short() const; + operator unsigned short() const; + operator signed char() const; + operator unsigned char() const; + operator int64_t() const; + operator uint64_t() const; + + operator wchar_t() const; + + operator float() const; + + operator double() const; + operator String() const; + operator Vector2() const; + operator Rect2() const; + operator Vector3() const; + operator Plane() const; + operator AABB() const; + operator Quat() const; + operator Basis() const; + operator Transform() const; + operator Transform2D() const; + + operator Color() const; + + operator NodePath() const; + operator RID() const; + operator godot_object *() const; + + template + operator T *() const { return static_cast(T::___get_from_variant(*this)); } + + operator Dictionary() const; + operator Array() const; + + operator PoolByteArray() const; + operator PoolIntArray() const; + operator PoolRealArray() const; + operator PoolStringArray() const; + operator PoolVector2Array() const; + operator PoolVector3Array() const; + operator PoolColorArray() const; + + Type get_type() const; + + Variant call(const String &method, const Variant **args, const int arg_count); + + bool has_method(const String &method); + + bool operator==(const Variant &b) const; + + bool operator!=(const Variant &b) const; + + bool operator<(const Variant &b) const; + + bool operator<=(const Variant &b) const; + + bool operator>(const Variant &b) const; + + bool operator>=(const Variant &b) const; + + bool hash_compare(const Variant &b) const; + + bool booleanize() const; + + ~Variant(); +}; + +} // namespace godot + +#endif // VARIANT_H diff --git a/include/core/Vector2.hpp b/include/core/Vector2.hpp new file mode 100644 index 0000000..73111a6 --- /dev/null +++ b/include/core/Vector2.hpp @@ -0,0 +1,306 @@ +/*************************************************************************/ +/* Vector2.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef VECTOR2_H +#define VECTOR2_H + +#include + +#include "Defs.hpp" + +#include + +namespace godot { + +class String; + +struct Vector2 { + enum Axis { + AXIS_X = 0, + AXIS_Y, + AXIS_COUNT + }; + + static const Vector2 ZERO; + static const Vector2 ONE; + static const Vector2 INF; + + // Coordinate system of the 2D engine + static const Vector2 LEFT; + static const Vector2 RIGHT; + static const Vector2 UP; + static const Vector2 DOWN; + + union { + real_t x; + real_t width; + }; + union { + real_t y; + real_t height; + }; + + inline Vector2(real_t p_x, real_t p_y) { + x = p_x; + y = p_y; + } + + inline Vector2() { + x = 0; + y = 0; + } + + inline real_t &operator[](int p_idx) { + return p_idx ? y : x; + } + + inline const real_t &operator[](int p_idx) const { + return p_idx ? y : x; + } + + inline Vector2 operator+(const Vector2 &p_v) const { + return Vector2(x + p_v.x, y + p_v.y); + } + + inline void operator+=(const Vector2 &p_v) { + x += p_v.x; + y += p_v.y; + } + + inline Vector2 operator-(const Vector2 &p_v) const { + return Vector2(x - p_v.x, y - p_v.y); + } + + inline void operator-=(const Vector2 &p_v) { + x -= p_v.x; + y -= p_v.y; + } + + inline Vector2 operator*(const Vector2 &p_v1) const { + return Vector2(x * p_v1.x, y * p_v1.y); + } + + inline Vector2 operator*(const real_t &rvalue) const { + return Vector2(x * rvalue, y * rvalue); + } + + inline void operator*=(const real_t &rvalue) { + x *= rvalue; + y *= rvalue; + } + + inline void operator*=(const Vector2 &rvalue) { + *this = *this * rvalue; + } + + inline Vector2 operator/(const Vector2 &p_v1) const { + return Vector2(x / p_v1.x, y / p_v1.y); + } + + inline Vector2 operator/(const real_t &rvalue) const { + return Vector2(x / rvalue, y / rvalue); + } + + inline void operator/=(const real_t &rvalue) { + x /= rvalue; + y /= rvalue; + } + + inline Vector2 operator-() const { + return Vector2(-x, -y); + } + + bool operator==(const Vector2 &p_vec2) const; + + bool operator!=(const Vector2 &p_vec2) const; + + inline bool operator<(const Vector2 &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } + inline bool operator<=(const Vector2 &p_vec2) const { return (x == p_vec2.x) ? (y <= p_vec2.y) : (x <= p_vec2.x); } + + inline void normalize() { + real_t l = x * x + y * y; + if (l != 0) { + l = sqrt(l); + x /= l; + y /= l; + } + } + + inline Vector2 normalized() const { + Vector2 v = *this; + v.normalize(); + return v; + } + + inline real_t length() const { + return sqrt(x * x + y * y); + } + + inline real_t length_squared() const { + return x * x + y * y; + } + + inline real_t distance_to(const Vector2 &p_vector2) const { + return sqrt((x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y)); + } + + inline real_t distance_squared_to(const Vector2 &p_vector2) const { + return (x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y); + } + + inline real_t angle_to(const Vector2 &p_vector2) const { + return atan2(cross(p_vector2), dot(p_vector2)); + } + + inline real_t angle_to_point(const Vector2 &p_vector2) const { + return atan2(y - p_vector2.y, x - p_vector2.x); + } + + inline Vector2 direction_to(const Vector2 &p_b) const { + Vector2 ret(p_b.x - x, p_b.y - y); + ret.normalize(); + return ret; + } + + inline real_t dot(const Vector2 &p_other) const { + return x * p_other.x + y * p_other.y; + } + + inline real_t cross(const Vector2 &p_other) const { + return x * p_other.y - y * p_other.x; + } + + inline Vector2 cross(real_t p_other) const { + return Vector2(p_other * y, -p_other * x); + } + + Vector2 project(const Vector2 &p_vec) const; + + Vector2 plane_project(real_t p_d, const Vector2 &p_vec) const; + + Vector2 clamped(real_t p_len) const; + + static inline Vector2 linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_t) { + Vector2 res = p_a; + res.x += (p_t * (p_b.x - p_a.x)); + res.y += (p_t * (p_b.y - p_a.y)); + return res; + } + + inline Vector2 linear_interpolate(const Vector2 &p_b, real_t p_t) const { + Vector2 res = *this; + res.x += (p_t * (p_b.x - x)); + res.y += (p_t * (p_b.y - y)); + return res; + } + + Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_t) const; + + Vector2 move_toward(const Vector2 &p_to, const real_t p_delta) const { + Vector2 v = *this; + Vector2 vd = p_to - v; + real_t len = vd.length(); + return len <= p_delta || len < CMP_EPSILON ? p_to : v + vd / len * p_delta; + } + + inline Vector2 slide(const Vector2 &p_vec) const { + return p_vec - *this * this->dot(p_vec); + } + + inline Vector2 bounce(const Vector2 &p_normal) const { + return -reflect(p_normal); + } + + inline Vector2 reflect(const Vector2 &p_normal) const { + return -(*this - p_normal * this->dot(p_normal) * 2.0); + } + + inline real_t angle() const { + return atan2(y, x); + } + + inline void set_rotation(real_t p_radians) { + x = cosf(p_radians); + y = sinf(p_radians); + } + + inline Vector2 abs() const { + return Vector2(fabs(x), fabs(y)); + } + + inline Vector2 rotated(real_t p_by) const { + Vector2 v; + v.set_rotation(angle() + p_by); + v *= length(); + return v; + } + + inline Vector2 tangent() const { + return Vector2(y, -x); + } + + inline Vector2 floor() const { + return Vector2(Math::floor(x), Math::floor(y)); + } + + inline Vector2 snapped(const Vector2 &p_by) const { + return Vector2( + Math::stepify(x, p_by.x), + Math::stepify(y, p_by.y)); + } + + inline real_t aspect() const { return width / height; } + + operator String() const; +}; + +inline Vector2 operator*(real_t p_scalar, const Vector2 &p_vec) { + return p_vec * p_scalar; +} + +namespace Math { + +// Convenience, since they exist in GDScript + +inline Vector2 cartesian2polar(Vector2 v) { + return Vector2(Math::sqrt(v.x * v.x + v.y * v.y), Math::atan2(v.y, v.x)); +} + +inline Vector2 polar2cartesian(Vector2 v) { + // x == radius + // y == angle + return Vector2(v.x * Math::cos(v.y), v.x * Math::sin(v.y)); +} + +} // namespace Math + +} // namespace godot + +#endif // VECTOR2_H diff --git a/include/core/Vector3.hpp b/include/core/Vector3.hpp new file mode 100644 index 0000000..ac3c7d5 --- /dev/null +++ b/include/core/Vector3.hpp @@ -0,0 +1,342 @@ +/*************************************************************************/ +/* Vector3.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef VECTOR3_H +#define VECTOR3_H + +#include + +#include "Defs.hpp" + +#include "String.hpp" + +#include + +namespace godot { + +class Basis; + +struct Vector3 { + enum Axis { + AXIS_X, + AXIS_Y, + AXIS_Z, + AXIS_COUNT + }; + + static const Vector3 ZERO; + static const Vector3 ONE; + static const Vector3 INF; + + // Coordinate system of the 3D engine + static const Vector3 LEFT; + static const Vector3 RIGHT; + static const Vector3 UP; + static const Vector3 DOWN; + static const Vector3 FORWARD; + static const Vector3 BACK; + + union { + struct { + real_t x; + real_t y; + real_t z; + }; + + real_t coord[3]; // Not for direct access, use [] operator instead + }; + + inline Vector3(real_t x, real_t y, real_t z) { + this->x = x; + this->y = y; + this->z = z; + } + + inline Vector3() { + this->x = 0; + this->y = 0; + this->z = 0; + } + + inline const real_t &operator[](int p_axis) const { + return coord[p_axis]; + } + + inline real_t &operator[](int p_axis) { + return coord[p_axis]; + } + + inline Vector3 &operator+=(const Vector3 &p_v) { + x += p_v.x; + y += p_v.y; + z += p_v.z; + return *this; + } + + inline Vector3 operator+(const Vector3 &p_v) const { + Vector3 v = *this; + v += p_v; + return v; + } + + inline Vector3 &operator-=(const Vector3 &p_v) { + x -= p_v.x; + y -= p_v.y; + z -= p_v.z; + return *this; + } + + inline Vector3 operator-(const Vector3 &p_v) const { + Vector3 v = *this; + v -= p_v; + return v; + } + + inline Vector3 &operator*=(const Vector3 &p_v) { + x *= p_v.x; + y *= p_v.y; + z *= p_v.z; + return *this; + } + + inline Vector3 operator*(const Vector3 &p_v) const { + Vector3 v = *this; + v *= p_v; + return v; + } + + inline Vector3 &operator/=(const Vector3 &p_v) { + x /= p_v.x; + y /= p_v.y; + z /= p_v.z; + return *this; + } + + inline Vector3 operator/(const Vector3 &p_v) const { + Vector3 v = *this; + v /= p_v; + return v; + } + + inline Vector3 &operator*=(real_t p_scalar) { + *this *= Vector3(p_scalar, p_scalar, p_scalar); + return *this; + } + + inline Vector3 operator*(real_t p_scalar) const { + Vector3 v = *this; + v *= p_scalar; + return v; + } + + inline Vector3 &operator/=(real_t p_scalar) { + *this /= Vector3(p_scalar, p_scalar, p_scalar); + return *this; + } + + inline Vector3 operator/(real_t p_scalar) const { + Vector3 v = *this; + v /= p_scalar; + return v; + } + + inline Vector3 operator-() const { + return Vector3(-x, -y, -z); + } + + inline bool operator==(const Vector3 &p_v) const { + return (x == p_v.x && y == p_v.y && z == p_v.z); + } + + inline bool operator!=(const Vector3 &p_v) const { + return (x != p_v.x || y != p_v.y || z != p_v.z); + } + + bool operator<(const Vector3 &p_v) const; + + bool operator<=(const Vector3 &p_v) const; + + inline Vector3 abs() const { + return Vector3(::fabs(x), ::fabs(y), ::fabs(z)); + } + + inline Vector3 ceil() const { + return Vector3(::ceil(x), ::ceil(y), ::ceil(z)); + } + + inline Vector3 cross(const Vector3 &b) const { + Vector3 ret( + (y * b.z) - (z * b.y), + (z * b.x) - (x * b.z), + (x * b.y) - (y * b.x)); + + return ret; + } + + inline Vector3 linear_interpolate(const Vector3 &p_b, real_t p_t) const { + return Vector3( + x + (p_t * (p_b.x - x)), + y + (p_t * (p_b.y - y)), + z + (p_t * (p_b.z - z))); + } + + inline Vector3 slerp(const Vector3 &p_b, real_t p_t) const { + real_t theta = angle_to(p_b); + return rotated(cross(p_b).normalized(), theta * p_t); + } + + Vector3 cubic_interpolate(const Vector3 &b, const Vector3 &pre_a, const Vector3 &post_b, const real_t t) const; + + Vector3 move_toward(const Vector3 &p_to, const real_t p_delta) const { + Vector3 v = *this; + Vector3 vd = p_to - v; + real_t len = vd.length(); + return len <= p_delta || len < CMP_EPSILON ? p_to : v + vd / len * p_delta; + } + + Vector3 bounce(const Vector3 &p_normal) const { + return -reflect(p_normal); + } + + inline real_t length() const { + real_t x2 = x * x; + real_t y2 = y * y; + real_t z2 = z * z; + + return ::sqrt(x2 + y2 + z2); + } + + inline real_t length_squared() const { + real_t x2 = x * x; + real_t y2 = y * y; + real_t z2 = z * z; + + return x2 + y2 + z2; + } + + inline real_t distance_squared_to(const Vector3 &b) const { + return (b - *this).length_squared(); + } + + inline real_t distance_to(const Vector3 &b) const { + return (b - *this).length(); + } + + inline real_t dot(const Vector3 &b) const { + return x * b.x + y * b.y + z * b.z; + } + + inline Vector3 project(const Vector3 &p_b) const { + return p_b * (dot(p_b) / p_b.length_squared()); + } + + inline real_t angle_to(const Vector3 &b) const { + return std::atan2(cross(b).length(), dot(b)); + } + + inline Vector3 direction_to(const Vector3 &p_b) const { + Vector3 ret(p_b.x - x, p_b.y - y, p_b.z - z); + ret.normalize(); + return ret; + } + + inline Vector3 floor() const { + return Vector3(::floor(x), ::floor(y), ::floor(z)); + } + + inline Vector3 inverse() const { + return Vector3(1.f / x, 1.f / y, 1.f / z); + } + + inline bool is_normalized() const { + return std::abs(length_squared() - 1.f) < 0.00001f; + } + + Basis outer(const Vector3 &b) const; + + int max_axis() const; + + int min_axis() const; + + inline void normalize() { + real_t l = length(); + if (l == 0) { + x = y = z = 0; + } else { + x /= l; + y /= l; + z /= l; + } + } + + inline Vector3 normalized() const { + Vector3 v = *this; + v.normalize(); + return v; + } + + inline Vector3 reflect(const Vector3 &p_normal) const { + return -(*this - p_normal * this->dot(p_normal) * 2.0); + } + + inline Vector3 rotated(const Vector3 &axis, const real_t phi) const { + Vector3 v = *this; + v.rotate(axis, phi); + return v; + } + + void rotate(const Vector3 &p_axis, real_t p_phi); + + inline Vector3 slide(const Vector3 &by) const { + return *this - by * this->dot(by); + } + + void snap(real_t p_val); + + inline Vector3 snapped(const float by) { + Vector3 v = *this; + v.snap(by); + return v; + } + + operator String() const; +}; + +inline Vector3 operator*(real_t p_scalar, const Vector3 &p_vec) { + return p_vec * p_scalar; +} + +inline Vector3 vec3_cross(const Vector3 &p_a, const Vector3 &p_b) { + return p_a.cross(p_b); +} + +} // namespace godot + +#endif // VECTOR3_H diff --git a/include/core/Wrapped.hpp b/include/core/Wrapped.hpp new file mode 100644 index 0000000..003a178 --- /dev/null +++ b/include/core/Wrapped.hpp @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* Wrapped.hpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef WRAPPED_HPP +#define WRAPPED_HPP + +#include + +namespace godot { + +// This is an internal base class used by the bindings. You should not need to access its members. +class _Wrapped { +public: + godot_object *_owner; + size_t _type_tag; +}; + +} // namespace godot + +#endif // WRAPPED_HPP diff --git a/include/gen/.gitignore b/include/gen/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/include/gen/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/misc/ci/sources.list b/misc/ci/sources.list new file mode 100644 index 0000000..4d8f94f --- /dev/null +++ b/misc/ci/sources.list @@ -0,0 +1,4 @@ +deb http://archive.ubuntu.com/ubuntu/ focal main restricted universe multiverse +deb http://archive.ubuntu.com/ubuntu/ focal-updates main restricted universe multiverse +deb http://archive.ubuntu.com/ubuntu/ focal-security main restricted universe multiverse +deb http://archive.ubuntu.com/ubuntu/ focal-backports main restricted universe multiverse diff --git a/misc/hooks/README.md b/misc/hooks/README.md new file mode 100644 index 0000000..6ec90fc --- /dev/null +++ b/misc/hooks/README.md @@ -0,0 +1,18 @@ +# Git hooks for Godot Engine + +This folder contains git hooks meant to be installed locally by Godot Engine +contributors to make sure they comply with our requirements. + +## List of hooks + +- Pre-commit hook for clang-format: Applies clang-format to the staged files + before accepting a commit; blocks the commit and generates a patch if the + style is not respected. + Should work on Linux and macOS. You may need to edit the file if your + clang-format binary is not in the $PATH, or if you want to enable colored + output with pygmentize. + +## Installation + +Copy all the files from this folder into your .git/hooks folder, and make sure +the hooks and helper scripts are executable. diff --git a/misc/hooks/canonicalize_filename.sh b/misc/hooks/canonicalize_filename.sh new file mode 100644 index 0000000..5eecabf --- /dev/null +++ b/misc/hooks/canonicalize_filename.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +# Provide the canonicalize filename (physical filename with out any symlinks) +# like the GNU version readlink with the -f option regardless of the version of +# readlink (GNU or BSD). + +# This file is part of a set of unofficial pre-commit hooks available +# at github. +# Link: https://github.com/githubbrowser/Pre-commit-hooks +# Contact: David Martin, david.martin.mailbox@googlemail.com + +########################################################### +# There should be no need to change anything below this line. + +# Canonicalize by recursively following every symlink in every component of the +# specified filename. This should reproduce the results of the GNU version of +# readlink with the -f option. +# +# Reference: http://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac +canonicalize_filename () { + local target_file="$1" + local physical_directory="" + local result="" + + # Need to restore the working directory after work. + local working_dir="`pwd`" + + cd -- "$(dirname -- "$target_file")" + target_file="$(basename -- "$target_file")" + + # Iterate down a (possible) chain of symlinks + while [ -L "$target_file" ] + do + target_file="$(readlink -- "$target_file")" + cd -- "$(dirname -- "$target_file")" + target_file="$(basename -- "$target_file")" + done + + # Compute the canonicalized name by finding the physical path + # for the directory we're in and appending the target file. + physical_directory="`pwd -P`" + result="$physical_directory/$target_file" + + # restore the working directory after work. + cd -- "$working_dir" + + echo "$result" +} diff --git a/misc/hooks/pre-commit b/misc/hooks/pre-commit new file mode 100644 index 0000000..fc50ed7 --- /dev/null +++ b/misc/hooks/pre-commit @@ -0,0 +1,50 @@ +#!/bin/sh +# Git pre-commit hook that runs multiple hooks specified in $HOOKS. +# Make sure this script is executable. Bypass hooks with git commit --no-verify. + +# This file is part of a set of unofficial pre-commit hooks available +# at github. +# Link: https://github.com/githubbrowser/Pre-commit-hooks +# Contact: David Martin, david.martin.mailbox@googlemail.com + + +########################################################### +# CONFIGURATION: +# pre-commit hooks to be executed. They should be in the same .git/hooks/ folder +# as this script. Hooks should return 0 if successful and nonzero to cancel the +# commit. They are executed in the order in which they are listed. +#HOOKS="pre-commit-compile pre-commit-uncrustify" +HOOKS="pre-commit-clang-format" +########################################################### +# There should be no need to change anything below this line. + +. "$(dirname -- "$0")/canonicalize_filename.sh" + +# exit on error +set -e + +# Absolute path to this script, e.g. /home/user/bin/foo.sh +SCRIPT="$(canonicalize_filename "$0")" + +# Absolute path this script is in, thus /home/user/bin +SCRIPTPATH="$(dirname -- "$SCRIPT")" + + +for hook in $HOOKS +do + echo "Running hook: $hook" + # run hook if it exists + # if it returns with nonzero exit with 1 and thus abort the commit + if [ -f "$SCRIPTPATH/$hook" ]; then + "$SCRIPTPATH/$hook" + if [ $? != 0 ]; then + exit 1 + fi + else + echo "Error: file $hook not found." + echo "Aborting commit. Make sure the hook is in $SCRIPTPATH and executable." + echo "You can disable it by removing it from the list in $SCRIPT." + echo "You can skip all pre-commit hooks with --no-verify (not recommended)." + exit 1 + fi +done diff --git a/misc/hooks/pre-commit-clang-format b/misc/hooks/pre-commit-clang-format new file mode 100644 index 0000000..db241ad --- /dev/null +++ b/misc/hooks/pre-commit-clang-format @@ -0,0 +1,147 @@ +#!/usr/bin/env bash + +# git pre-commit hook that runs a clang-format stylecheck. +# Features: +# - abort commit when commit does not comply with the style guidelines +# - create a patch of the proposed style changes +# Modifications for clang-format by rene.milk@wwu.de + +# This file is part of a set of unofficial pre-commit hooks available +# at github. +# Link: https://github.com/githubbrowser/Pre-commit-hooks +# Contact: David Martin, david.martin.mailbox@googlemail.com + +# Some quality of life modifications made for Godot Engine. + +################################################################## +# SETTINGS +# Set path to clang-format binary +# CLANG_FORMAT="/usr/bin/clang-format" +CLANG_FORMAT=`which clang-format` + +# Remove any older patches from previous commits. Set to true or false. +# DELETE_OLD_PATCHES=false +DELETE_OLD_PATCHES=false + +# Only parse files with the extensions in FILE_EXTS. Set to true or false. +# If false every changed file in the commit will be parsed with clang-format. +# If true only files matching one of the extensions are parsed with clang-format. +# PARSE_EXTS=true +PARSE_EXTS=true + +# File types to parse. Only effective when PARSE_EXTS is true. +# FILE_EXTS=".c .h .cpp .hpp" +FILE_EXTS=".c .h .cpp .hpp .cc .hh .cxx .m .mm .inc .java .glsl" + +# Use pygmentize instead of cat to parse diff with highlighting. +# Install it with `pip install pygments` (Linux) or `easy_install Pygments` (Mac) +# READER="pygmentize -l diff" +READER=cat + +################################################################## +# There should be no need to change anything below this line. + +. "$(dirname -- "$0")/canonicalize_filename.sh" + +# exit on error +set -e + +# check whether the given file matches any of the set extensions +matches_extension() { + local filename=$(basename "$1") + local extension=".${filename##*.}" + local ext + + for ext in $FILE_EXTS; do [[ "$ext" == "$extension" ]] && return 0; done + + return 1 +} + +# necessary check for initial commit +if git rev-parse --verify HEAD >/dev/null 2>&1 ; then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +if [ ! -x "$CLANG_FORMAT" ] ; then + printf "Error: clang-format executable not found.\n" + printf "Set the correct path in $(canonicalize_filename "$0").\n" + exit 1 +fi + +# create a random filename to store our generated patch +prefix="pre-commit-clang-format" +suffix="$(date +%s)" +patch="/tmp/$prefix-$suffix.patch" + +# clean up any older clang-format patches +$DELETE_OLD_PATCHES && rm -f /tmp/$prefix*.patch + +# create one patch containing all changes to the files +git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file; +do + # ignore thirdparty files + if grep -q "thirdparty" <<< $file; then + continue; + fi + + # ignore file if we do check for file extensions and the file + # does not match any of the extensions specified in $FILE_EXTS + if $PARSE_EXTS && ! matches_extension "$file"; then + continue; + fi + + # clang-format our sourcefile, create a patch with diff and append it to our $patch + # The sed call is necessary to transform the patch from + # --- $file timestamp + # +++ - timestamp + # to both lines working on the same file and having a/ and b/ prefix. + # Else it can not be applied with 'git apply'. + "$CLANG_FORMAT" -style=file "$file" | \ + diff -u "$file" - | \ + sed -e "1s|--- |--- a/|" -e "2s|+++ -|+++ b/$file|" >> "$patch" +done + +# if no patch has been generated all is ok, clean up the file stub and exit +if [ ! -s "$patch" ] ; then + printf "Files in this commit comply with the clang-format rules.\n" + rm -f "$patch" + exit 0 +fi + +# a patch has been created, notify the user and exit +printf "\nThe following differences were found between the code to commit " +printf "and the clang-format rules:\n\n" +$READER "$patch" +printf "\n" + +# Allows us to read user input below, assigns stdin to keyboard +exec < /dev/tty + +while true; do + read -p "Do you want to apply that patch (Y - Apply, N - Do not apply, S - Apply and stage files)? [Y/N/S] " yn + case $yn in + [Yy] ) git apply $patch; + printf "The patch was applied. You can now stage the changes and commit again.\n\n"; + break + ;; + [Nn] ) printf "\nYou can apply these changes with:\n git apply $patch\n"; + printf "(may need to be called from the root directory of your repository)\n"; + printf "Aborting commit. Apply changes and commit again or skip checking with"; + printf " --no-verify (not recommended).\n\n"; + break + ;; + [Ss] ) git apply $patch; + git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file; + do git add $file; + done + printf "The patch was applied and the changed files staged. You can now commit.\n\n"; + break + ;; + * ) echo "Please answer yes or no." + ;; + esac +done +exit 1 # we don't commit in any case diff --git a/misc/scripts/check_get_file_list.py b/misc/scripts/check_get_file_list.py new file mode 100755 index 0000000..4295504 --- /dev/null +++ b/misc/scripts/check_get_file_list.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +import os, sys + +from pathlib import Path + +sys.path.insert(1, os.path.join(os.path.dirname(__file__), "..", "..")) + +from binding_generator import get_file_list, generate_bindings + +api_filepath = "godot-headers/api.json" +bits = "64" +double = "float" +output_dir = "self_test" + +generate_bindings(api_filepath, use_template_get_node=False, output_dir=output_dir) +flist = get_file_list(api_filepath, output_dir, headers=True, sources=True) + +p = Path(output_dir) +allfiles = [str(f.as_posix()) for f in p.glob("**/*.*")] +missing = list(filter((lambda f: f not in flist), allfiles)) +extras = list(filter((lambda f: f not in allfiles), flist)) +if len(missing) > 0 or len(extras) > 0: + print("Error!") + for f in missing: + print("MISSING: " + str(f)) + for f in extras: + print("EXTRA: " + str(f)) + sys.exit(1) +else: + print("OK!") diff --git a/misc/scripts/clang_format.sh b/misc/scripts/clang_format.sh new file mode 100644 index 0000000..0d1511e --- /dev/null +++ b/misc/scripts/clang_format.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +# This script runs clang-format on all relevant files in the repo. +# This is the primary script responsible for fixing style violations. + +set -uo pipefail +IFS=$'\n\t' + +CLANG_FORMAT_FILE_EXTS=(".c" ".h" ".cpp" ".hpp" ".cc" ".hh" ".cxx" ".m" ".mm" ".inc" ".java" ".glsl") + +# Loops through all text files tracked by Git. +git grep -zIl '' | +while IFS= read -rd '' f; do + # Exclude some files. + if [[ "$f" == "thirdparty"* ]]; then + continue + fi + + for extension in ${CLANG_FORMAT_FILE_EXTS[@]}; do + if [[ "$f" == *"$extension" ]]; then + # Run clang-format. + clang-format -i "$f" + continue 2 + fi + done +done + +git diff > patch.patch + +# If no patch has been generated all is OK, clean up, and exit. +if [ ! -s patch.patch ] ; then + printf "Files in this commit comply with the clang-format style rules.\n" + rm -f patch.patch + exit 0 +fi + +# A patch has been created, notify the user, clean up, and exit. +printf "\n*** The following differences were found between the code " +printf "and the formatting rules:\n\n" +cat patch.patch +printf "\n*** Aborting, please fix your commit(s) with 'git commit --amend' or 'git rebase -i '\n" +rm -f patch.patch +exit 1 diff --git a/src/core/AABB.cpp b/src/core/AABB.cpp new file mode 100644 index 0000000..5fdb4ce --- /dev/null +++ b/src/core/AABB.cpp @@ -0,0 +1,604 @@ +/*************************************************************************/ +/* AABB.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "AABB.hpp" +#include "Plane.hpp" +#include "Vector3.hpp" + +#include + +namespace godot { + +bool AABB::intersects(const AABB &p_aabb) const { + if (position.x >= (p_aabb.position.x + p_aabb.size.x)) + return false; + if ((position.x + size.x) <= p_aabb.position.x) + return false; + if (position.y >= (p_aabb.position.y + p_aabb.size.y)) + return false; + if ((position.y + size.y) <= p_aabb.position.y) + return false; + if (position.z >= (p_aabb.position.z + p_aabb.size.z)) + return false; + if ((position.z + size.z) <= p_aabb.position.z) + return false; + + return true; +} + +bool AABB::intersects_inclusive(const AABB &p_aabb) const { + if (position.x > (p_aabb.position.x + p_aabb.size.x)) + return false; + if ((position.x + size.x) < p_aabb.position.x) + return false; + if (position.y > (p_aabb.position.y + p_aabb.size.y)) + return false; + if ((position.y + size.y) < p_aabb.position.y) + return false; + if (position.z > (p_aabb.position.z + p_aabb.size.z)) + return false; + if ((position.z + size.z) < p_aabb.position.z) + return false; + + return true; +} + +bool AABB::encloses(const AABB &p_aabb) const { + Vector3 src_min = position; + Vector3 src_max = position + size; + Vector3 dst_min = p_aabb.position; + Vector3 dst_max = p_aabb.position + p_aabb.size; + + return ( + (src_min.x <= dst_min.x) && + (src_max.x > dst_max.x) && + (src_min.y <= dst_min.y) && + (src_max.y > dst_max.y) && + (src_min.z <= dst_min.z) && + (src_max.z > dst_max.z)); +} + +Vector3 AABB::get_support(const Vector3 &p_normal) const { + Vector3 half_extents = size * 0.5; + Vector3 ofs = position + half_extents; + + return Vector3( + (p_normal.x > 0) ? -half_extents.x : half_extents.x, + (p_normal.y > 0) ? -half_extents.y : half_extents.y, + (p_normal.z > 0) ? -half_extents.z : half_extents.z) + + ofs; +} + +Vector3 AABB::get_endpoint(int p_point) const { + switch (p_point) { + case 0: + return Vector3(position.x, position.y, position.z); + case 1: + return Vector3(position.x, position.y, position.z + size.z); + case 2: + return Vector3(position.x, position.y + size.y, position.z); + case 3: + return Vector3(position.x, position.y + size.y, position.z + size.z); + case 4: + return Vector3(position.x + size.x, position.y, position.z); + case 5: + return Vector3(position.x + size.x, position.y, position.z + size.z); + case 6: + return Vector3(position.x + size.x, position.y + size.y, position.z); + case 7: + return Vector3(position.x + size.x, position.y + size.y, position.z + size.z); + }; + + ERR_FAIL_V(Vector3()); +} + +bool AABB::intersects_convex_shape(const Plane *p_planes, int p_plane_count) const { + Vector3 half_extents = size * 0.5; + Vector3 ofs = position + half_extents; + + for (int i = 0; i < p_plane_count; i++) { + const Plane &p = p_planes[i]; + Vector3 point( + (p.normal.x > 0) ? -half_extents.x : half_extents.x, + (p.normal.y > 0) ? -half_extents.y : half_extents.y, + (p.normal.z > 0) ? -half_extents.z : half_extents.z); + point += ofs; + if (p.is_point_over(point)) + return false; + } + + return true; +} + +bool AABB::has_point(const Vector3 &p_point) const { + if (p_point.x < position.x) + return false; + if (p_point.y < position.y) + return false; + if (p_point.z < position.z) + return false; + if (p_point.x > position.x + size.x) + return false; + if (p_point.y > position.y + size.y) + return false; + if (p_point.z > position.z + size.z) + return false; + + return true; +} + +void AABB::expand_to(const Vector3 &p_vector) { + Vector3 begin = position; + Vector3 end = position + size; + + if (p_vector.x < begin.x) + begin.x = p_vector.x; + if (p_vector.y < begin.y) + begin.y = p_vector.y; + if (p_vector.z < begin.z) + begin.z = p_vector.z; + + if (p_vector.x > end.x) + end.x = p_vector.x; + if (p_vector.y > end.y) + end.y = p_vector.y; + if (p_vector.z > end.z) + end.z = p_vector.z; + + position = begin; + size = end - begin; +} + +void AABB::project_range_in_plane(const Plane &p_plane, real_t &r_min, real_t &r_max) const { + Vector3 half_extents(size.x * 0.5, size.y * 0.5, size.z * 0.5); + Vector3 center(position.x + half_extents.x, position.y + half_extents.y, position.z + half_extents.z); + + real_t length = p_plane.normal.abs().dot(half_extents); + real_t distance = p_plane.distance_to(center); + r_min = distance - length; + r_max = distance + length; +} + +real_t AABB::get_longest_axis_size() const { + real_t max_size = size.x; + + if (size.y > max_size) { + max_size = size.y; + } + + if (size.z > max_size) { + max_size = size.z; + } + + return max_size; +} + +real_t AABB::get_shortest_axis_size() const { + real_t max_size = size.x; + + if (size.y < max_size) { + max_size = size.y; + } + + if (size.z < max_size) { + max_size = size.z; + } + + return max_size; +} + +bool AABB::smits_intersect_ray(const Vector3 &from, const Vector3 &dir, real_t t0, real_t t1) const { + real_t divx = 1.0 / dir.x; + real_t divy = 1.0 / dir.y; + real_t divz = 1.0 / dir.z; + + Vector3 upbound = position + size; + real_t tmin, tmax, tymin, tymax, tzmin, tzmax; + if (dir.x >= 0) { + tmin = (position.x - from.x) * divx; + tmax = (upbound.x - from.x) * divx; + } else { + tmin = (upbound.x - from.x) * divx; + tmax = (position.x - from.x) * divx; + } + if (dir.y >= 0) { + tymin = (position.y - from.y) * divy; + tymax = (upbound.y - from.y) * divy; + } else { + tymin = (upbound.y - from.y) * divy; + tymax = (position.y - from.y) * divy; + } + if ((tmin > tymax) || (tymin > tmax)) + return false; + if (tymin > tmin) + tmin = tymin; + if (tymax < tmax) + tmax = tymax; + if (dir.z >= 0) { + tzmin = (position.z - from.z) * divz; + tzmax = (upbound.z - from.z) * divz; + } else { + tzmin = (upbound.z - from.z) * divz; + tzmax = (position.z - from.z) * divz; + } + if ((tmin > tzmax) || (tzmin > tmax)) + return false; + if (tzmin > tmin) + tmin = tzmin; + if (tzmax < tmax) + tmax = tzmax; + return ((tmin < t1) && (tmax > t0)); +} + +void AABB::grow_by(real_t p_amount) { + position.x -= p_amount; + position.y -= p_amount; + position.z -= p_amount; + size.x += 2.0 * p_amount; + size.y += 2.0 * p_amount; + size.z += 2.0 * p_amount; +} + +real_t AABB::get_area() const { + return size.x * size.y * size.z; +} + +bool AABB::operator==(const AABB &p_rval) const { + return ((position == p_rval.position) && (size == p_rval.size)); +} +bool AABB::operator!=(const AABB &p_rval) const { + return ((position != p_rval.position) || (size != p_rval.size)); +} + +void AABB::merge_with(const AABB &p_aabb) { + Vector3 beg_1, beg_2; + Vector3 end_1, end_2; + Vector3 min, max; + + beg_1 = position; + beg_2 = p_aabb.position; + end_1 = Vector3(size.x, size.y, size.z) + beg_1; + end_2 = Vector3(p_aabb.size.x, p_aabb.size.y, p_aabb.size.z) + beg_2; + + min.x = (beg_1.x < beg_2.x) ? beg_1.x : beg_2.x; + min.y = (beg_1.y < beg_2.y) ? beg_1.y : beg_2.y; + min.z = (beg_1.z < beg_2.z) ? beg_1.z : beg_2.z; + + max.x = (end_1.x > end_2.x) ? end_1.x : end_2.x; + max.y = (end_1.y > end_2.y) ? end_1.y : end_2.y; + max.z = (end_1.z > end_2.z) ? end_1.z : end_2.z; + + position = min; + size = max - min; +} + +AABB AABB::intersection(const AABB &p_aabb) const { + Vector3 src_min = position; + Vector3 src_max = position + size; + Vector3 dst_min = p_aabb.position; + Vector3 dst_max = p_aabb.position + p_aabb.size; + + Vector3 min, max; + + if (src_min.x > dst_max.x || src_max.x < dst_min.x) + return AABB(); + else { + min.x = (src_min.x > dst_min.x) ? src_min.x : dst_min.x; + max.x = (src_max.x < dst_max.x) ? src_max.x : dst_max.x; + } + + if (src_min.y > dst_max.y || src_max.y < dst_min.y) + return AABB(); + else { + min.y = (src_min.y > dst_min.y) ? src_min.y : dst_min.y; + max.y = (src_max.y < dst_max.y) ? src_max.y : dst_max.y; + } + + if (src_min.z > dst_max.z || src_max.z < dst_min.z) + return AABB(); + else { + min.z = (src_min.z > dst_min.z) ? src_min.z : dst_min.z; + max.z = (src_max.z < dst_max.z) ? src_max.z : dst_max.z; + } + + return AABB(min, max - min); +} + +bool AABB::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *r_clip, Vector3 *r_normal) const { + Vector3 c1, c2; + Vector3 end = position + size; + real_t near = -1e20; + real_t far = 1e20; + int axis = 0; + + for (int i = 0; i < 3; i++) { + if (p_dir[i] == 0) { + if ((p_from[i] < position[i]) || (p_from[i] > end[i])) { + return false; + } + } else { // ray not parallel to planes in this direction + c1[i] = (position[i] - p_from[i]) / p_dir[i]; + c2[i] = (end[i] - p_from[i]) / p_dir[i]; + + if (c1[i] > c2[i]) { + std::swap(c1, c2); + } + if (c1[i] > near) { + near = c1[i]; + axis = i; + } + if (c2[i] < far) { + far = c2[i]; + } + if ((near > far) || (far < 0)) { + return false; + } + } + } + + if (r_clip) + *r_clip = c1; + if (r_normal) { + *r_normal = Vector3(); + (*r_normal)[axis] = p_dir[axis] ? -1 : 1; + } + + return true; +} + +bool AABB::intersects_segment(const Vector3 &p_from, const Vector3 &p_to, Vector3 *r_clip, Vector3 *r_normal) const { + real_t min = 0, max = 1; + int axis = 0; + real_t sign = 0; + + for (int i = 0; i < 3; i++) { + real_t seg_from = p_from[i]; + real_t seg_to = p_to[i]; + real_t box_begin = position[i]; + real_t box_end = box_begin + size[i]; + real_t cmin, cmax; + real_t csign; + + if (seg_from < seg_to) { + if (seg_from > box_end || seg_to < box_begin) + return false; + real_t length = seg_to - seg_from; + cmin = (seg_from < box_begin) ? ((box_begin - seg_from) / length) : 0; + cmax = (seg_to > box_end) ? ((box_end - seg_from) / length) : 1; + csign = -1.0; + + } else { + if (seg_to > box_end || seg_from < box_begin) + return false; + real_t length = seg_to - seg_from; + cmin = (seg_from > box_end) ? (box_end - seg_from) / length : 0; + cmax = (seg_to < box_begin) ? (box_begin - seg_from) / length : 1; + csign = 1.0; + } + + if (cmin > min) { + min = cmin; + axis = i; + sign = csign; + } + if (cmax < max) + max = cmax; + if (max < min) + return false; + } + + Vector3 rel = p_to - p_from; + + if (r_normal) { + Vector3 normal; + normal[axis] = sign; + *r_normal = normal; + } + + if (r_clip) + *r_clip = p_from + rel * min; + + return true; +} + +bool AABB::intersects_plane(const Plane &p_plane) const { + Vector3 points[8] = { + Vector3(position.x, position.y, position.z), + Vector3(position.x, position.y, position.z + size.z), + Vector3(position.x, position.y + size.y, position.z), + Vector3(position.x, position.y + size.y, position.z + size.z), + Vector3(position.x + size.x, position.y, position.z), + Vector3(position.x + size.x, position.y, position.z + size.z), + Vector3(position.x + size.x, position.y + size.y, position.z), + Vector3(position.x + size.x, position.y + size.y, position.z + size.z), + }; + + bool over = false; + bool under = false; + + for (int i = 0; i < 8; i++) { + if (p_plane.distance_to(points[i]) > 0) + over = true; + else + under = true; + } + + return under && over; +} + +Vector3 AABB::get_longest_axis() const { + Vector3 axis(1, 0, 0); + real_t max_size = size.x; + + if (size.y > max_size) { + axis = Vector3(0, 1, 0); + max_size = size.y; + } + + if (size.z > max_size) { + axis = Vector3(0, 0, 1); + max_size = size.z; + } + + return axis; +} +int AABB::get_longest_axis_index() const { + int axis = 0; + real_t max_size = size.x; + + if (size.y > max_size) { + axis = 1; + max_size = size.y; + } + + if (size.z > max_size) { + axis = 2; + max_size = size.z; + } + + return axis; +} + +Vector3 AABB::get_shortest_axis() const { + Vector3 axis(1, 0, 0); + real_t max_size = size.x; + + if (size.y < max_size) { + axis = Vector3(0, 1, 0); + max_size = size.y; + } + + if (size.z < max_size) { + axis = Vector3(0, 0, 1); + max_size = size.z; + } + + return axis; +} +int AABB::get_shortest_axis_index() const { + int axis = 0; + real_t max_size = size.x; + + if (size.y < max_size) { + axis = 1; + max_size = size.y; + } + + if (size.z < max_size) { + axis = 2; + max_size = size.z; + } + + return axis; +} + +AABB AABB::merge(const AABB &p_with) const { + AABB aabb = *this; + aabb.merge_with(p_with); + return aabb; +} +AABB AABB::expand(const Vector3 &p_vector) const { + AABB aabb = *this; + aabb.expand_to(p_vector); + return aabb; +} +AABB AABB::grow(real_t p_by) const { + AABB aabb = *this; + aabb.grow_by(p_by); + return aabb; +} + +void AABB::get_edge(int p_edge, Vector3 &r_from, Vector3 &r_to) const { + ERR_FAIL_INDEX(p_edge, 12); + switch (p_edge) { + case 0: { + r_from = Vector3(position.x + size.x, position.y, position.z); + r_to = Vector3(position.x, position.y, position.z); + } break; + case 1: { + r_from = Vector3(position.x + size.x, position.y, position.z + size.z); + r_to = Vector3(position.x + size.x, position.y, position.z); + } break; + case 2: { + r_from = Vector3(position.x, position.y, position.z + size.z); + r_to = Vector3(position.x + size.x, position.y, position.z + size.z); + + } break; + case 3: { + r_from = Vector3(position.x, position.y, position.z); + r_to = Vector3(position.x, position.y, position.z + size.z); + + } break; + case 4: { + r_from = Vector3(position.x, position.y + size.y, position.z); + r_to = Vector3(position.x + size.x, position.y + size.y, position.z); + } break; + case 5: { + r_from = Vector3(position.x + size.x, position.y + size.y, position.z); + r_to = Vector3(position.x + size.x, position.y + size.y, position.z + size.z); + } break; + case 6: { + r_from = Vector3(position.x + size.x, position.y + size.y, position.z + size.z); + r_to = Vector3(position.x, position.y + size.y, position.z + size.z); + + } break; + case 7: { + r_from = Vector3(position.x, position.y + size.y, position.z + size.z); + r_to = Vector3(position.x, position.y + size.y, position.z); + + } break; + case 8: { + r_from = Vector3(position.x, position.y, position.z + size.z); + r_to = Vector3(position.x, position.y + size.y, position.z + size.z); + + } break; + case 9: { + r_from = Vector3(position.x, position.y, position.z); + r_to = Vector3(position.x, position.y + size.y, position.z); + + } break; + case 10: { + r_from = Vector3(position.x + size.x, position.y, position.z); + r_to = Vector3(position.x + size.x, position.y + size.y, position.z); + + } break; + case 11: { + r_from = Vector3(position.x + size.x, position.y, position.z + size.z); + r_to = Vector3(position.x + size.x, position.y + size.y, position.z + size.z); + + } break; + } +} + +AABB::operator String() const { + return String() + position + " - " + size; +} + +} // namespace godot diff --git a/src/core/Array.cpp b/src/core/Array.cpp new file mode 100644 index 0000000..72663c9 --- /dev/null +++ b/src/core/Array.cpp @@ -0,0 +1,226 @@ +/*************************************************************************/ +/* Array.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "Array.hpp" +#include "GodotGlobal.hpp" +#include "Variant.hpp" + +#include + +namespace godot { + +class Object; + +Array::Array() { + godot::api->godot_array_new(&_godot_array); +} + +Array::Array(const Array &other) { + godot::api->godot_array_new_copy(&_godot_array, &other._godot_array); +} + +Array &Array::operator=(const Array &other) { + godot::api->godot_array_destroy(&_godot_array); + godot::api->godot_array_new_copy(&_godot_array, &other._godot_array); + return *this; +} + +Array::Array(const PoolByteArray &a) { + godot::api->godot_array_new_pool_byte_array(&_godot_array, (godot_pool_byte_array *)&a); +} + +Array::Array(const PoolIntArray &a) { + godot::api->godot_array_new_pool_int_array(&_godot_array, (godot_pool_int_array *)&a); +} + +Array::Array(const PoolRealArray &a) { + godot::api->godot_array_new_pool_real_array(&_godot_array, (godot_pool_real_array *)&a); +} + +Array::Array(const PoolStringArray &a) { + godot::api->godot_array_new_pool_string_array(&_godot_array, (godot_pool_string_array *)&a); +} + +Array::Array(const PoolVector2Array &a) { + godot::api->godot_array_new_pool_vector2_array(&_godot_array, (godot_pool_vector2_array *)&a); +} + +Array::Array(const PoolVector3Array &a) { + godot::api->godot_array_new_pool_vector3_array(&_godot_array, (godot_pool_vector3_array *)&a); +} + +Array::Array(const PoolColorArray &a) { + godot::api->godot_array_new_pool_color_array(&_godot_array, (godot_pool_color_array *)&a); +} + +Variant &Array::operator[](const int idx) { + godot_variant *v = godot::api->godot_array_operator_index(&_godot_array, idx); + // We assume it's ok to reinterpret because the value is a pointer whose data is already owned by the array, + // so can return a reference without constructing a Variant + return *reinterpret_cast(v); +} + +const Variant &Array::operator[](const int idx) const { + // Yes, I'm casting away the const... you can hate me now. + // since the result is + godot_variant *v = godot::api->godot_array_operator_index((godot_array *)&_godot_array, idx); + return *reinterpret_cast(v); +} + +void Array::append(const Variant &v) { + godot::api->godot_array_append(&_godot_array, (godot_variant *)&v); +} + +void Array::clear() { + godot::api->godot_array_clear(&_godot_array); +} + +int Array::count(const Variant &v) { + return godot::api->godot_array_count(&_godot_array, (godot_variant *)&v); +} + +bool Array::empty() const { + return godot::api->godot_array_empty(&_godot_array); +} + +void Array::erase(const Variant &v) { + godot::api->godot_array_erase(&_godot_array, (godot_variant *)&v); +} + +Variant Array::front() const { + godot_variant v = godot::api->godot_array_front(&_godot_array); + return Variant(v); +} + +Variant Array::back() const { + godot_variant v = godot::api->godot_array_back(&_godot_array); + return Variant(v); +} + +int Array::find(const Variant &what, const int from) const { + return godot::api->godot_array_find(&_godot_array, (godot_variant *)&what, from); +} + +int Array::find_last(const Variant &what) const { + return godot::api->godot_array_find_last(&_godot_array, (godot_variant *)&what); +} + +bool Array::has(const Variant &what) const { + return godot::api->godot_array_has(&_godot_array, (godot_variant *)&what); +} + +uint32_t Array::hash() const { + return godot::api->godot_array_hash(&_godot_array); +} + +void Array::insert(const int pos, const Variant &value) { + godot::api->godot_array_insert(&_godot_array, pos, (godot_variant *)&value); +} + +void Array::invert() { + godot::api->godot_array_invert(&_godot_array); +} + +Variant Array::pop_back() { + godot_variant v = godot::api->godot_array_pop_back(&_godot_array); + return Variant(v); +} + +Variant Array::pop_front() { + godot_variant v = godot::api->godot_array_pop_front(&_godot_array); + return Variant(v); +} + +void Array::push_back(const Variant &v) { + godot::api->godot_array_push_back(&_godot_array, (godot_variant *)&v); +} + +void Array::push_front(const Variant &v) { + godot::api->godot_array_push_front(&_godot_array, (godot_variant *)&v); +} + +void Array::remove(const int idx) { + godot::api->godot_array_remove(&_godot_array, idx); +} + +int Array::size() const { + return godot::api->godot_array_size(&_godot_array); +} + +void Array::resize(const int size) { + godot::api->godot_array_resize(&_godot_array, size); +} + +int Array::rfind(const Variant &what, const int from) const { + return godot::api->godot_array_rfind(&_godot_array, (godot_variant *)&what, from); +} + +void Array::sort() { + godot::api->godot_array_sort(&_godot_array); +} + +void Array::sort_custom(Object *obj, const String &func) { + godot::api->godot_array_sort_custom(&_godot_array, (godot_object *)obj, (godot_string *)&func); +} + +int Array::bsearch(const Variant &value, const bool before) { + return godot::api->godot_array_bsearch(&_godot_array, (godot_variant *)&value, before); +} + +int Array::bsearch_custom(const Variant &value, const Object *obj, + const String &func, const bool before) { + return godot::api->godot_array_bsearch_custom(&_godot_array, (godot_variant *)&value, + (godot_object *)obj, (godot_string *)&func, before); +} + +Array Array::duplicate(const bool deep) const { + godot_array arr = godot::core_1_1_api->godot_array_duplicate(&_godot_array, deep); + return Array(arr); +} + +Variant Array::max() const { + godot_variant v = godot::core_1_1_api->godot_array_max(&_godot_array); + return Variant(v); +} + +Variant Array::min() const { + godot_variant v = godot::core_1_1_api->godot_array_min(&_godot_array); + return Variant(v); +} + +void Array::shuffle() { + godot::core_1_1_api->godot_array_shuffle(&_godot_array); +} + +Array::~Array() { + godot::api->godot_array_destroy(&_godot_array); +} + +} // namespace godot diff --git a/src/core/Basis.cpp b/src/core/Basis.cpp new file mode 100644 index 0000000..6bae9d9 --- /dev/null +++ b/src/core/Basis.cpp @@ -0,0 +1,710 @@ +/*************************************************************************/ +/* Basis.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "Basis.hpp" +#include "Defs.hpp" +#include "Quat.hpp" +#include "Vector3.hpp" + +#include + +namespace godot { + +const Basis Basis::IDENTITY = Basis(); +const Basis Basis::FLIP_X = Basis(-1, 0, 0, 0, 1, 0, 0, 0, 1); +const Basis Basis::FLIP_Y = Basis(1, 0, 0, 0, -1, 0, 0, 0, 1); +const Basis Basis::FLIP_Z = Basis(1, 0, 0, 0, 1, 0, 0, 0, -1); + +Basis::Basis(const Vector3 &row0, const Vector3 &row1, const Vector3 &row2) { + elements[0] = row0; + elements[1] = row1; + elements[2] = row2; +} + +Basis::Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) { + set(xx, xy, xz, yx, yy, yz, zx, zy, zz); +} + +Basis::Basis() { + elements[0][0] = 1; + elements[0][1] = 0; + elements[0][2] = 0; + elements[1][0] = 0; + elements[1][1] = 1; + elements[1][2] = 0; + elements[2][0] = 0; + elements[2][1] = 0; + elements[2][2] = 1; +} + +#define cofac(row1, col1, row2, col2) \ + (elements[row1][col1] * elements[row2][col2] - elements[row1][col2] * elements[row2][col1]) + +void Basis::invert() { + real_t co[3] = { + cofac(1, 1, 2, 2), cofac(1, 2, 2, 0), cofac(1, 0, 2, 1) + }; + real_t det = elements[0][0] * co[0] + + elements[0][1] * co[1] + + elements[0][2] * co[2]; + + ERR_FAIL_COND(det == 0); + + real_t s = 1.0 / det; + + set(co[0] * s, cofac(0, 2, 2, 1) * s, cofac(0, 1, 1, 2) * s, + co[1] * s, cofac(0, 0, 2, 2) * s, cofac(0, 2, 1, 0) * s, + co[2] * s, cofac(0, 1, 2, 0) * s, cofac(0, 0, 1, 1) * s); +} +#undef cofac + +bool Basis::isequal_approx(const Basis &a, const Basis &b) const { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if ((::fabs(a.elements[i][j] - b.elements[i][j]) < CMP_EPSILON) == false) + return false; + } + } + + return true; +} + +bool Basis::is_orthogonal() const { + Basis id; + Basis m = (*this) * transposed(); + + return isequal_approx(id, m); +} + +bool Basis::is_rotation() const { + return ::fabs(determinant() - 1) < CMP_EPSILON && is_orthogonal(); +} + +void Basis::transpose() { + std::swap(elements[0][1], elements[1][0]); + std::swap(elements[0][2], elements[2][0]); + std::swap(elements[1][2], elements[2][1]); +} + +Basis Basis::inverse() const { + Basis b = *this; + b.invert(); + return b; +} + +Basis Basis::transposed() const { + Basis b = *this; + b.transpose(); + return b; +} + +real_t Basis::determinant() const { + return elements[0][0] * (elements[1][1] * elements[2][2] - elements[2][1] * elements[1][2]) - + elements[1][0] * (elements[0][1] * elements[2][2] - elements[2][1] * elements[0][2]) + + elements[2][0] * (elements[0][1] * elements[1][2] - elements[1][1] * elements[0][2]); +} + +Vector3 Basis::get_axis(int p_axis) const { + // get actual basis axis (elements is transposed for performance) + return Vector3(elements[0][p_axis], elements[1][p_axis], elements[2][p_axis]); +} +void Basis::set_axis(int p_axis, const Vector3 &p_value) { + // get actual basis axis (elements is transposed for performance) + elements[0][p_axis] = p_value.x; + elements[1][p_axis] = p_value.y; + elements[2][p_axis] = p_value.z; +} + +void Basis::rotate(const Vector3 &p_axis, real_t p_phi) { + *this = rotated(p_axis, p_phi); +} + +Basis Basis::rotated(const Vector3 &p_axis, real_t p_phi) const { + return Basis(p_axis, p_phi) * (*this); +} + +void Basis::scale(const Vector3 &p_scale) { + elements[0][0] *= p_scale.x; + elements[0][1] *= p_scale.x; + elements[0][2] *= p_scale.x; + elements[1][0] *= p_scale.y; + elements[1][1] *= p_scale.y; + elements[1][2] *= p_scale.y; + elements[2][0] *= p_scale.z; + elements[2][1] *= p_scale.z; + elements[2][2] *= p_scale.z; +} + +Basis Basis::scaled(const Vector3 &p_scale) const { + Basis b = *this; + b.scale(p_scale); + return b; +} + +Vector3 Basis::get_scale() const { + // We are assuming M = R.S, and performing a polar decomposition to extract R and S. + // FIXME: We eventually need a proper polar decomposition. + // As a cheap workaround until then, to ensure that R is a proper rotation matrix with determinant +1 + // (such that it can be represented by a Quat or Euler angles), we absorb the sign flip into the scaling matrix. + // As such, it works in conjuction with get_rotation(). + real_t det_sign = determinant() > 0 ? 1 : -1; + return det_sign * Vector3( + Vector3(elements[0][0], elements[1][0], elements[2][0]).length(), + Vector3(elements[0][1], elements[1][1], elements[2][1]).length(), + Vector3(elements[0][2], elements[1][2], elements[2][2]).length()); +} + +// TODO: implement this directly without using quaternions to make it more efficient +Basis Basis::slerp(Basis b, float t) const { + ERR_FAIL_COND_V(!is_rotation(), Basis()); + ERR_FAIL_COND_V(!b.is_rotation(), Basis()); + Quat from(*this); + Quat to(b); + return Basis(from.slerp(to, t)); +} + +// get_euler_xyz returns a vector containing the Euler angles in the format +// (a1,a2,a3), where a3 is the angle of the first rotation, and a1 is the last +// (following the convention they are commonly defined in the literature). +// +// The current implementation uses XYZ convention (Z is the first rotation), +// so euler.z is the angle of the (first) rotation around Z axis and so on, +// +// And thus, assuming the matrix is a rotation matrix, this function returns +// the angles in the decomposition R = X(a1).Y(a2).Z(a3) where Z(a) rotates +// around the z-axis by a and so on. +Vector3 Basis::get_euler_xyz() const { + // Euler angles in XYZ convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cy*cz -cy*sz sy + // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx + // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy + + Vector3 euler; + + ERR_FAIL_COND_V(is_rotation() == false, euler); + + real_t sy = elements[0][2]; + if (sy < 1.0) { + if (sy > -1.0) { + // is this a pure Y rotation? + if (elements[1][0] == 0.0 && elements[0][1] == 0.0 && elements[1][2] == 0 && elements[2][1] == 0 && elements[1][1] == 1) { + // return the simplest form (human friendlier in editor and scripts) + euler.x = 0; + euler.y = atan2(elements[0][2], elements[0][0]); + euler.z = 0; + } else { + euler.x = ::atan2(-elements[1][2], elements[2][2]); + euler.y = ::asin(sy); + euler.z = ::atan2(-elements[0][1], elements[0][0]); + } + } else { + euler.x = -::atan2(elements[0][1], elements[1][1]); + euler.y = -Math_PI / 2.0; + euler.z = 0.0; + } + } else { + euler.x = ::atan2(elements[0][1], elements[1][1]); + euler.y = Math_PI / 2.0; + euler.z = 0.0; + } + return euler; +} + +// set_euler_xyz expects a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// The current implementation uses XYZ convention (Z is the first rotation). +void Basis::set_euler_xyz(const Vector3 &p_euler) { + real_t c, s; + + c = ::cos(p_euler.x); + s = ::sin(p_euler.x); + Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c); + + c = ::cos(p_euler.y); + s = ::sin(p_euler.y); + Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c); + + c = ::cos(p_euler.z); + s = ::sin(p_euler.z); + Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0); + + //optimizer will optimize away all this anyway + *this = xmat * (ymat * zmat); +} + +// get_euler_yxz returns a vector containing the Euler angles in the YXZ convention, +// as in first-Z, then-X, last-Y. The angles for X, Y, and Z rotations are returned +// as the x, y, and z components of a Vector3 respectively. +Vector3 Basis::get_euler_yxz() const { + // Euler angles in YXZ convention. + // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix + // + // rot = cy*cz+sy*sx*sz cz*sy*sx-cy*sz cx*sy + // cx*sz cx*cz -sx + // cy*sx*sz-cz*sy cy*cz*sx+sy*sz cy*cx + + Vector3 euler; + + ERR_FAIL_COND_V(is_rotation() == false, euler); + + real_t m12 = elements[1][2]; + + if (m12 < 1) { + if (m12 > -1) { + // is this a pure X rotation? + if (elements[1][0] == 0 && elements[0][1] == 0 && elements[0][2] == 0 && elements[2][0] == 0 && elements[0][0] == 1) { + // return the simplest form (human friendlier in editor and scripts) + euler.x = atan2(-m12, elements[1][1]); + euler.y = 0; + euler.z = 0; + } else { + euler.x = asin(-m12); + euler.y = atan2(elements[0][2], elements[2][2]); + euler.z = atan2(elements[1][0], elements[1][1]); + } + } else { // m12 == -1 + euler.x = Math_PI * 0.5; + euler.y = -atan2(-elements[0][1], elements[0][0]); + euler.z = 0; + } + } else { // m12 == 1 + euler.x = -Math_PI * 0.5; + euler.y = -atan2(-elements[0][1], elements[0][0]); + euler.z = 0; + } + + return euler; +} + +// set_euler_yxz expects a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// The current implementation uses YXZ convention (Z is the first rotation). +void Basis::set_euler_yxz(const Vector3 &p_euler) { + real_t c, s; + + c = ::cos(p_euler.x); + s = ::sin(p_euler.x); + Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c); + + c = ::cos(p_euler.y); + s = ::sin(p_euler.y); + Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c); + + c = ::cos(p_euler.z); + s = ::sin(p_euler.z); + Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0); + + //optimizer will optimize away all this anyway + *this = ymat * xmat * zmat; +} + +// transposed dot products +real_t Basis::tdotx(const Vector3 &v) const { + return elements[0][0] * v[0] + elements[1][0] * v[1] + elements[2][0] * v[2]; +} +real_t Basis::tdoty(const Vector3 &v) const { + return elements[0][1] * v[0] + elements[1][1] * v[1] + elements[2][1] * v[2]; +} +real_t Basis::tdotz(const Vector3 &v) const { + return elements[0][2] * v[0] + elements[1][2] * v[1] + elements[2][2] * v[2]; +} + +bool Basis::operator==(const Basis &p_matrix) const { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (elements[i][j] != p_matrix.elements[i][j]) + return false; + } + } + + return true; +} + +bool Basis::operator!=(const Basis &p_matrix) const { + return (!(*this == p_matrix)); +} + +Vector3 Basis::xform(const Vector3 &p_vector) const { + return Vector3( + elements[0].dot(p_vector), + elements[1].dot(p_vector), + elements[2].dot(p_vector)); +} + +Vector3 Basis::xform_inv(const Vector3 &p_vector) const { + return Vector3( + (elements[0][0] * p_vector.x) + (elements[1][0] * p_vector.y) + (elements[2][0] * p_vector.z), + (elements[0][1] * p_vector.x) + (elements[1][1] * p_vector.y) + (elements[2][1] * p_vector.z), + (elements[0][2] * p_vector.x) + (elements[1][2] * p_vector.y) + (elements[2][2] * p_vector.z)); +} +void Basis::operator*=(const Basis &p_matrix) { + set( + p_matrix.tdotx(elements[0]), p_matrix.tdoty(elements[0]), p_matrix.tdotz(elements[0]), + p_matrix.tdotx(elements[1]), p_matrix.tdoty(elements[1]), p_matrix.tdotz(elements[1]), + p_matrix.tdotx(elements[2]), p_matrix.tdoty(elements[2]), p_matrix.tdotz(elements[2])); +} + +Basis Basis::operator*(const Basis &p_matrix) const { + return Basis( + p_matrix.tdotx(elements[0]), p_matrix.tdoty(elements[0]), p_matrix.tdotz(elements[0]), + p_matrix.tdotx(elements[1]), p_matrix.tdoty(elements[1]), p_matrix.tdotz(elements[1]), + p_matrix.tdotx(elements[2]), p_matrix.tdoty(elements[2]), p_matrix.tdotz(elements[2])); +} + +void Basis::operator+=(const Basis &p_matrix) { + elements[0] += p_matrix.elements[0]; + elements[1] += p_matrix.elements[1]; + elements[2] += p_matrix.elements[2]; +} + +Basis Basis::operator+(const Basis &p_matrix) const { + Basis ret(*this); + ret += p_matrix; + return ret; +} + +void Basis::operator-=(const Basis &p_matrix) { + elements[0] -= p_matrix.elements[0]; + elements[1] -= p_matrix.elements[1]; + elements[2] -= p_matrix.elements[2]; +} + +Basis Basis::operator-(const Basis &p_matrix) const { + Basis ret(*this); + ret -= p_matrix; + return ret; +} + +void Basis::operator*=(real_t p_val) { + elements[0] *= p_val; + elements[1] *= p_val; + elements[2] *= p_val; +} + +Basis Basis::operator*(real_t p_val) const { + Basis ret(*this); + ret *= p_val; + return ret; +} + +Basis::operator String() const { + String s; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (i != 0 || j != 0) + s += ", "; + + s += String::num(elements[i][j]); + } + } + return s; +} + +/* create / set */ + +void Basis::set(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) { + elements[0][0] = xx; + elements[0][1] = xy; + elements[0][2] = xz; + elements[1][0] = yx; + elements[1][1] = yy; + elements[1][2] = yz; + elements[2][0] = zx; + elements[2][1] = zy; + elements[2][2] = zz; +} +Vector3 Basis::get_column(int i) const { + return Vector3(elements[0][i], elements[1][i], elements[2][i]); +} + +Vector3 Basis::get_row(int i) const { + return Vector3(elements[i][0], elements[i][1], elements[i][2]); +} +Vector3 Basis::get_main_diagonal() const { + return Vector3(elements[0][0], elements[1][1], elements[2][2]); +} + +void Basis::set_row(int i, const Vector3 &p_row) { + elements[i][0] = p_row.x; + elements[i][1] = p_row.y; + elements[i][2] = p_row.z; +} + +Basis Basis::transpose_xform(const Basis &m) const { + return Basis( + elements[0].x * m[0].x + elements[1].x * m[1].x + elements[2].x * m[2].x, + elements[0].x * m[0].y + elements[1].x * m[1].y + elements[2].x * m[2].y, + elements[0].x * m[0].z + elements[1].x * m[1].z + elements[2].x * m[2].z, + elements[0].y * m[0].x + elements[1].y * m[1].x + elements[2].y * m[2].x, + elements[0].y * m[0].y + elements[1].y * m[1].y + elements[2].y * m[2].y, + elements[0].y * m[0].z + elements[1].y * m[1].z + elements[2].y * m[2].z, + elements[0].z * m[0].x + elements[1].z * m[1].x + elements[2].z * m[2].x, + elements[0].z * m[0].y + elements[1].z * m[1].y + elements[2].z * m[2].y, + elements[0].z * m[0].z + elements[1].z * m[1].z + elements[2].z * m[2].z); +} + +void Basis::orthonormalize() { + ERR_FAIL_COND(determinant() == 0); + + // Gram-Schmidt Process + + Vector3 x = get_axis(0); + Vector3 y = get_axis(1); + Vector3 z = get_axis(2); + + x.normalize(); + y = (y - x * (x.dot(y))); + y.normalize(); + z = (z - x * (x.dot(z)) - y * (y.dot(z))); + z.normalize(); + + set_axis(0, x); + set_axis(1, y); + set_axis(2, z); +} + +Basis Basis::orthonormalized() const { + Basis b = *this; + b.orthonormalize(); + return b; +} + +bool Basis::is_symmetric() const { + if (::fabs(elements[0][1] - elements[1][0]) > CMP_EPSILON) + return false; + if (::fabs(elements[0][2] - elements[2][0]) > CMP_EPSILON) + return false; + if (::fabs(elements[1][2] - elements[2][1]) > CMP_EPSILON) + return false; + + return true; +} + +Basis Basis::diagonalize() { + // I love copy paste + + if (!is_symmetric()) + return Basis(); + + const int ite_max = 1024; + + real_t off_matrix_norm_2 = elements[0][1] * elements[0][1] + elements[0][2] * elements[0][2] + elements[1][2] * elements[1][2]; + + int ite = 0; + Basis acc_rot; + while (off_matrix_norm_2 > CMP_EPSILON2 && ite++ < ite_max) { + real_t el01_2 = elements[0][1] * elements[0][1]; + real_t el02_2 = elements[0][2] * elements[0][2]; + real_t el12_2 = elements[1][2] * elements[1][2]; + // Find the pivot element + int i, j; + if (el01_2 > el02_2) { + if (el12_2 > el01_2) { + i = 1; + j = 2; + } else { + i = 0; + j = 1; + } + } else { + if (el12_2 > el02_2) { + i = 1; + j = 2; + } else { + i = 0; + j = 2; + } + } + + // Compute the rotation angle + real_t angle; + if (::fabs(elements[j][j] - elements[i][i]) < CMP_EPSILON) { + angle = Math_PI / 4; + } else { + angle = 0.5 * ::atan(2 * elements[i][j] / (elements[j][j] - elements[i][i])); + } + + // Compute the rotation matrix + Basis rot; + rot.elements[i][i] = rot.elements[j][j] = ::cos(angle); + rot.elements[i][j] = -(rot.elements[j][i] = ::sin(angle)); + + // Update the off matrix norm + off_matrix_norm_2 -= elements[i][j] * elements[i][j]; + + // Apply the rotation + *this = rot * *this * rot.transposed(); + acc_rot = rot * acc_rot; + } + + return acc_rot; +} + +static const Basis _ortho_bases[24] = { + Basis(1, 0, 0, 0, 1, 0, 0, 0, 1), + Basis(0, -1, 0, 1, 0, 0, 0, 0, 1), + Basis(-1, 0, 0, 0, -1, 0, 0, 0, 1), + Basis(0, 1, 0, -1, 0, 0, 0, 0, 1), + Basis(1, 0, 0, 0, 0, -1, 0, 1, 0), + Basis(0, 0, 1, 1, 0, 0, 0, 1, 0), + Basis(-1, 0, 0, 0, 0, 1, 0, 1, 0), + Basis(0, 0, -1, -1, 0, 0, 0, 1, 0), + Basis(1, 0, 0, 0, -1, 0, 0, 0, -1), + Basis(0, 1, 0, 1, 0, 0, 0, 0, -1), + Basis(-1, 0, 0, 0, 1, 0, 0, 0, -1), + Basis(0, -1, 0, -1, 0, 0, 0, 0, -1), + Basis(1, 0, 0, 0, 0, 1, 0, -1, 0), + Basis(0, 0, -1, 1, 0, 0, 0, -1, 0), + Basis(-1, 0, 0, 0, 0, -1, 0, -1, 0), + Basis(0, 0, 1, -1, 0, 0, 0, -1, 0), + Basis(0, 0, 1, 0, 1, 0, -1, 0, 0), + Basis(0, -1, 0, 0, 0, 1, -1, 0, 0), + Basis(0, 0, -1, 0, -1, 0, -1, 0, 0), + Basis(0, 1, 0, 0, 0, -1, -1, 0, 0), + Basis(0, 0, 1, 0, -1, 0, 1, 0, 0), + Basis(0, 1, 0, 0, 0, 1, 1, 0, 0), + Basis(0, 0, -1, 0, 1, 0, 1, 0, 0), + Basis(0, -1, 0, 0, 0, -1, 1, 0, 0) +}; + +int Basis::get_orthogonal_index() const { + //could be sped up if i come up with a way + Basis orth = *this; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + real_t v = orth[i][j]; + if (v > 0.5) + v = 1.0; + else if (v < -0.5) + v = -1.0; + else + v = 0; + + orth[i][j] = v; + } + } + + for (int i = 0; i < 24; i++) { + if (_ortho_bases[i] == orth) + return i; + } + + return 0; +} + +void Basis::set_orthogonal_index(int p_index) { + //there only exist 24 orthogonal bases in r3 + ERR_FAIL_COND(p_index >= 24); + + *this = _ortho_bases[p_index]; +} + +Basis::Basis(const Vector3 &p_euler) { + set_euler(p_euler); +} + +} // namespace godot + +#include "Quat.hpp" + +namespace godot { + +Basis::Basis(const Quat &p_quat) { + real_t d = p_quat.length_squared(); + real_t s = 2.0 / d; + real_t xs = p_quat.x * s, ys = p_quat.y * s, zs = p_quat.z * s; + real_t wx = p_quat.w * xs, wy = p_quat.w * ys, wz = p_quat.w * zs; + real_t xx = p_quat.x * xs, xy = p_quat.x * ys, xz = p_quat.x * zs; + real_t yy = p_quat.y * ys, yz = p_quat.y * zs, zz = p_quat.z * zs; + set(1.0 - (yy + zz), xy - wz, xz + wy, + xy + wz, 1.0 - (xx + zz), yz - wx, + xz - wy, yz + wx, 1.0 - (xx + yy)); +} + +Basis::Basis(const Vector3 &p_axis, real_t p_phi) { + // Rotation matrix from axis and angle, see https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle + + Vector3 axis_sq(p_axis.x * p_axis.x, p_axis.y * p_axis.y, p_axis.z * p_axis.z); + + real_t cosine = ::cos(p_phi); + real_t sine = ::sin(p_phi); + + elements[0][0] = axis_sq.x + cosine * (1.0 - axis_sq.x); + elements[0][1] = p_axis.x * p_axis.y * (1.0 - cosine) - p_axis.z * sine; + elements[0][2] = p_axis.z * p_axis.x * (1.0 - cosine) + p_axis.y * sine; + + elements[1][0] = p_axis.x * p_axis.y * (1.0 - cosine) + p_axis.z * sine; + elements[1][1] = axis_sq.y + cosine * (1.0 - axis_sq.y); + elements[1][2] = p_axis.y * p_axis.z * (1.0 - cosine) - p_axis.x * sine; + + elements[2][0] = p_axis.z * p_axis.x * (1.0 - cosine) - p_axis.y * sine; + elements[2][1] = p_axis.y * p_axis.z * (1.0 - cosine) + p_axis.x * sine; + elements[2][2] = axis_sq.z + cosine * (1.0 - axis_sq.z); +} + +Basis::operator Quat() const { + //commenting this check because precision issues cause it to fail when it shouldn't + //ERR_FAIL_COND_V(is_rotation() == false, Quat()); + + real_t trace = elements[0][0] + elements[1][1] + elements[2][2]; + real_t temp[4]; + + if (trace > 0.0) { + real_t s = ::sqrt(trace + 1.0); + temp[3] = (s * 0.5); + s = 0.5 / s; + + temp[0] = ((elements[2][1] - elements[1][2]) * s); + temp[1] = ((elements[0][2] - elements[2][0]) * s); + temp[2] = ((elements[1][0] - elements[0][1]) * s); + } else { + int i = elements[0][0] < elements[1][1] ? + (elements[1][1] < elements[2][2] ? 2 : 1) : + (elements[0][0] < elements[2][2] ? 2 : 0); + int j = (i + 1) % 3; + int k = (i + 2) % 3; + + real_t s = ::sqrt(elements[i][i] - elements[j][j] - elements[k][k] + 1.0); + temp[i] = s * 0.5; + s = 0.5 / s; + + temp[3] = (elements[k][j] - elements[j][k]) * s; + temp[j] = (elements[j][i] + elements[i][j]) * s; + temp[k] = (elements[k][i] + elements[i][k]) * s; + } + + return Quat(temp[0], temp[1], temp[2], temp[3]); +} + +} // namespace godot diff --git a/src/core/CameraMatrix.cpp b/src/core/CameraMatrix.cpp new file mode 100644 index 0000000..29267c3 --- /dev/null +++ b/src/core/CameraMatrix.cpp @@ -0,0 +1,655 @@ +/*************************************************************************/ +/* CameraMatrix.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "CameraMatrix.hpp" + +void CameraMatrix::set_identity() { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + matrix[i][j] = (i == j) ? 1 : 0; + } + } +} + +void CameraMatrix::set_zero() { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + matrix[i][j] = 0; + } + } +} + +Plane CameraMatrix::xform4(const Plane &p_vec4) const { + Plane ret; + + ret.normal.x = matrix[0][0] * p_vec4.normal.x + matrix[1][0] * p_vec4.normal.y + matrix[2][0] * p_vec4.normal.z + matrix[3][0] * p_vec4.d; + ret.normal.y = matrix[0][1] * p_vec4.normal.x + matrix[1][1] * p_vec4.normal.y + matrix[2][1] * p_vec4.normal.z + matrix[3][1] * p_vec4.d; + ret.normal.z = matrix[0][2] * p_vec4.normal.x + matrix[1][2] * p_vec4.normal.y + matrix[2][2] * p_vec4.normal.z + matrix[3][2] * p_vec4.d; + ret.d = matrix[0][3] * p_vec4.normal.x + matrix[1][3] * p_vec4.normal.y + matrix[2][3] * p_vec4.normal.z + matrix[3][3] * p_vec4.d; + return ret; +} + +void CameraMatrix::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov) { + if (p_flip_fov) { + p_fovy_degrees = get_fovy(p_fovy_degrees, 1.0 / p_aspect); + } + + real_t sine, cotangent, deltaZ; + real_t radians = p_fovy_degrees / 2.0 * Math_PI / 180.0; + + deltaZ = p_z_far - p_z_near; + sine = sin(radians); + + if ((deltaZ == 0) || (sine == 0) || (p_aspect == 0)) { + return; + } + cotangent = cos(radians) / sine; + + set_identity(); + + matrix[0][0] = cotangent / p_aspect; + matrix[1][1] = cotangent; + matrix[2][2] = -(p_z_far + p_z_near) / deltaZ; + matrix[2][3] = -1; + matrix[3][2] = -2 * p_z_near * p_z_far / deltaZ; + matrix[3][3] = 0; +} + +void CameraMatrix::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist) { + if (p_flip_fov) { + p_fovy_degrees = get_fovy(p_fovy_degrees, 1.0 / p_aspect); + } + + real_t left, right, modeltranslation, ymax, xmax, frustumshift; + + ymax = p_z_near * tan(p_fovy_degrees * Math_PI / 360.0f); + xmax = ymax * p_aspect; + frustumshift = (p_intraocular_dist / 2.0) * p_z_near / p_convergence_dist; + + switch (p_eye) { + case 1: { // left eye + left = -xmax + frustumshift; + right = xmax + frustumshift; + modeltranslation = p_intraocular_dist / 2.0; + }; break; + case 2: { // right eye + left = -xmax - frustumshift; + right = xmax - frustumshift; + modeltranslation = -p_intraocular_dist / 2.0; + }; break; + default: { // mono, should give the same result as set_perspective(p_fovy_degrees,p_aspect,p_z_near,p_z_far,p_flip_fov) + left = -xmax; + right = xmax; + modeltranslation = 0.0; + }; break; + }; + + set_frustum(left, right, -ymax, ymax, p_z_near, p_z_far); + + // translate matrix by (modeltranslation, 0.0, 0.0) + CameraMatrix cm; + cm.set_identity(); + cm.matrix[3][0] = modeltranslation; + *this = *this * cm; +} + +void CameraMatrix::set_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far) { + // we first calculate our base frustum on our values without taking our lens magnification into account. + real_t f1 = (p_intraocular_dist * 0.5) / p_display_to_lens; + real_t f2 = ((p_display_width - p_intraocular_dist) * 0.5) / p_display_to_lens; + real_t f3 = (p_display_width / 4.0) / p_display_to_lens; + + // now we apply our oversample factor to increase our FOV. how much we oversample is always a balance we strike between performance and how much + // we're willing to sacrifice in FOV. + real_t add = ((f1 + f2) * (p_oversample - 1.0)) / 2.0; + f1 += add; + f2 += add; + f3 *= p_oversample; + + // always apply KEEP_WIDTH aspect ratio + f3 /= p_aspect; + + switch (p_eye) { + case 1: { // left eye + set_frustum(-f2 * p_z_near, f1 * p_z_near, -f3 * p_z_near, f3 * p_z_near, p_z_near, p_z_far); + }; break; + case 2: { // right eye + set_frustum(-f1 * p_z_near, f2 * p_z_near, -f3 * p_z_near, f3 * p_z_near, p_z_near, p_z_far); + }; break; + default: { // mono, does not apply here! + }; break; + }; +}; + +void CameraMatrix::set_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar) { + set_identity(); + + matrix[0][0] = 2.0 / (p_right - p_left); + matrix[3][0] = -((p_right + p_left) / (p_right - p_left)); + matrix[1][1] = 2.0 / (p_top - p_bottom); + matrix[3][1] = -((p_top + p_bottom) / (p_top - p_bottom)); + matrix[2][2] = -2.0 / (p_zfar - p_znear); + matrix[3][2] = -((p_zfar + p_znear) / (p_zfar - p_znear)); + matrix[3][3] = 1.0; +} + +void CameraMatrix::set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov) { + if (!p_flip_fov) { + p_size *= p_aspect; + } + + set_orthogonal(-p_size / 2, +p_size / 2, -p_size / p_aspect / 2, +p_size / p_aspect / 2, p_znear, p_zfar); +} + +void CameraMatrix::set_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far) { + ERR_FAIL_COND(p_right <= p_left); + ERR_FAIL_COND(p_top <= p_bottom); + ERR_FAIL_COND(p_far <= p_near); + + real_t *te = &matrix[0][0]; + real_t x = 2 * p_near / (p_right - p_left); + real_t y = 2 * p_near / (p_top - p_bottom); + + real_t a = (p_right + p_left) / (p_right - p_left); + real_t b = (p_top + p_bottom) / (p_top - p_bottom); + real_t c = -(p_far + p_near) / (p_far - p_near); + real_t d = -2 * p_far * p_near / (p_far - p_near); + + te[0] = x; + te[1] = 0; + te[2] = 0; + te[3] = 0; + te[4] = 0; + te[5] = y; + te[6] = 0; + te[7] = 0; + te[8] = a; + te[9] = b; + te[10] = c; + te[11] = -1; + te[12] = 0; + te[13] = 0; + te[14] = d; + te[15] = 0; +} + +void CameraMatrix::set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov) { + if (!p_flip_fov) { + p_size *= p_aspect; + } + + set_frustum(-p_size / 2 + p_offset.x, +p_size / 2 + p_offset.x, -p_size / p_aspect / 2 + p_offset.y, +p_size / p_aspect / 2 + p_offset.y, p_near, p_far); +} + +real_t CameraMatrix::get_z_far() const { + const real_t *matrix = (const real_t *)this->matrix; + Plane new_plane = Plane(matrix[3] - matrix[2], + matrix[7] - matrix[6], + matrix[11] - matrix[10], + matrix[15] - matrix[14]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + + return new_plane.d; +} +real_t CameraMatrix::get_z_near() const { + const real_t *matrix = (const real_t *)this->matrix; + Plane new_plane = Plane(matrix[3] + matrix[2], + matrix[7] + matrix[6], + matrix[11] + matrix[10], + -matrix[15] - matrix[14]); + + new_plane.normalize(); + return new_plane.d; +} + +Vector2 CameraMatrix::get_viewport_half_extents() const { + const real_t *matrix = (const real_t *)this->matrix; + ///////--- Near Plane ---/////// + Plane near_plane = Plane(matrix[3] + matrix[2], + matrix[7] + matrix[6], + matrix[11] + matrix[10], + -matrix[15] - matrix[14]); + near_plane.normalize(); + + ///////--- Right Plane ---/////// + Plane right_plane = Plane(matrix[3] - matrix[0], + matrix[7] - matrix[4], + matrix[11] - matrix[8], + -matrix[15] + matrix[12]); + right_plane.normalize(); + + Plane top_plane = Plane(matrix[3] - matrix[1], + matrix[7] - matrix[5], + matrix[11] - matrix[9], + -matrix[15] + matrix[13]); + top_plane.normalize(); + + Vector3 res; + near_plane.intersect_3(right_plane, top_plane, &res); + + return Vector2(res.x, res.y); +} + +bool CameraMatrix::get_endpoints(const Transform &p_transform, Vector3 *p_8points) const { + std::vector planes = get_projection_planes(Transform()); + const Planes intersections[8][3] = { + { PLANE_FAR, PLANE_LEFT, PLANE_TOP }, + { PLANE_FAR, PLANE_LEFT, PLANE_BOTTOM }, + { PLANE_FAR, PLANE_RIGHT, PLANE_TOP }, + { PLANE_FAR, PLANE_RIGHT, PLANE_BOTTOM }, + { PLANE_NEAR, PLANE_LEFT, PLANE_TOP }, + { PLANE_NEAR, PLANE_LEFT, PLANE_BOTTOM }, + { PLANE_NEAR, PLANE_RIGHT, PLANE_TOP }, + { PLANE_NEAR, PLANE_RIGHT, PLANE_BOTTOM }, + }; + + for (int i = 0; i < 8; i++) { + Vector3 point; + bool res = planes[intersections[i][0]].intersect_3(planes[intersections[i][1]], planes[intersections[i][2]], &point); + ERR_FAIL_COND_V(!res, false); + p_8points[i] = p_transform.xform(point); + } + + return true; +} + +std::vector CameraMatrix::get_projection_planes(const Transform &p_transform) const { + /** Fast Plane Extraction from combined modelview/projection matrices. + * References: + * https://web.archive.org/web/20011221205252/http://www.markmorley.com/opengl/frustumculling.html + * https://web.archive.org/web/20061020020112/http://www2.ravensoft.com/users/ggribb/plane%20extraction.pdf + */ + + std::vector planes; + + const real_t *matrix = (const real_t *)this->matrix; + + Plane new_plane; + + ///////--- Near Plane ---/////// + new_plane = Plane(matrix[3] + matrix[2], + matrix[7] + matrix[6], + matrix[11] + matrix[10], + matrix[15] + matrix[14]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + + planes.push_back(p_transform.xform(new_plane)); + + ///////--- Far Plane ---/////// + new_plane = Plane(matrix[3] - matrix[2], + matrix[7] - matrix[6], + matrix[11] - matrix[10], + matrix[15] - matrix[14]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + + planes.push_back(p_transform.xform(new_plane)); + + ///////--- Left Plane ---/////// + new_plane = Plane(matrix[3] + matrix[0], + matrix[7] + matrix[4], + matrix[11] + matrix[8], + matrix[15] + matrix[12]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + + planes.push_back(p_transform.xform(new_plane)); + + ///////--- Top Plane ---/////// + new_plane = Plane(matrix[3] - matrix[1], + matrix[7] - matrix[5], + matrix[11] - matrix[9], + matrix[15] - matrix[13]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + + planes.push_back(p_transform.xform(new_plane)); + + ///////--- Right Plane ---/////// + new_plane = Plane(matrix[3] - matrix[0], + matrix[7] - matrix[4], + matrix[11] - matrix[8], + matrix[15] - matrix[12]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + + planes.push_back(p_transform.xform(new_plane)); + + ///////--- Bottom Plane ---/////// + new_plane = Plane(matrix[3] + matrix[1], + matrix[7] + matrix[5], + matrix[11] + matrix[9], + matrix[15] + matrix[13]); + + new_plane.normal = -new_plane.normal; + new_plane.normalize(); + + planes.push_back(p_transform.xform(new_plane)); + + return planes; +} + +CameraMatrix CameraMatrix::inverse() const { + CameraMatrix cm = *this; + cm.invert(); + return cm; +} + +void CameraMatrix::invert() { + int i, j, k; + int pvt_i[4], pvt_j[4]; /* Locations of pivot matrix */ + real_t pvt_val; /* Value of current pivot element */ + real_t hold; /* Temporary storage */ + real_t determinat; /* Determinant */ + + determinat = 1.0; + for (k = 0; k < 4; k++) { + /** Locate k'th pivot element **/ + pvt_val = matrix[k][k]; /** Initialize for search **/ + pvt_i[k] = k; + pvt_j[k] = k; + for (i = k; i < 4; i++) { + for (j = k; j < 4; j++) { + if (absd(matrix[i][j]) > absd(pvt_val)) { + pvt_i[k] = i; + pvt_j[k] = j; + pvt_val = matrix[i][j]; + } + } + } + + /** Product of pivots, gives determinant when finished **/ + determinat *= pvt_val; + if (absd(determinat) < 1e-7) { + return; //(false); /** Matrix is singular (zero determinant). **/ + } + + /** "Interchange" rows (with sign change stuff) **/ + i = pvt_i[k]; + if (i != k) { /** If rows are different **/ + for (j = 0; j < 4; j++) { + hold = -matrix[k][j]; + matrix[k][j] = matrix[i][j]; + matrix[i][j] = hold; + } + } + + /** "Interchange" columns **/ + j = pvt_j[k]; + if (j != k) { /** If columns are different **/ + for (i = 0; i < 4; i++) { + hold = -matrix[i][k]; + matrix[i][k] = matrix[i][j]; + matrix[i][j] = hold; + } + } + + /** Divide column by minus pivot value **/ + for (i = 0; i < 4; i++) { + if (i != k) + matrix[i][k] /= (-pvt_val); + } + + /** Reduce the matrix **/ + for (i = 0; i < 4; i++) { + hold = matrix[i][k]; + for (j = 0; j < 4; j++) { + if (i != k && j != k) + matrix[i][j] += hold * matrix[k][j]; + } + } + + /** Divide row by pivot **/ + for (j = 0; j < 4; j++) { + if (j != k) + matrix[k][j] /= pvt_val; + } + + /** Replace pivot by reciprocal (at last we can touch it). **/ + matrix[k][k] = 1.0 / pvt_val; + } + + /* That was most of the work, one final pass of row/column interchange */ + /* to finish */ + for (k = 4 - 2; k >= 0; k--) { /* Don't need to work with 1 by 1 corner*/ + i = pvt_j[k]; /* Rows to swap correspond to pivot COLUMN */ + if (i != k) { /* If rows are different */ + for (j = 0; j < 4; j++) { + hold = matrix[k][j]; + matrix[k][j] = -matrix[i][j]; + matrix[i][j] = hold; + } + } + + j = pvt_i[k]; /* Columns to swap correspond to pivot ROW */ + if (j != k) /* If columns are different */ + for (i = 0; i < 4; i++) { + hold = matrix[i][k]; + matrix[i][k] = -matrix[i][j]; + matrix[i][j] = hold; + } + } +} + +CameraMatrix::CameraMatrix() { + set_identity(); +} + +CameraMatrix CameraMatrix::operator*(const CameraMatrix &p_matrix) const { + CameraMatrix new_matrix; + + for (int j = 0; j < 4; j++) { + for (int i = 0; i < 4; i++) { + real_t ab = 0; + for (int k = 0; k < 4; k++) + ab += matrix[k][i] * p_matrix.matrix[j][k]; + new_matrix.matrix[j][i] = ab; + } + } + + return new_matrix; +} + +void CameraMatrix::set_light_bias() { + real_t *m = &matrix[0][0]; + + m[0] = 0.5; + m[1] = 0.0; + m[2] = 0.0; + m[3] = 0.0; + m[4] = 0.0; + m[5] = 0.5; + m[6] = 0.0; + m[7] = 0.0; + m[8] = 0.0; + m[9] = 0.0; + m[10] = 0.5; + m[11] = 0.0; + m[12] = 0.5; + m[13] = 0.5; + m[14] = 0.5; + m[15] = 1.0; +} + +void CameraMatrix::set_light_atlas_rect(const Rect2 &p_rect) { + real_t *m = &matrix[0][0]; + + m[0] = p_rect.size.width; + m[1] = 0.0; + m[2] = 0.0; + m[3] = 0.0; + m[4] = 0.0; + m[5] = p_rect.size.height; + m[6] = 0.0; + m[7] = 0.0; + m[8] = 0.0; + m[9] = 0.0; + m[10] = 1.0; + m[11] = 0.0; + m[12] = p_rect.position.x; + m[13] = p_rect.position.y; + m[14] = 0.0; + m[15] = 1.0; +} + +CameraMatrix::operator String() const { + String str; + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + str += String((j > 0) ? ", " : "\n") + String::num(matrix[i][j]); + + return str; +} + +real_t CameraMatrix::get_aspect() const { + Vector2 vp_he = get_viewport_half_extents(); + return vp_he.x / vp_he.y; +} + +int CameraMatrix::get_pixels_per_meter(int p_for_pixel_width) const { + Vector3 result = xform(Vector3(1, 0, -1)); + + return int((result.x * 0.5 + 0.5) * p_for_pixel_width); +} + +bool CameraMatrix::is_orthogonal() const { + return matrix[3][3] == 1.0; +} + +real_t CameraMatrix::get_fov() const { + const real_t *matrix = (const real_t *)this->matrix; + + Plane right_plane = Plane(matrix[3] - matrix[0], + matrix[7] - matrix[4], + matrix[11] - matrix[8], + -matrix[15] + matrix[12]); + right_plane.normalize(); + + if ((matrix[8] == 0) && (matrix[9] == 0)) { + return Math::rad2deg(acos(std::abs(right_plane.normal.x))) * 2.0; + } else { + // our frustum is asymmetrical need to calculate the left planes angle separately.. + Plane left_plane = Plane(matrix[3] + matrix[0], + matrix[7] + matrix[4], + matrix[11] + matrix[8], + matrix[15] + matrix[12]); + left_plane.normalize(); + + return Math::rad2deg(acos(std::abs(left_plane.normal.x))) + Math::rad2deg(acos(std::abs(right_plane.normal.x))); + } +} + +void CameraMatrix::make_scale(const Vector3 &p_scale) { + set_identity(); + matrix[0][0] = p_scale.x; + matrix[1][1] = p_scale.y; + matrix[2][2] = p_scale.z; +} + +void CameraMatrix::scale_translate_to_fit(const AABB &p_aabb) { + Vector3 min = p_aabb.position; + Vector3 max = p_aabb.position + p_aabb.size; + + matrix[0][0] = 2 / (max.x - min.x); + matrix[1][0] = 0; + matrix[2][0] = 0; + matrix[3][0] = -(max.x + min.x) / (max.x - min.x); + + matrix[0][1] = 0; + matrix[1][1] = 2 / (max.y - min.y); + matrix[2][1] = 0; + matrix[3][1] = -(max.y + min.y) / (max.y - min.y); + + matrix[0][2] = 0; + matrix[1][2] = 0; + matrix[2][2] = 2 / (max.z - min.z); + matrix[3][2] = -(max.z + min.z) / (max.z - min.z); + + matrix[0][3] = 0; + matrix[1][3] = 0; + matrix[2][3] = 0; + matrix[3][3] = 1; +} + +CameraMatrix::operator Transform() const { + Transform tr; + const real_t *m = &matrix[0][0]; + + tr.basis.elements[0][0] = m[0]; + tr.basis.elements[1][0] = m[1]; + tr.basis.elements[2][0] = m[2]; + + tr.basis.elements[0][1] = m[4]; + tr.basis.elements[1][1] = m[5]; + tr.basis.elements[2][1] = m[6]; + + tr.basis.elements[0][2] = m[8]; + tr.basis.elements[1][2] = m[9]; + tr.basis.elements[2][2] = m[10]; + + tr.origin.x = m[12]; + tr.origin.y = m[13]; + tr.origin.z = m[14]; + + return tr; +} + +CameraMatrix::CameraMatrix(const Transform &p_transform) { + const Transform &tr = p_transform; + real_t *m = &matrix[0][0]; + + m[0] = tr.basis.elements[0][0]; + m[1] = tr.basis.elements[1][0]; + m[2] = tr.basis.elements[2][0]; + m[3] = 0.0; + m[4] = tr.basis.elements[0][1]; + m[5] = tr.basis.elements[1][1]; + m[6] = tr.basis.elements[2][1]; + m[7] = 0.0; + m[8] = tr.basis.elements[0][2]; + m[9] = tr.basis.elements[1][2]; + m[10] = tr.basis.elements[2][2]; + m[11] = 0.0; + m[12] = tr.origin.x; + m[13] = tr.origin.y; + m[14] = tr.origin.z; + m[15] = 1.0; +} + +CameraMatrix::~CameraMatrix() { +} diff --git a/src/core/Color.cpp b/src/core/Color.cpp new file mode 100644 index 0000000..e0d4772 --- /dev/null +++ b/src/core/Color.cpp @@ -0,0 +1,655 @@ +/*************************************************************************/ +/* Color.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "Color.hpp" +#include "Defs.hpp" +#include "String.hpp" + +#include +#include + +namespace godot { + +#define MIN(a, b) (a < b ? a : b) +#define MAX(a, b) (a > b ? a : b) + +static String _to_hex(float p_val); + +static float _parse_col(const String &p_str, int p_ofs) { + int ig = 0; + + for (int i = 0; i < 2; i++) { + int c = (int)(wchar_t)p_str[i + p_ofs]; + int v = 0; + + if (c >= '0' && c <= '9') { + v = c - '0'; + } else if (c >= 'a' && c <= 'f') { + v = c - 'a'; + v += 10; + } else if (c >= 'A' && c <= 'F') { + v = c - 'A'; + v += 10; + } else { + return -1; + } + + if (i == 0) + ig += v * 16; + else + ig += v; + } + + return ig; +} + +uint32_t Color::to_32() const { + uint32_t c = (uint8_t)(a * 255); + c <<= 8; + c |= (uint8_t)(r * 255); + c <<= 8; + c |= (uint8_t)(g * 255); + c <<= 8; + c |= (uint8_t)(b * 255); + + return c; +} + +uint32_t Color::to_ARGB32() const { + uint32_t c = (uint8_t)(a * 255); + c <<= 8; + c |= (uint8_t)(r * 255); + c <<= 8; + c |= (uint8_t)(g * 255); + c <<= 8; + c |= (uint8_t)(b * 255); + + return c; +} + +uint32_t Color::to_ABGR32() const { + uint32_t c = (uint8_t)(a * 255); + c <<= 8; + c |= (uint8_t)(b * 255); + c <<= 8; + c |= (uint8_t)(g * 255); + c <<= 8; + c |= (uint8_t)(r * 255); + + return c; +} + +uint64_t Color::to_ABGR64() const { + uint64_t c = (uint16_t)(a * 65535); + c <<= 16; + c |= (uint16_t)(b * 65535); + c <<= 16; + c |= (uint16_t)(g * 65535); + c <<= 16; + c |= (uint16_t)(r * 65535); + + return c; +} + +uint64_t Color::to_ARGB64() const { + uint64_t c = (uint16_t)(a * 65535); + c <<= 16; + c |= (uint16_t)(r * 65535); + c <<= 16; + c |= (uint16_t)(g * 65535); + c <<= 16; + c |= (uint16_t)(b * 65535); + + return c; +} + +uint32_t Color::to_RGBA32() const { + uint32_t c = (uint8_t)(r * 255); + c <<= 8; + c |= (uint8_t)(g * 255); + c <<= 8; + c |= (uint8_t)(b * 255); + c <<= 8; + c |= (uint8_t)(a * 255); + + return c; +} + +uint64_t Color::to_RGBA64() const { + uint64_t c = (uint16_t)(r * 65535); + c <<= 16; + c |= (uint16_t)(g * 65535); + c <<= 16; + c |= (uint16_t)(b * 65535); + c <<= 16; + c |= (uint16_t)(a * 65535); + + return c; +} + +float Color::gray() const { + return (r + g + b) / 3.0; +} + +uint8_t Color::get_r8() const { + return (uint8_t)(r * 255.0); +} + +uint8_t Color::get_g8() const { + return (uint8_t)(g * 255.0); +} + +uint8_t Color::get_b8() const { + return (uint8_t)(b * 255.0); +} + +uint8_t Color::get_a8() const { + return (uint8_t)(a * 255.0); +} + +float Color::get_h() const { + float min = MIN(r, g); + min = MIN(min, b); + float max = MAX(r, g); + max = MAX(max, b); + + float delta = max - min; + + if (delta == 0) + return 0; + + float h; + if (r == max) + h = (g - b) / delta; // between yellow & magenta + else if (g == max) + h = 2 + (b - r) / delta; // between cyan & yellow + else + h = 4 + (r - g) / delta; // between magenta & cyan + + h /= 6.0; + if (h < 0) + h += 1.0; + + return h; +} + +float Color::get_s() const { + float min = MIN(r, g); + min = MIN(min, b); + float max = MAX(r, g); + max = MAX(max, b); + float delta = max - min; + return (max != 0) ? (delta / max) : 0; +} + +float Color::get_v() const { + float max = MAX(r, g); + max = MAX(max, b); + return max; +} + +void Color::set_hsv(float p_h, float p_s, float p_v, float p_alpha) { + int i; + float f, p, q, t; + a = p_alpha; + + if (p_s == 0) { + // acp_hromatic (grey) + r = g = b = p_v; + return; + } + + p_h *= 6.0; + p_h = ::fmod(p_h, 6); + i = ::floor(p_h); + + f = p_h - i; + p = p_v * (1 - p_s); + q = p_v * (1 - p_s * f); + t = p_v * (1 - p_s * (1 - f)); + + switch (i) { + case 0: // Red is the dominant color + r = p_v; + g = t; + b = p; + break; + case 1: // Green is the dominant color + r = q; + g = p_v; + b = p; + break; + case 2: + r = p; + g = p_v; + b = t; + break; + case 3: // Blue is the dominant color + r = p; + g = q; + b = p_v; + break; + case 4: + r = t; + g = p; + b = p_v; + break; + default: // (5) Red is the dominant color + r = p_v; + g = p; + b = q; + break; + } +} + +Color Color::darkened(const float p_amount) const { + Color res = *this; + res.r = res.r * (1.0f - p_amount); + res.g = res.g * (1.0f - p_amount); + res.b = res.b * (1.0f - p_amount); + return res; +} + +Color Color::lightened(const float p_amount) const { + Color res = *this; + res.r = res.r + (1.0f - res.r) * p_amount; + res.g = res.g + (1.0f - res.g) * p_amount; + res.b = res.b + (1.0f - res.b) * p_amount; + return res; +} + +Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) const { + p_h = ::fmod(p_h * 360.0f, 360.0f); + if (p_h < 0.0) + p_h += 360.0f; + + const float h_ = p_h / 60.0f; + const float c = p_v * p_s; + const float x = c * (1.0f - ::fabs(::fmod(h_, 2.0f) - 1.0f)); + float r, g, b; + + switch ((int)h_) { + case 0: { + r = c; + g = x; + b = 0; + } break; + case 1: { + r = x; + g = c; + b = 0; + } break; + case 2: { + r = 0; + g = c; + b = x; + } break; + case 3: { + r = 0; + g = x; + b = c; + } break; + case 4: { + r = x; + g = 0; + b = c; + } break; + case 5: { + r = c; + g = 0; + b = x; + } break; + default: { + r = 0; + g = 0; + b = 0; + } break; + } + + const float m = p_v - c; + return Color(m + r, m + g, m + b, p_a); +} + +void Color::invert() { + r = 1.0 - r; + g = 1.0 - g; + b = 1.0 - b; +} + +void Color::contrast() { + r = ::fmod(r + 0.5, 1.0); + g = ::fmod(g + 0.5, 1.0); + b = ::fmod(b + 0.5, 1.0); +} +Color Color::inverted() const { + Color c = *this; + c.invert(); + return c; +} +Color Color::contrasted() const { + Color c = *this; + c.contrast(); + return c; +} + +Color Color::linear_interpolate(const Color &p_b, float p_t) const { + Color res = *this; + + res.r += (p_t * (p_b.r - r)); + res.g += (p_t * (p_b.g - g)); + res.b += (p_t * (p_b.b - b)); + res.a += (p_t * (p_b.a - a)); + + return res; +} + +Color Color::blend(const Color &p_over) const { + Color res; + float sa = 1.0 - p_over.a; + res.a = a * sa + p_over.a; + if (res.a == 0) { + return Color(0, 0, 0, 0); + } else { + res.r = (r * a * sa + p_over.r * p_over.a) / res.a; + res.g = (g * a * sa + p_over.g * p_over.a) / res.a; + res.b = (b * a * sa + p_over.b * p_over.a) / res.a; + } + return res; +} + +Color Color::to_linear() const { + return Color( + r < 0.04045 ? r * (1.0 / 12.92) : ::pow((r + 0.055) * (1.0 / (1 + 0.055)), 2.4), + g < 0.04045 ? g * (1.0 / 12.92) : ::pow((g + 0.055) * (1.0 / (1 + 0.055)), 2.4), + b < 0.04045 ? b * (1.0 / 12.92) : ::pow((b + 0.055) * (1.0 / (1 + 0.055)), 2.4), + a); +} + +Color Color::hex(uint32_t p_hex) { + float a = (p_hex & 0xFF) / 255.0; + p_hex >>= 8; + float b = (p_hex & 0xFF) / 255.0; + p_hex >>= 8; + float g = (p_hex & 0xFF) / 255.0; + p_hex >>= 8; + float r = (p_hex & 0xFF) / 255.0; + + return Color(r, g, b, a); +} + +Color Color::html(const String &p_color) { + String color = p_color; + if (color.length() == 0) + return Color(); + if (color[0] == '#') + color = color.substr(1, color.length() - 1); + + bool alpha = false; + + if (color.length() == 8) { + alpha = true; + } else if (color.length() == 6) { + alpha = false; + } else { + ERR_PRINTS(String("Invalid Color Code: ") + p_color); + ERR_FAIL_V(Color()); + } + + int a = 255; + if (alpha) { + a = _parse_col(color, 0); + if (a < 0) { + ERR_PRINTS(String("Invalid Color Code: ") + p_color); + ERR_FAIL_V(Color()); + } + } + + int from = alpha ? 2 : 0; + + int r = _parse_col(color, from + 0); + if (r < 0) { + ERR_PRINTS(String("Invalid Color Code: ") + p_color); + ERR_FAIL_V(Color()); + } + int g = _parse_col(color, from + 2); + if (g < 0) { + ERR_PRINTS(String("Invalid Color Code: ") + p_color); + ERR_FAIL_V(Color()); + } + int b = _parse_col(color, from + 4); + if (b < 0) { + ERR_PRINTS(String("Invalid Color Code: ") + p_color); + ERR_FAIL_V(Color()); + } + + return Color(r / 255.0, g / 255.0, b / 255.0, a / 255.0); +} + +bool Color::html_is_valid(const String &p_color) { + String color = p_color; + + if (color.length() == 0) + return false; + if (color[0] == '#') + color = color.substr(1, color.length() - 1); + + bool alpha = false; + + if (color.length() == 8) { + alpha = true; + } else if (color.length() == 6) { + alpha = false; + } else { + return false; + } + + int a = 255; + if (alpha) { + a = _parse_col(color, 0); + if (a < 0) { + return false; + } + } + + int from = alpha ? 2 : 0; + + int r = _parse_col(color, from + 0); + if (r < 0) { + return false; + } + int g = _parse_col(color, from + 2); + if (g < 0) { + return false; + } + int b = _parse_col(color, from + 4); + if (b < 0) { + return false; + } + + return true; +} + +#ifndef CLAMP +#define CLAMP(m_a, m_min, m_max) (((m_a) < (m_min)) ? (m_min) : (((m_a) > (m_max)) ? m_max : m_a)) +#endif +static String _to_hex(float p_val) { + int v = p_val * 255; + v = CLAMP(v, 0, 255); + String ret; + + for (int i = 0; i < 2; i++) { + wchar_t c[2] = { 0, 0 }; + int lv = v & 0xF; + if (lv < 10) + c[0] = '0' + lv; + else + c[0] = 'a' + lv - 10; + + v >>= 4; + String cs = (const wchar_t *)c; + ret = cs + ret; + } + + return ret; +} + +String Color::to_html(bool p_alpha) const { + String txt; + txt += _to_hex(r); + txt += _to_hex(g); + txt += _to_hex(b); + if (p_alpha) + txt = _to_hex(a) + txt; + return txt; +} + +Color::operator String() const { + return String::num(r) + ", " + String::num(g) + ", " + String::num(b) + ", " + String::num(a); +} + +bool Color::operator<(const Color &p_color) const { + if (r == p_color.r) { + if (g == p_color.g) { + if (b == p_color.b) { + return (a < p_color.a); + } else + return (b < p_color.b); + } else + return g < p_color.g; + } else + return r < p_color.r; +} + +Color Color::operator+(const Color &p_color) const { + return Color( + r + p_color.r, + g + p_color.g, + b + p_color.b, + a + p_color.a); +} + +void Color::operator+=(const Color &p_color) { + r = r + p_color.r; + g = g + p_color.g; + b = b + p_color.b; + a = a + p_color.a; +} + +Color Color::operator-(const Color &p_color) const { + return Color( + r - p_color.r, + g - p_color.g, + b - p_color.b, + a - p_color.a); +} + +void Color::operator-=(const Color &p_color) { + r = r - p_color.r; + g = g - p_color.g; + b = b - p_color.b; + a = a - p_color.a; +} + +Color Color::operator*(const Color &p_color) const { + return Color( + r * p_color.r, + g * p_color.g, + b * p_color.b, + a * p_color.a); +} + +Color Color::operator*(const real_t &rvalue) const { + return Color( + r * rvalue, + g * rvalue, + b * rvalue, + a * rvalue); +} + +void Color::operator*=(const Color &p_color) { + r = r * p_color.r; + g = g * p_color.g; + b = b * p_color.b; + a = a * p_color.a; +} + +void Color::operator*=(const real_t &rvalue) { + r = r * rvalue; + g = g * rvalue; + b = b * rvalue; + a = a * rvalue; +} + +Color Color::operator/(const Color &p_color) const { + return Color( + r / p_color.r, + g / p_color.g, + b / p_color.b, + a / p_color.a); +} + +Color Color::operator/(const real_t &rvalue) const { + return Color( + r / rvalue, + g / rvalue, + b / rvalue, + a / rvalue); +} + +void Color::operator/=(const Color &p_color) { + r = r / p_color.r; + g = g / p_color.g; + b = b / p_color.b; + a = a / p_color.a; +} + +void Color::operator/=(const real_t &rvalue) { + if (rvalue == 0) { + r = 1.0; + g = 1.0; + b = 1.0; + a = 1.0; + } else { + r = r / rvalue; + g = g / rvalue; + b = b / rvalue; + a = a / rvalue; + } +} + +Color Color::operator-() const { + return Color( + 1.0 - r, + 1.0 - g, + 1.0 - b, + 1.0 - a); +} + +} // namespace godot diff --git a/src/core/Dictionary.cpp b/src/core/Dictionary.cpp new file mode 100644 index 0000000..95e89fb --- /dev/null +++ b/src/core/Dictionary.cpp @@ -0,0 +1,110 @@ +/*************************************************************************/ +/* Dictionary.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "Dictionary.hpp" +#include "Array.hpp" +#include "GodotGlobal.hpp" +#include "Variant.hpp" + +namespace godot { + +Dictionary::Dictionary() { + godot::api->godot_dictionary_new(&_godot_dictionary); +} + +Dictionary::Dictionary(const Dictionary &other) { + godot::api->godot_dictionary_new_copy(&_godot_dictionary, &other._godot_dictionary); +} + +Dictionary &Dictionary::operator=(const Dictionary &other) { + godot::api->godot_dictionary_destroy(&_godot_dictionary); + godot::api->godot_dictionary_new_copy(&_godot_dictionary, &other._godot_dictionary); + return *this; +} + +void Dictionary::clear() { + godot::api->godot_dictionary_clear(&_godot_dictionary); +} + +bool Dictionary::empty() const { + return godot::api->godot_dictionary_empty(&_godot_dictionary); +} + +void Dictionary::erase(const Variant &key) { + godot::api->godot_dictionary_erase(&_godot_dictionary, (godot_variant *)&key); +} + +bool Dictionary::has(const Variant &key) const { + return godot::api->godot_dictionary_has(&_godot_dictionary, (godot_variant *)&key); +} + +bool Dictionary::has_all(const Array &keys) const { + return godot::api->godot_dictionary_has_all(&_godot_dictionary, (godot_array *)&keys); +} + +uint32_t Dictionary::hash() const { + return godot::api->godot_dictionary_hash(&_godot_dictionary); +} + +Array Dictionary::keys() const { + godot_array a = godot::api->godot_dictionary_keys(&_godot_dictionary); + return Array(a); +} + +Variant &Dictionary::operator[](const Variant &key) { + godot_variant *v = godot::api->godot_dictionary_operator_index(&_godot_dictionary, (godot_variant *)&key); + return *reinterpret_cast(v); +} + +const Variant &Dictionary::operator[](const Variant &key) const { + // oops I did it again + godot_variant *v = godot::api->godot_dictionary_operator_index((godot_dictionary *)&_godot_dictionary, (godot_variant *)&key); + return *reinterpret_cast(v); +} + +int Dictionary::size() const { + return godot::api->godot_dictionary_size(&_godot_dictionary); +} + +String Dictionary::to_json() const { + godot_string s = godot::api->godot_dictionary_to_json(&_godot_dictionary); + return String(s); +} + +Array Dictionary::values() const { + godot_array a = godot::api->godot_dictionary_values(&_godot_dictionary); + return Array(a); +} + +Dictionary::~Dictionary() { + godot::api->godot_dictionary_destroy(&_godot_dictionary); +} + +} // namespace godot diff --git a/src/core/GodotGlobal.cpp b/src/core/GodotGlobal.cpp new file mode 100644 index 0000000..511ed75 --- /dev/null +++ b/src/core/GodotGlobal.cpp @@ -0,0 +1,208 @@ +/*************************************************************************/ +/* GodotGlobal.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "GodotGlobal.hpp" + +#include "String.hpp" + +#include "Wrapped.hpp" + +static GDCALLINGCONV void *wrapper_create(void *data, const void *type_tag, godot_object *instance) { + godot::_Wrapped *wrapper_memory = (godot::_Wrapped *)godot::api->godot_alloc(sizeof(godot::_Wrapped)); + + if (!wrapper_memory) + return NULL; + wrapper_memory->_owner = instance; + wrapper_memory->_type_tag = (size_t)type_tag; + + return (void *)wrapper_memory; +} + +static GDCALLINGCONV void wrapper_destroy(void *data, void *wrapper) { + if (wrapper) + godot::api->godot_free(wrapper); +} + +namespace godot { + +void *_RegisterState::nativescript_handle; +int _RegisterState::language_index; + +const godot_gdnative_core_api_struct *api = nullptr; +const godot_gdnative_core_1_1_api_struct *core_1_1_api = nullptr; +const godot_gdnative_core_1_2_api_struct *core_1_2_api = nullptr; + +const godot_gdnative_ext_nativescript_api_struct *nativescript_api = nullptr; +const godot_gdnative_ext_nativescript_1_1_api_struct *nativescript_1_1_api = nullptr; +const godot_gdnative_ext_pluginscript_api_struct *pluginscript_api = nullptr; +const godot_gdnative_ext_android_api_struct *android_api = nullptr; +const godot_gdnative_ext_arvr_api_struct *arvr_api = nullptr; +const godot_gdnative_ext_videodecoder_api_struct *videodecoder_api = nullptr; +const godot_gdnative_ext_net_api_struct *net_api = nullptr; +const godot_gdnative_ext_net_3_2_api_struct *net_3_2_api = nullptr; + +const void *gdnlib = NULL; + +void Godot::print(const String &message) { + godot::api->godot_print((godot_string *)&message); +} + +void Godot::print_warning(const String &description, const String &function, const String &file, int line) { + int len; + + char *c_desc = description.alloc_c_string(); + char *c_func = function.alloc_c_string(); + char *c_file = file.alloc_c_string(); + + if (c_desc != nullptr && c_func != nullptr && c_file != nullptr) { + godot::api->godot_print_warning(c_desc, c_func, c_file, line); + }; + + if (c_desc != nullptr) + godot::api->godot_free(c_desc); + if (c_func != nullptr) + godot::api->godot_free(c_func); + if (c_file != nullptr) + godot::api->godot_free(c_file); +} + +void Godot::print_error(const String &description, const String &function, const String &file, int line) { + int len; + + char *c_desc = description.alloc_c_string(); + char *c_func = function.alloc_c_string(); + char *c_file = file.alloc_c_string(); + + if (c_desc != nullptr && c_func != nullptr && c_file != nullptr) { + godot::api->godot_print_error(c_desc, c_func, c_file, line); + }; + + if (c_desc != nullptr) + godot::api->godot_free(c_desc); + if (c_func != nullptr) + godot::api->godot_free(c_func); + if (c_file != nullptr) + godot::api->godot_free(c_file); +} + +void ___register_types(); +void ___init_method_bindings(); + +void Godot::gdnative_init(godot_gdnative_init_options *options) { + godot::api = options->api_struct; + godot::gdnlib = options->gd_native_library; + + const godot_gdnative_api_struct *core_extension = godot::api->next; + + while (core_extension) { + if (core_extension->version.major == 1 && core_extension->version.minor == 1) { + godot::core_1_1_api = (const godot_gdnative_core_1_1_api_struct *)core_extension; + } else if (core_extension->version.major == 1 && core_extension->version.minor == 2) { + godot::core_1_2_api = (const godot_gdnative_core_1_2_api_struct *)core_extension; + } + core_extension = core_extension->next; + } + + // now find our extensions + for (int i = 0; i < godot::api->num_extensions; i++) { + switch (godot::api->extensions[i]->type) { + case GDNATIVE_EXT_NATIVESCRIPT: { + godot::nativescript_api = (const godot_gdnative_ext_nativescript_api_struct *)godot::api->extensions[i]; + + const godot_gdnative_api_struct *extension = godot::nativescript_api->next; + + while (extension) { + if (extension->version.major == 1 && extension->version.minor == 1) { + godot::nativescript_1_1_api = (const godot_gdnative_ext_nativescript_1_1_api_struct *)extension; + } + + extension = extension->next; + } + } break; + case GDNATIVE_EXT_PLUGINSCRIPT: { + godot::pluginscript_api = (const godot_gdnative_ext_pluginscript_api_struct *)godot::api->extensions[i]; + } break; + case GDNATIVE_EXT_ANDROID: { + godot::android_api = (const godot_gdnative_ext_android_api_struct *)godot::api->extensions[i]; + } break; + case GDNATIVE_EXT_ARVR: { + godot::arvr_api = (const godot_gdnative_ext_arvr_api_struct *)godot::api->extensions[i]; + } break; + case GDNATIVE_EXT_VIDEODECODER: { + godot::videodecoder_api = (const godot_gdnative_ext_videodecoder_api_struct *)godot::api->extensions[i]; + } break; + case GDNATIVE_EXT_NET: { + godot::net_api = (const godot_gdnative_ext_net_api_struct *)godot::api->extensions[i]; + + const godot_gdnative_api_struct *extension = godot::net_api->next; + + while (extension) { + if (extension->version.major == 3 && extension->version.minor == 2) { + godot::net_3_2_api = (const godot_gdnative_ext_net_3_2_api_struct *)extension; + } + + extension = extension->next; + } + } break; + + default: + break; + } + } + + // Initialize the `language_index` here since `__register_types()` makes use of it. + godot_instance_binding_functions binding_funcs = {}; + binding_funcs.alloc_instance_binding_data = wrapper_create; + binding_funcs.free_instance_binding_data = wrapper_destroy; + + godot::_RegisterState::language_index = godot::nativescript_1_1_api->godot_nativescript_register_instance_binding_data_functions(binding_funcs); + + // register these now + ___register_types(); + ___init_method_bindings(); +} + +void Godot::gdnative_terminate(godot_gdnative_terminate_options *options) { + // reserved for future use. +} + +void Godot::gdnative_profiling_add_data(const char *p_signature, uint64_t p_time) { + godot::nativescript_1_1_api->godot_nativescript_profiling_add_data(p_signature, p_time); +} + +void Godot::nativescript_init(void *handle) { + godot::_RegisterState::nativescript_handle = handle; +} + +void Godot::nativescript_terminate(void *handle) { + godot::nativescript_1_1_api->godot_nativescript_unregister_instance_binding_data_functions(godot::_RegisterState::language_index); +} + +} // namespace godot diff --git a/src/core/NodePath.cpp b/src/core/NodePath.cpp new file mode 100644 index 0000000..3d291e3 --- /dev/null +++ b/src/core/NodePath.cpp @@ -0,0 +1,114 @@ +/*************************************************************************/ +/* NodePath.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "NodePath.hpp" +#include "GodotGlobal.hpp" +#include "String.hpp" + +#include + +namespace godot { + +NodePath::NodePath() { + String from = ""; + godot::api->godot_node_path_new(&_node_path, (godot_string *)&from); +} + +NodePath::NodePath(const NodePath &other) { + String from = other; + godot::api->godot_node_path_new(&_node_path, (godot_string *)&from); +} + +NodePath::NodePath(const String &from) { + godot::api->godot_node_path_new(&_node_path, (godot_string *)&from); +} + +NodePath::NodePath(const char *contents) { + String from = contents; + godot::api->godot_node_path_new(&_node_path, (godot_string *)&from); +} + +String NodePath::get_name(const int idx) const { + godot_string str = godot::api->godot_node_path_get_name(&_node_path, idx); + return String(str); +} + +int NodePath::get_name_count() const { + return godot::api->godot_node_path_get_name_count(&_node_path); +} + +String NodePath::get_subname(const int idx) const { + godot_string str = godot::api->godot_node_path_get_subname(&_node_path, idx); + return String(str); +} + +int NodePath::get_subname_count() const { + return godot::api->godot_node_path_get_subname_count(&_node_path); +} + +bool NodePath::is_absolute() const { + return godot::api->godot_node_path_is_absolute(&_node_path); +} + +bool NodePath::is_empty() const { + return godot::api->godot_node_path_is_empty(&_node_path); +} + +NodePath NodePath::get_as_property_path() const { + godot_node_path path = godot::core_1_1_api->godot_node_path_get_as_property_path(&_node_path); + return NodePath(path); +} +String NodePath::get_concatenated_subnames() const { + godot_string str = godot::api->godot_node_path_get_concatenated_subnames(&_node_path); + return String(str); +} + +NodePath::operator String() const { + godot_string str = godot::api->godot_node_path_as_string(&_node_path); + return String(str); +} + +bool NodePath::operator==(const NodePath &other) { + return godot::api->godot_node_path_operator_equal(&_node_path, &other._node_path); +} + +void NodePath::operator=(const NodePath &other) { + godot::api->godot_node_path_destroy(&_node_path); + + String other_string = (String)other; + + godot::api->godot_node_path_new(&_node_path, (godot_string *)&other_string); +} + +NodePath::~NodePath() { + godot::api->godot_node_path_destroy(&_node_path); +} + +} // namespace godot diff --git a/src/core/Plane.cpp b/src/core/Plane.cpp new file mode 100644 index 0000000..8fe51a7 --- /dev/null +++ b/src/core/Plane.cpp @@ -0,0 +1,203 @@ +/*************************************************************************/ +/* Plane.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "Plane.hpp" +#include "Vector3.hpp" + +#include + +namespace godot { + +void Plane::set_normal(const Vector3 &p_normal) { + this->normal = p_normal; +} + +Vector3 Plane::project(const Vector3 &p_point) const { + return p_point - normal * distance_to(p_point); +} + +void Plane::normalize() { + real_t l = normal.length(); + if (l == 0) { + *this = Plane(0, 0, 0, 0); + return; + } + normal /= l; + d /= l; +} + +Plane Plane::normalized() const { + Plane p = *this; + p.normalize(); + return p; +} + +Vector3 Plane::get_any_point() const { + return get_normal() * d; +} + +Vector3 Plane::get_any_perpendicular_normal() const { + static const Vector3 p1 = Vector3(1, 0, 0); + static const Vector3 p2 = Vector3(0, 1, 0); + Vector3 p; + + if (::fabs(normal.dot(p1)) > 0.99) // if too similar to p1 + p = p2; // use p2 + else + p = p1; // use p1 + + p -= normal * normal.dot(p); + p.normalize(); + + return p; +} + +/* intersections */ + +bool Plane::intersect_3(const Plane &p_plane1, const Plane &p_plane2, Vector3 *r_result) const { + const Plane &p_plane0 = *this; + Vector3 normal0 = p_plane0.normal; + Vector3 normal1 = p_plane1.normal; + Vector3 normal2 = p_plane2.normal; + + real_t denom = vec3_cross(normal0, normal1).dot(normal2); + + if (::fabs(denom) <= CMP_EPSILON) + return false; + + if (r_result) { + *r_result = ((vec3_cross(normal1, normal2) * p_plane0.d) + + (vec3_cross(normal2, normal0) * p_plane1.d) + + (vec3_cross(normal0, normal1) * p_plane2.d)) / + denom; + } + + return true; +} + +bool Plane::intersects_ray(Vector3 p_from, Vector3 p_dir, Vector3 *p_intersection) const { + Vector3 segment = p_dir; + real_t den = normal.dot(segment); + + //printf("den is %i\n",den); + if (::fabs(den) <= CMP_EPSILON) { + return false; + } + + real_t dist = (normal.dot(p_from) - d) / den; + //printf("dist is %i\n",dist); + + if (dist > CMP_EPSILON) { //this is a ray, before the emiting pos (p_from) doesnt exist + + return false; + } + + dist = -dist; + *p_intersection = p_from + segment * dist; + + return true; +} + +bool Plane::intersects_segment(Vector3 p_begin, Vector3 p_end, Vector3 *p_intersection) const { + Vector3 segment = p_begin - p_end; + real_t den = normal.dot(segment); + + //printf("den is %i\n",den); + if (::fabs(den) <= CMP_EPSILON) { + return false; + } + + real_t dist = (normal.dot(p_begin) - d) / den; + //printf("dist is %i\n",dist); + + if (dist < -CMP_EPSILON || dist > (1.0 + CMP_EPSILON)) { + return false; + } + + dist = -dist; + *p_intersection = p_begin + segment * dist; + + return true; +} + +/* misc */ + +bool Plane::is_almost_like(const Plane &p_plane) const { + return (normal.dot(p_plane.normal) > _PLANE_EQ_DOT_EPSILON && ::fabs(d - p_plane.d) < _PLANE_EQ_D_EPSILON); +} + +Plane::operator String() const { + // return normal.operator String() + ", " + rtos(d); + return String(); // @Todo +} + +bool Plane::is_point_over(const Vector3 &p_point) const { + return (normal.dot(p_point) > d); +} + +real_t Plane::distance_to(const Vector3 &p_point) const { + return (normal.dot(p_point) - d); +} + +bool Plane::has_point(const Vector3 &p_point, real_t _epsilon) const { + real_t dist = normal.dot(p_point) - d; + dist = ::fabs(dist); + return (dist <= _epsilon); +} + +Plane::Plane(const Vector3 &p_normal, real_t p_d) { + normal = p_normal; + d = p_d; +} + +Plane::Plane(const Vector3 &p_point, const Vector3 &p_normal) { + normal = p_normal; + d = p_normal.dot(p_point); +} + +Plane::Plane(const Vector3 &p_point1, const Vector3 &p_point2, const Vector3 &p_point3, ClockDirection p_dir) { + if (p_dir == CLOCKWISE) + normal = (p_point1 - p_point3).cross(p_point1 - p_point2); + else + normal = (p_point1 - p_point2).cross(p_point1 - p_point3); + + normal.normalize(); + d = normal.dot(p_point1); +} + +bool Plane::operator==(const Plane &p_plane) const { + return normal == p_plane.normal && d == p_plane.d; +} + +bool Plane::operator!=(const Plane &p_plane) const { + return normal != p_plane.normal || d != p_plane.d; +} + +} // namespace godot diff --git a/src/core/PoolArrays.cpp b/src/core/PoolArrays.cpp new file mode 100644 index 0000000..aefacfd --- /dev/null +++ b/src/core/PoolArrays.cpp @@ -0,0 +1,571 @@ +/*************************************************************************/ +/* PoolArrays.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "PoolArrays.hpp" +#include "Color.hpp" +#include "Defs.hpp" +#include "GodotGlobal.hpp" +#include "String.hpp" +#include "Vector2.hpp" +#include "Vector3.hpp" + +#include + +namespace godot { + +PoolByteArray::PoolByteArray() { + godot::api->godot_pool_byte_array_new(&_godot_array); +} + +PoolByteArray::PoolByteArray(const PoolByteArray &p_other) { + godot::api->godot_pool_byte_array_new_copy(&_godot_array, &p_other._godot_array); +} + +PoolByteArray &PoolByteArray::operator=(const PoolByteArray &p_other) { + godot::api->godot_pool_byte_array_destroy(&_godot_array); + godot::api->godot_pool_byte_array_new_copy(&_godot_array, &p_other._godot_array); + return *this; +} + +PoolByteArray::PoolByteArray(const Array &array) { + godot::api->godot_pool_byte_array_new_with_array(&_godot_array, (godot_array *)&array); +} + +PoolByteArray::Read PoolByteArray::read() const { + Read read; + read._read_access = godot::api->godot_pool_byte_array_read(&_godot_array); + return read; +} + +PoolByteArray::Write PoolByteArray::write() { + Write write; + write._write_access = godot::api->godot_pool_byte_array_write(&_godot_array); + return write; +} + +void PoolByteArray::append(const uint8_t data) { + godot::api->godot_pool_byte_array_append(&_godot_array, data); +} + +void PoolByteArray::append_array(const PoolByteArray &array) { + godot::api->godot_pool_byte_array_append_array(&_godot_array, &array._godot_array); +} + +int PoolByteArray::insert(const int idx, const uint8_t data) { + return godot::api->godot_pool_byte_array_insert(&_godot_array, idx, data); +} + +void PoolByteArray::invert() { + godot::api->godot_pool_byte_array_invert(&_godot_array); +} + +void PoolByteArray::push_back(const uint8_t data) { + godot::api->godot_pool_byte_array_push_back(&_godot_array, data); +} + +void PoolByteArray::remove(const int idx) { + godot::api->godot_pool_byte_array_remove(&_godot_array, idx); +} + +void PoolByteArray::resize(const int size) { + godot::api->godot_pool_byte_array_resize(&_godot_array, size); +} + +void PoolByteArray::set(const int idx, const uint8_t data) { + godot::api->godot_pool_byte_array_set(&_godot_array, idx, data); +} + +uint8_t PoolByteArray::operator[](const int idx) { + return godot::api->godot_pool_byte_array_get(&_godot_array, idx); +} + +int PoolByteArray::size() const { + return godot::api->godot_pool_byte_array_size(&_godot_array); +} + +PoolByteArray::~PoolByteArray() { + godot::api->godot_pool_byte_array_destroy(&_godot_array); +} + +PoolIntArray::PoolIntArray() { + godot::api->godot_pool_int_array_new(&_godot_array); +} + +PoolIntArray::PoolIntArray(const PoolIntArray &p_other) { + godot::api->godot_pool_int_array_new_copy(&_godot_array, &p_other._godot_array); +} + +PoolIntArray &PoolIntArray::operator=(const PoolIntArray &p_other) { + godot::api->godot_pool_int_array_destroy(&_godot_array); + godot::api->godot_pool_int_array_new_copy(&_godot_array, &p_other._godot_array); + return *this; +} + +PoolIntArray::PoolIntArray(const Array &array) { + godot::api->godot_pool_int_array_new_with_array(&_godot_array, (godot_array *)&array); +} + +PoolIntArray::Read PoolIntArray::read() const { + Read read; + read._read_access = godot::api->godot_pool_int_array_read(&_godot_array); + return read; +} + +PoolIntArray::Write PoolIntArray::write() { + Write write; + write._write_access = godot::api->godot_pool_int_array_write(&_godot_array); + return write; +} + +void PoolIntArray::append(const int data) { + godot::api->godot_pool_int_array_append(&_godot_array, data); +} + +void PoolIntArray::append_array(const PoolIntArray &array) { + godot::api->godot_pool_int_array_append_array(&_godot_array, &array._godot_array); +} + +int PoolIntArray::insert(const int idx, const int data) { + return godot::api->godot_pool_int_array_insert(&_godot_array, idx, data); +} + +void PoolIntArray::invert() { + godot::api->godot_pool_int_array_invert(&_godot_array); +} + +void PoolIntArray::push_back(const int data) { + godot::api->godot_pool_int_array_push_back(&_godot_array, data); +} + +void PoolIntArray::remove(const int idx) { + godot::api->godot_pool_int_array_remove(&_godot_array, idx); +} + +void PoolIntArray::resize(const int size) { + godot::api->godot_pool_int_array_resize(&_godot_array, size); +} + +void PoolIntArray::set(const int idx, const int data) { + godot::api->godot_pool_int_array_set(&_godot_array, idx, data); +} + +int PoolIntArray::operator[](const int idx) { + return godot::api->godot_pool_int_array_get(&_godot_array, idx); +} + +int PoolIntArray::size() const { + return godot::api->godot_pool_int_array_size(&_godot_array); +} + +PoolIntArray::~PoolIntArray() { + godot::api->godot_pool_int_array_destroy(&_godot_array); +} + +PoolRealArray::PoolRealArray() { + godot::api->godot_pool_real_array_new(&_godot_array); +} + +PoolRealArray::PoolRealArray(const PoolRealArray &p_other) { + godot::api->godot_pool_real_array_new_copy(&_godot_array, &p_other._godot_array); +} + +PoolRealArray &PoolRealArray::operator=(const PoolRealArray &p_other) { + godot::api->godot_pool_real_array_destroy(&_godot_array); + godot::api->godot_pool_real_array_new_copy(&_godot_array, &p_other._godot_array); + return *this; +} + +PoolRealArray::Read PoolRealArray::read() const { + Read read; + read._read_access = godot::api->godot_pool_real_array_read(&_godot_array); + return read; +} + +PoolRealArray::Write PoolRealArray::write() { + Write write; + write._write_access = godot::api->godot_pool_real_array_write(&_godot_array); + return write; +} + +PoolRealArray::PoolRealArray(const Array &array) { + godot::api->godot_pool_real_array_new_with_array(&_godot_array, (godot_array *)&array); +} + +void PoolRealArray::append(const real_t data) { + godot::api->godot_pool_real_array_append(&_godot_array, data); +} + +void PoolRealArray::append_array(const PoolRealArray &array) { + godot::api->godot_pool_real_array_append_array(&_godot_array, &array._godot_array); +} + +int PoolRealArray::insert(const int idx, const real_t data) { + return godot::api->godot_pool_real_array_insert(&_godot_array, idx, data); +} + +void PoolRealArray::invert() { + godot::api->godot_pool_real_array_invert(&_godot_array); +} + +void PoolRealArray::push_back(const real_t data) { + godot::api->godot_pool_real_array_push_back(&_godot_array, data); +} + +void PoolRealArray::remove(const int idx) { + godot::api->godot_pool_real_array_remove(&_godot_array, idx); +} + +void PoolRealArray::resize(const int size) { + godot::api->godot_pool_real_array_resize(&_godot_array, size); +} + +void PoolRealArray::set(const int idx, const real_t data) { + godot::api->godot_pool_real_array_set(&_godot_array, idx, data); +} + +real_t PoolRealArray::operator[](const int idx) { + return godot::api->godot_pool_real_array_get(&_godot_array, idx); +} + +int PoolRealArray::size() const { + return godot::api->godot_pool_real_array_size(&_godot_array); +} + +PoolRealArray::~PoolRealArray() { + godot::api->godot_pool_real_array_destroy(&_godot_array); +} + +PoolStringArray::PoolStringArray() { + godot::api->godot_pool_string_array_new(&_godot_array); +} + +PoolStringArray::PoolStringArray(const PoolStringArray &p_other) { + godot::api->godot_pool_string_array_new_copy(&_godot_array, &p_other._godot_array); +} + +PoolStringArray &PoolStringArray::operator=(const PoolStringArray &p_other) { + godot::api->godot_pool_string_array_destroy(&_godot_array); + godot::api->godot_pool_string_array_new_copy(&_godot_array, &p_other._godot_array); + return *this; +} + +PoolStringArray::PoolStringArray(const Array &array) { + godot::api->godot_pool_string_array_new_with_array(&_godot_array, (godot_array *)&array); +} + +PoolStringArray::Read PoolStringArray::read() const { + Read read; + read._read_access = godot::api->godot_pool_string_array_read(&_godot_array); + return read; +} + +PoolStringArray::Write PoolStringArray::write() { + Write write; + write._write_access = godot::api->godot_pool_string_array_write(&_godot_array); + return write; +} + +void PoolStringArray::append(const String &data) { + godot::api->godot_pool_string_array_append(&_godot_array, (godot_string *)&data); +} + +void PoolStringArray::append_array(const PoolStringArray &array) { + godot::api->godot_pool_string_array_append_array(&_godot_array, &array._godot_array); +} + +int PoolStringArray::insert(const int idx, const String &data) { + return godot::api->godot_pool_string_array_insert(&_godot_array, idx, (godot_string *)&data); +} + +void PoolStringArray::invert() { + godot::api->godot_pool_string_array_invert(&_godot_array); +} + +void PoolStringArray::push_back(const String &data) { + godot::api->godot_pool_string_array_push_back(&_godot_array, (godot_string *)&data); +} + +void PoolStringArray::remove(const int idx) { + godot::api->godot_pool_string_array_remove(&_godot_array, idx); +} + +void PoolStringArray::resize(const int size) { + godot::api->godot_pool_string_array_resize(&_godot_array, size); +} + +void PoolStringArray::set(const int idx, const String &data) { + godot::api->godot_pool_string_array_set(&_godot_array, idx, (godot_string *)&data); +} + +const String PoolStringArray::operator[](const int idx) { + String s; + godot_string str = godot::api->godot_pool_string_array_get(&_godot_array, idx); + godot::api->godot_string_new_copy((godot_string *)&s, &str); + godot::api->godot_string_destroy(&str); + return s; +} + +int PoolStringArray::size() const { + return godot::api->godot_pool_string_array_size(&_godot_array); +} + +PoolStringArray::~PoolStringArray() { + godot::api->godot_pool_string_array_destroy(&_godot_array); +} + +PoolVector2Array::PoolVector2Array() { + godot::api->godot_pool_vector2_array_new(&_godot_array); +} + +PoolVector2Array::PoolVector2Array(const PoolVector2Array &p_other) { + godot::api->godot_pool_vector2_array_new_copy(&_godot_array, &p_other._godot_array); +} + +PoolVector2Array &PoolVector2Array::operator=(const PoolVector2Array &p_other) { + godot::api->godot_pool_vector2_array_destroy(&_godot_array); + godot::api->godot_pool_vector2_array_new_copy(&_godot_array, &p_other._godot_array); + return *this; +} + +PoolVector2Array::PoolVector2Array(const Array &array) { + godot::api->godot_pool_vector2_array_new_with_array(&_godot_array, (godot_array *)&array); +} + +PoolVector2Array::Read PoolVector2Array::read() const { + Read read; + read._read_access = godot::api->godot_pool_vector2_array_read(&_godot_array); + return read; +} + +PoolVector2Array::Write PoolVector2Array::write() { + Write write; + write._write_access = godot::api->godot_pool_vector2_array_write(&_godot_array); + return write; +} + +void PoolVector2Array::append(const Vector2 &data) { + godot::api->godot_pool_vector2_array_append(&_godot_array, (godot_vector2 *)&data); +} + +void PoolVector2Array::append_array(const PoolVector2Array &array) { + godot::api->godot_pool_vector2_array_append_array(&_godot_array, &array._godot_array); +} + +int PoolVector2Array::insert(const int idx, const Vector2 &data) { + return godot::api->godot_pool_vector2_array_insert(&_godot_array, idx, (godot_vector2 *)&data); +} + +void PoolVector2Array::invert() { + godot::api->godot_pool_vector2_array_invert(&_godot_array); +} + +void PoolVector2Array::push_back(const Vector2 &data) { + godot::api->godot_pool_vector2_array_push_back(&_godot_array, (godot_vector2 *)&data); +} + +void PoolVector2Array::remove(const int idx) { + godot::api->godot_pool_vector2_array_remove(&_godot_array, idx); +} + +void PoolVector2Array::resize(const int size) { + godot::api->godot_pool_vector2_array_resize(&_godot_array, size); +} + +void PoolVector2Array::set(const int idx, const Vector2 &data) { + godot::api->godot_pool_vector2_array_set(&_godot_array, idx, (godot_vector2 *)&data); +} + +const Vector2 PoolVector2Array::operator[](const int idx) { + Vector2 v; + *(godot_vector2 *)&v = godot::api->godot_pool_vector2_array_get(&_godot_array, idx); + return v; +} + +int PoolVector2Array::size() const { + return godot::api->godot_pool_vector2_array_size(&_godot_array); +} + +PoolVector2Array::~PoolVector2Array() { + godot::api->godot_pool_vector2_array_destroy(&_godot_array); +} + +PoolVector3Array::PoolVector3Array() { + godot::api->godot_pool_vector3_array_new(&_godot_array); +} + +PoolVector3Array::PoolVector3Array(const PoolVector3Array &p_other) { + godot::api->godot_pool_vector3_array_new_copy(&_godot_array, &p_other._godot_array); +} + +PoolVector3Array &PoolVector3Array::operator=(const PoolVector3Array &p_other) { + godot::api->godot_pool_vector3_array_destroy(&_godot_array); + godot::api->godot_pool_vector3_array_new_copy(&_godot_array, &p_other._godot_array); + return *this; +} + +PoolVector3Array::PoolVector3Array(const Array &array) { + godot::api->godot_pool_vector3_array_new_with_array(&_godot_array, (godot_array *)&array); +} + +PoolVector3Array::Read PoolVector3Array::read() const { + Read read; + read._read_access = godot::api->godot_pool_vector3_array_read(&_godot_array); + return read; +} + +PoolVector3Array::Write PoolVector3Array::write() { + Write write; + write._write_access = godot::api->godot_pool_vector3_array_write(&_godot_array); + return write; +} + +void PoolVector3Array::append(const Vector3 &data) { + godot::api->godot_pool_vector3_array_append(&_godot_array, (godot_vector3 *)&data); +} + +void PoolVector3Array::append_array(const PoolVector3Array &array) { + godot::api->godot_pool_vector3_array_append_array(&_godot_array, &array._godot_array); +} + +int PoolVector3Array::insert(const int idx, const Vector3 &data) { + return godot::api->godot_pool_vector3_array_insert(&_godot_array, idx, (godot_vector3 *)&data); +} + +void PoolVector3Array::invert() { + godot::api->godot_pool_vector3_array_invert(&_godot_array); +} + +void PoolVector3Array::push_back(const Vector3 &data) { + godot::api->godot_pool_vector3_array_push_back(&_godot_array, (godot_vector3 *)&data); +} + +void PoolVector3Array::remove(const int idx) { + godot::api->godot_pool_vector3_array_remove(&_godot_array, idx); +} + +void PoolVector3Array::resize(const int size) { + godot::api->godot_pool_vector3_array_resize(&_godot_array, size); +} + +void PoolVector3Array::set(const int idx, const Vector3 &data) { + godot::api->godot_pool_vector3_array_set(&_godot_array, idx, (godot_vector3 *)&data); +} + +const Vector3 PoolVector3Array::operator[](const int idx) { + Vector3 v; + *(godot_vector3 *)&v = godot::api->godot_pool_vector3_array_get(&_godot_array, idx); + return v; +} + +int PoolVector3Array::size() const { + return godot::api->godot_pool_vector3_array_size(&_godot_array); +} + +PoolVector3Array::~PoolVector3Array() { + godot::api->godot_pool_vector3_array_destroy(&_godot_array); +} + +PoolColorArray::PoolColorArray() { + godot::api->godot_pool_color_array_new(&_godot_array); +} + +PoolColorArray::PoolColorArray(const PoolColorArray &p_other) { + godot::api->godot_pool_color_array_new_copy(&_godot_array, &p_other._godot_array); +} + +PoolColorArray &PoolColorArray::operator=(const PoolColorArray &p_other) { + godot::api->godot_pool_color_array_destroy(&_godot_array); + godot::api->godot_pool_color_array_new_copy(&_godot_array, &p_other._godot_array); + return *this; +} + +PoolColorArray::PoolColorArray(const Array &array) { + godot::api->godot_pool_color_array_new_with_array(&_godot_array, (godot_array *)&array); +} + +PoolColorArray::Read PoolColorArray::read() const { + Read read; + read._read_access = godot::api->godot_pool_color_array_read(&_godot_array); + return read; +} + +PoolColorArray::Write PoolColorArray::write() { + Write write; + write._write_access = godot::api->godot_pool_color_array_write(&_godot_array); + return write; +} + +void PoolColorArray::append(const Color &data) { + godot::api->godot_pool_color_array_append(&_godot_array, (godot_color *)&data); +} + +void PoolColorArray::append_array(const PoolColorArray &array) { + godot::api->godot_pool_color_array_append_array(&_godot_array, &array._godot_array); +} + +int PoolColorArray::insert(const int idx, const Color &data) { + return godot::api->godot_pool_color_array_insert(&_godot_array, idx, (godot_color *)&data); +} + +void PoolColorArray::invert() { + godot::api->godot_pool_color_array_invert(&_godot_array); +} + +void PoolColorArray::push_back(const Color &data) { + godot::api->godot_pool_color_array_push_back(&_godot_array, (godot_color *)&data); +} + +void PoolColorArray::remove(const int idx) { + godot::api->godot_pool_color_array_remove(&_godot_array, idx); +} + +void PoolColorArray::resize(const int size) { + godot::api->godot_pool_color_array_resize(&_godot_array, size); +} + +void PoolColorArray::set(const int idx, const Color &data) { + godot::api->godot_pool_color_array_set(&_godot_array, idx, (godot_color *)&data); +} + +const Color PoolColorArray::operator[](const int idx) { + Color v; + *(godot_color *)&v = godot::api->godot_pool_color_array_get(&_godot_array, idx); + return v; +} + +int PoolColorArray::size() const { + return godot::api->godot_pool_color_array_size(&_godot_array); +} + +PoolColorArray::~PoolColorArray() { + godot::api->godot_pool_color_array_destroy(&_godot_array); +} + +} // namespace godot diff --git a/src/core/Quat.cpp b/src/core/Quat.cpp new file mode 100644 index 0000000..a8bb3e1 --- /dev/null +++ b/src/core/Quat.cpp @@ -0,0 +1,352 @@ +/*************************************************************************/ +/* Quat.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "Quat.hpp" +#include "Basis.hpp" +#include "Defs.hpp" +#include "Vector3.hpp" + +#include + +namespace godot { + +const Quat Quat::IDENTITY = Quat(); + +// set_euler_xyz expects a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// This implementation uses XYZ convention (Z is the first rotation). +void Quat::set_euler_xyz(const Vector3 &p_euler) { + real_t half_a1 = p_euler.x * 0.5; + real_t half_a2 = p_euler.y * 0.5; + real_t half_a3 = p_euler.z * 0.5; + + // R = X(a1).Y(a2).Z(a3) convention for Euler angles. + // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-2) + // a3 is the angle of the first rotation, following the notation in this reference. + + real_t cos_a1 = ::cos(half_a1); + real_t sin_a1 = ::sin(half_a1); + real_t cos_a2 = ::cos(half_a2); + real_t sin_a2 = ::sin(half_a2); + real_t cos_a3 = ::cos(half_a3); + real_t sin_a3 = ::sin(half_a3); + + set(sin_a1 * cos_a2 * cos_a3 + sin_a2 * sin_a3 * cos_a1, + -sin_a1 * sin_a3 * cos_a2 + sin_a2 * cos_a1 * cos_a3, + sin_a1 * sin_a2 * cos_a3 + sin_a3 * cos_a1 * cos_a2, + -sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3); +} + +// get_euler_xyz returns a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// This implementation uses XYZ convention (Z is the first rotation). +Vector3 Quat::get_euler_xyz() const { + Basis m(*this); + return m.get_euler_xyz(); +} + +// set_euler_yxz expects a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// This implementation uses YXZ convention (Z is the first rotation). +void Quat::set_euler_yxz(const Vector3 &p_euler) { + real_t half_a1 = p_euler.y * 0.5; + real_t half_a2 = p_euler.x * 0.5; + real_t half_a3 = p_euler.z * 0.5; + + // R = Y(a1).X(a2).Z(a3) convention for Euler angles. + // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6) + // a3 is the angle of the first rotation, following the notation in this reference. + + real_t cos_a1 = ::cos(half_a1); + real_t sin_a1 = ::sin(half_a1); + real_t cos_a2 = ::cos(half_a2); + real_t sin_a2 = ::sin(half_a2); + real_t cos_a3 = ::cos(half_a3); + real_t sin_a3 = ::sin(half_a3); + + set(sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3, + sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3, + -sin_a1 * sin_a2 * cos_a3 + cos_a1 * sin_a2 * sin_a3, + sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3); +} + +// get_euler_yxz returns a vector containing the Euler angles in the format +// (ax,ay,az), where ax is the angle of rotation around x axis, +// and similar for other axes. +// This implementation uses YXZ convention (Z is the first rotation). +Vector3 Quat::get_euler_yxz() const { + Basis m(*this); + return m.get_euler_yxz(); +} + +real_t Quat::length() const { + return ::sqrt(length_squared()); +} + +void Quat::normalize() { + *this /= length(); +} + +Quat Quat::normalized() const { + return *this / length(); +} + +bool Quat::is_normalized() const { + return std::abs(length_squared() - 1.0) < 0.00001; +} + +Quat Quat::inverse() const { + return Quat(-x, -y, -z, w); +} + +Quat Quat::slerp(const Quat &q, const real_t &t) const { + Quat to1; + real_t omega, cosom, sinom, scale0, scale1; + + // calc cosine + cosom = dot(q); + + // adjust signs (if necessary) + if (cosom < 0.0) { + cosom = -cosom; + to1.x = -q.x; + to1.y = -q.y; + to1.z = -q.z; + to1.w = -q.w; + } else { + to1.x = q.x; + to1.y = q.y; + to1.z = q.z; + to1.w = q.w; + } + + // calculate coefficients + + if ((1.0 - cosom) > CMP_EPSILON) { + // standard case (slerp) + omega = ::acos(cosom); + sinom = ::sin(omega); + scale0 = ::sin((1.0 - t) * omega) / sinom; + scale1 = ::sin(t * omega) / sinom; + } else { + // "from" and "to" quaternions are very close + // ... so we can do a linear interpolation + scale0 = 1.0 - t; + scale1 = t; + } + // calculate final values + return Quat( + scale0 * x + scale1 * to1.x, + scale0 * y + scale1 * to1.y, + scale0 * z + scale1 * to1.z, + scale0 * w + scale1 * to1.w); +} + +Quat Quat::slerpni(const Quat &q, const real_t &t) const { + const Quat &from = *this; + + real_t dot = from.dot(q); + + if (::fabs(dot) > 0.9999) + return from; + + real_t theta = ::acos(dot), + sinT = 1.0 / ::sin(theta), + newFactor = ::sin(t * theta) * sinT, + invFactor = ::sin((1.0 - t) * theta) * sinT; + + return Quat(invFactor * from.x + newFactor * q.x, + invFactor * from.y + newFactor * q.y, + invFactor * from.z + newFactor * q.z, + invFactor * from.w + newFactor * q.w); +} + +Quat Quat::cubic_slerp(const Quat &q, const Quat &prep, const Quat &postq, const real_t &t) const { + //the only way to do slerp :| + real_t t2 = (1.0 - t) * t * 2; + Quat sp = this->slerp(q, t); + Quat sq = prep.slerpni(postq, t); + return sp.slerpni(sq, t2); +} + +void Quat::get_axis_and_angle(Vector3 &r_axis, real_t &r_angle) const { + r_angle = 2 * ::acos(w); + r_axis.x = x / ::sqrt(1 - w * w); + r_axis.y = y / ::sqrt(1 - w * w); + r_axis.z = z / ::sqrt(1 - w * w); +} + +void Quat::set_axis_angle(const Vector3 &axis, const float angle) { + ERR_FAIL_COND(!axis.is_normalized()); + + real_t d = axis.length(); + if (d == 0) + set(0, 0, 0, 0); + else { + real_t sin_angle = ::sin(angle * 0.5); + real_t cos_angle = ::cos(angle * 0.5); + real_t s = sin_angle / d; + set(axis.x * s, axis.y * s, axis.z * s, + cos_angle); + } +} + +Quat Quat::operator*(const Vector3 &v) const { + return Quat(w * v.x + y * v.z - z * v.y, + w * v.y + z * v.x - x * v.z, + w * v.z + x * v.y - y * v.x, + -x * v.x - y * v.y - z * v.z); +} + +Vector3 Quat::xform(const Vector3 &v) const { + Quat q = *this * v; + q *= this->inverse(); + return Vector3(q.x, q.y, q.z); +} + +Quat::operator String() const { + return String(); // @Todo +} + +Quat::Quat(const Vector3 &axis, const real_t &angle) { + real_t d = axis.length(); + if (d == 0) + set(0, 0, 0, 0); + else { + real_t sin_angle = ::sin(angle * 0.5); + real_t cos_angle = ::cos(angle * 0.5); + real_t s = sin_angle / d; + set(axis.x * s, axis.y * s, axis.z * s, + cos_angle); + } +} + +Quat::Quat(const Vector3 &v0, const Vector3 &v1) // shortest arc +{ + Vector3 c = v0.cross(v1); + real_t d = v0.dot(v1); + + if (d < -1.0 + CMP_EPSILON) { + x = 0; + y = 1; + z = 0; + w = 0; + } else { + real_t s = ::sqrt((1.0 + d) * 2.0); + real_t rs = 1.0 / s; + + x = c.x * rs; + y = c.y * rs; + z = c.z * rs; + w = s * 0.5; + } +} + +real_t Quat::dot(const Quat &q) const { + return x * q.x + y * q.y + z * q.z + w * q.w; +} + +real_t Quat::length_squared() const { + return dot(*this); +} + +void Quat::operator+=(const Quat &q) { + x += q.x; + y += q.y; + z += q.z; + w += q.w; +} + +void Quat::operator-=(const Quat &q) { + x -= q.x; + y -= q.y; + z -= q.z; + w -= q.w; +} + +void Quat::operator*=(const Quat &q) { + set(w * q.x + x * q.w + y * q.z - z * q.y, + w * q.y + y * q.w + z * q.x - x * q.z, + w * q.z + z * q.w + x * q.y - y * q.x, + w * q.w - x * q.x - y * q.y - z * q.z); +} + +void Quat::operator*=(const real_t &s) { + x *= s; + y *= s; + z *= s; + w *= s; +} + +void Quat::operator/=(const real_t &s) { + *this *= 1.0 / s; +} + +Quat Quat::operator+(const Quat &q2) const { + const Quat &q1 = *this; + return Quat(q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w); +} + +Quat Quat::operator-(const Quat &q2) const { + const Quat &q1 = *this; + return Quat(q1.x - q2.x, q1.y - q2.y, q1.z - q2.z, q1.w - q2.w); +} + +Quat Quat::operator*(const Quat &q2) const { + Quat q1 = *this; + q1 *= q2; + return q1; +} + +Quat Quat::operator-() const { + const Quat &q2 = *this; + return Quat(-q2.x, -q2.y, -q2.z, -q2.w); +} + +Quat Quat::operator*(const real_t &s) const { + return Quat(x * s, y * s, z * s, w * s); +} + +Quat Quat::operator/(const real_t &s) const { + return *this * (1.0 / s); +} + +bool Quat::operator==(const Quat &p_quat) const { + return x == p_quat.x && y == p_quat.y && z == p_quat.z && w == p_quat.w; +} + +bool Quat::operator!=(const Quat &p_quat) const { + return x != p_quat.x || y != p_quat.y || z != p_quat.z || w != p_quat.w; +} + +} // namespace godot diff --git a/src/core/RID.cpp b/src/core/RID.cpp new file mode 100644 index 0000000..88bc071 --- /dev/null +++ b/src/core/RID.cpp @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* RID.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "RID.hpp" + +#include + +#include "GodotGlobal.hpp" + +namespace godot { + +RID::RID() { + godot::api->godot_rid_new(&_godot_rid); +} + +RID::RID(Object *p) { + godot::api->godot_rid_new_with_resource(&_godot_rid, (const godot_object *)p); +} + +godot_rid RID::_get_godot_rid() const { + return _godot_rid; +} + +int32_t RID::get_id() const { + return godot::api->godot_rid_get_id(&_godot_rid); +} + +bool RID::operator==(const RID &p_other) const { + return godot::api->godot_rid_operator_equal(&_godot_rid, &p_other._godot_rid); +} + +bool RID::operator!=(const RID &p_other) const { + return !(*this == p_other); +} + +bool RID::operator<(const RID &p_other) const { + return godot::api->godot_rid_operator_less(&_godot_rid, &p_other._godot_rid); +} + +bool RID::operator>(const RID &p_other) const { + return !(*this < p_other) && *this != p_other; +} + +bool RID::operator<=(const RID &p_other) const { + return (*this < p_other) || *this == p_other; +} + +bool RID::operator>=(const RID &p_other) const { + return !(*this < p_other); +} + +} // namespace godot diff --git a/src/core/Rect2.cpp b/src/core/Rect2.cpp new file mode 100644 index 0000000..2b4aaaf --- /dev/null +++ b/src/core/Rect2.cpp @@ -0,0 +1,323 @@ +/*************************************************************************/ +/* Rect2.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "Rect2.hpp" +#include "String.hpp" +#include "Transform2D.hpp" +#include "Vector2.hpp" + +#include + +namespace godot { + +#ifndef MAX +#define MAX(a, b) (a > b ? a : b) +#endif + +#ifndef MIN +#define MIN(a, b) (a < b ? a : b) +#endif + +real_t Rect2::distance_to(const Vector2 &p_point) const { + real_t dist = 0.0; + bool inside = true; + + if (p_point.x < position.x) { + real_t d = position.x - p_point.x; + dist = d; + inside = false; + } + if (p_point.y < position.y) { + real_t d = position.y - p_point.y; + dist = inside ? d : MIN(dist, d); + inside = false; + } + if (p_point.x >= (position.x + size.x)) { + real_t d = p_point.x - (position.x + size.x); + dist = inside ? d : MIN(dist, d); + inside = false; + } + if (p_point.y >= (position.y + size.y)) { + real_t d = p_point.y - (position.y + size.y); + dist = inside ? d : MIN(dist, d); + inside = false; + } + + if (inside) { + return 0; + } else { + return dist; + } +} + +Rect2 Rect2::clip(const Rect2 &p_rect) const { /// return a clipped rect + + Rect2 new_rect = p_rect; + + if (!intersects(new_rect)) + return Rect2(); + + new_rect.position.x = MAX(p_rect.position.x, position.x); + new_rect.position.y = MAX(p_rect.position.y, position.y); + + Point2 p_rect_end = p_rect.position + p_rect.size; + Point2 end = position + size; + + new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x; + new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y; + + return new_rect; +} + +Rect2 Rect2::merge(const Rect2 &p_rect) const { ///< return a merged rect + + Rect2 new_rect; + + new_rect.position.x = MIN(p_rect.position.x, position.x); + new_rect.position.y = MIN(p_rect.position.y, position.y); + + new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x); + new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y); + + new_rect.size = new_rect.size - new_rect.position; //make relative again + + return new_rect; +} + +Rect2::operator String() const { + return String(position) + ", " + String(size); +} + +bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_position, Point2 *r_normal) const { + real_t min = 0, max = 1; + int axis = 0; + real_t sign = 0; + + for (int i = 0; i < 2; i++) { + real_t seg_from = p_from[i]; + real_t seg_to = p_to[i]; + real_t box_begin = position[i]; + real_t box_end = box_begin + size[i]; + real_t cmin, cmax; + real_t csign; + + if (seg_from < seg_to) { + if (seg_from > box_end || seg_to < box_begin) + return false; + real_t length = seg_to - seg_from; + cmin = (seg_from < box_begin) ? ((box_begin - seg_from) / length) : 0; + cmax = (seg_to > box_end) ? ((box_end - seg_from) / length) : 1; + csign = -1.0; + + } else { + if (seg_to > box_end || seg_from < box_begin) + return false; + real_t length = seg_to - seg_from; + cmin = (seg_from > box_end) ? (box_end - seg_from) / length : 0; + cmax = (seg_to < box_begin) ? (box_begin - seg_from) / length : 1; + csign = 1.0; + } + + if (cmin > min) { + min = cmin; + axis = i; + sign = csign; + } + if (cmax < max) + max = cmax; + if (max < min) + return false; + } + + Vector2 rel = p_to - p_from; + + if (r_normal) { + Vector2 normal; + normal[axis] = sign; + *r_normal = normal; + } + + if (r_position) + *r_position = p_from + rel * min; + + return true; +} + +bool Rect2::intersects_transformed(const Transform2D &p_xform, const Rect2 &p_rect) const { + //SAT intersection between local and transformed rect2 + + Vector2 xf_points[4] = { + p_xform.xform(p_rect.position), + p_xform.xform(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y)), + p_xform.xform(Vector2(p_rect.position.x, p_rect.position.y + p_rect.size.y)), + p_xform.xform(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y + p_rect.size.y)), + }; + + real_t low_limit; + + //base rect2 first (faster) + + if (xf_points[0].y > position.y) + goto next1; + if (xf_points[1].y > position.y) + goto next1; + if (xf_points[2].y > position.y) + goto next1; + if (xf_points[3].y > position.y) + goto next1; + + return false; + +next1: + + low_limit = position.y + size.y; + + if (xf_points[0].y < low_limit) + goto next2; + if (xf_points[1].y < low_limit) + goto next2; + if (xf_points[2].y < low_limit) + goto next2; + if (xf_points[3].y < low_limit) + goto next2; + + return false; + +next2: + + if (xf_points[0].x > position.x) + goto next3; + if (xf_points[1].x > position.x) + goto next3; + if (xf_points[2].x > position.x) + goto next3; + if (xf_points[3].x > position.x) + goto next3; + + return false; + +next3: + + low_limit = position.x + size.x; + + if (xf_points[0].x < low_limit) + goto next4; + if (xf_points[1].x < low_limit) + goto next4; + if (xf_points[2].x < low_limit) + goto next4; + if (xf_points[3].x < low_limit) + goto next4; + + return false; + +next4: + + Vector2 xf_points2[4] = { + position, + Vector2(position.x + size.x, position.y), + Vector2(position.x, position.y + size.y), + Vector2(position.x + size.x, position.y + size.y), + }; + + real_t maxa = p_xform.elements[0].dot(xf_points2[0]); + real_t mina = maxa; + + real_t dp = p_xform.elements[0].dot(xf_points2[1]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + dp = p_xform.elements[0].dot(xf_points2[2]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + dp = p_xform.elements[0].dot(xf_points2[3]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + real_t maxb = p_xform.elements[0].dot(xf_points[0]); + real_t minb = maxb; + + dp = p_xform.elements[0].dot(xf_points[1]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + dp = p_xform.elements[0].dot(xf_points[2]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + dp = p_xform.elements[0].dot(xf_points[3]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + if (mina > maxb) + return false; + if (minb > maxa) + return false; + + maxa = p_xform.elements[1].dot(xf_points2[0]); + mina = maxa; + + dp = p_xform.elements[1].dot(xf_points2[1]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + dp = p_xform.elements[1].dot(xf_points2[2]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + dp = p_xform.elements[1].dot(xf_points2[3]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + maxb = p_xform.elements[1].dot(xf_points[0]); + minb = maxb; + + dp = p_xform.elements[1].dot(xf_points[1]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + dp = p_xform.elements[1].dot(xf_points[2]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + dp = p_xform.elements[1].dot(xf_points[3]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + if (mina > maxb) + return false; + if (minb > maxa) + return false; + + return true; +} + +} // namespace godot diff --git a/src/core/String.cpp b/src/core/String.cpp new file mode 100644 index 0000000..a7977ab --- /dev/null +++ b/src/core/String.cpp @@ -0,0 +1,522 @@ +/*************************************************************************/ +/* String.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "String.hpp" + +#include "Array.hpp" +#include "GodotGlobal.hpp" +#include "NodePath.hpp" +#include "PoolArrays.hpp" +#include "Variant.hpp" + +#include + +#include + +namespace godot { + +godot::CharString::~CharString() { + godot::api->godot_char_string_destroy(&_char_string); +} + +int godot::CharString::length() const { + return godot::api->godot_char_string_length(&_char_string); +} + +const char *godot::CharString::get_data() const { + return godot::api->godot_char_string_get_data(&_char_string); +} + +String String::num(double p_num, int p_decimals) { + return String(godot::api->godot_string_num_with_decimals(p_num, p_decimals)); +} + +String String::num_scientific(double p_num) { + return String(godot::api->godot_string_num_scientific(p_num)); +} + +String String::num_real(double p_num) { + return String(godot::api->godot_string_num_real(p_num)); +} + +String String::num_int64(int64_t p_num, int base, bool capitalize_hex) { + return String(godot::api->godot_string_num_int64_capitalized(p_num, base, capitalize_hex)); +} + +String String::chr(godot_char_type p_char) { + return String(godot::api->godot_string_chr(p_char)); +} + +String String::md5(const uint8_t *p_md5) { + return String(godot::api->godot_string_md5(p_md5)); +} + +String String::hex_encode_buffer(const uint8_t *p_buffer, int p_len) { + return String(godot::api->godot_string_hex_encode_buffer(p_buffer, p_len)); +} + +godot::String::String() { + godot::api->godot_string_new(&_godot_string); +} + +String::String(const char *contents) { + godot::api->godot_string_new(&_godot_string); + godot::api->godot_string_parse_utf8(&_godot_string, contents); +} + +String::String(const wchar_t *contents) { + godot::api->godot_string_new_with_wide_string(&_godot_string, contents, wcslen(contents)); +} + +String::String(const wchar_t c) { + godot::api->godot_string_new_with_wide_string(&_godot_string, &c, 1); +} + +String::String(const String &other) { + godot::api->godot_string_new_copy(&_godot_string, &other._godot_string); +} + +String::String(String &&other) { + godot::api->godot_string_new_copy(&_godot_string, &other._godot_string); +} + +String::~String() { + godot::api->godot_string_destroy(&_godot_string); +} + +wchar_t &String::operator[](const int idx) { + return *const_cast(godot::api->godot_string_operator_index(&_godot_string, idx)); +} + +wchar_t String::operator[](const int idx) const { + return *godot::api->godot_string_operator_index((godot_string *)&_godot_string, idx); +} + +int String::length() const { + return godot::api->godot_string_length(&_godot_string); +} + +void String::operator=(const String &s) { + godot::api->godot_string_destroy(&_godot_string); + godot::api->godot_string_new_copy(&_godot_string, &s._godot_string); +} + +void String::operator=(String &&s) { + godot::api->godot_string_destroy(&_godot_string); + godot::api->godot_string_new_copy(&_godot_string, &s._godot_string); +} + +bool String::operator==(const String &s) const { + return godot::api->godot_string_operator_equal(&_godot_string, &s._godot_string); +} + +bool String::operator!=(const String &s) const { + return !(*this == s); +} + +String String::operator+(const String &s) const { + return String(godot::api->godot_string_operator_plus(&_godot_string, &s._godot_string)); +} + +void String::operator+=(const String &s) { + *this = String(godot::api->godot_string_operator_plus(&_godot_string, &s._godot_string)); +} + +void String::operator+=(const wchar_t c) { + String _to_be_added = String(c); + *this = String(godot::api->godot_string_operator_plus(&_godot_string, &_to_be_added._godot_string)); +} + +bool String::operator<(const String &s) const { + return godot::api->godot_string_operator_less(&_godot_string, &s._godot_string); +} + +bool String::operator<=(const String &s) const { + return godot::api->godot_string_operator_less(&_godot_string, &s._godot_string) || + (*this == s); +} + +bool String::operator>(const String &s) const { + return !(*this <= s); +} + +bool String::operator>=(const String &s) const { + return !(*this < s); +} + +String::operator NodePath() const { + return NodePath(*this); +} + +const wchar_t *String::unicode_str() const { + return godot::api->godot_string_wide_str(&_godot_string); +} + +char *String::alloc_c_string() const { + godot_char_string contents = godot::api->godot_string_utf8(&_godot_string); + + int length = godot::api->godot_char_string_length(&contents); + + char *result = (char *)godot::api->godot_alloc(length + 1); + + if (result) { + memcpy(result, godot::api->godot_char_string_get_data(&contents), length + 1); + } + + godot::api->godot_char_string_destroy(&contents); + + return result; +} + +CharString String::utf8() const { + CharString ret; + + ret._char_string = godot::api->godot_string_utf8(&_godot_string); + + return ret; +} + +CharString String::ascii(bool p_extended) const { + CharString ret; + + if (p_extended) + ret._char_string = godot::api->godot_string_ascii_extended(&_godot_string); + else + ret._char_string = godot::api->godot_string_ascii(&_godot_string); + + return ret; +} + +String operator+(const char *a, const String &b) { + return String(a) + b; +} + +String operator+(const wchar_t *a, const String &b) { + return String(a) + b; +} + +bool String::begins_with(const String &p_string) const { + return godot::api->godot_string_begins_with(&_godot_string, &p_string._godot_string); +} + +bool String::begins_with_char_array(const char *p_char_array) const { + return godot::api->godot_string_begins_with_char_array(&_godot_string, p_char_array); +} + +PoolStringArray String::bigrams() const { + godot_array arr = godot::api->godot_string_bigrams(&_godot_string); + return Array(arr); +} + +String String::c_escape() const { + return String(godot::api->godot_string_c_escape(&_godot_string)); +} + +String String::c_unescape() const { + return String(godot::api->godot_string_c_unescape(&_godot_string)); +} + +String String::capitalize() const { + return String(godot::api->godot_string_capitalize(&_godot_string)); +} + +bool String::empty() const { + return godot::api->godot_string_empty(&_godot_string); +} + +bool String::ends_with(const String &p_string) const { + return godot::api->godot_string_ends_with(&_godot_string, &p_string._godot_string); +} + +void String::erase(int position, int chars) { + godot::api->godot_string_erase(&_godot_string, position, chars); +} + +int String::find(String p_what, int p_from) const { + return godot::api->godot_string_find_from(&_godot_string, p_what._godot_string, p_from); +} + +int String::find_last(String p_what) const { + return godot::api->godot_string_find_last(&_godot_string, p_what._godot_string); +} + +int String::findn(String p_what, int p_from) const { + return godot::api->godot_string_findn_from(&_godot_string, p_what._godot_string, p_from); +} + +String String::format(Variant values) const { + return String(godot::api->godot_string_format(&_godot_string, (godot_variant *)&values)); +} + +String String::format(Variant values, String placeholder) const { + godot_char_string contents = godot::api->godot_string_utf8(&placeholder._godot_string); + String new_string(godot::api->godot_string_format_with_custom_placeholder(&_godot_string, (godot_variant *)&values, godot::api->godot_char_string_get_data(&contents))); + godot::api->godot_char_string_destroy(&contents); + + return new_string; +} + +String String::get_base_dir() const { + return String(godot::api->godot_string_get_base_dir(&_godot_string)); +} + +String String::get_basename() const { + return String(godot::api->godot_string_get_basename(&_godot_string)); +} + +String String::get_extension() const { + return String(godot::api->godot_string_get_extension(&_godot_string)); +} + +String String::get_file() const { + return String(godot::api->godot_string_get_file(&_godot_string)); +} + +int String::hash() const { + return godot::api->godot_string_hash(&_godot_string); +} + +int String::hex_to_int() const { + return godot::api->godot_string_hex_to_int(&_godot_string); +} + +String String::insert(int position, String what) const { + return String(godot::api->godot_string_insert(&_godot_string, position, what._godot_string)); +} + +bool String::is_abs_path() const { + return godot::api->godot_string_is_abs_path(&_godot_string); +} + +bool String::is_rel_path() const { + return godot::api->godot_string_is_rel_path(&_godot_string); +} + +bool String::is_subsequence_of(String text) const { + return godot::api->godot_string_is_subsequence_of(&_godot_string, &text._godot_string); +} + +bool String::is_subsequence_ofi(String text) const { + return godot::api->godot_string_is_subsequence_ofi(&_godot_string, &text._godot_string); +} + +bool String::is_valid_float() const { + return godot::api->godot_string_is_valid_float(&_godot_string); +} + +bool String::is_valid_html_color() const { + return godot::api->godot_string_is_valid_html_color(&_godot_string); +} + +bool String::is_valid_identifier() const { + return godot::api->godot_string_is_valid_identifier(&_godot_string); +} + +bool String::is_valid_integer() const { + return godot::api->godot_string_is_numeric(&_godot_string); +} + +bool String::is_valid_ip_address() const { + return godot::api->godot_string_is_valid_ip_address(&_godot_string); +} + +String String::json_escape() const { + return String(godot::api->godot_string_json_escape(&_godot_string)); +} + +String String::left(int position) const { + return String(godot::api->godot_string_left(&_godot_string, position)); +} + +bool String::match(String expr) const { + return godot::api->godot_string_match(&_godot_string, &expr._godot_string); +} + +bool String::matchn(String expr) const { + return godot::api->godot_string_match(&_godot_string, &expr._godot_string); +} + +PoolByteArray String::md5_buffer() const { + godot_pool_byte_array arr = godot::api->godot_string_md5_buffer(&_godot_string); + return PoolByteArray(arr); +} + +String String::md5_text() const { + return String(godot::api->godot_string_md5_text(&_godot_string)); +} + +int String::ord_at(int at) const { + return godot::api->godot_string_ord_at(&_godot_string, at); +} + +String String::pad_decimals(int digits) const { + return String(godot::api->godot_string_pad_decimals(&_godot_string, digits)); +} + +String String::pad_zeros(int digits) const { + return String(godot::api->godot_string_pad_zeros(&_godot_string, digits)); +} + +String String::percent_decode() const { + return String(godot::api->godot_string_percent_decode(&_godot_string)); +} + +String String::percent_encode() const { + return String(godot::api->godot_string_percent_encode(&_godot_string)); +} + +String String::plus_file(String file) const { + return String(godot::api->godot_string_plus_file(&_godot_string, &file._godot_string)); +} + +String String::replace(String p_key, String p_with) const { + return String(godot::api->godot_string_replace(&_godot_string, p_key._godot_string, p_with._godot_string)); +} + +String String::replacen(String what, String forwhat) const { + return String(godot::api->godot_string_replacen(&_godot_string, what._godot_string, forwhat._godot_string)); +} + +int String::rfind(String p_what, int p_from) const { + return godot::api->godot_string_rfind_from(&_godot_string, p_what._godot_string, p_from); +} + +int String::rfindn(String p_what, int p_from) const { + return godot::api->godot_string_rfindn_from(&_godot_string, p_what._godot_string, p_from); +} + +String String::right(int position) const { + return String(godot::api->godot_string_right(&_godot_string, position)); +} + +PoolByteArray String::sha256_buffer() const { + godot_pool_byte_array arr = godot::api->godot_string_sha256_buffer(&_godot_string); + return PoolByteArray(arr); +} + +String String::sha256_text() const { + return String(godot::api->godot_string_sha256_text(&_godot_string)); +} + +float String::similarity(String text) const { + return godot::api->godot_string_similarity(&_godot_string, &text._godot_string); +} + +// TODO Suport allow_empty +PoolStringArray String::split(String divisor, bool /*allow_empty*/) const { + godot_array arr = godot::api->godot_string_split(&_godot_string, &divisor._godot_string); + return Array(arr); +} + +// TODO Suport allow_empty +PoolIntArray String::split_ints(String divisor, bool /*allow_empty*/) const { + godot_array arr = godot::api->godot_string_split_floats(&_godot_string, &divisor._godot_string); + return Array(arr); +} + +// TODO Suport allow_empty +PoolRealArray String::split_floats(String divisor, bool /*allow_empty*/) const { + // TODO The GDNative API returns godot_array, when according to the doc, it should have been godot_pool_real_array + godot_array arr = godot::api->godot_string_split_floats(&_godot_string, &divisor._godot_string); + Array wrapped_array(arr); + return PoolRealArray(wrapped_array); +} + +String String::strip_edges(bool left, bool right) const { + return String(godot::api->godot_string_strip_edges(&_godot_string, left, right)); +} + +String String::substr(int from, int len) const { + return String(godot::api->godot_string_substr(&_godot_string, from, len)); +} + +float String::to_float() const { + return godot::api->godot_string_to_float(&_godot_string); +} + +int64_t String::to_int() const { + return godot::api->godot_string_to_int(&_godot_string); +} + +String String::to_lower() const { + return String(godot::api->godot_string_to_lower(&_godot_string)); +} + +String String::to_upper() const { + return String(godot::api->godot_string_to_upper(&_godot_string)); +} + +String String::xml_escape() const { + return String(godot::api->godot_string_xml_escape(&_godot_string)); +} + +String String::xml_unescape() const { + return String(godot::api->godot_string_xml_unescape(&_godot_string)); +} + +signed char String::casecmp_to(String p_str) const { + return godot::api->godot_string_casecmp_to(&_godot_string, &p_str._godot_string); +} + +signed char String::nocasecmp_to(String p_str) const { + return godot::api->godot_string_nocasecmp_to(&_godot_string, &p_str._godot_string); +} + +signed char String::naturalnocasecmp_to(String p_str) const { + return godot::api->godot_string_naturalnocasecmp_to(&_godot_string, &p_str._godot_string); +} + +String String::dedent() const { + godot_string s = godot::core_1_1_api->godot_string_dedent(&_godot_string); + return String(s); +} + +PoolStringArray String::rsplit(const String &divisor, const bool allow_empty, const int maxsplit) const { + godot_pool_string_array arr = + godot::core_1_1_api->godot_string_rsplit(&_godot_string, &divisor._godot_string, allow_empty, maxsplit); + return PoolStringArray(arr); +} + +String String::rstrip(const String &chars) const { + godot_string s = godot::core_1_1_api->godot_string_rstrip(&_godot_string, &chars._godot_string); + return String(s); +} + +String String::trim_prefix(const String &prefix) const { + godot_string s = godot::core_1_1_api->godot_string_trim_prefix(&_godot_string, &prefix._godot_string); + return String(s); +} + +String String::trim_suffix(const String &suffix) const { + godot_string s = godot::core_1_1_api->godot_string_trim_suffix(&_godot_string, &suffix._godot_string); + return String(s); +} + +} // namespace godot diff --git a/src/core/TagDB.cpp b/src/core/TagDB.cpp new file mode 100644 index 0000000..4a2b133 --- /dev/null +++ b/src/core/TagDB.cpp @@ -0,0 +1,78 @@ +/*************************************************************************/ +/* TagDP.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "TagDB.hpp" + +#include + +#include + +namespace godot { + +namespace _TagDB { + +std::unordered_map parent_to; + +void register_type(size_t type_tag, size_t base_type_tag) { + if (type_tag == base_type_tag) { + return; + } + parent_to[type_tag] = base_type_tag; +} + +bool is_type_known(size_t type_tag) { + return parent_to.find(type_tag) != parent_to.end(); +} + +void register_global_type(const char *name, size_t type_tag, size_t base_type_tag) { + godot::nativescript_1_1_api->godot_nativescript_set_global_type_tag(godot::_RegisterState::language_index, name, (const void *)type_tag); + + register_type(type_tag, base_type_tag); +} + +bool is_type_compatible(size_t ask_tag, size_t have_tag) { + if (have_tag == 0) + return false; + + size_t tag = have_tag; + + while (tag != 0) { + if (tag == ask_tag) + return true; + + tag = parent_to[tag]; + } + + return false; +} + +} // namespace _TagDB + +} // namespace godot diff --git a/src/core/Transform.cpp b/src/core/Transform.cpp new file mode 100644 index 0000000..d430c72 --- /dev/null +++ b/src/core/Transform.cpp @@ -0,0 +1,305 @@ +/*************************************************************************/ +/* Transform.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "Transform.hpp" + +#include "Basis.hpp" + +#include "AABB.hpp" +#include "Plane.hpp" + +#include "Quat.hpp" + +namespace godot { + +const Transform Transform::IDENTITY = Transform(); +const Transform Transform::FLIP_X = Transform(-1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0); +const Transform Transform::FLIP_Y = Transform(1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0); +const Transform Transform::FLIP_Z = Transform(1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0); + +Transform Transform::inverse_xform(const Transform &t) const { + Vector3 v = t.origin - origin; + return Transform(basis.transpose_xform(t.basis), + basis.xform(v)); +} + +void Transform::set(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t tx, real_t ty, real_t tz) { + basis.elements[0][0] = xx; + basis.elements[0][1] = xy; + basis.elements[0][2] = xz; + basis.elements[1][0] = yx; + basis.elements[1][1] = yy; + basis.elements[1][2] = yz; + basis.elements[2][0] = zx; + basis.elements[2][1] = zy; + basis.elements[2][2] = zz; + origin.x = tx; + origin.y = ty; + origin.z = tz; +} + +Vector3 Transform::xform(const Vector3 &p_vector) const { + return Vector3( + basis.elements[0].dot(p_vector) + origin.x, + basis.elements[1].dot(p_vector) + origin.y, + basis.elements[2].dot(p_vector) + origin.z); +} +Vector3 Transform::xform_inv(const Vector3 &p_vector) const { + Vector3 v = p_vector - origin; + + return Vector3( + (basis.elements[0][0] * v.x) + (basis.elements[1][0] * v.y) + (basis.elements[2][0] * v.z), + (basis.elements[0][1] * v.x) + (basis.elements[1][1] * v.y) + (basis.elements[2][1] * v.z), + (basis.elements[0][2] * v.x) + (basis.elements[1][2] * v.y) + (basis.elements[2][2] * v.z)); +} + +Plane Transform::xform(const Plane &p_plane) const { + Vector3 point = p_plane.normal * p_plane.d; + Vector3 point_dir = point + p_plane.normal; + point = xform(point); + point_dir = xform(point_dir); + + Vector3 normal = point_dir - point; + normal.normalize(); + real_t d = normal.dot(point); + + return Plane(normal, d); +} +Plane Transform::xform_inv(const Plane &p_plane) const { + Vector3 point = p_plane.normal * p_plane.d; + Vector3 point_dir = point + p_plane.normal; + point = xform_inv(point); + point_dir = xform_inv(point_dir); + + Vector3 normal = point_dir - point; + normal.normalize(); + real_t d = normal.dot(point); + + return Plane(normal, d); +} + +AABB Transform::xform(const AABB &p_aabb) const { + /* define vertices */ + Vector3 x = basis.get_axis(0) * p_aabb.size.x; + Vector3 y = basis.get_axis(1) * p_aabb.size.y; + Vector3 z = basis.get_axis(2) * p_aabb.size.z; + Vector3 pos = xform(p_aabb.position); + //could be even further optimized + AABB new_aabb; + new_aabb.position = pos; + new_aabb.expand_to(pos + x); + new_aabb.expand_to(pos + y); + new_aabb.expand_to(pos + z); + new_aabb.expand_to(pos + x + y); + new_aabb.expand_to(pos + x + z); + new_aabb.expand_to(pos + y + z); + new_aabb.expand_to(pos + x + y + z); + return new_aabb; +} +AABB Transform::xform_inv(const AABB &p_aabb) const { + /* define vertices */ + Vector3 vertices[8] = { + Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb.size.z), + Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z), + Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y, p_aabb.position.z + p_aabb.size.z), + Vector3(p_aabb.position.x + p_aabb.size.x, p_aabb.position.y, p_aabb.position.z), + Vector3(p_aabb.position.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z + p_aabb.size.z), + Vector3(p_aabb.position.x, p_aabb.position.y + p_aabb.size.y, p_aabb.position.z), + Vector3(p_aabb.position.x, p_aabb.position.y, p_aabb.position.z + p_aabb.size.z), + Vector3(p_aabb.position.x, p_aabb.position.y, p_aabb.position.z) + }; + + AABB ret; + + ret.position = xform_inv(vertices[0]); + + for (int i = 1; i < 8; i++) { + ret.expand_to(xform_inv(vertices[i])); + } + + return ret; +} + +void Transform::affine_invert() { + basis.invert(); + origin = basis.xform(-origin); +} + +Transform Transform::affine_inverse() const { + Transform ret = *this; + ret.affine_invert(); + return ret; +} + +void Transform::invert() { + basis.transpose(); + origin = basis.xform(-origin); +} + +Transform Transform::inverse() const { + // FIXME: this function assumes the basis is a rotation matrix, with no scaling. + // Transform::affine_inverse can handle matrices with scaling, so GDScript should eventually use that. + Transform ret = *this; + ret.invert(); + return ret; +} + +void Transform::rotate(const Vector3 &p_axis, real_t p_phi) { + *this = rotated(p_axis, p_phi); +} + +Transform Transform::rotated(const Vector3 &p_axis, real_t p_phi) const { + return Transform(Basis(p_axis, p_phi), Vector3()) * (*this); +} + +void Transform::rotate_basis(const Vector3 &p_axis, real_t p_phi) { + basis.rotate(p_axis, p_phi); +} + +Transform Transform::looking_at(const Vector3 &p_target, const Vector3 &p_up) const { + Transform t = *this; + t.set_look_at(origin, p_target, p_up); + return t; +} + +void Transform::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up) { + // Reference: MESA source code + Vector3 v_x, v_y, v_z; + + /* Make rotation matrix */ + + /* Z vector */ + v_z = p_eye - p_target; + + v_z.normalize(); + + v_y = p_up; + + v_x = v_y.cross(v_z); + + /* Recompute Y = Z cross X */ + v_y = v_z.cross(v_x); + + v_x.normalize(); + v_y.normalize(); + + basis.set_axis(0, v_x); + basis.set_axis(1, v_y); + basis.set_axis(2, v_z); + origin = p_eye; +} + +Transform Transform::interpolate_with(const Transform &p_transform, real_t p_c) const { + /* not sure if very "efficient" but good enough? */ + + Vector3 src_scale = basis.get_scale(); + Quat src_rot = basis; + Vector3 src_loc = origin; + + Vector3 dst_scale = p_transform.basis.get_scale(); + Quat dst_rot = p_transform.basis; + Vector3 dst_loc = p_transform.origin; + + Transform dst; + dst.basis = src_rot.slerp(dst_rot, p_c); + dst.basis.scale(src_scale.linear_interpolate(dst_scale, p_c)); + dst.origin = src_loc.linear_interpolate(dst_loc, p_c); + + return dst; +} + +void Transform::scale(const Vector3 &p_scale) { + basis.scale(p_scale); + origin *= p_scale; +} + +Transform Transform::scaled(const Vector3 &p_scale) const { + Transform t = *this; + t.scale(p_scale); + return t; +} + +void Transform::scale_basis(const Vector3 &p_scale) { + basis.scale(p_scale); +} + +void Transform::translate(real_t p_tx, real_t p_ty, real_t p_tz) { + translate(Vector3(p_tx, p_ty, p_tz)); +} +void Transform::translate(const Vector3 &p_translation) { + for (int i = 0; i < 3; i++) { + origin[i] += basis.elements[i].dot(p_translation); + } +} + +Transform Transform::translated(const Vector3 &p_translation) const { + Transform t = *this; + t.translate(p_translation); + return t; +} + +void Transform::orthonormalize() { + basis.orthonormalize(); +} + +Transform Transform::orthonormalized() const { + Transform _copy = *this; + _copy.orthonormalize(); + return _copy; +} + +bool Transform::operator==(const Transform &p_transform) const { + return (basis == p_transform.basis && origin == p_transform.origin); +} +bool Transform::operator!=(const Transform &p_transform) const { + return (basis != p_transform.basis || origin != p_transform.origin); +} + +void Transform::operator*=(const Transform &p_transform) { + origin = xform(p_transform.origin); + basis *= p_transform.basis; +} + +Transform Transform::operator*(const Transform &p_transform) const { + Transform t = *this; + t *= p_transform; + return t; +} + +Transform::operator String() const { + return basis.operator String() + " - " + origin.operator String(); +} + +Transform::Transform(const Basis &p_basis, const Vector3 &p_origin) { + basis = p_basis; + origin = p_origin; +} + +} // namespace godot diff --git a/src/core/Transform2D.cpp b/src/core/Transform2D.cpp new file mode 100644 index 0000000..7edc9c3 --- /dev/null +++ b/src/core/Transform2D.cpp @@ -0,0 +1,332 @@ +/*************************************************************************/ +/* Transform2D.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "Transform2D.hpp" +#include "Rect2.hpp" +#include "String.hpp" +#include "Vector2.hpp" + +#include + +namespace godot { + +const Transform2D Transform2D::IDENTITY; +const Transform2D Transform2D::FLIP_X = Transform2D(-1, 0, 0, 1, 0, 0); +const Transform2D Transform2D::FLIP_Y = Transform2D(1, 0, 0, -1, 0, 0); + +Transform2D::Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy) { + elements[0][0] = xx; + elements[0][1] = xy; + elements[1][0] = yx; + elements[1][1] = yy; + elements[2][0] = ox; + elements[2][1] = oy; +} + +Vector2 Transform2D::basis_xform(const Vector2 &v) const { + return Vector2( + tdotx(v), + tdoty(v)); +} + +Vector2 Transform2D::basis_xform_inv(const Vector2 &v) const { + return Vector2( + elements[0].dot(v), + elements[1].dot(v)); +} + +Vector2 Transform2D::xform(const Vector2 &v) const { + return Vector2( + tdotx(v), + tdoty(v)) + + elements[2]; +} +Vector2 Transform2D::xform_inv(const Vector2 &p_vec) const { + Vector2 v = p_vec - elements[2]; + + return Vector2( + elements[0].dot(v), + elements[1].dot(v)); +} +Rect2 Transform2D::xform(const Rect2 &p_rect) const { + Vector2 x = elements[0] * p_rect.size.x; + Vector2 y = elements[1] * p_rect.size.y; + Vector2 position = xform(p_rect.position); + + Rect2 new_rect; + new_rect.position = position; + new_rect.expand_to(position + x); + new_rect.expand_to(position + y); + new_rect.expand_to(position + x + y); + return new_rect; +} + +void Transform2D::set_rotation_and_scale(real_t p_rot, const Size2 &p_scale) { + elements[0][0] = ::cos(p_rot) * p_scale.x; + elements[1][1] = ::cos(p_rot) * p_scale.y; + elements[1][0] = -::sin(p_rot) * p_scale.y; + elements[0][1] = ::sin(p_rot) * p_scale.x; +} + +Rect2 Transform2D::xform_inv(const Rect2 &p_rect) const { + Vector2 ends[4] = { + xform_inv(p_rect.position), + xform_inv(Vector2(p_rect.position.x, p_rect.position.y + p_rect.size.y)), + xform_inv(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y + p_rect.size.y)), + xform_inv(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y)) + }; + + Rect2 new_rect; + new_rect.position = ends[0]; + new_rect.expand_to(ends[1]); + new_rect.expand_to(ends[2]); + new_rect.expand_to(ends[3]); + + return new_rect; +} + +void Transform2D::invert() { + // FIXME: this function assumes the basis is a rotation matrix, with no scaling. + // Transform2D::affine_inverse can handle matrices with scaling, so GDScript should eventually use that. + std::swap(elements[0][1], elements[1][0]); + elements[2] = basis_xform(-elements[2]); +} + +Transform2D Transform2D::inverse() const { + Transform2D inv = *this; + inv.invert(); + return inv; +} + +void Transform2D::affine_invert() { + real_t det = basis_determinant(); + ERR_FAIL_COND(det == 0); + real_t idet = 1.0 / det; + + std::swap(elements[0][0], elements[1][1]); + elements[0] *= Vector2(idet, -idet); + elements[1] *= Vector2(-idet, idet); + + elements[2] = basis_xform(-elements[2]); +} + +Transform2D Transform2D::affine_inverse() const { + Transform2D inv = *this; + inv.affine_invert(); + return inv; +} + +void Transform2D::rotate(real_t p_phi) { + *this = Transform2D(p_phi, Vector2()) * (*this); +} + +real_t Transform2D::get_rotation() const { + real_t det = basis_determinant(); + Transform2D m = orthonormalized(); + if (det < 0) { + m.scale_basis(Size2(-1, -1)); + } + return ::atan2(m[0].y, m[0].x); +} + +void Transform2D::set_rotation(real_t p_rot) { + real_t cr = ::cos(p_rot); + real_t sr = ::sin(p_rot); + elements[0][0] = cr; + elements[0][1] = sr; + elements[1][0] = -sr; + elements[1][1] = cr; +} + +Transform2D::Transform2D(real_t p_rot, const Vector2 &p_position) { + real_t cr = ::cos(p_rot); + real_t sr = ::sin(p_rot); + elements[0][0] = cr; + elements[0][1] = sr; + elements[1][0] = -sr; + elements[1][1] = cr; + elements[2] = p_position; +} + +Size2 Transform2D::get_scale() const { + real_t det_sign = basis_determinant() > 0 ? 1 : -1; + return det_sign * Size2(elements[0].length(), elements[1].length()); +} + +void Transform2D::scale(const Size2 &p_scale) { + scale_basis(p_scale); + elements[2] *= p_scale; +} +void Transform2D::scale_basis(const Size2 &p_scale) { + elements[0][0] *= p_scale.x; + elements[0][1] *= p_scale.y; + elements[1][0] *= p_scale.x; + elements[1][1] *= p_scale.y; +} +void Transform2D::translate(real_t p_tx, real_t p_ty) { + translate(Vector2(p_tx, p_ty)); +} +void Transform2D::translate(const Vector2 &p_translation) { + elements[2] += basis_xform(p_translation); +} + +void Transform2D::orthonormalize() { + // Gram-Schmidt Process + + Vector2 x = elements[0]; + Vector2 y = elements[1]; + + x.normalize(); + y = (y - x * (x.dot(y))); + y.normalize(); + + elements[0] = x; + elements[1] = y; +} +Transform2D Transform2D::orthonormalized() const { + Transform2D on = *this; + on.orthonormalize(); + return on; +} + +bool Transform2D::operator==(const Transform2D &p_transform) const { + for (int i = 0; i < 3; i++) { + if (elements[i] != p_transform.elements[i]) + return false; + } + + return true; +} + +bool Transform2D::operator!=(const Transform2D &p_transform) const { + for (int i = 0; i < 3; i++) { + if (elements[i] != p_transform.elements[i]) + return true; + } + + return false; +} + +void Transform2D::operator*=(const Transform2D &p_transform) { + elements[2] = xform(p_transform.elements[2]); + + real_t x0, x1, y0, y1; + + x0 = tdotx(p_transform.elements[0]); + x1 = tdoty(p_transform.elements[0]); + y0 = tdotx(p_transform.elements[1]); + y1 = tdoty(p_transform.elements[1]); + + elements[0][0] = x0; + elements[0][1] = x1; + elements[1][0] = y0; + elements[1][1] = y1; +} + +Transform2D Transform2D::operator*(const Transform2D &p_transform) const { + Transform2D t = *this; + t *= p_transform; + return t; +} + +Transform2D Transform2D::scaled(const Size2 &p_scale) const { + Transform2D copy = *this; + copy.scale(p_scale); + return copy; +} + +Transform2D Transform2D::basis_scaled(const Size2 &p_scale) const { + Transform2D copy = *this; + copy.scale_basis(p_scale); + return copy; +} + +Transform2D Transform2D::untranslated() const { + Transform2D copy = *this; + copy.elements[2] = Vector2(); + return copy; +} + +Transform2D Transform2D::translated(const Vector2 &p_offset) const { + Transform2D copy = *this; + copy.translate(p_offset); + return copy; +} + +Transform2D Transform2D::rotated(real_t p_phi) const { + Transform2D copy = *this; + copy.rotate(p_phi); + return copy; +} + +real_t Transform2D::basis_determinant() const { + return elements[0].x * elements[1].y - elements[0].y * elements[1].x; +} + +Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, real_t p_c) const { + //extract parameters + Vector2 p1 = get_origin(); + Vector2 p2 = p_transform.get_origin(); + + real_t r1 = get_rotation(); + real_t r2 = p_transform.get_rotation(); + + Size2 s1 = get_scale(); + Size2 s2 = p_transform.get_scale(); + + //slerp rotation + Vector2 v1(::cos(r1), ::sin(r1)); + Vector2 v2(::cos(r2), ::sin(r2)); + + real_t dot = v1.dot(v2); + + dot = (dot < -1.0) ? -1.0 : ((dot > 1.0) ? 1.0 : dot); //clamp dot to [-1,1] + + Vector2 v; + + if (dot > 0.9995) { + v = Vector2::linear_interpolate(v1, v2, p_c).normalized(); //linearly interpolate to avoid numerical precision issues + } else { + real_t angle = p_c * ::acos(dot); + Vector2 v3 = (v2 - v1 * dot).normalized(); + v = v1 * ::cos(angle) + v3 * ::sin(angle); + } + + //construct matrix + Transform2D res(::atan2(v.y, v.x), Vector2::linear_interpolate(p1, p2, p_c)); + res.scale_basis(Vector2::linear_interpolate(s1, s2, p_c)); + return res; +} + +Transform2D::operator String() const { + return String(String() + elements[0] + ", " + elements[1] + ", " + elements[2]); +} + +} // namespace godot diff --git a/src/core/Variant.cpp b/src/core/Variant.cpp new file mode 100644 index 0000000..d72e7c5 --- /dev/null +++ b/src/core/Variant.cpp @@ -0,0 +1,382 @@ +/*************************************************************************/ +/* Variant.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "Variant.hpp" + +#include + +#include "CoreTypes.hpp" +#include "Defs.hpp" +#include "GodotGlobal.hpp" +#include "Object.hpp" + +namespace godot { + +Variant::Variant() { + godot::api->godot_variant_new_nil(&_godot_variant); +} + +Variant::Variant(const Variant &v) { + godot::api->godot_variant_new_copy(&_godot_variant, &v._godot_variant); +} + +Variant::Variant(bool p_bool) { + godot::api->godot_variant_new_bool(&_godot_variant, p_bool); +} + +Variant::Variant(signed int p_int) // real one +{ + godot::api->godot_variant_new_int(&_godot_variant, p_int); +} + +Variant::Variant(unsigned int p_int) { + godot::api->godot_variant_new_uint(&_godot_variant, p_int); +} + +Variant::Variant(signed short p_short) // real one +{ + godot::api->godot_variant_new_int(&_godot_variant, (int)p_short); +} + +Variant::Variant(int64_t p_char) // real one +{ + godot::api->godot_variant_new_int(&_godot_variant, p_char); +} + +Variant::Variant(uint64_t p_char) { + godot::api->godot_variant_new_uint(&_godot_variant, p_char); +} + +Variant::Variant(float p_float) { + godot::api->godot_variant_new_real(&_godot_variant, p_float); +} + +Variant::Variant(double p_double) { + godot::api->godot_variant_new_real(&_godot_variant, p_double); +} + +Variant::Variant(const String &p_string) { + godot::api->godot_variant_new_string(&_godot_variant, (godot_string *)&p_string); +} + +Variant::Variant(const char *const p_cstring) { + String s = String(p_cstring); + godot::api->godot_variant_new_string(&_godot_variant, (godot_string *)&s); +} + +Variant::Variant(const wchar_t *p_wstring) { + String s = p_wstring; + godot::api->godot_variant_new_string(&_godot_variant, (godot_string *)&s); +} + +Variant::Variant(const Vector2 &p_vector2) { + godot::api->godot_variant_new_vector2(&_godot_variant, (godot_vector2 *)&p_vector2); +} + +Variant::Variant(const Rect2 &p_rect2) { + godot::api->godot_variant_new_rect2(&_godot_variant, (godot_rect2 *)&p_rect2); +} + +Variant::Variant(const Vector3 &p_vector3) { + godot::api->godot_variant_new_vector3(&_godot_variant, (godot_vector3 *)&p_vector3); +} + +Variant::Variant(const Plane &p_plane) { + godot::api->godot_variant_new_plane(&_godot_variant, (godot_plane *)&p_plane); +} + +Variant::Variant(const AABB &p_aabb) { + godot::api->godot_variant_new_aabb(&_godot_variant, (godot_aabb *)&p_aabb); +} + +Variant::Variant(const Quat &p_quat) { + godot::api->godot_variant_new_quat(&_godot_variant, (godot_quat *)&p_quat); +} + +Variant::Variant(const Basis &p_transform) { + godot::api->godot_variant_new_basis(&_godot_variant, (godot_basis *)&p_transform); +} + +Variant::Variant(const Transform2D &p_transform) { + godot::api->godot_variant_new_transform2d(&_godot_variant, (godot_transform2d *)&p_transform); +} + +Variant::Variant(const Transform &p_transform) { + godot::api->godot_variant_new_transform(&_godot_variant, (godot_transform *)&p_transform); +} + +Variant::Variant(const Color &p_color) { + godot::api->godot_variant_new_color(&_godot_variant, (godot_color *)&p_color); +} + +Variant::Variant(const NodePath &p_path) { + godot::api->godot_variant_new_node_path(&_godot_variant, (godot_node_path *)&p_path); +} + +Variant::Variant(const RID &p_rid) { + godot::api->godot_variant_new_rid(&_godot_variant, (godot_rid *)&p_rid); +} + +Variant::Variant(const Object *p_object) { + if (p_object) + godot::api->godot_variant_new_object(&_godot_variant, p_object->_owner); + else + godot::api->godot_variant_new_nil(&_godot_variant); +} + +Variant::Variant(const Dictionary &p_dictionary) { + godot::api->godot_variant_new_dictionary(&_godot_variant, (godot_dictionary *)&p_dictionary); +} + +Variant::Variant(const Array &p_array) { + godot::api->godot_variant_new_array(&_godot_variant, (godot_array *)&p_array); +} + +Variant::Variant(const PoolByteArray &p_raw_array) { + godot::api->godot_variant_new_pool_byte_array(&_godot_variant, (godot_pool_byte_array *)&p_raw_array); +} + +Variant::Variant(const PoolIntArray &p_int_array) { + godot::api->godot_variant_new_pool_int_array(&_godot_variant, (godot_pool_int_array *)&p_int_array); +} + +Variant::Variant(const PoolRealArray &p_real_array) { + godot::api->godot_variant_new_pool_real_array(&_godot_variant, (godot_pool_real_array *)&p_real_array); +} + +Variant::Variant(const PoolStringArray &p_string_array) { + godot::api->godot_variant_new_pool_string_array(&_godot_variant, (godot_pool_string_array *)&p_string_array); +} + +Variant::Variant(const PoolVector2Array &p_vector2_array) { + godot::api->godot_variant_new_pool_vector2_array(&_godot_variant, (godot_pool_vector2_array *)&p_vector2_array); +} + +Variant::Variant(const PoolVector3Array &p_vector3_array) { + godot::api->godot_variant_new_pool_vector3_array(&_godot_variant, (godot_pool_vector3_array *)&p_vector3_array); +} + +Variant::Variant(const PoolColorArray &p_color_array) { + godot::api->godot_variant_new_pool_color_array(&_godot_variant, (godot_pool_color_array *)&p_color_array); +} + +Variant &Variant::operator=(const Variant &v) { + godot::api->godot_variant_destroy(&_godot_variant); + godot::api->godot_variant_new_copy(&_godot_variant, &v._godot_variant); + return *this; +} + +Variant::operator bool() const { + return booleanize(); +} +Variant::operator signed int() const { + return godot::api->godot_variant_as_int(&_godot_variant); +} +Variant::operator unsigned int() const // this is the real one +{ + return godot::api->godot_variant_as_uint(&_godot_variant); +} +Variant::operator signed short() const { + return godot::api->godot_variant_as_int(&_godot_variant); +} +Variant::operator unsigned short() const { + return godot::api->godot_variant_as_uint(&_godot_variant); +} +Variant::operator signed char() const { + return godot::api->godot_variant_as_int(&_godot_variant); +} +Variant::operator unsigned char() const { + return godot::api->godot_variant_as_uint(&_godot_variant); +} +Variant::operator int64_t() const { + return godot::api->godot_variant_as_int(&_godot_variant); +} +Variant::operator uint64_t() const { + return godot::api->godot_variant_as_uint(&_godot_variant); +} + +Variant::operator wchar_t() const { + return godot::api->godot_variant_as_int(&_godot_variant); +} + +Variant::operator float() const { + return godot::api->godot_variant_as_real(&_godot_variant); +} + +Variant::operator double() const { + return godot::api->godot_variant_as_real(&_godot_variant); +} +Variant::operator String() const { + godot_string s = godot::api->godot_variant_as_string(&_godot_variant); + return String(s); +} +Variant::operator Vector2() const { + godot_vector2 s = godot::api->godot_variant_as_vector2(&_godot_variant); + return *(Vector2 *)&s; +} +Variant::operator Rect2() const { + godot_rect2 s = godot::api->godot_variant_as_rect2(&_godot_variant); + return *(Rect2 *)&s; +} +Variant::operator Vector3() const { + godot_vector3 s = godot::api->godot_variant_as_vector3(&_godot_variant); + return *(Vector3 *)&s; +} +Variant::operator Plane() const { + godot_plane s = godot::api->godot_variant_as_plane(&_godot_variant); + return *(Plane *)&s; +} +Variant::operator AABB() const { + godot_aabb s = godot::api->godot_variant_as_aabb(&_godot_variant); + return *(AABB *)&s; +} +Variant::operator Quat() const { + godot_quat s = godot::api->godot_variant_as_quat(&_godot_variant); + return *(Quat *)&s; +} +Variant::operator Basis() const { + godot_basis s = godot::api->godot_variant_as_basis(&_godot_variant); + return *(Basis *)&s; +} +Variant::operator Transform() const { + godot_transform s = godot::api->godot_variant_as_transform(&_godot_variant); + return *(Transform *)&s; +} +Variant::operator Transform2D() const { + godot_transform2d s = godot::api->godot_variant_as_transform2d(&_godot_variant); + return *(Transform2D *)&s; +} + +Variant::operator Color() const { + godot_color s = godot::api->godot_variant_as_color(&_godot_variant); + return *(Color *)&s; +} +Variant::operator NodePath() const { + godot_node_path ret = godot::api->godot_variant_as_node_path(&_godot_variant); + return NodePath(ret); +} +Variant::operator RID() const { + godot_rid s = godot::api->godot_variant_as_rid(&_godot_variant); + return *(RID *)&s; +} + +Variant::operator Dictionary() const { + Dictionary ret(godot::api->godot_variant_as_dictionary(&_godot_variant)); + return ret; +} + +Variant::operator Array() const { + Array ret(godot::api->godot_variant_as_array(&_godot_variant)); + return ret; +} + +Variant::operator PoolByteArray() const { + godot_pool_byte_array ret = godot::api->godot_variant_as_pool_byte_array(&_godot_variant); + return PoolByteArray(ret); +} +Variant::operator PoolIntArray() const { + godot_pool_int_array ret = godot::api->godot_variant_as_pool_int_array(&_godot_variant); + return PoolIntArray(ret); +} +Variant::operator PoolRealArray() const { + godot_pool_real_array ret = godot::api->godot_variant_as_pool_real_array(&_godot_variant); + return PoolRealArray(ret); +} +Variant::operator PoolStringArray() const { + godot_pool_string_array ret = godot::api->godot_variant_as_pool_string_array(&_godot_variant); + return PoolStringArray(ret); +} +Variant::operator PoolVector2Array() const { + godot_pool_vector2_array ret = godot::api->godot_variant_as_pool_vector2_array(&_godot_variant); + return PoolVector2Array(ret); +} +Variant::operator PoolVector3Array() const { + godot_pool_vector3_array ret = godot::api->godot_variant_as_pool_vector3_array(&_godot_variant); + return PoolVector3Array(ret); +} +Variant::operator PoolColorArray() const { + godot_pool_color_array ret = godot::api->godot_variant_as_pool_color_array(&_godot_variant); + return PoolColorArray(ret); +} +Variant::operator godot_object *() const { + return godot::api->godot_variant_as_object(&_godot_variant); +} + +Variant::Type Variant::get_type() const { + return static_cast(godot::api->godot_variant_get_type(&_godot_variant)); +} + +Variant Variant::call(const String &method, const Variant **args, const int arg_count) { + godot_variant v = godot::api->godot_variant_call( + &_godot_variant, (godot_string *)&method, (const godot_variant **)args, arg_count, nullptr); + return Variant(v); +} + +bool Variant::has_method(const String &method) { + return godot::api->godot_variant_has_method(&_godot_variant, (godot_string *)&method); +} + +bool Variant::operator==(const Variant &b) const { + return godot::api->godot_variant_operator_equal(&_godot_variant, &b._godot_variant); +} + +bool Variant::operator!=(const Variant &b) const { + return !(*this == b); +} + +bool Variant::operator<(const Variant &b) const { + return godot::api->godot_variant_operator_less(&_godot_variant, &b._godot_variant); +} + +bool Variant::operator<=(const Variant &b) const { + return (*this < b) || (*this == b); +} + +bool Variant::operator>(const Variant &b) const { + return !(*this <= b); +} + +bool Variant::operator>=(const Variant &b) const { + return !(*this < b); +} + +bool Variant::hash_compare(const Variant &b) const { + return godot::api->godot_variant_hash_compare(&_godot_variant, &b._godot_variant); +} + +bool Variant::booleanize() const { + return godot::api->godot_variant_booleanize(&_godot_variant); +} + +Variant::~Variant() { + godot::api->godot_variant_destroy(&_godot_variant); +} + +} // namespace godot diff --git a/src/core/Vector2.cpp b/src/core/Vector2.cpp new file mode 100644 index 0000000..4e70465 --- /dev/null +++ b/src/core/Vector2.cpp @@ -0,0 +1,100 @@ +/*************************************************************************/ +/* Vector2.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "Vector2.hpp" + +#include + +#include "String.hpp" + +namespace godot { + +const Vector2 Vector2::ZERO = Vector2(); +const Vector2 Vector2::ONE = Vector2(1, 1); +const Vector2 Vector2::INF = Vector2(INFINITY, INFINITY); + +const Vector2 Vector2::LEFT = Vector2(-1, 0); +const Vector2 Vector2::RIGHT = Vector2(1, 0); +const Vector2 Vector2::UP = Vector2(0, -1); +const Vector2 Vector2::DOWN = Vector2(0, 1); + +bool Vector2::operator==(const Vector2 &p_vec2) const { + return x == p_vec2.x && y == p_vec2.y; +} + +bool Vector2::operator!=(const Vector2 &p_vec2) const { + return x != p_vec2.x || y != p_vec2.y; +} + +Vector2 Vector2::project(const Vector2 &p_vec) const { + Vector2 v1 = p_vec; + Vector2 v2 = *this; + return v2 * (v1.dot(v2) / v2.dot(v2)); +} + +Vector2 Vector2::plane_project(real_t p_d, const Vector2 &p_vec) const { + return p_vec - *this * (dot(p_vec) - p_d); +} + +Vector2 Vector2::clamped(real_t p_len) const { + real_t l = length(); + Vector2 v = *this; + if (l > 0 && p_len < l) { + v /= l; + v *= p_len; + } + return v; +} + +Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_t) const { + Vector2 p0 = p_pre_a; + Vector2 p1 = *this; + Vector2 p2 = p_b; + Vector2 p3 = p_post_b; + + real_t t = p_t; + real_t t2 = t * t; + real_t t3 = t2 * t; + + Vector2 out; + out = ((p1 * 2.0) + + (-p0 + p2) * t + + (p0 * 2.0 - p1 * 5.0 + p2 * 4 - p3) * t2 + + (-p0 + p1 * 3.0 - p2 * 3.0 + p3) * t3) * + 0.5; + + return out; +} + +Vector2::operator String() const { + return String::num(x) + ", " + String::num(y); +} + +} // namespace godot diff --git a/src/core/Vector3.cpp b/src/core/Vector3.cpp new file mode 100644 index 0000000..4bf37c3 --- /dev/null +++ b/src/core/Vector3.cpp @@ -0,0 +1,121 @@ +/*************************************************************************/ +/* Vector3.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "Vector3.hpp" + +#include "String.hpp" + +#include + +#include "Basis.hpp" + +namespace godot { + +const Vector3 Vector3::ZERO = Vector3(); +const Vector3 Vector3::ONE = Vector3(1, 1, 1); +const Vector3 Vector3::INF = Vector3(INFINITY, INFINITY, INFINITY); + +const Vector3 Vector3::LEFT = Vector3(-1, 0, 0); +const Vector3 Vector3::RIGHT = Vector3(1, 0, 0); +const Vector3 Vector3::UP = Vector3(0, 1, 0); +const Vector3 Vector3::DOWN = Vector3(0, -1, 0); +const Vector3 Vector3::FORWARD = Vector3(0, 0, -1); +const Vector3 Vector3::BACK = Vector3(0, 0, 1); + +bool Vector3::operator<(const Vector3 &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) + return z < p_v.z; + else + return y < p_v.y; + } else { + return x < p_v.x; + } +} + +bool Vector3::operator<=(const Vector3 &p_v) const { + if (x == p_v.x) { + if (y == p_v.y) + return z <= p_v.z; + else + return y < p_v.y; + } else { + return x < p_v.x; + } +} + +Vector3 Vector3::cubic_interpolate(const Vector3 &b, const Vector3 &pre_a, const Vector3 &post_b, const real_t t) const { + Vector3 p0 = pre_a; + Vector3 p1 = *this; + Vector3 p2 = b; + Vector3 p3 = post_b; + + real_t t2 = t * t; + real_t t3 = t2 * t; + + Vector3 out; + out = ((p1 * 2.0) + + (-p0 + p2) * t + + (p0 * 2.0 - p1 * 5.0 + p2 * 4 - p3) * t2 + + (-p0 + p1 * 3.0 - p2 * 3.0 + p3) * t3) * + 0.5; + return out; +} + +Basis Vector3::outer(const Vector3 &b) const { + Vector3 row0(x * b.x, x * b.y, x * b.z); + Vector3 row1(y * b.x, y * b.y, y * b.z); + Vector3 row2(z * b.x, z * b.y, z * b.z); + return Basis(row0, row1, row2); +} + +int Vector3::max_axis() const { + return x < y ? (y < z ? 2 : 1) : (x < z ? 2 : 0); +} + +int Vector3::min_axis() const { + return x < y ? (x < z ? 0 : 2) : (y < z ? 1 : 2); +} + +void Vector3::rotate(const Vector3 &p_axis, real_t p_phi) { + *this = Basis(p_axis, p_phi).xform(*this); +} + +void Vector3::snap(real_t p_val) { + x = Math::stepify(x, p_val); + y = Math::stepify(y, p_val); + z = Math::stepify(z, p_val); +} + +Vector3::operator String() const { + return String::num(x) + ", " + String::num(y) + ", " + String::num(z); +} + +} // namespace godot diff --git a/src/gen/.gitignore b/src/gen/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/src/gen/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..e0b51db --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,21 @@ +# Godot 4+ specific ignores +.godot/ + +# Godot-specific ignores +.import/ +export.cfg +export_presets.cfg +# Dummy HTML5 export presets file for continuous integration +!.github/dist/export_presets.cfg + +# Imported translations (automatically generated from CSV files) +*.translation + +# Mono-specific ignores +.mono/ +data_*/ +mono_crash.*.json + +# System/tool-specific ignores +.directory +*~ diff --git a/test/SConstruct b/test/SConstruct new file mode 100644 index 0000000..9bfd43d --- /dev/null +++ b/test/SConstruct @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +env = SConscript("../SConstruct") + +# tweak this if you want to use different folders, or more folders, to store your source code in. +env.Append(CPPPATH=['src/']) +sources = Glob('src/*.cpp') + +library = env.SharedLibrary( + "bin/libgdexample.{}.{}.{}{}".format( + env["platform"], env["target"], env["arch_suffix"], env["SHLIBSUFFIX"] + ), + source=sources, +) + +Default(library) diff --git a/test/gdexample.gdnlib b/test/gdexample.gdnlib new file mode 100644 index 0000000..9744b98 --- /dev/null +++ b/test/gdexample.gdnlib @@ -0,0 +1,20 @@ +[general] + +singleton=false +load_once=true +symbol_prefix="godot_" +reloadable=false + +[entry] + +X11.64="res://bin/libgdexample.linux.release.64.so" +Server.64="res://bin/libgdexample.linux.release.64.so" +Windows.64="res://bin/libgdexample.windows.release.64.dll" +OSX.64="res://bin/libgdexample.osx.release.64.dylib" + +[dependencies] + +X11.64=[] +Server.64=[] +Windows.64=[] +OSX.64=[] diff --git a/test/gdexample.gdns b/test/gdexample.gdns new file mode 100644 index 0000000..a9d02d3 --- /dev/null +++ b/test/gdexample.gdns @@ -0,0 +1,9 @@ +[gd_resource type="NativeScript" load_steps=2 format=2] + +[ext_resource path="res://gdexample.gdnlib" type="GDNativeLibrary" id=1] + +[resource] + +resource_name = "gdexample" +class_name = "SimpleClass" +library = ExtResource( 1 ) diff --git a/test/project.godot b/test/project.godot new file mode 100644 index 0000000..a3e6045 --- /dev/null +++ b/test/project.godot @@ -0,0 +1,17 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=4 + +_global_script_classes=[ ] +_global_script_class_icons={ +} + +[application] + +config/name="Test CI project" diff --git a/test/script.gd b/test/script.gd new file mode 100644 index 0000000..94f8ecb --- /dev/null +++ b/test/script.gd @@ -0,0 +1,30 @@ +extends MainLoop + + +func _initialize(): + OS.exit_code = 1 + var native_script = load("res://gdexample.gdns") + print("Native Script ", native_script) + if not native_script || !is_instance_valid(native_script): + return + print("Library ", native_script.library) + if not native_script.library || !is_instance_valid(native_script.library): + return + var ref = native_script.new() + print("Reference ", ref) + if not ref || !is_instance_valid(ref): + return + print("Reference name ", ref.name) + if ref.name != "SimpleClass": + return + print("Reference value ", ref.value) + if ref.value != 0: + return + print("Call method ", ref.method(1)) + if ref.method(1) != 1: + return + OS.exit_code = 0 + + +func _idle(_delta): + return true diff --git a/test/src/init.cpp b/test/src/init.cpp new file mode 100644 index 0000000..9714970 --- /dev/null +++ b/test/src/init.cpp @@ -0,0 +1,103 @@ +/*************************************************************************/ +/* init.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include +#include + +using namespace godot; + +class SimpleClass : public Reference { + GODOT_CLASS(SimpleClass, Reference); + +public: + SimpleClass() {} + + /** `_init` must exist as it is called by Godot. */ + void _init() { + _name = String("SimpleClass"); + _value = 0; + } + + void test_void_method() { + Godot::print("This is test"); + } + + Variant method(Variant arg) { + Variant ret; + ret = arg; + + return ret; + } + + static void _register_methods() { + register_method("method", &SimpleClass::method); + + /** + * The line below is equivalent to the following GDScript export: + * export var _name = "SimpleClass" + **/ + register_property("name", &SimpleClass::_name, String("SimpleClass")); + + /** Alternatively, with getter and setter methods: */ + register_property("value", &SimpleClass::set_value, &SimpleClass::get_value, 0); + + /** Registering a signal: **/ + register_signal("signal_name0"); // windows: error C2668: 'godot::register_signal': ambiguous call to overloaded function + register_signal("signal_name1", "string_argument", GODOT_VARIANT_TYPE_STRING); + } + + String _name; + int _value; + + void set_value(int p_value) { + _value = p_value; + } + + int get_value() const { + return _value; + } +}; + +/** GDNative Initialize **/ +extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) { + godot::Godot::gdnative_init(o); +} + +/** GDNative Terminate **/ +extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) { + godot::Godot::gdnative_terminate(o); +} + +/** NativeScript Initialize **/ +extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) { + godot::Godot::nativescript_init(handle); + + godot::register_class(); +}