diff --git a/Redot-Documentation/Components/Layout/MainLayout.razor.css b/Redot-Documentation/Components/Layout/MainLayout.razor.css index 67599a5..f3e9ea7 100644 --- a/Redot-Documentation/Components/Layout/MainLayout.razor.css +++ b/Redot-Documentation/Components/Layout/MainLayout.razor.css @@ -52,7 +52,7 @@ main { } .sidebar { - width: 250px; + width: 350px; height: 100vh; position: sticky; top: 0; diff --git a/Redot-Documentation/Components/Layout/NavMenu.razor.css b/Redot-Documentation/Components/Layout/NavMenu.razor.css index d319781..80a84de 100644 --- a/Redot-Documentation/Components/Layout/NavMenu.razor.css +++ b/Redot-Documentation/Components/Layout/NavMenu.razor.css @@ -1,7 +1,7 @@ .navbar-toggler { appearance: none; cursor: pointer; - width: 3.5rem; + width: 5.5rem; height: 2.5rem; color: white; position: absolute; @@ -99,12 +99,12 @@ } .nav-item ::deep .nav-article-link { - padding-left: calc(0.75rem + var(--section-level, 0) * 1rem); + padding-left: calc(0.5rem + var(--section-level, 0) * 0.25rem); } .nav-item ::deep .nav-section { margin: 0; - padding-left: calc(var(--section-level, 0) * 0.75rem); + padding-left: calc(var(--section-level, 0) * 0.25rem); } .nav-item ::deep .nav-section-summary { diff --git a/Redot-Documentation/Services/DocRendererService.cs b/Redot-Documentation/Services/DocRendererService.cs index 828db78..1687711 100644 --- a/Redot-Documentation/Services/DocRendererService.cs +++ b/Redot-Documentation/Services/DocRendererService.cs @@ -36,7 +36,13 @@ public async Task RenderToHtmlAsync(string documentPath, VersionProvider if (!File.Exists(fullPath)) { - throw new FileNotFoundException($"Markdown document not found: {documentPath}", fullPath); + fullPath = Path.GetFullPath(Path.Combine(versionProvider.VersionRoot, normalizedPath)); + if (!fullPath.StartsWith(docsRootPath, StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException("Document path points outside the docs directory."); + } + if (!File.Exists(fullPath)) + throw new FileNotFoundException($"Markdown document not found: {documentPath}", fullPath); } var markdown = await File.ReadAllTextAsync(fullPath, cancellationToken); diff --git a/Redot-Documentation/Versioning/IRanking.cs b/Redot-Documentation/Versioning/IRanking.cs index caabcc4..6836fb6 100644 --- a/Redot-Documentation/Versioning/IRanking.cs +++ b/Redot-Documentation/Versioning/IRanking.cs @@ -45,20 +45,43 @@ public interface IRanking : IComparable }; + private static Dictionary _specialWords = new Dictionary() + { + {"faq", "FAQ"}, + {"ip", "IP"}, + {"mac", "MAC"} + }; + public string GetDisplayName() { - string temp = Name.Replace('_', ' ').Trim(); + string temp = Name.Replace('_', ' '); + if (temp.EndsWith(".md", StringComparison.OrdinalIgnoreCase)) + { + temp = temp[..^3]; + } + temp = temp.Trim(); StringBuilder builder = new StringBuilder(temp.Length); string[] words = temp.Split(' '); for (int i = 0; i < words.Length; i++) { - if (i > 0 && _notCapitalizedWords.Contains(words[i].ToLower())) + void AddSpace() { - builder.Append(words[i].ToLower()); if (i < words.Length - 1) { builder.Append(" "); } + } + string lowerVersion = words[i].ToLowerInvariant(); + if (i > 0 && _notCapitalizedWords.Contains(lowerVersion)) + { + builder.Append(words[i].ToLower()); + AddSpace(); + continue; + } + if (_specialWords.TryGetValue(lowerVersion, out string? specialWord)) + { + builder.Append(specialWord); + AddSpace(); continue; } for (int j = 0; j < words[i].Length; j++) @@ -72,10 +95,7 @@ public string GetDisplayName() builder.Append(words[i][j].ToString()); } } - if (i < words.Length - 1) - { - builder.Append(" "); - } + AddSpace(); } return builder.ToString(); } diff --git a/Redot-Documentation/Versioning/Section.cs b/Redot-Documentation/Versioning/Section.cs index 640d878..8b56ce1 100644 --- a/Redot-Documentation/Versioning/Section.cs +++ b/Redot-Documentation/Versioning/Section.cs @@ -61,14 +61,15 @@ public void LoadAndParse() excludedItems.Add("img"); excludedItems.Add("video"); } - + if (string.IsNullOrEmpty(Slug)) + Slug = Name.ToLowerInvariant(); FileInfo[] files = directory.GetFiles("*.md"); foreach (FileInfo file in files) { if (excludedItems.Contains(file.Name)) continue; int priority = rankingPriorities.GetValueOrDefault(file.Name, 1024); Article article = new Article(file.Name, FilePath.Combine(Path, file.Name), SlugPrefix + file.Name, priority); - if (article.Name == "index") + if (article.Name == "index" || article.Name == "index.md") { IndexArticle = article; IndexArticle.Name = Name; diff --git a/Redot-Documentation/Versioning/VersionProvider.cs b/Redot-Documentation/Versioning/VersionProvider.cs index ad75f69..0a92f65 100644 --- a/Redot-Documentation/Versioning/VersionProvider.cs +++ b/Redot-Documentation/Versioning/VersionProvider.cs @@ -9,6 +9,8 @@ public class VersionProvider public Section CommunitySection { get; set; } = new("Community", "./docs/Community/", 1); + public Section ContributingSection { get; set; } = new("Contributing", "./docs/Contributing/", 2); + public Section? VersionedDocsSection { get; set; } = null; private List _sortedRankings = new List(); @@ -23,6 +25,8 @@ public VersionProvider() AboutSection.SortRankings(); CommunitySection.LoadAndParse(); CommunitySection.SortRankings(); + ContributingSection.LoadAndParse(); + ContributingSection.SortRankings(); } public VersionProvider(string versionName) : this() { @@ -40,6 +44,7 @@ public void SortRankings() _sortedRankings.Clear(); _sortedRankings.Add(AboutSection); _sortedRankings.Add(CommunitySection); + _sortedRankings.Add(ContributingSection); _sortedRankings.Sort(); if (VersionedDocsSection != null) { diff --git a/Redot-Documentation/docs/Contributing/Development/best_practices_for_engine_contributors.md b/Redot-Documentation/docs/Contributing/Development/best_practices_for_engine_contributors.md new file mode 100644 index 0000000..81864e8 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/best_practices_for_engine_contributors.md @@ -0,0 +1,225 @@ + +# Best practices for engine contributors + +## Introduction + +Redot has a large amount of users who have the ability to contribute because the +project itself is aimed mainly at users who can code. That being said, not all +of them have the same level of experience working in large projects or in +software engineering, which can lead to common misunderstandings and bad +practices during the process of contributing code to the project. + +## Language + +The scope of this document is to be a list of best practices for contributors to +follow, as well as to create a language they can use to refer to common +situations that arise in the process of submitting their contributions. + +While a generalized list of software development best practices might be useful, +we'll focus on the situations that are most common in our project. + +Contributions are most of the time categorized as bug fixes, enhancements or new +features. To abstract this idea, we will call them *Solutions*, because they +always seek to solve something that can be described as a *Problem*. + +## Best Practices + +### #1: The problem always comes first + +Many contributors are extremely creative and just enjoy the process of designing +abstract data structures, creating nice user interfaces, or simply love +programming. Whatever the case may be, they come up with cool ideas, which may +or may not solve real problems. + +![Image](/img/Contributing/Development/best_practices1.png) + +These are usually called *solutions in search of a problem*. In an ideal world, +they would not be harmful but, in reality, code takes time to write, takes up +space and requires maintenance once it exists. Avoiding the addition of anything +unnecessary is always considered a good practice in software development. + +### #2: To solve the problem, it has to exist in the first place + +This is a variation of the previous practice. Adding anything unnecessary is not +a good idea, but what constitutes what is necessary and what isn't? + +![Image](/img/Contributing/Development/best_practices2.png) + +The answer to this question is that the problem needs to *exist* before it can +be actually solved. It must not be speculation or a belief. The user must be +using the software as intended to create something they *need*. In this process, +the user may stumble upon a problem that requires a solution to proceed, or in +order to achieve greater productivity. In this case, *a solution is needed*. + +Believing that problems may arise in the future and that the software needs to +be ready to solve them by the time they appear is called *"Future proofing"* and +its characterized by lines of thought such as: + +- I think it would be useful for users to... +- I think users will eventually need to... + +This is generally considered a bad habit because trying to solve problems that +*don't actually exist* in the present will often lead to code that will be +written but never used, or that is considerably more complex to use and maintain +than it needs to be. + +### #3: The problem has to be complex or frequent + +Software is designed to solve problems, but we can't expect it to solve *every +problem that exists under the sun*. As a game engine, Redot will help you make +games better and faster, but it won't make an *entire game* for you. A line must +be drawn somewhere. + +![Image](/img/Contributing/Development/best_practices3.png) + +Whether a problem is worth solving is determined by the effort that is required +to work around it. The required effort depends on: + +- The complexity of the problem +- The frequency the problem + +If the problem is *too complex* for most users to solve, then the software +should offer a ready-made solution for it. Likewise, if the problem is easy for +the user to work around, offering such a solution is unnecessary. + +The exception, however, is when the user encounters a problem *frequently +enough* that having to do the simple solution every time becomes an annoyance. +In this case, the software should offer a solution to simplify the use case. + +It's usually easy to tell if a problem is complex or frequent, but it can be +difficult. This is why discussing with other developers (next point) is always +advised. + +### #4: The solution must be discussed with others + +Often, users will be immersed in their own projects when they stumble upon +problems. These users will naturally try to solve the problem from their +perspective, thinking only about their own use case. As a result, user proposed +solutions don't always contemplate all use cases and are often biased towards +the user's own requirements. + +![Image](/img/Contributing/Development/best_practices4.png) + +For developers, the perspective is different. They may find the user's problem +too unique to justify a solution (instead of a workaround), or they might +suggest a partial (usually simpler or lower level) solution that applies to a +wider range of known problems and leave the rest of the solution up to the +user. + +In any case, before attempting to contribute, it is important to discuss the +actual problems with the other developers or contributors, so a better agreement +on implementation can be reached. + +The only exception is when an area of code has a clear agreed upon owner, who +talks to users directly and has the most knowledge to implement a solution +directly. + +Also, Redot's philosophy is to favor ease of use and maintenance over absolute +performance. Performance optimizations will be considered, but they may not +be accepted if they make something too difficult to use or if they add too much +complexity to the codebase. + +### #5: To each problem, its own solution + +For programmers, it is always a most enjoyable challenge to find the most +optimal solutions to problems. It is possible to go overboard, though. +Sometimes, contributors will try to come up with solutions that solve as many +problems as possible. + +The situation will often take a turn for the worse when, in order to make this +solution appear even more fantastic and flexible, the pure speculation-based +problems (as described in #2) also make their appearance on stage. + +![Image](/img/Contributing/Development/best_practices5.png) + +The main problem is that, in reality, it rarely works this way. Most of the +time, writing an individual solution to each problem results in code that +is simpler and more maintainable. + +Additionally, solutions that target individual problems are better for the +users. Targeted solutions allow users find something that does exactly what they +need, without having to learn a more complex system they will only need for simple +tasks. + +Big and flexible solutions also have an additional drawback which is that, over +time, they are rarely flexible enough for all users. Users end up requesting +more and more functionality which ends up making the API and codebase +more and more complex. + +### #6: Cater to common use cases, leave the door open for the rare ones + +This is a continuation of the previous point, which further explains why this +way of thinking and designing software is preferred. + +As mentioned before (in point #2), it is very difficult for us (as human beings +who design software) to actually understand all future user needs. Trying to +write very flexible structures that cater to many use cases at once is often a +mistake. + +We may come up with something we believe is brilliant, but later find out that +users will never even use half of it or that they require features that don't +quite fit into our original design, forcing us to either throw it away +or make it even more complex. + +The question is then, how do we design software that both allows users to do +*what we know they need to do* now and allows them to do *what we don't yet know +they'll need to do* in the future? + +![Image](/img/Contributing/Development/best_practices6.png) + +The answer to this question is that, to ensure users still can do what they want +to do, we need to give them access to a *low-level API* that they can use to +achieve what they want, even if it's more work for them because it means +reimplementing some logic that already exists. + +In real-life scenarios, these use cases will be at most rare and uncommon +anyway, so it makes sense a custom solution needs to be written. This is why +it's important to still provide users the basic building blocks to do it. + +### #7: Prefer local solutions + +When looking for a solution to a problem, be it implementing a new feature or +fixing a bug, sometimes the easiest path is to add data or a new function in the +core layers of code. + +The main problem here is, adding something to the core layers that will only be +used from a single location far away will not only make the code more difficult +to follow (split in two), but also make the core API larger, more complex, more +difficult to understand in general. + +This is bad, because readability and cleanness of core APIs is always of extreme +importance given how much code relies on it, and because it's key for new +contributors as a starting point to learning the codebase. + +![Image](/img/Contributing/Development/best_practices7.png) + +A common reason for wanting to do this is that it's usually less code to simply +add a hack in the core layers. + +Doing so is not advised. Generally, the code for a solution should be closer to +where the problem originates, even if it involves additional, duplicated, more +complex, or less efficient code. More creativity might be needed, but this path +is always the advised one. + +### #8: Don't use complex canned solutions for simple problems + +Not every problem has a simple solution and, many times, the right choice is to +use a third-party library to solve the problem. + +As Redot requires to be shipped in a large amount of platforms, we can't +link libraries dynamically. Instead, we bundle them in our source tree. + +![Image](/img/Contributing/Development/best_practices8.png) + +As a result, we are very picky with what goes in, and we tend to prefer smaller +libraries (single header ones are our favorite). We will only bundle something +larger if there is no other choice. + +Libraries must use a permissive enough license to be included into Redot. +Some examples of acceptable licenses are Apache 2.0, BSD, MIT, ISC, and MPL 2.0. +In particular, we cannot accept libraries licensed under the GPL or LGPL since +these licenses effectively disallow static linking in proprietary software +(which Redot is distributed as in most exported projects). This requirement also +applies to the editor, since we may want to run it on iOS in the long term. +Since iOS doesn't support dynamic linking, static linking is the only option on +that platform. \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/code_style_guidelines.md b/Redot-Documentation/docs/Contributing/Development/code_style_guidelines.md new file mode 100644 index 0000000..f3f3132 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/code_style_guidelines.md @@ -0,0 +1,387 @@ + +# Code style guidelines + + +When contributing to Redot's source code, you will be expected to follow the +style guidelines outlined below. Some of them are checked via the Continuous +Integration process and reviewers will ask you to fix potential issues, so +best setup your system as outlined below to ensure all your commits follow the +guidelines. + +## C++ and Objective-C + +There are no written guidelines, but the code style agreed upon by the +developers is enforced via the [clang-format](https://clang.llvm.org/docs/ClangFormat.html) +code beautifier, which takes care for you of all our conventions. +To name a few: + +- Indentation and alignment are both tab based (respectively one and two tabs) +- One space around math and assignments operators as well as after commas +- Pointer and reference operators are affixed to the variable identifier, not + to the type name +- See further down regarding header includes + +The rules used by clang-format are outlined in the +[.clang-format](https://github.com/redot-engine/redot-engine/blob/master/.clang-format) +file of the Redot repository. + +As long as you ensure that your style matches the surrounding code and that you're +not introducing trailing whitespace or space-based indentation, you should be +fine. If you plan to contribute regularly, however, we strongly advise that you +set up clang-format locally to check and automatically fix all your commits. + +:::warning +Redot's code style should *not* be applied to third-party code, +i.e. code that is included in Redot's source tree, but was not written +specifically for our project. Such code usually comes from +different upstream projects with their own style guides (or lack +thereof), and don't want to introduce differences that would make +syncing with upstream repositories harder. + +Third-party code is usually included in the ``thirdparty/`` folder +and can thus easily be excluded from formatting scripts. For the +rare cases where a third-party code snippet needs to be included +directly within a Redot file, you can use +``/* clang-format off */`` and ``/* clang-format on */`` to tell +clang-format to ignore a chunk of code. + +::: + +:::info + +These guidelines only cover code formatting. See [C++ Usage Guidelines](cpp_usage_guidelines) +for a list of language features that are permitted in pull requests. + +::: + +### Using clang-format locally + +You need to use **clang-format 17** to be compatible with Redot's format. Later versions might +be suitable, but previous versions may not support all used options, or format +some things differently, leading to style issues in pull requests. + +#### Pre-commit hook + +For ease of use, we provide hooks for Git with the [pre-commit](https://pre-commit.com/) +Python framework that will run clang-format automatically on all your commits with the +correct version of clang-format. +To set up: + +``` +pip install pre-commit +pre-commit install + +``` + +You can also run the hook manually with ``pre-commit run -a``. + +:::note + +Previously, we supplied a hook in the folder ``misc/hooks``. If you copied the +script manually, these hooks should still work, but symlinks will be broken. +If you are using the new system, run ``rm .git/hooks/*`` to remove the old hooks +that are no longer needed. + +::: + +#### Installation + +Here's how to install clang-format: + +- Linux: It will usually be available out-of-the-box with the clang toolchain + packaged by your distribution. If your distro version is not the required one, + you can download a pre-compiled version from the + [LLVM website](https://releases.llvm.org/download.html), or if you are on + a Debian derivative, use the [upstream repos](https://apt.llvm.org/). +- macOS and Windows: You can download precompiled binaries from the + [LLVM website](https://releases.llvm.org/download.html). You may need to add + the path to the binary's folder to your system's ``PATH`` environment + variable to be able to call clang-format out of the box. + +You then have different possibilities to apply clang-format to your changes: + +#### Manual usage + +You can apply clang-format manually for one or more files with the following +command: + +``` +clang-format -i + +``` + +- ``-i`` means that the changes should be written directly to the file (by + default clang-format would only output the fixed version to the terminal). +- The path can point to several files, either one after the other or using + wildcards like in a typical Unix shell. Be careful when globbing so that + you don't run clang-format on compiled objects (.o and .a files) that are + in Redot's tree. So better use ``core/*.{cpp,h}`` than ``core/*``. + +#### IDE plugin + +Most IDEs or code editors have beautifier plugins that can be configured to run +clang-format automatically, for example, each time you save a file. + +Here is a non-exhaustive list of beautifier plugins for some IDEs: + +- Qt Creator: [Beautifier plugin](https://doc.qt.io/qtcreator/creator-beautifier.html) +- Visual Studio Code: [Clang-Format](https://marketplace.visualstudio.com/items?itemName=xaver.clang-format) +- Visual Studio: [Clang Power Tools 2022](https://marketplace.visualstudio.com/items?itemName=caphyon.ClangPowerTools2022) +- vim: [vim-clang-format](https://github.com/rhysd/vim-clang-format) +- CLion: Starting from version ``2019.1``, no plugin is required. Instead, enable + [ClangFormat](https://www.jetbrains.com/help/clion/clangformat-as-alternative-formatter.html#clion-support) + +(Pull requests are welcome to extend this list with tested plugins.) + +### Header includes + +When adding new C++ or Objective-C files or including new headers in existing +ones, the following rules should be followed: + +- The first lines in the file should be Redot's copyright header and MIT + license, copy-pasted from another file. Make sure to adjust the filename. +- In a ``.h`` header, include guards should be used with the form + ``FILENAME_H``. + +- In a ``.cpp`` file (e.g. ``filename.cpp``), the first include should be the + one where the class is declared (e.g. ``#include "filename.h"``), followed by + an empty line for separation. +- Then come headers from Redot's own code base, included in alphabetical order + (enforced by ``clang-format``) with paths relative to the root folder. Those + includes should be done with quotes, e.g. ``#include "core/object.h"``. The + block of Redot header includes should then be followed by an empty line for + separation. +- Finally, third-party headers (either from ``thirdparty`` or from the system's + include paths) come next and should be included with the < and > symbols, e.g. + `#include `. The block of third-party headers should also be followed + by an empty line for separation. +- Redot and third-party headers should be included in the file that requires + them, i.e. in the `.h` header if used in the declarative code or in the `.cpp` + if used only in the imperative code. + +Example: + +```cpp +/**************************************************************************/ +/* my_new_file.h */ +/**************************************************************************/ +/* This file is part of: */ +/* Redot ENGINE */ +/* https://redotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present the Redot community, modified from an */ +/* original work by G-dot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 MY_NEW_FILE_H +#define MY_NEW_FILE_H + +#include "core/hash_map.h" +#include "core/list.h" +#include "scene/gui/control.h" + +#include + +... + +#endif // MY_NEW_FILE_H + +``` + +```cpp +/**************************************************************************/ +/* my_new_file.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* Redot ENGINE */ +/* https://redotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present the Redot community, modified from an */ +/* original work by Redot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "my_new_file.h" + +#include "core/math/math_funcs.h" +#include "scene/gui/line_edit.h" + +#include +#include + +``` + +## Java + +Redot's Java code (mostly in ``platform/android``) is also enforced via +``clang-format``, so see the instructions above to set it up. Keep in mind that +this style guide only applies to code written and maintained by Redot, not +third-party code such as the ``java/src/com/google`` subfolder. + +## Python + +Redot's SCons buildsystem is written in Python, and various scripts included +in the source tree are also using Python. + +For those, we use the [Ruff linter and code formatter](https://docs.astral.sh/ruff/). + +### Using ruff locally + +First of all, you will need to install Ruff. Ruff requires Python 3.7+ to run. + +#### Installation + +Here's how to install ruff: + +``` +pip3 install ruff --user + +``` + +You then have different possibilities to apply ruff to your changes: + +#### Manual usage + +You can apply ``ruff`` manually to one or more files with the following +command: + +``` +ruff -l 120 + +``` + +- ``-l 120`` means that the allowed number of characters per line is 120. + This number was agreed upon by the developers. +- The path can point to several files, either one after the other or using + wildcards like in a typical Unix shell. + +### Pre-commit hook + +For ease of use, we provide hooks for Git with the [pre-commit](https://pre-commit.com/) +Python framework that will run ``ruff`` automatically on all your commits with the +correct version of ``ruff``. +To set up: + +``` +pip install pre-commit +pre-commit install + +``` + +You can also run the hook manually with ``pre-commit run -a``. + +:::note + +Previously, we supplied a hook in the folder ``misc/hooks``. If you copied the +script manually, these hooks should still work, but symlinks will be broken. +If you are using the new system, run ``rm .git/hooks/*`` to remove the old hooks +that are no longer needed. + +::: + +#### Editor integration + +Many IDEs or code editors have beautifier plugins that can be configured to run +ruff automatically, for example, each time you save a file. For details, you can +check [Ruff Integrations](https://docs.astral.sh/ruff/integrations/). + +## Comment style guide + +This comment style guide applies to all programming languages used within +Redot's codebase. + +- Begin comments with a space character to distinguish them from disabled code. +- Use sentence case for comments. Begin comments with an uppercase character and + always end them with a period. +- Reference variable/function names and values using backticks. +- Wrap comments to ~100 characters. +- You can use ``TODO:``, ``FIXME:``, ``NOTE:``, ``WARNING:``, or ``HACK:`` as admonitions + when needed. + +**Example:** + +```cpp +// Compute the first 10,000 decimals of Pi. +// FIXME: Don't crash when computing the 1,337th decimal due to `increment` +// being negative. + +``` + +Don't repeat what the code says in a comment. Explain the *why* rather than *how*. + +**Bad:** + +```cpp +// Draw loading screen. +draw_load_screen(); + +``` + +You can use Javadoc-style comments above function or macro definitions. It's +recommended to use Javadoc-style comments *only* for methods which are not +exposed to scripting. This is because exposed methods should be documented in +the [class reference XML](../Documentation/updating_the_class_reference.md) +instead. + +**Example:** + +```cpp +/** + * Returns the number of nodes in the universe. + * This can potentially be a very large number, hence the 64-bit return type. + */ +uint64_t Universe::get_node_count() { + // ... +} + +``` + +For member variables, don't use Javadoc-style comments, but use single-line comments instead: + +```cpp +class Universe { + // The cached number of nodes in the universe. + // This value may not always be up-to-date with the current number of nodes + // in the universe. + uint64_t node_count_cached = 0; +}; +``` diff --git a/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_android.md b/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_android.md new file mode 100644 index 0000000..5df0f9e --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_android.md @@ -0,0 +1,303 @@ + +# Compiling for Android + +.. highlight:: shell + +:::info + +This page describes how to compile Android export template binaries from source. +If you're looking to export your project to Android instead, read [doc_exporting_for_android](../../../tutorials/export/exporting_for_android.md). + +::: + +## Note + +In most cases, using the built-in deployer and export templates is good +enough. Compiling the Android APK manually is mostly useful for custom +builds or custom packages for the deployer. + +Also, you still need to follow the steps mentioned in the +[doc_exporting_for_android](../../../tutorials/export/exporting_for_android.md) tutorial before attempting to build +a custom export template. + +## Requirements + +For compiling under Windows, Linux or macOS, the following is required: + +- [Python 3.8+](https://www.python.org/downloads/). +- [SCons 4.0+](https://scons.org/pages/download.html) build system. +- [Android SDK](https://developer.android.com/studio/#command-tools) + (command-line tools are sufficient). + + - Required SDK components will be automatically installed. + - On Linux, **do not use an Android SDK provided by your distribution's repositories** as it will often be outdated. + - On macOS, **do not use an Android SDK provided by Homebrew** as it will not be installed in a unified location. + +- Gradle (will be downloaded and installed automatically if missing). +- JDK 17 (either OpenJDK or Oracle JDK). + + - You can download a build from [Adoptium](https://adoptium.net/temurin/releases/?variant=openjdk17). + +:::info +To get the Redot source code for compiling, see +[doc_getting_source](getting_source.md). + +For a general overview of SCons usage for Redot, see +[doc_introduction_to_the_buildsystem](introduction_to_the_buildsystem.md). + +::: + +## Setting up the buildsystem + +- Set the environment variable ``ANDROID_HOME`` to point to the Android + SDK. If you downloaded the Android command-line tools, this would be + the folder where you extracted the contents of the ZIP archive. + + - Windows: Press `Windows + R`, type "control system", + then click on **Advanced system settings** in the left pane, + then click on **Environment variables** on the window that appears. + + - Linux or macOS: Add the text ``export ANDROID_HOME="/path/to/android-sdk"`` + to your ``.bashrc`` or ``.zshrc`` where ``/path/to/android-sdk`` points to + the root of the SDK directories. + +- Install the necessary SDK components in this folder: + + - Accept the SDK component licenses by running the following command + where ``android_sdk_path`` is the path to the Android SDK, then answering all the prompts with ``y``: + +``` +cmdline-tools/latest/bin/sdkmanager --sdk_root= --licenses + +``` + + - Complete setup by running the following command where ``android_sdk_path`` is the path to the Android SDK. + +``` +cmdline-tools/latest/bin/sdkmanager --sdk_root= "platform-tools" "build-tools;34.0.0" "platforms;android-34" "cmdline-tools;latest" "cmake;3.10.2.4988404" "ndk;23.2.8568313" + +``` + +- After setting up the SDK and environment variables, be sure to + **restart your terminal** to apply the changes. If you are using + an IDE with an integrated terminal, you need to restart the IDE. + +- Run ``scons platform=android``. If this fails, go back and check the steps. + If you completed the setup correctly, the NDK will begin downloading. + If you are trying to compile GDExtension, you need to first compile + the engine to download the NDK, then you can compile GDExtension. + +## Building the export templates + +Redot needs three export templates for Android: the optimized "release" +template (``android_release.apk``), the debug template (``android_debug.apk``), +and the Gradle build template (``android_source.zip``). +As Google requires all APKs to include ARMv8 (64-bit) libraries since August 2019, +the commands below build templates containing both ARMv7 and ARMv8 libraries. + +Compiling the standard export templates is done by calling SCons from the Redot +root directory with the following arguments: + +- Release template (used when exporting with **Debugging Enabled** unchecked) + +``` +scons platform=android target=template_release arch=arm32 +scons platform=android target=template_release arch=arm64 generate_apk=yes + +``` + +- Debug template (used when exporting with **Debugging Enabled** checked) + +``` +scons platform=android target=template_debug arch=arm32 +scons platform=android target=template_debug arch=arm64 generate_apk=yes + +``` + +- (**Optional**) Dev template (used when troubleshooting) + +``` +scons platform=android target=template_debug arch=arm32 dev_build=yes +scons platform=android target=template_debug arch=arm64 dev_build=yes generate_apk=yes + +``` + +The resulting templates will be located under the ``bin`` directory: + +- ``bin/android_release.apk`` for the release template +- ``bin/android_debug.apk`` for the debug template +- ``bin/android_dev.apk`` for the dev template +- ``bin/android_source.zip`` for the Gradle build template + +:::note + +- If you are changing the list of architectures you're building, remember to add ``generate_apk=yes`` to the *last* architecture you're building, so that the template files are generated after the build. + +- To include debug symbols in the generated templates, add the ``debug_symbols=yes`` parameter to the SCons command. + +::: + +:::info + +If you want to enable Vulkan validation layers, see +[Vulkan validation layers on Android](doc_vulkan_validation_layers_android). + +::: + +### Adding support for x86 devices + +If you also want to include support for x86 and x86_64 devices, run the SCons +command a third and fourth time with the ``arch=x86_32``, and +``arch=x86_64`` arguments before building the APK with Gradle. For +example, for the release template: + +``` +scons platform=android target=template_release arch=arm32 +scons platform=android target=template_release arch=arm64 +scons platform=android target=template_release arch=x86_32 +scons platform=android target=template_release arch=x86_64 generate_apk=yes + +``` + +This will create template binaries that works on all platforms. +The final binary size of exported projects will depend on the platforms you choose +to support when exporting; in other words, unused platforms will be removed from +the binary. + +### Cleaning the generated export templates + +You can use the following commands to remove the generated export templates: + +``` +cd platform/android/java +# On Windows +.\gradlew clean +# On Linux and macOS +./gradlew clean + +``` + +## Using the export templates + +Redot needs release and debug binaries that were compiled against the same +version/commit as the editor. If you are using official binaries +for the editor, make sure to install the matching export templates, +or build your own from the same version. + +When exporting your game, Redot uses the templates as a base, and updates their content as needed. + +### Installing the templates + +The newly-compiled templates (``android_debug.apk`` +, ``android_release.apk``, and ``android_source.zip``) must be copied to Redot's templates folder +with their respective names. The templates folder can be located in: + +- Windows: ``%APPDATA%\Redot\export_templates\<version>\`` +- Linux: ``$HOME/.local/share/Redot/export_templates/<version>/`` +- macOS: ``$HOME/Library/Application Support/Redot/export_templates/<version>/`` + +``<version>`` is of the form ``major.minor[.patch].status`` using values from +``version.py`` in your Redot source repository (e.g. ``4.1.3.stable`` or ``4.2.dev``). +You also need to write this same version string to a ``version.txt`` file located +next to your export templates. + +However, if you are writing your custom modules or custom C++ code, you +might instead want to configure your template binaries as custom export templates +here: + +![Image](img/andtemplates.png) + +You don't even need to copy them, you can just reference the resulting +file in the ``bin\`` directory of your Redot source folder, so that the +next time you build you will automatically have the custom templates +referenced. + +## Building the Redot editor + +Compiling the editor is done by calling SCons from the Redot +root directory with the following arguments: + +``` +scons platform=android arch=arm32 production=yes target=editor +scons platform=android arch=arm64 production=yes target=editor +scons platform=android arch=x86_32 production=yes target=editor +scons platform=android arch=x86_64 production=yes target=editor generate_apk=yes + +``` + +- You can add the ``dev_build=yes`` parameter to generate a dev build of the Redot editor. + +- You can add the ``debug_symbols=yes`` parameter to include the debug symbols in the generated build. + +- You can skip certain architectures depending on your target device to speed up compilation. + +Remember to add ``generate_apk=yes`` to the *last* architecture you're building, so that binaries are generated after the build. + +The resulting binaries will be located under ``bin/android_editor_builds/``. + +## Removing the Editor binaries + +You can use the following commands to remove the generated editor binaries: + +``` +cd platform/android/java +# On Windows +``` + + .\gradlew clean + # On Linux and macOS + ./gradlew clean + +## Installing the Redot editor APK + +With an Android device with Developer Options enabled, connect the Android device to your computer via its charging cable to a USB/USB-C port. +Open up a Terminal/Command Prompt and run the following commands from the root directory with the following arguments: + +``` +adb install ./bin/android_editor_builds/android_editor-release.apk + +``` + +## Troubleshooting + +### Platform doesn't appear in SCons + +Double-check that you've set the ``ANDROID_HOME`` +environment variable. This is required for the platform to appear in SCons' +list of detected platforms. +See [Setting up the buildsystem](doc_android_setting_up_the_buildsystem) +for more information. + +### Application not installed + +Android might complain the application is not correctly installed. +If so: + +- Check that the debug keystore is properly generated. +- Check that the jarsigner executable is from JDK 8. + +If it still fails, open a command line and run [logcat](https://developer.android.com/studio/command-line/logcat): + +``` +adb logcat + +``` + +Then check the output while the application is installed; +the error message should be presented there. +Seek assistance if you can't figure it out. + +### Application exits immediately + +If the application runs but exits immediately, this might be due to +one of the following reasons: + +- Make sure to use export templates that match your editor version; if + you use a new Redot version, you *have* to update the templates too. +- ``libRedot_android.so`` is not in ``libs/<arch>/`` + where ``<arch>`` is the device's architecture. +- The device's architecture does not match the exported one(s). + Make sure your templates were built for that device's architecture, + and that the export settings included support for that architecture. + +In any case, ``adb logcat`` should also show the cause of the error. \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_ios.md b/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_ios.md new file mode 100644 index 0000000..fed36cf --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_ios.md @@ -0,0 +1,99 @@ + +# Compiling for iOS + +.. highlight:: shell + +:::info + +This page describes how to compile iOS export template binaries from source. +If you're looking to export your project to iOS instead, read [doc_exporting_for_ios](doc_exporting_for_ios). + +::: + +## Requirements + +- [Python 3.8+](https://www.python.org/downloads/macos/). +- [SCons 4.0+](https://scons.org/pages/download.html) build system. +- [Xcode](https://apps.apple.com/us/app/xcode/id497799835). + - Launch Xcode once and install iOS support. If you have already launched + Xcode and need to install iOS support, go to *Xcode -> Settings... -> Platforms*. + - Go to *Xcode -> Settings... -> Locations -> Command Line Tools* and select + an installed version. Even if one is already selected, re-select it. +- Download and follow README instructions to build a static ``.xcframework`` + from the [MoltenVK SDK](https://github.com/KhronosGroup/MoltenVK#fetching-moltenvk-source-code). + +:::note +If you have [Homebrew](https://brew.sh/) installed, you can easily +install SCons using the following command + +``` +brew install scons + +``` + +Installing Homebrew will also fetch the Command Line Tools +for Xcode automatically if you don't have them already. + +Similarly, if you have [MacPorts](https://www.macports.org/) +installed, you can easily install SCons using the +following command + +``` +sudo port install scons +``` + +::: + +:::info +To get the Redot source code for compiling, see +[doc_getting_source](doc_getting_source). + +For a general overview of SCons usage for Redot, see +[doc_introduction_to_the_buildsystem](doc_introduction_to_the_buildsystem). + +::: + +## Compiling + +Open a Terminal, go to the root folder of the engine source code and type +the following to compile a debug build: + +``` +scons platform=ios target=template_debug generate_bundle=yes + +``` + +To compile a release build: + +``` +scons platform=ios target=template_release generate_bundle=yes + +``` + +Alternatively, you can run the following command for Xcode simulator libraries (optional): + +``` +scons platform=ios target=template_debug ios_simulator=yes arch=arm64 +scons platform=ios target=template_debug ios_simulator=yes arch=x86_64 generate_bundle=yes + +``` + +These simulator libraries cannot be used to run the exported project on the +target device. Instead, they can be used to run the exported project directly on +your Mac while still testing iOS platform-specific functionality. + +To create an Xcode project like in the official builds, you need to use the +template located in ``misc/dist/ios_xcode``. The release and debug libraries +should be placed in ``libRedot.ios.debug.xcframework`` and +``libRedot.ios.release.xcframework`` respectively. This process can be automated +by using the ``generate_bundle=yes`` option on the *last* SCons command used to +build export templates (so that all binaries can be included). + +The MoltenVK static ``.xcframework`` folder must also be placed in the +``ios_xcode`` folder once it has been created. MoltenVK is always statically +linked on iOS; there is no dynamic linking option available, unlike macOS. + +## Run + +To run on a device or simulator, follow these instructions: +[doc_exporting_for_ios](doc_exporting_for_ios). \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_linuxbsd.md b/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_linuxbsd.md new file mode 100644 index 0000000..a6a27ed --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_linuxbsd.md @@ -0,0 +1,664 @@ +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +# Compiling for Linux, \*BSD + +.. highlight:: shell + +:::info + +This page describes how to compile Linux editor and export template binaries from source. +If you're looking to export your project to Linux instead, read [doc_exporting_for_linux](../../../tutorials/export/exporting_for_linux.md). + +::: + +## Requirements + +For compiling under Linux or other Unix variants, the following is +required: + +- GCC 9+ or Clang 6+. +- [Python 3.8+](https://www.python.org/downloads/). +- [SCons 4.0+](https://scons.org/pages/download.html) build system. +- pkg-config (used to detect the development libraries listed below). +- Development libraries: + + - X11, Xcursor, Xinerama, Xi and XRandR. + - Wayland and wayland-scanner. + - Mesa. + - ALSA. + - PulseAudio. + +- *Optional* - libudev (build with ``udev=yes``). + +:::info + +To get the Redot source code for compiling, see [doc_getting_source](getting_source.md). + +For a general overview of SCons usage for Redot, see [doc_introduction_to_the_buildsystem](introduction_to_the_buildsystem.md). + +::: + +### Distro-specific one-liners + + + + + +apk add \ + scons \ + pkgconf \ + gcc \ + g++ \ + libx11-dev \ + libxcursor-dev \ + libxinerama-dev \ + libxi-dev \ + libxrandr-dev \ + mesa-dev \ + eudev-dev \ + alsa-lib-dev \ + pulseaudio-dev + + + + + +pacman -Sy --noconfirm --needed \ + scons \ + pkgconf \ + gcc \ + libxcursor \ + libxinerama \ + libxi \ + libxrandr \ + wayland-utils \ + mesa \ + glu \ + libglvnd \ + alsa-lib \ + pulseaudio + + + + + +sudo apt-get update +sudo apt-get install -y \ + build-essential \ + scons \ + pkg-config \ + libx11-dev \ + libxcursor-dev \ + libxinerama-dev \ + libgl1-mesa-dev \ + libglu1-mesa-dev \ + libasound2-dev \ + libpulse-dev \ + libudev-dev \ + libxi-dev \ + libxrandr-dev \ + libwayland-dev + + + + + +sudo dnf install -y \ + scons \ + pkgconfig \ + libX11-devel \ + libXcursor-devel \ + libXrandr-devel \ + libXinerama-devel \ + libXi-devel \ + wayland-devel \ + mesa-libGL-devel \ + mesa-libGLU-devel \ + alsa-lib-devel \ + pulseaudio-libs-devel \ + libudev-devel \ + gcc-c++ \ + libstdc++-static \ + libatomic-static + + + + + +pkg install \ + py37-scons \ + pkgconf \ + xorg-libraries \ + libXcursor \ + libXrandr \ + libXi \ + xorgproto \ + libGLU \ + alsa-lib \ + pulseaudio + + + + + +emerge --sync +emerge -an \ + dev-build/scons \ + x11-libs/libX11 \ + x11-libs/libXcursor \ + x11-libs/libXinerama \ + x11-libs/libXi \ + dev-util/wayland-scanner \ + media-libs/mesa \ + media-libs/glu \ + media-libs/alsa-lib \ + media-sound/pulseaudio + + + + + +sudo urpmi --auto \ + scons \ + task-c++-devel \ + wayland-devel \ + "pkgconfig(alsa)" \ + "pkgconfig(glu)" \ + "pkgconfig(libpulse)" \ + "pkgconfig(udev)" \ + "pkgconfig(x11)" \ + "pkgconfig(xcursor)" \ + "pkgconfig(xinerama)" \ + "pkgconfig(xi)" \ + "pkgconfig(xrandr)" + + + + + + pkg_add \ + pkg-config \ + py37-scons + +:::tip + +For audio support, you can optionally install ``pulseaudio``. + +::: + + + + + +pkg_add \ + python \ + scons \ + llvm + + + + + +sudo apt update +sudo apt install -y \ + python3-pip \ + build-essential \ + pkg-config \ + libx11-dev \ + libxcursor-dev \ + libxinerama-dev \ + libgl1-mesa-dev \ + libglu1-mesa-dev \ + libasound2-dev \ + libpulse-dev \ + libudev-dev \ + libxi-dev \ + libxrandr-dev \ + libwayland-dev +sudo pip install scons + + + + + +sudo zypper install -y \ + scons \ + pkgconfig \ + libX11-devel \ + libXcursor-devel \ + libXrandr-devel \ + libXinerama-devel \ + libXi-devel \ + wayland-devel \ + Mesa-libGL-devel \ + alsa-devel \ + libpulse-devel \ + libudev-devel \ + gcc-c++ \ + libGLU1 + + + + + +eopkg install -y \ + -c system.devel \ + scons \ + libxcursor-devel \ + libxinerama-devel \ + libxi-devel \ + libxrandr-devel \ + wayland-devel \ + mesalib-devel \ + libglu \ + alsa-lib-devel \ + pulseaudio-devel + + + + + +## Compiling + +Start a terminal, go to the root dir of the engine source code and type: + +``` +scons platform=linuxbsd + +``` + +:::note + +Prior to Redot 4.0, the Linux/\*BSD target was called ``x11`` instead of +``linuxbsd``. If you are looking to compile Redot 3.x, make sure to use the +[3.x branch of this documentation](https://docs.redotengine.org/en/3.6/development/compiling/compiling_for_x11.html). + +::: + +:::tip + +If you are compiling Redot to make changes or contribute to the engine, +you may want to use the SCons options ``dev_build=yes`` or ``dev_mode=yes``. +See [doc_introduction_to_the_buildsystem_development_and_production_aliases](doc_introduction_to_the_buildsystem_development_and_production_aliases) +for more info. + +::: + +If all goes well, the resulting binary executable will be placed in the +"bin" subdirectory. This executable file contains the whole engine and +runs without any dependencies. Executing it will bring up the Project +Manager. + +:::note + +If you wish to compile using Clang rather than GCC, use this command: + +``` +scons platform=linuxbsd use_llvm=yes + +``` + +Using Clang appears to be a requirement for OpenBSD, otherwise fonts +would not build. +For RISC-V architecture devices, use the Clang compiler instead of the GCC compiler. + +::: + +:::tip +If you are compiling Redot for production use, you can +make the final executable smaller and faster by adding the +SCons option ``production=yes``. This enables additional compiler +optimizations and link-time optimization. + +LTO takes some time to run and requires about 7 GB of available RAM +while compiling. If you're running out of memory with the above option, +use ``production=yes lto=none`` or ``production=yes lto=thin`` for a +lightweight but less effective form of LTO. + +::: + +:::note +If you want to use separate editor settings for your own Redot builds +and official releases, you can enable +[doc_data_paths_self_contained_mode](doc_data_paths_self_contained_mode) by creating a file called +``._sc_`` or ``_sc_`` in the ``bin/`` folder. + +::: + +## Running a headless/server build + +To run in *headless* mode which provides editor functionality to export +projects in an automated manner, use the normal build + +``` +scons platform=linuxbsd target=editor + +``` + +And then use the ``--headless`` command line argument + +``` +./bin/Redot.linuxbsd.editor.x86_64 --headless + +``` + +To compile a debug *server* build which can be used with +:ref:`remote debugging tools `, use + +``` +scons platform=linuxbsd target=template_debug + +``` + +To compile a *server* build which is optimized to run dedicated game servers, +use + +``` +scons platform=linuxbsd target=template_release production=yes + +``` + +## Building export templates + +:::warning +Linux binaries usually won't run on distributions that are +older than the distribution they were built on. If you wish to +distribute binaries that work on most distributions, +you should build them on an old distribution such as Ubuntu 16.04. +You can use a virtual machine or a container to set up a suitable +build environment. + +::: + +To build Linux or \*BSD export templates, run the build system with the +following parameters: + +- (32 bits) + +``` +scons platform=linuxbsd target=template_release arch=x86_32 +scons platform=linuxbsd target=template_debug arch=x86_32 + +``` + +- (64 bits) + +``` +scons platform=linuxbsd target=template_release arch=x86_64 +scons platform=linuxbsd target=template_debug arch=x86_64 + +``` + +Note that cross-compiling for the opposite bits (64/32) as your host +platform is not always straight-forward and might need a chroot environment. + +To create standard export templates, the resulting files in the ``bin/`` folder +must be copied to: + +``` +$HOME/.local/share/Redot/export_templates// + +``` + +and named like this (even for \*BSD which is seen as "Linux/X11" by Redot): + +``` +linux_debug.arm32 +linux_debug.arm64 +linux_debug.x86_32 +linux_debug.x86_64 +linux_release.arm32 +linux_release.arm64 +linux_release.x86_32 +linux_release.x86_64 + +``` + +However, if you are writing your custom modules or custom C++ code, you +might instead want to configure your binaries as custom export templates +here: + +![Image](img/lintemplates.png) + +You don't even need to copy them, you can just reference the resulting +files in the ``bin/`` directory of your Redot source folder, so the next +time you build, you automatically have the custom templates referenced. + +## Cross-compiling for RISC-V devices + +To cross-compile Redot for RISC-V devices, we need to setup the following items: + +- [riscv-gnu-toolchain](https://github.com/riscv-collab/riscv-gnu-toolchain/releases). + While we are not going to use this directly, it provides us with a sysroot, as well + as header and libraries files that we will need. There are many versions to choose + from, however, the older the toolchain, the more compatible our final binaries will be. + If in doubt, [use this version](https://github.com/riscv-collab/riscv-gnu-toolchain/releases/tag/2021.12.22), + and download ``riscv64-glibc-ubuntu-18.04-nightly-2021.12.22-nightly.tar.gz``. Extract + it somewhere and remember its path. +- [mold](https://github.com/rui314/mold/releases). This fast linker, + is the only one that correctly links the resulting binary. Download it, extract it, + and make sure to add its ``bin`` folder to your PATH. Run + ``mold --help | grep support`` to check if your version of Mold supports RISC-V. + If you don't see RISC-V, your Mold may need to be updated. + +To make referencing our toolchain easier, we can set an environment +variable like this: + +``` +export RISCV_TOOLCHAIN_PATH="path to toolchain here" + +``` + +This way, we won't have to manually set the directory location +each time we want to reference it. + +With all the above setup, we are now ready to build Redot. + +Go to the root of the source code, and execute the following build command: + +``` +PATH="$RISCV_TOOLCHAIN_PATH/bin:$PATH" \ +scons arch=rv64 use_llvm=yes linker=mold lto=none target=editor \ + ccflags="--sysroot=$RISCV_TOOLCHAIN_PATH/sysroot --gcc-toolchain=$RISCV_TOOLCHAIN_PATH -target riscv64-unknown-linux-gnu" \ + linkflags="--sysroot=$RISCV_TOOLCHAIN_PATH/sysroot --gcc-toolchain=$RISCV_TOOLCHAIN_PATH -target riscv64-unknown-linux-gnu" + +``` + +:::note + +RISC-V GCC has [bugs with its atomic operations](https://github.com/riscv-collab/riscv-gcc/issues/15) +which prevent it from compiling Redot correctly. That's why Clang is used instead. Make sure that +it *can* compile to RISC-V. You can verify by executing this command ``clang -print-targets``, +make sure you see ``riscv64`` on the list of targets. + +::: + +:::warning +The code above includes adding ``$RISCV_TOOLCHAIN_PATH/bin`` to the PATH, +but only for the following ``scons`` command. Since riscv-gnu-toolchain uses +its own Clang located in the ``bin`` folder, adding ``$RISCV_TOOLCHAIN_PATH/bin`` +to your user's PATH environment variable may block you from accessing another +version of Clang if one is installed. For this reason it's not recommended to make +adding the bin folder permanent. You can also omit the ``PATH="$RISCV_TOOLCHAIN_PATH/bin:$PATH"`` line +if you want to use scons with self-installed version of Clang, but it may have +compatibility issues with riscv-gnu-toolchain. + +::: + +The command is similar in nature, but with some key changes. ``ccflags`` and +``linkflags`` append additional flags to the build. ``--sysroot`` points to +a folder simulating a Linux system, it contains all the headers, libraries, +and ``.so`` files Clang will use. ``--gcc-toolchain`` tells Clang where +the complete toolchain is, and ``-target riscv64-unknown-linux-gnu`` +indicates to Clang the target architecture, and OS we want to build for. + +If all went well, you should now see a ``bin`` directory, and within it, +a binary similar to the following: + +``` +Redot.linuxbsd.editor.rv64.llvm + +``` + +You can now copy this executable to your favorite RISC-V device, +then launch it there by double-clicking, which should bring up +the project manager. + +If you later decide to compile the export templates, copy the above +build command but change the value of ``target`` to ``template_debug`` for +a debug build, or ``template_release`` for a release build. + +## Using Clang and LLD for faster development + +You can also use Clang and LLD to build Redot. This has two upsides compared to +the default GCC + GNU ld setup: + +- LLD links Redot significantly faster compared to GNU ld or gold. This leads to + faster iteration times. +- Clang tends to give more useful error messages compared to GCC. + +To do so, install Clang and the ``lld`` package from your distribution's package manager +then use the following SCons command + +``` +scons platform=linuxbsd use_llvm=yes linker=lld + +``` + +After the build is completed, a new binary with a ``.llvm`` suffix will be +created in the ``bin/`` folder. + +It's still recommended to use GCC for production builds as they can be compiled using +link-time optimization, making the resulting binaries smaller and faster. + +If this error occurs + +``` +/usr/bin/ld: cannot find -l:libatomic.a: No such file or directory + +``` + +There are two solutions: + +- In your SCons command, add the parameter ``use_static_cpp=no``. +- Follow [these instructions](https://github.com/ivmai/libatomic_ops#installation-and-usage) to configure, build, and + install ``libatomic_ops``. Then, copy ``/usr/lib/libatomic_ops.a`` to ``/usr/lib/libatomic.a``, or create a soft link + to ``libatomic_ops`` by command ``ln -s /usr/lib/libatomic_ops.a /usr/lib/libatomic.a``. The soft link can ensure the + latest ``libatomic_ops`` will be used without the need to copy it every time when it is updated. + +## Using mold for faster development + +For even faster linking compared to LLD, you can use [mold](https://github.com/rui314/mold). +mold can be used with either GCC or Clang. + +As of January 2023, mold is not readily available in Linux distribution +repositories, so you will have to install its binaries manually. + +- Download mold binaries from its [releases page](https://github.com/rui314/mold/releases/latest). +- Extract the ``.tar.gz`` file, then move the extracted folder to a location such as ``.local/share/mold``. +- Add ``$HOME/.local/share/mold/bin`` to your user's ``PATH`` environment variable. + For example, you can add the following line at the end of your ``$HOME/.bash_profile`` file: + +``` +PATH="$HOME/.local/share/mold/bin:$PATH" + +``` + +- Open a new terminal (or run ``source "$HOME/.bash_profile"``), +then use the following SCons command when compiling Redot + +``` +scons platform=linuxbsd linker=mold + +``` + +## Using system libraries for faster development + +[Redot bundles the source code of various third-party libraries.](https://github.com/redot-engine/redot-engine/tree/master/thirdparty) +You can choose to use system versions of third-party libraries instead. +This makes the Redot binary faster to link, as third-party libraries are +dynamically linked. Therefore, they don't need to be statically linked +every time you build the engine (even on small incremental changes). + +However, not all Linux distributions have packages for third-party libraries +available (or they may not be up-to-date). + +Moving to system libraries can reduce linking times by several seconds on slow +CPUs, but it requires manual testing depending on your Linux distribution. Also, +you may not be able to use system libraries for everything due to bugs in the +system library packages (or in the build system, as this feature is less +tested). + +To compile Redot with system libraries, install these dependencies **on top** of the ones +listed in the [doc_compiling_for_linuxbsd_oneliners](doc_compiling_for_linuxbsd_oneliners): + + + + + +sudo apt-get update +sudo apt-get install -y \ + libembree-dev \ + libenet-dev \ + libfreetype-dev \ + libpng-dev \ + zlib1g-dev \ + libgraphite2-dev \ + libharfbuzz-dev \ + libogg-dev \ + libtheora-dev \ + libvorbis-dev \ + libwebp-dev \ + libmbedtls-dev \ + libminiupnpc-dev \ + libpcre2-dev \ + libzstd-dev \ + libsquish-dev \ + libicu-dev + + + + + +sudo dnf install -y \ + embree-devel \ + enet-devel \ + glslang-devel \ + graphite2-devel \ + harfbuzz-devel \ + libicu-devel \ + libsquish-devel \ + libtheora-devel \ + libvorbis-devel \ + libwebp-devel \ + libzstd-devel \ + mbedtls-devel \ + miniupnpc-devel + + + + + +After installing all required packages, use the following command to build Redot: + +``` +scons platform=linuxbsd builtin_embree=no builtin_enet=no builtin_freetype=no builtin_graphite=no builtin_harfbuzz=no builtin_libogg=no builtin_libpng=no builtin_libtheora=no builtin_libvorbis=no builtin_libwebp=no builtin_mbedtls=no builtin_miniupnpc=no builtin_pcre2=no builtin_zlib=no builtin_zstd=no + +``` + +On Debian stable, you will need to remove `builtin_embree=no` as the system-provided +Embree version is too old to work with Redot's latest `master` branch +(which requires Embree 4). + +You can view a list of all built-in libraries that have system alternatives by +running ``scons -h``, then looking for options starting with ``builtin_``. + +:::warning + +When using system libraries, the resulting library is **not** portable +across Linux distributions anymore. Do not use this approach for creating +binaries you intend to distribute to others, unless you're creating a +package for a Linux distribution. + +::: diff --git a/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_macos.md b/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_macos.md new file mode 100644 index 0000000..548de08 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_macos.md @@ -0,0 +1,267 @@ + +# Compiling for macOS + +.. highlight:: shell + +:::note + +This page describes how to compile macOS editor and export template binaries from source. +If you're looking to export your project to macOS instead, read [doc_exporting_for_macos](../../../tutorials/export/exporting_for_macos.md). + +::: + +## Requirements + +For compiling under macOS, the following is required: + +- [Python 3.8+](https://www.python.org/downloads/macos/). +- [SCons 4.0+](https://scons.org/pages/download.html) build system. +- [Xcode](https://apps.apple.com/us/app/xcode/id497799835) + (or the more lightweight Command Line Tools for Xcode). +- [Vulkan SDK](https://sdk.lunarg.com/sdk/download/latest/mac/vulkan-sdk.dmg) + for MoltenVK (macOS doesn't support Vulkan out of the box). + The latest Vulkan SDK version can be installed quickly by running + ``misc/scripts/install_vulkan_sdk_macos.sh`` within the Redot source repository. + +:::note +If you have [Homebrew](https://brew.sh/) installed, you can easily +install SCons using the following command + +``` +brew install scons + +``` + +Installing Homebrew will also fetch the Command Line Tools +for Xcode automatically if you don't have them already. + +Similarly, if you have [MacPorts](https://www.macports.org/) +installed, you can easily install SCons using the +following command + +``` +sudo port install scons +``` + +::: + +:::info +To get the Redot source code for compiling, see +[doc_getting_source](getting_source.md). + +For a general overview of SCons usage for Redot, see +[doc_introduction_to_the_buildsystem](introduction_to_the_buildsystem.md). + +::: + +## Compiling + +Start a terminal, go to the root directory of the engine source code. + +To compile for Intel (x86-64) powered Macs, use + +``` +scons platform=macos arch=x86_64 + +``` + +To compile for Apple Silicon (ARM64) powered Macs, use + +``` +scons platform=macos arch=arm64 + +``` + +To support both architectures in a single "Universal 2" binary, run the above two commands and then use ``lipo`` to bundle them together + +``` +lipo -create bin/Redot.macos.editor.x86_64 bin/Redot.macos.editor.arm64 -output bin/Redot.macos.editor.universal + +``` + +:::tip + +If you are compiling Redot to make changes or contribute to the engine, +you may want to use the SCons options ``dev_build=yes`` or ``dev_mode=yes``. +See [doc_introduction_to_the_buildsystem_development_and_production_aliases](doc_introduction_to_the_buildsystem_development_and_production_aliases) +for more info. + +::: + +If all goes well, the resulting binary executable will be placed in the +``bin/`` subdirectory. This executable file contains the whole engine and +runs without any dependencies. Executing it will bring up the Project +Manager. + +:::note +If you want to use separate editor settings for your own Redot builds +and official releases, you can enable +[doc_data_paths_self_contained_mode](doc_data_paths_self_contained_mode) by creating a file called +``._sc_`` or ``_sc_`` in the ``bin/`` folder. + +::: + +To create an ``.app`` bundle like in the official builds, you need to use the +template located in ``misc/dist/macos_tools.app``. Typically, for an optimized +editor binary built with ``dev_build=yes`` + +``` +cp -r misc/dist/macos_tools.app ./Redot.app +mkdir -p Redot.app/Contents/MacOS +cp bin/Redot.macos.editor.universal Redot.app/Contents/MacOS/Redot +chmod +x Redot.app/Contents/MacOS/Redot +codesign --force --timestamp --options=runtime --entitlements misc/dist/macos/editor.entitlements -s - Redot.app + +``` + +:::note + +If you are building the ``master`` branch, you also need to include support +for the MoltenVK Vulkan portability library. By default, it will be linked +statically from your installation of the Vulkan SDK for macOS. +You can also choose to link it dynamically by passing ``use_volk=yes`` and +including the dynamic library in your ``.app`` bundle + +``` +mkdir -p Redot.app/Contents/Frameworks +cp /macOS/lib/libMoltenVK.dylib Redot.app/Contents/Frameworks/libMoltenVK.dylib +``` + +::: + +## Running a headless/server build + +To run in *headless* mode which provides editor functionality to export +projects in an automated manner, use the normal build + +``` +scons platform=macos target=editor + +``` + +And then use the ``--headless`` command line argument + +``` +./bin/Redot.macos.editor.x86_64 --headless + +``` + +To compile a debug *server* build which can be used with +:ref:`remote debugging tools `, use + +``` +scons platform=macos target=template_debug + +``` + +To compile a release *server* build which is optimized to run dedicated game servers, +use + +``` +scons platform=macos target=template_release production=yes + +``` + +## Building export templates + +To build macOS export templates, you have to compile using the targets without +the editor: ``target=template_release`` (release template) and +``target=template_debug``. + +Official templates are *Universal 2* binaries which support both ARM64 and Intel +x86_64 architectures. + +- To support ARM64 (Apple Silicon) + Intel x86_64 + +``` +scons platform=macos target=template_debug arch=arm64 +scons platform=macos target=template_release arch=arm64 +scons platform=macos target=template_debug arch=x86_64 +scons platform=macos target=template_release arch=x86_64 generate_bundle=yes + +``` + +- To support ARM64 (Apple Silicon) only (smaller file size, but less compatible with older hardware) + +``` +scons platform=macos target=template_debug arch=arm64 +scons platform=macos target=template_release arch=arm64 generate_bundle=yes + +``` + +To create an ``.app`` bundle like in the official builds, you need to use the +template located in ``misc/dist/macos_template.app``. This process can be automated by using +the ``generate_bundle=yes`` option on the *last* SCons command used to build export templates +(so that all binaries can be included). This option also takes care of calling ``lipo`` to create +an *Universal 2* binary from two separate ARM64 and x86_64 binaries (if both were compiled beforehand). + +:::note + +You also need to include support for the MoltenVK Vulkan portability +library. By default, it will be linked statically from your installation of +the Vulkan SDK for macOS. You can also choose to link it dynamically by +passing ``use_volk=yes`` and including the dynamic library in your ``.app`` +bundle + +``` +mkdir -p macos_template.app/Contents/Frameworks +cp /macOS/libs/libMoltenVK.dylib macos_template.app/Contents/Frameworks/libMoltenVK.dylib + +``` + +In most cases, static linking should be preferred as it makes distribution +easier. The main upside of dynamic linking is that it allows updating +MoltenVK without having to recompile export templates. + +::: + +You can then zip the ``macos_template.app`` folder to reproduce the ``macos.zip`` +template from the official Redot distribution + +``` +zip -r9 macos.zip macos_template.app + +``` + +## Cross-compiling for macOS from Linux + +It is possible to compile for macOS in a Linux environment (and maybe also in +Windows using the Windows Subsystem for Linux). For that, you'll need to install +[OSXCross](https://github.com/tpoechtrager/osxcross) to be able to use macOS +as a target. First, follow the instructions to install it: + +Clone the [OSXCross repository](https://github.com/tpoechtrager/osxcross) +somewhere on your machine (or download a ZIP file and extract it somewhere), +e.g. + +``` +git clone --depth=1 https://github.com/tpoechtrager/osxcross.git "$HOME/osxcross" + +``` + +1. Follow the instructions to package the SDK: + https://github.com/tpoechtrager/osxcross#packaging-the-sdk +2. Follow the instructions to install OSXCross: + https://github.com/tpoechtrager/osxcross#installation + +After that, you will need to define the ``OSXCROSS_ROOT`` as the path to +the OSXCross installation (the same place where you cloned the +repository/extracted the zip), e.g. + +``` +export OSXCROSS_ROOT="$HOME/osxcross" + +``` + +Now you can compile with SCons like you normally would + +``` +scons platform=macos + +``` + +If you have an OSXCross SDK version different from the one expected by the SCons buildsystem, you can specify a custom one with the ``osxcross_sdk`` argument + +``` +scons platform=macos osxcross_sdk=darwin15 +``` diff --git a/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_web.md b/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_web.md new file mode 100644 index 0000000..dbf0706 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_web.md @@ -0,0 +1,155 @@ + +# Compiling for the Web + +:::info + +This page describes how to compile HTML5 editor and export template binaries from source. +If you're looking to export your project to HTML5 instead, read [doc_exporting_for_web](../../../tutorials/export/exporting_for_web.md). + +::: + +.. highlight:: shell + +## Requirements + +To compile export templates for the Web, the following is required: + +- [Emscripten 3.1.62+](https://emscripten.org). +- [Python 3.8+](https://www.python.org/). +- [SCons 4.0+](https://scons.org/pages/download.html) build system. + +:::info +To get the Redot source code for compiling, see +[doc_getting_source](getting_source.md). + +For a general overview of SCons usage for Redot, see +[doc_introduction_to_the_buildsystem](introduction_to_the_buildsystem.md). + +::: + +## Building export templates + +Before starting, confirm that ``emcc`` is available in your PATH. This is +usually configured by the Emscripten SDK, e.g. when invoking ``emsdk activate`` +and ``source ./emsdk_env.sh``/``emsdk_env.bat``. + +Open a terminal and navigate to the root directory of the engine source code. +Then instruct SCons to build the Web platform. Specify ``target`` as +either ``template_release`` for a release build or ``template_debug`` for a debug build + +``` +scons platform=web target=template_release +scons platform=web target=template_debug + +``` + +By default, the [JavaScriptBridge singleton](../../../tutorials/platform/web/javascript_bridge.md) will be built +into the engine. Official export templates also have the JavaScript singleton +enabled. Since ``eval()`` calls can be a security concern, the +``javascript_eval`` option can be used to build without the singleton + +``` +scons platform=web target=template_release javascript_eval=no +scons platform=web target=template_debug javascript_eval=no + +``` + +By default, WebWorker threads support is enabled. To disable it and only use a single thread, +the ``threads`` option can be used to build the web template without threads support + +``` +scons platform=web target=template_release threads=no +scons platform=web target=template_debug threads=no + +``` + +The engine will now be compiled to WebAssembly by Emscripten. Once finished, +the resulting file will be placed in the ``bin`` subdirectory. Its name is +``Redot.web.template_release.wasm32.zip`` for release or ``Redot.web.template_debug.wasm32.zip`` +for debug. + +Finally, rename the zip archive to ``web_release.zip`` for the +release template + +``` +mv bin/Redot.web.template_release.wasm32.zip bin/web_release.zip + +``` + +And ``web_debug.zip`` for the debug template + +``` +mv bin/Redot.web.template_debug.wasm32.zip bin/web_debug.zip + +``` + +## GDExtension + +The default export templates do not include GDExtension support for +performance and compatibility reasons. See the +[export page](doc_javascript_export_options) for more info. + +You can build the export templates using the option ``dlink_enabled=yes`` +to enable GDExtension support + +``` +scons platform=web dlink_enabled=yes target=template_release +scons platform=web dlink_enabled=yes target=template_debug + +``` + +Once finished, the resulting file will be placed in the ``bin`` subdirectory. +Its name will have ``_dlink`` added. + +Finally, rename the zip archives to ``web_dlink_release.zip`` and +``web_dlink_release.zip`` for the release template + +``` +mv bin/Redot.web.template_release.wasm32.dlink.zip bin/web_dlink_release.zip +mv bin/Redot.web.template_debug.wasm32.dlink.zip bin/web_dlink_debug.zip + +``` + +## Building the editor + +It is also possible to build a version of the Redot editor that can run in the +browser. The editor version is not recommended +over the native build. You can build the editor with + +``` +scons platform=web target=editor + +``` + +Once finished, the resulting file will be placed in the ``bin`` subdirectory. +Its name will be ``Redot.web.editor.wasm32.zip``. You can upload the +zip content to your web server and visit it with your browser to use the editor. + +Refer to the [export page](doc_javascript_export_options) for the web +server requirements. + +:::tip + +The Redot repository includes a +[Python script to host a local web server](https://raw.githubusercontent.com/Redot-engine/Redot-engine/master/platform/web/serve.py). +This can be used to test the web editor locally. + +After compiling the editor, extract the ZIP archive that was created in the +``bin/`` folder, then run the following command in the Redot repository +root: + +``` +# You may need to replace `python` with `python3` on some platforms. +python platform/web/serve.py + +``` + +This will serve the contents of the ``bin/`` folder and open the default web +browser automatically. In the page that opens, access ``Redot.editor.html`` +and you should be able to test the web editor this way. + +Note that for production use cases, this Python-based web server should not +be used. Instead, you should use an established web server such as Apache or +nginx. + +::: diff --git a/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_windows.md b/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_windows.md new file mode 100644 index 0000000..d5d1739 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/compiling/compiling_for_windows.md @@ -0,0 +1,545 @@ + +# Compiling for Windows + +.. highlight:: shell + +:::info + +This page describes how to compile Windows editor and export template binaries from source. +If you're looking to export your project to Windows instead, read [doc_exporting_for_windows](../../../tutorials/export/exporting_for_windows.md). + +::: + +## Requirements + +For compiling under Windows, the following is required: + +- A C++ compiler. Use one of the following: + + - [Visual Studio Community](https://www.visualstudio.com/vs/community/), + version 2019 or later. Visual Studio 2022 is recommended. + **Make sure to enable C++ in the list of workflows to install.** + If you've already installed Visual Studio without C++ support, run the installer + again; it should present you a **Modify** button. + Supports ``x86_64``, ``x86_32``, and ``arm64``. + - [MinGW-w64](https://mingw-w64.org/) with GCC can be used as an alternative to + Visual Studio. Be sure to install/configure it to use the ``posix`` thread model. + **Important:** When using MinGW to compile the ``master`` branch, you need GCC 9 or later. + Supports ``x86_64`` and ``x86_32`` only. + - [MinGW-LLVM](https://github.com/mstorsjo/llvm-mingw/releases) with clang can be used as + an alternative to Visual Studio and MinGW-w64. + Supports ``x86_64``, ``x86_32``, and ``arm64``. +- [Python 3.8+](https://www.python.org/downloads/windows/). + **Make sure to enable the option to add Python to the** ``PATH`` **in the installer.** +- [SCons 4.0+](https://scons.org/pages/download.html) build system. Using the + latest release is recommended, especially for proper support of recent Visual + Studio releases. + +:::note +If you have [Scoop](https://scoop.sh/) installed, you can easily +install MinGW and other dependencies using the following command + +``` +scoop install gcc python scons make mingw +``` + +::: + +:::note +If you have [MSYS2](https://www.msys2.org/) installed, you can easily +install MinGW and other dependencies using the following command + +``` +pacman -S mingw-w64-x86_64-python3-pip mingw-w64-x86_64-gcc \ + mingw-w64-i686-python3-pip mingw-w64-i686-gcc make + +``` + +For each MSYS2 MinGW subsystem, you should then run +`pip3 install scons` in its shell. + +::: + +:::info +To get the Redot source code for compiling, see +[doc_getting_source](getting_source.md). + +For a general overview of SCons usage for Redot, see +[doc_introduction_to_the_buildsystem](introduction_to_the_buildsystem.md). + +::: + +## Setting up SCons + +To install SCons, open the command prompt and run the following command + +``` +python -m pip install scons + +``` + +If you are prompted with the message +``Defaulting to user installation because normal site-packages is not +writeable``, you may have to run that command again using elevated +permissions. Open a new command prompt as an Administrator then run the command +again to ensure that SCons is available from the ``PATH``. + +To check whether you have installed Python and SCons correctly, you can +type ``python --version`` and ``scons --version`` into a command prompt +(``cmd.exe``). + +If the commands above don't work, make sure to add Python to your ``PATH`` +environment variable after installing it, then check again. +You can do so by running the Python installer again and enabling the option +to add Python to the ``PATH``. + +If SCons cannot detect your Visual Studio installation, it might be that your +SCons version is too old. Update it to the latest version with +``python -m pip install --upgrade scons``. + +## Downloading Redot's source + +Refer to [doc_getting_source](getting_source.md) for detailed instructions. + +The tutorial will assume from now on that you placed the source code in +``C:\Redot``. + +:::warning + +To prevent slowdowns caused by continuous virus scanning during compilation, +add the Redot source folder to the list of exceptions in your antivirus +software. + +For Windows Defender, hit the `Windows` key, type "Windows Security" +then hit `Enter`. Click on **Virus & threat protection** on the left +panel. Under **Virus & threat protection settings** click on **Manage Settings** +and scroll down to **Exclusions**. Click **Add or remove exclusions** then +add the Redot source folder. + +::: + +## Compiling + +### Selecting a compiler + +SCons will automatically find and use an existing Visual Studio installation. +If you do not have Visual Studio installed, it will attempt to use +MinGW instead. If you already have Visual Studio installed and want to +use MinGW-w64, pass ``use_mingw=yes`` to the SCons command line. Note that MSVC +builds cannot be performed from the MSYS2 or MinGW shells. Use either +``cmd.exe`` or PowerShell instead. If you are using MinGW-LLVM, pass both +``use_mingw=yes`` and ``use_llvm=yes`` to the SCons command line. + +:::tip + +During development, using the Visual Studio compiler is usually a better +idea, as it links the Redot binary much faster than MinGW. However, MinGW +can produce more optimized binaries using link-time optimization (see +below), making it a better choice for production use. This is particularly +the case for the GDScript VM which performs much better with MinGW compared +to MSVC. Therefore, it's recommended to use MinGW to produce builds that you +distribute to players. + +All official Redot binaries are built in +[custom containers](https://github.com/redot-engine/build-containers) +using MinGW. + +::: + +### Running SCons + +After opening a command prompt, change to the root directory of +the engine source code (using ``cd``) and type: + +```doscon +C:\Redot> scons platform=windows + +``` + +:::note +When compiling with multiple CPU threads, SCons may warn about +pywin32 being missing. You can safely ignore this warning. + +::: + +:::tip + +If you are compiling Redot to make changes or contribute to the engine, +you may want to use the SCons options ``dev_build=yes`` or ``dev_mode=yes``. +See [doc_introduction_to_the_buildsystem_development_and_production_aliases](doc_introduction_to_the_buildsystem_development_and_production_aliases) +for more info. + +::: + +If all goes well, the resulting binary executable will be placed in +``C:\Redot\bin\`` with the name ``Redot.windows.editor.x86_32.exe`` or +``Redot.windows.editor.x86_64.exe``. By default, SCons will build a binary matching +your CPU architecture, but this can be overridden using ``arch=x86_64``, +``arch=x86_32``, or ``arch=arm64``. + +This executable file contains the whole engine and runs without any +dependencies. Running it will bring up the Project Manager. + +:::tip +If you are compiling Redot for production use, you can +make the final executable smaller and faster by adding the +SCons option ``production=yes``. This enables additional compiler +optimizations and link-time optimization. + +LTO takes some time to run and requires up to 30 GB of available RAM +while compiling (depending on toolchain). If you're running out of memory +with the above option, use ``production=yes lto=none`` or ``production=yes lto=thin`` +(LLVM only) for a lightweight but less effective form of LTO. + +::: + +:::note +If you want to use separate editor settings for your own Redot builds +and official releases, you can enable +[doc_data_paths_self_contained_mode](doc_data_paths_self_contained_mode) by creating a file called +``._sc_`` or ``_sc_`` in the ``bin/`` folder. + +::: + +## Compiling with support for Direct3D 12 + +By default, builds of Redot do not contain support for the Direct3D 12 graphics +API. + +You can install the required dependencies by running +``python misc/scripts/install_d3d12_sdk_windows.py`` +in the Redot source repository. After running this script, add the ``d3d12=yes`` +SCons option to enable Direct3D 12 support. This will use the default paths for +the various dependencies, which match the ones used in the script. + +You can find the detailed steps below if you wish to set up dependencies +manually, but the above script handles everything for you (including the +optional PIX and Agility SDK components). + +- [Redot-nir-static library](https://github.com/redot-engine/redot-engine-nir-static/releases/). + We compile the Mesa libraries you will need into a static library. Download it + anywhere, unzip it and remember the path to the unzipped folder, you will + need it below. + +:::note +You can optionally build the Redot-nir-static libraries yourself with +the following steps: + +1. Install the Python package [mako](https://www.makotemplates.org) + which is needed to generate some files. +2. Clone the [Redot-nir-static](https://github.com/redot-engine/redot-engine-nir-static) + directory and navigate to it. +3. Run the following + +``` +git submodule update --init +./update_mesa.sh +scons + +``` + + If you are building with MinGW-w64, add ``use_mingw=yes`` to the ``scons`` + command, you can also specify build architecture using ``arch={architecture}``. + If you are building with MinGW-LLVM, add both ``use_mingw=yes`` and + ``use_llvm=yes`` to the ``scons`` command. + + If you are building with MinGW and the binaries are not located in + the ``PATH``, add ``mingw_prefix="/path/to/mingw"`` to the ``scons`` + command. + + Mesa static library should be built using the same compiler and the + same CRT (if you are building with MinGW) you are using for building + Redot. + +::: + +Optionally, you can compile with the following for additional features: + +- [PIX](https://devblogs.microsoft.com/pix/download) is a performance tuning + and debugging application for Direct3D12 applications. If you compile-in + support for it, you can get much more detailed information through PIX that + will help you optimize your game and troubleshoot graphics bugs. To use it, + download the WinPixEventRuntime package. You will be taken to a NuGet package + page where you can click "Download package" to get it. Once downloaded, change + the file extension to .zip and unzip the file to some path. +- [Agility SDK](https://devblogs.microsoft.com/directx/directx12agility) can + be used to provide access to the latest Direct3D 12 features without relying + on driver updates. To use it, download the latest Agility SDK package. You + will be taken to a NuGet package page where you can click "Download package" + to get it. Once downloaded, change the file extension to .zip and unzip the + file to some path. + +:::note +If you use a preview version of the Agility SDK, remember to enable +developer mode in Windows; otherwise it won't be used. + +::: + +:::note +If you want to use a PIX with MinGW build, navigate to PIX runtime +directory and use the following commands to generate import library + +``` +# For x86-64: +gendef ./bin/x64/WinPixEventRuntime.dll +dlltool --machine i386:x86-64 --no-leading-underscore -d WinPixEventRuntime.def -D WinPixEventRuntime.dll -l ./bin/x64/libWinPixEventRuntime.a + +# For ARM64: +gendef ./bin/ARM64/WinPixEventRuntime.dll +dlltool --machine arm64 --no-leading-underscore -d WinPixEventRuntime.def -D WinPixEventRuntime.dll -l ./bin/ARM64/libWinPixEventRuntime.a +``` + +::: + +When building Redot, you will need to tell SCons to use Direct3D 12 and where to +look for the additional libraries: + +```doscon +C:\Redot> scons platform=windows d3d12=yes mesa_libs=<...> + +``` + +Or, with all options enabled: + +```doscon +C:\Redot> scons platform=windows d3d12=yes mesa_libs=<...> agility_sdk_path=<...> pix_path=<...> + +``` + +:::note +For the Agility SDK's DLLs you have to explicitly choose the kind of +workflow. Single-arch is the default (DLLs copied to ``bin/``). If you +pass ``agility_sdk_multi_arch=yes`` to SCons, you'll opt-in for +multi-arch. DLLs will be copied to the appropriate ``bin/<arch>/`` +subdirectories and at runtime the right one will be loaded. + +::: + +## Compiling with ANGLE support + +ANGLE provides a translation layer from OpenGL ES 3.x to Direct3D 11 and can be used +to improve support for the Compatibility renderer on some older GPUs with outdated +OpenGL drivers and on Windows for ARM. + +By default, Redot is built with dynamically linked ANGLE, you can use it by placing +``libEGL.dll`` and ``libGLESv2.dll`` alongside the executable. + +:::note +You can use dynamically linked ANGLE with export templates as well, rename +aforementioned DLLs to ``libEGL.{architecture}.dll`` and ``libGLESv2.{architecture}.dll`` +and place them alongside export template executables, and libraries will +be automatically copied during the export process. + +::: + +To compile Redot with statically linked ANGLE: + +- Download pre-built static libraries from [Redot-angle-static library](https://github.com/redot-engine/redot-engine-angle-static/releases), and unzip them. +- When building Redot, add ``angle_libs={path}`` to tell SCons where to look for the ANGLE libraries + +``` +scons platform=windows angle_libs=<...> + +``` + +:::note +You can optionally build the Redot-angle-static libraries yourself with +the following steps: + +1. Clone the [Redot-angle-static](https://github.com/redot-engine/redot-engine-angle-static) + directory and navigate to it. +2. Run the following command + +``` +git submodule update --init +./update_angle.sh +scons + +``` + + If you are buildng with MinGW, add ``use_mingw=yes`` to the command, + you can also specify build architecture using ``arch={architecture}``. + If you are building with MinGW-LLVM, add both ``use_mingw=yes`` and + ``use_llvm=yes`` to the ``scons`` command. + + If you are building with MinGW and the binaries are not located in + the ``PATH``, add ``mingw_prefix="/path/to/mingw"`` to the ``scons`` + command. + + ANGLE static library should be built using the same compiler and the + same CRT (if you are building with MinGW) you are using for building + Redot. + +::: + +## Development in Visual Studio + +Using an IDE is not required to compile Redot, as SCons takes care of everything. +But if you intend to do engine development or debugging of the engine's C++ code, +you may be interested in configuring a code editor or an IDE. + +Folder-based editors don't require any particular setup to start working with Redot's +codebase. To edit projects with Visual Studio they need to be set up as a solution. + +You can create a Visual Studio solution via SCons by running SCons with +the ``vsproj=yes`` parameter, like this + +``` +scons platform=windows vsproj=yes + +``` + +You will be able to open Redot's source in a Visual Studio solution now, +and able to build Redot using Visual Studio's **Build** button. + +:::info +See [doc_configuring_an_ide_vs](../configuring_an_ide/visual_studio.md) for further details. + +::: + +## Cross-compiling for Windows from other operating systems + +If you are a Linux or macOS user, you need to install +[MinGW-w64](https://www.mingw-w64.org/), which typically comes in 32-bit +and 64-bit variants, or [MinGW-LLVM](https://github.com/mstorsjo/llvm-mingw/releases), +which comes as a single archive for all target architectures. +The package names may differ based on your distribution, here are some known ones: + +| **Arch Linux** | `pacman -Sy mingw-w64` | +| --- | --- | +| **Debian** / **Ubuntu** | `apt install mingw-w64` | +| **Fedora** | `dnf install mingw64-gcc-c++ mingw64-winpthreads-static \ mingw32-gcc-c++ mingw32-winpthreads-static` | +| **macOS** | `brew install mingw-w64` | +| **Mageia** | `urpmi mingw64-gcc-c++ mingw64-winpthreads-static \ mingw32-gcc-c++ mingw32-winpthreads-static` | + +Before attempting the compilation, SCons will check for +the following binaries in your ``PATH`` environment variable + +``` +# for MinGW-w64 +i686-w64-mingw32-gcc +x86_64-w64-mingw32-gcc + +# for MinGW-LLVM +aarch64-w64-mingw32-clang +i686-w64-mingw32-clang +x86_64-w64-mingw32-clang + +``` + +If the binaries are not located in the ``PATH`` (e.g. ``/usr/bin``), +you can define the following environment variable to give a hint to +the build system + +``` +export MINGW_PREFIX="/path/to/mingw" + +``` + +Where ``/path/to/mingw`` is the path containing the ``bin`` directory where +``i686-w64-mingw32-gcc`` and ``x86_64-w64-mingw32-gcc`` are located (e.g. +``/opt/mingw-w64`` if the binaries are located in ``/opt/mingw-w64/bin``). + +To make sure you are doing things correctly, executing the following in +the shell should result in a working compiler (the version output may +differ based on your system) + +``` +${MINGW_PREFIX}/bin/x86_64-w64-mingw32-gcc --version +# x86_64-w64-mingw32-gcc (GCC) 13.2.0 + +``` + +:::note +If you are building with MinGW-LLVM, add ``use_llvm=yes`` to the ``scons`` command. + +::: + +:::note +When cross-compiling for Windows using MinGW-w64, keep in mind only +``x86_64`` and ``x86_32`` architectures are supported. MinGW-LLVM supports +``arm64`` as well. Be sure to specify the right ``arch=`` option when +invoking SCons if building from a different architecture. + +::: + +### Troubleshooting + +Cross-compiling from some Ubuntu versions may lead to +[this bug](https://github.com/redot-engine/redot-engine/issues/9258), +due to a default configuration lacking support for POSIX threading. + +You can change that configuration following those instructions, +for 64-bit + +``` +sudo update-alternatives --config x86_64-w64-mingw32-gcc + +sudo update-alternatives --config x86_64-w64-mingw32-g++ + + +``` + +And for 32-bit + +``` +sudo update-alternatives --config i686-w64-mingw32-gcc + +sudo update-alternatives --config i686-w64-mingw32-g++ + + +``` + +## Creating Windows export templates + +Windows export templates are created by compiling Redot without the editor, +with the following flags: + +```doscon +C:\Redot> scons platform=windows target=template_debug arch=x86_32 +C:\Redot> scons platform=windows target=template_release arch=x86_32 +C:\Redot> scons platform=windows target=template_debug arch=x86_64 +C:\Redot> scons platform=windows target=template_release arch=x86_64 +C:\Redot> scons platform=windows target=template_debug arch=arm64 +C:\Redot> scons platform=windows target=template_release arch=arm64 + +``` + +If you plan on replacing the standard export templates, copy these to the +following location, replacing ``<version>`` with the version identifier +(such as ``4.2.1.stable`` or ``4.3.dev``): + +```none +%APPDATA%\Redot\export_templates\\ + +``` + +With the following names + +``` +windows_debug_x86_32_console.exe +windows_debug_x86_32.exe +windows_debug_x86_64_console.exe +windows_debug_x86_64.exe +windows_debug_arm64_console.exe +windows_debug_arm64.exe +windows_release_x86_32_console.exe +windows_release_x86_32.exe +windows_release_x86_64_console.exe +windows_release_x86_64.exe +windows_release_arm64_console.exe +windows_release_arm64.exe + +``` + +However, if you are using custom modules or custom engine code, you +may instead want to configure your binaries as custom export templates +here: + +![Image](img/wintemplates.webp) + +Select matching architecture in the export config. + +You don't need to copy them in this case, just reference the resulting +files in the ``bin\`` directory of your Redot source folder, so the next +time you build, you will automatically have the custom templates referenced. \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/compiling/compiling_with_dotnet.md b/Redot-Documentation/docs/Contributing/Development/compiling/compiling_with_dotnet.md new file mode 100644 index 0000000..04ebb9d --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/compiling/compiling_with_dotnet.md @@ -0,0 +1,231 @@ + +# Compiling with .NET + +.. highlight:: shell + +## Requirements + +- [.NET SDK 8.0+](https://dotnet.microsoft.com/download) + + You can use ``dotnet --info`` to check which .NET SDK versions are installed. + +## Enable the .NET module + +:::note +C# support for Redot has historically used the +[Mono](https://www.mono-project.com/) runtime instead of the +[.NET Runtime](https://github.com/dotnet/runtime) and internally +many things are still named ``mono`` instead of ``dotnet`` or +otherwise referred to as ``mono``. + +::: + +By default, the .NET module is disabled when building. To enable it, add the +option ``module_mono_enabled=yes`` to the SCons command line, while otherwise +following the instructions for building the desired Redot binaries. + +## Generate the glue + +Parts of the sources of the managed libraries are generated from the ClassDB. +These source files must be generated before building the managed libraries. +They can be generated by any .NET-enabled Redot editor binary by running it with +the parameters ``--headless --generate-mono-glue`` followed by the path to an +output directory. +This path must be ``modules/mono/glue`` in the Redot directory + +``` + --headless --generate-mono-glue modules/mono/glue + +``` + +This command will tell Redot to generate the C# bindings for the Redot API at +``modules/mono/glue/RedotSharp/RedotSharp/Generated``, and the C# bindings for +the editor tools at ``modules/mono/glue/RedotSharp/RedotSharpEditor/Generated``. +Once these files are generated, you can build Redot's managed libraries for all +the desired targets without having to repeat this process. + +``<Redot_binary>`` refers to the editor binary you compiled with the .NET module +enabled. Its exact name will differ based on your system and configuration, but +should be of the form ``bin/Redot.<platform>.editor.<arch>.mono``, e.g. +``bin/Redot.linuxbsd.editor.x86_64.mono`` or +``bin/Redot.windows.editor.x86_32.mono.exe``. Be especially aware of the +**.mono** suffix! If you've previously compiled Redot without .NET support, you +might have similarly named binaries without this suffix. These binaries can't be +used to generate the .NET glue. + +:::note +The glue sources must be regenerated every time the ClassDB-registered +API changes. That is, for example, when a new method is registered to +the scripting API or one of the parameters of such a method changes. +Redot will print an error at startup if there is an API mismatch +between ClassDB and the glue sources. + +::: + +## Building the managed libraries + +Once you have generated the .NET glue, you can build the managed libraries with +the ``build_assemblies.py`` script + +``` +./modules/mono/build_scripts/build_assemblies.py --Redot-output-dir=./bin + +``` + +If everything went well, the ``RedotSharp`` directory, containing the managed +libraries, should have been created in the ``bin`` directory. + +:::note +By default, all development builds share a version number, which can +cause some issues with caching of the NuGet packages. To solve this +issue either use ``Redot_VERSION_STATUS`` to give every build a unique +version or delete ``RedotNuGetFallbackFolder`` after every build to +clear the package cache. + +::: + +Unlike "classical" Redot builds, when building with the .NET module enabled +(and depending on the target platform), a data directory may be created both +for the editor and for exported projects. This directory is important for +proper functioning and must be distributed together with Redot. +More details about this directory in +[Data directory](compiling_with_dotnet_data_directory). + +### Build Platform + +Provide the `[--Redot-platform=](platform)` argument to control for which +platform specific the libraries are built. Omit this argument to build for the +current system. + +This currently only controls the inclusion of the support for Visual Studio as +an external editor, the libraries are otherwise identical. + +### NuGet packages + +The API assemblies, source generators, and custom MSBuild project SDK are +distributed as NuGet packages. This is all transparent to the user, but it can +make things complicated during development. + +In order to use Redot with a development version of those packages, a local +NuGet source must be created where MSBuild can find them. + +First, pick a location for the local NuGet source. If you don't have a +preference, create an empty directory at one of these recommended locations: + +- On Windows, ``C:\Users\<username>\MyLocalNugetSource`` +- On Linux, \*BSD, etc., ``~/MyLocalNugetSource`` + +This path is referred to later as ``<my_local_source>``. + +After picking a directory, run this .NET CLI command to configure NuGet to use +your local source: + +``` +dotnet nuget add source --name MyLocalNugetSource + +``` + +When you run the ``build_assemblies.py`` script, pass ``<my_local_source>`` to +the ``--push-nupkgs-local`` option: + +``` +./modules/mono/build_scripts/build_assemblies.py --Redot-output-dir ./bin --push-nupkgs-local + +``` + +This option ensures the packages will be added to the specified local NuGet +source and that conflicting versions of the package are removed from the NuGet +cache. It's recommended to always use this option when building the C# solutions +during development to avoid mistakes. + +### Building without depending on deprecated features (NO_DEPRECATED) + +When building Redot without deprecated classes and functions, i.e. the ``deprecated=no`` +argument for scons, the managed libraries must also be built without dependencies to deprecated code. +This is done by passing the ``--no-deprecated`` argument: + +``` +./modules/mono/build_scripts/build_assemblies.py --Redot-output-dir ./bin --push-nupkgs-local --no-deprecated + +``` + +### Double Precision Support (REAL_T_IS_DOUBLE) + +When building Redot with double precision support, i.e. the ``precision=double`` +argument for scons, the managed libraries must be adjusted to match by passing +the ``--precision=double`` argument: + +``` +./modules/mono/build_scripts/build_assemblies.py --Redot-output-dir ./bin --push-nupkgs-local --precision=double + +``` + +## Examples + +### Example (Windows) + +``` +# Build editor binary +scons platform=windows target=editor module_mono_enabled=yes +# Build export templates +scons platform=windows target=template_debug module_mono_enabled=yes +scons platform=windows target=template_release module_mono_enabled=yes + +# Generate glue sources +bin/Redot.windows.editor.x86_64.mono --headless --generate-mono-glue modules/mono/glue +# Build .NET assemblies +./modules/mono/build_scripts/build_assemblies.py --Redot-output-dir=./bin --Redot-platform=windows + +``` + +### Example (Linux, \*BSD) + +``` +# Build editor binary +scons platform=linuxbsd target=editor module_mono_enabled=yes +# Build export templates +scons platform=linuxbsd target=template_debug module_mono_enabled=yes +scons platform=linuxbsd target=template_release module_mono_enabled=yes + +# Generate glue sources +bin/Redot.linuxbsd.editor.x86_64.mono --headless --generate-mono-glue modules/mono/glue +# Generate binaries +./modules/mono/build_scripts/build_assemblies.py --Redot-output-dir=./bin --Redot-platform=linuxbsd + +``` + +## Data directory + +The data directory is a dependency for Redot binaries built with the .NET module +enabled. It contains important files for the correct functioning of Redot. It +must be distributed together with the Redot executable. + +### Editor + +The name of the data directory for the Redot editor will always be +``RedotSharp``. This directory contains an ``Api`` subdirectory with the Redot +API assemblies and a ``Tools`` subdirectory with the tools required by the +editor, like the ``RedotTools`` assemblies and its dependencies. + +On macOS, if the Redot editor is distributed as a bundle, the ``RedotSharp`` +directory may be placed in the ``<bundle_name>.app/Contents/Resources/`` +directory inside the bundle. + +### Export templates + +The data directory for exported projects is generated by the editor during the +export. It is named ``data_<APPNAME>_<ARCH>``, where ``<APPNAME>`` is the +application name as specified in the project setting ``application/config/name`` +and ``<ARCH>`` is the current architecture of the export. + +In the case of multi-architecture exports multiple such data directories will be +generated. + +## Command-line options + +The following is the list of command-line options available when building with +the .NET module: + +- **module_mono_enabled**\ =yes | **no** + + - Build Redot with the .NET module enabled. \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/compiling/compiling_with_script_encryption_key.md b/Redot-Documentation/docs/Contributing/Development/compiling/compiling_with_script_encryption_key.md new file mode 100644 index 0000000..d4616b5 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/compiling/compiling_with_script_encryption_key.md @@ -0,0 +1,113 @@ +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +# Compiling with PCK encryption key + +.. highlight:: shell + +The export dialog gives you the option to encrypt your PCK file with a 256-bit +AES key when releasing your project. This will make sure your scenes, scripts +and other resources are not stored in plain text and can not easily be ripped +by some script kiddie. + +Of course, the key needs to be stored in the binary, but if it's compiled, +optimized and without symbols, it would take some effort to find it. + +For this to work, you need to build the export templates from source, +with that same key. + +:::warning + +This will **not** work if you use official, precompiled export templates. +It is absolutely **required** to compile your own export templates to use +PCK encryption. + +::: + +:::warning + +By default, Android exports store assets directly in the APK file and +aren't affected by PCK encryption. To use PCK encryption on Android, enable +**APK expansion** in the export options. + +::: + +## Step by step + +1. Generate a 256-bit AES key in hexadecimal format. You can use the aes-256-cbc variant from + [this service](https://asecuritysite.com/encryption/keygen). + + Alternatively, you can generate it yourself using + [OpenSSL](https://www.openssl.org/) command-line tools: + +``` +openssl rand -hex 32 > Redot.gdkey + +``` + + The output in ``Redot.gdkey`` should be similar to: + +``` +# NOTE: Do not use the key below! Generate your own key instead. +aeb1bc56aaf580cc31784e9c41551e9ed976ecba10d315db591e749f3f64890f + +``` + + You can generate the key without redirecting the output to a file, but + that way you can minimize the risk of exposing the key. + +2. Set this key as environment variable in the console that you will use to + compile Redot, like this: + + + + + +```bash +export SCRIPT_AES256_ENCRYPTION_KEY="your_generated_key" + +``` + + + + + +```bat +set SCRIPT_AES256_ENCRYPTION_KEY=your_generated_key + +``` + + + + + +```bat +$env:SCRIPT_AES256_ENCRYPTION_KEY="your_generated_key" +``` + + + + + +3. Compile Redot export templates and set them as custom export templates + in the export preset options. + +4. Set the encryption key in the **Encryption** tab of the export preset: + +![Image](img/encryption_key.png) + +5. Add filters for the files/folders to encrypt. **By default**, include filters + are empty and **nothing will be encrypted**. + +6. Export the project. The project should run with the files encrypted now. + +## Troubleshooting + +If you get an error like below, it means the key wasn't properly included in +your Redot build. Redot is encrypting PCK file during export, but can't read +it at runtime. + +``` +ERROR: open_and_parse: Condition "String::md5(md5.digest) != String::md5(md5d)" is true. Returning: ERR_FILE_CORRUPT + At: core/io/file_access_encrypted.cpp:103 +``` diff --git a/Redot-Documentation/docs/Contributing/Development/compiling/cross-compiling_for_ios_on_linux.md b/Redot-Documentation/docs/Contributing/Development/compiling/cross-compiling_for_ios_on_linux.md new file mode 100644 index 0000000..0a833cd --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/compiling/cross-compiling_for_ios_on_linux.md @@ -0,0 +1,125 @@ + +# Cross-compiling for iOS on Linux + +.. highlight:: shell + +The procedure for this is somewhat complex and requires a lot of steps, +but once you have the environment properly configured you can +compile Redot for iOS anytime you want. + +## Disclaimer + +While it is possible to compile for iOS on a Linux environment, Apple is +very restrictive about the tools to be used (especially hardware-wise), +allowing pretty much only their products to be used for development. So +this is **not official**. However, in 2010 Apple said they relaxed some of the +[App Store review guidelines](https://developer.apple.com/app-store/review/guidelines/) +to allow any tool to be used, as long as the resulting binary does not +download any code, which means it should be OK to use the procedure +described here and cross-compiling the binary. + +## Requirements + +- [XCode with the iOS SDK](https://developer.apple.com/download/all/?q=Xcode) + (you must be logged into an Apple ID to download Xcode). +- [Clang >= 3.5](https://clang.llvm.org) for your development + machine installed and in the ``PATH``. It has to be version >= 3.5 + to target ``arm64`` architecture. +- [xar](https://mackyle.github.io/xar/) and [pbzx](https://github.com/NiklasRosenstein/pbzx) + (required to extract the ``.xip`` archive Xcode comes in). + + - For building xar and pbzx, you may want to follow + [this guide](https://gist.github.com/phracker/1944ce190e01963c550566b749bd2b54). + +- [cctools-port](https://github.com/tpoechtrager/cctools-port) + for the needed build tools. The procedure for building is quite + peculiar and is described below. + + - This also has some extra dependencies: automake, autogen, libtool. + +## Configuring the environment + +### Preparing the SDK + +Extract the Xcode ``.xip`` file you downloaded from Apple's developer website: + +``` +mkdir xcode +xar -xf /path/to/Xcode_X.x.xip -C xcode +pbzx -n Content | cpio -i + +[...] +######### Blocks + +``` + +Note that for the commands below, you will need to replace the version (``x.x``) +with whatever iOS SDK version you're using. If you don't know your iPhone SDK +version, you can see the JSON file inside of +``Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs``. + +Extract the iOS SDK: + +``` +export IOS_SDK_VERSION="x.x" +mkdir -p iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk +cp -r xcode/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/* iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk +cp -r xcode/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/* iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk/usr/include/c++ +fusermount -u xcode + +``` + +Pack the SDK so that cctools can use it: + +``` +cd iPhoneSDK +tar -cf - * | xz -9 -c - > iPhoneOS${IOS_SDK_VERSION}.sdk.tar.xz + +``` + +### Toolchain + +Build cctools: + +``` +git clone https://github.com/tpoechtrager/cctools-port.git +cd cctools-port/usage_examples/ios_toolchain +./build.sh /path/iPhoneOS${IOS_SDK_VERSION}.sdk.tar.xz arm64 + +``` + +Copy the tools to a nicer place. Note that the SCons scripts for +building will look under ``usr/bin`` inside the directory you provide +for the toolchain binaries, so you must copy to such subdirectory, akin +to the following commands: + +``` +mkdir -p "$HOME/iostoolchain/usr" +cp -r target/bin "$HOME/iostoolchain/usr/" + +``` + +Now you should have the iOS toolchain binaries in +``$HOME/iostoolchain/usr/bin``. + +## Compiling Redot for iPhone + +Once you've done the above steps, you should keep two things in your +environment: the built toolchain and the iPhoneOS SDK directory. Those +can stay anywhere you want since you have to provide their paths to the +SCons build command. + +For the iPhone platform to be detected, you need the ``OSXCROSS_IOS`` +environment variable defined to anything. + +``` +export OSXCROSS_IOS="anything" + +``` + +Now you can compile for iPhone using SCons like the standard Redot +way, with some additional arguments to provide the correct paths: + +``` +scons platform=ios arch=arm64 target=template_release IOS_SDK_PATH="/path/to/iPhoneSDK" IOS_TOOLCHAIN_PATH="/path/to/iostoolchain" ios_triple="arm-apple-darwin11-" +``` diff --git a/Redot-Documentation/docs/Contributing/Development/compiling/getting_source.md b/Redot-Documentation/docs/Contributing/Development/compiling/getting_source.md new file mode 100644 index 0000000..c273d6e --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/compiling/getting_source.md @@ -0,0 +1,66 @@ + +# Getting the source + +.. highlight:: shell + +## Downloading the Redot source code + +Before [getting into the SCons build system](introduction_to_the_buildsystem.md) +and compiling Redot, you need to actually download the Redot source code. + +The source code is available on [GitHub](https://github.com/redot-engine/redot-engine) +and while you can manually download it via the website, in general you want to +do it via the ``git`` version control system. + +If you are compiling in order to make contributions or pull requests, you should +follow the instructions from the [Pull Request workflow](../../Workflow/pr_workflow.md). + +If you don't know much about ``git`` yet, there are a great number of +[tutorials](https://git-scm.com/book) available on various websites. + +In general, you need to install ``git`` and/or one of the various GUI clients. + +Afterwards, to get the latest development version of the Redot source code +(the unstable ``master`` branch), you can use ``git clone``. + +If you are using the ``git`` command line client, this is done by entering +the following in a terminal: + +``` +git clone https://github.com/redot-engine/redot-engine.git +# You can add the --depth 1 argument to omit the commit history (shallow clone). +# A shallow clone is faster, but not all Git operations (like blame) will work. + +``` + +For any stable release, visit the [release page](https://github.com/redot-engine/redot-engine/releases) +and click on the link for the release you want. +You can then download and extract the source from the download link on the page. + +With ``git``, you can also clone a stable release by specifying its branch or tag +after the ``--branch`` (or just ``-b``) argument + +``` +# Clone the continuously maintained stable branch (`4.3` as of writing). +git clone https://github.com/redot-engine/Redot.git -b 4.3 + +# Clone the `4.3-stable` tag. This is a fixed revision that will never change. +git clone https://github.com/redot-engine/Redot.git -b 4.3-stable + +# After cloning, optionally go to a specific commit. +# This can be used to access the source code at a specific point in time, +# e.g. for development snapshots, betas and release candidates. +cd Redot +git checkout f4af8201bac157b9d47e336203d3e8a8ef729de2 + +``` + +The [maintenance branches](https://github.com/redot-engine/redot-engine/branches/all) +are used to release further patches on each minor version. + +You can get the source code for each release and pre-release in ``.tar.xz`` format from +[Redotengine/Redot-builds on GitHub](https://github.com/redot-engine/Redot-builds/releases). +This lacks version control information but has a slightly smaller download size. + +After downloading the Redot source code, +you can [continue to compiling Redot](introduction_to_the_buildsystem.md). \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/compiling/introduction_to_the_buildsystem.md b/Redot-Documentation/docs/Contributing/Development/compiling/introduction_to_the_buildsystem.md new file mode 100644 index 0000000..78dfd5e --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/compiling/introduction_to_the_buildsystem.md @@ -0,0 +1,507 @@ +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +# Introduction to the buildsystem + +.. highlight:: shell + +Redot is a primarily C++ project and it [uses the SCons build system.](doc_faq_why_scons) +We love SCons for how maintainable and easy to set up it makes our buildsystem. And thanks to +that compiling Redot from source can be as simple as running + +``` +scons + +``` + +This produces an editor build for your current platform, operating system, and architecture. +You can change what gets built by specifying a target, a platform, and/or an architecture. +For example, to build an export template used for running exported games, you can run + +``` +scons target=template_release + +``` + +If you plan to debug or develop the engine, then you might want to enable the ``dev_build`` +option to enable dev-only debugging code + +``` +scons dev_build=yes + +``` + +Following sections in the article will explain these and other universal options in more detail. But +before you can compile Redot, you need to install a few prerequisites. Please refer to the platform +documentation to learn more: + +- [doc_compiling_for_android](compiling_for_android.md) +- [doc_compiling_for_ios](compiling_for_ios.md) +- [doc_compiling_for_linuxbsd](compiling_for_linuxbsd.md) +- [doc_compiling_for_macos](compiling_for_macos.md) +- [doc_compiling_for_web](compiling_for_web.md) +- [doc_compiling_for_windows](compiling_for_windows.md) + +These articles cover in great detail both how to setup your environment to compile Redot on a specific +platform, and how to compile for that platform. Please feel free to go back and forth between them and +this article to reference platform-specific and universal configuration options. + +## Using multi-threading + +The build process may take a while, depending on how powerful your system is. By default, Redot's +SCons setup is configured to use all CPU threads but one (to keep the system responsive during +compilation). If you want to adjust how many CPU threads SCons will use, use the `[-j](threads)` +parameter to specify how many threads will be used for the build. + +Example for using 4 threads + +``` +scons -j4 + +``` + +## Platform selection + +Redot's build system will begin by detecting the platforms it can build +for. If not detected, the platform will simply not appear on the list of +available platforms. The build requirements for each platform are +described in the rest of this tutorial section. + +SCons is invoked by just calling ``scons``. If no platform is specified, +SCons will detect the target platform automatically based on the host platform. +It will then start building for the target platform right away. + +To list the available target platforms, use ``scons platform=list`` + +``` +scons platform=list +scons: Reading SConscript files ... +The following platforms are available: + + android + javascript + linuxbsd + server + windows + +Please run SCons again and select a valid platform: platform= + +``` + +To build for a platform (for example, ``linuxbsd``), run with the ``platform=`` +(or ``p=`` to make it short) argument: + +``` +scons platform=linuxbsd + +``` + +## Resulting binary + +The resulting binaries will be placed in the ``bin/`` subdirectory, +generally with this naming convention + +``` +Redot..[.dev][.double].[.][.] + +``` + +For the previous build attempt, the result would look like this: + +```console +ls bin +bin/Redot.linuxbsd.editor.x86_64 + +``` + +This means that the binary is for Linux *or* \*BSD (*not* both), is not optimized, has the +whole editor compiled in, and is meant for 64 bits. + +A Windows binary with the same configuration will look like this: + +```doscon +C:\Redot> dir bin/ +Redot.windows.editor.64.exe + +``` + +Copy that binary to any location you like, as it contains the Project Manager, +editor and all means to execute the game. However, it lacks the data to export +it to the different platforms. For that the export templates are needed (which +can be either downloaded from [redotengine.org](https://redotengine.org/), or +you can build them yourself). + +Aside from that, there are a few standard options that can be set in all +build targets, and which will be explained below. + +## Target + +Target controls if the editor is contained and debug flags are used. +All builds are optimized. Each mode means: + +- ``target=editor``: Build with editor, optimized, with debugging code (defines: ``TOOLS_ENABLED``, ``DEBUG_ENABLED``, ``-O2``/``/O2``) +- ``target=template_debug``: Build with C++ debugging symbols (defines: ``DEBUG_ENABLED``, ``-O2``/``/O2``) +- ``target=template_release``: Build without symbols (defines: ``-O3``/``/O2``) + +The editor is enabled by default in all PC targets (Linux, Windows, macOS), +disabled for everything else. Disabling the editor produces a binary that can +run projects but does not include the editor or the Project Manager. + +``` +scons platform= target=editor/template_debug/template_release + +``` + +## Development and production aliases + +When creating builds for development (running debugging/[profiling](../debugging/using_cpp_profilers.md) +tools), you often have different goals compared to production builds +(making binaries as fast and small as possible). + +Redot provides two aliases for this purpose: + +- ``dev_mode=yes`` is an alias for ``verbose=yes warnings=extra werror=yes + tests=yes``. This enables warnings-as-errors behavior (similar to Redot's + continuous integration setup) and also builds :ref:`unit tests + <doc_unit_testing>` so you can run them locally. +- ``production=yes`` is an alias for ``use_static_cpp=yes debug_symbols=no + lto=auto``. Statically linking libstdc++ allows for better binary portability + when compiling for Linux. This alias also enables link-time optimization when + compiling for Linux, Web and Windows with MinGW, but keeps LTO disabled when + compiling for macOS, iOS or Windows with MSVC. This is because LTO on those + platforms is very slow to link or has issues with the generated code. + +You can manually override options from those aliases by specifying them on the +same command line with different values. For example, you can use ``scons +production=yes debug_symbols=yes`` to create production-optimized binaries with +debugging symbols included. + +## Dev build + +:::note + +``dev_build`` should **not** be confused with ``dev_mode``, which is an +alias for several development-related options (see above). + +::: + +When doing engine development the ``dev_build`` option can be used together +with ``target`` to enable dev-specific code. ``dev_build`` defines ``DEV_ENABLED``, +disables optimization (``-O0``/``/0d``), enables generating debug symbols, and +does not define ``NDEBUG`` (so ``assert()`` works in thirdparty libraries). + +``` +scons platform= dev_build=yes + +``` + +This flag appends the ``.dev`` suffix (for development) to the generated +binary name. + +:::info + +There are additional SCons options to enable *sanitizers*, which are tools +you can enable at compile-time to better debug certain engine issues. +See [doc_using_sanitizers](../debugging/using_sanitizers.md) for more information. + +::: + +## Debugging symbols + +By default, ``debug_symbols=no`` is used, which means **no** debugging symbols +are included in compiled binaries. Use ``debug_symbols=yes`` to include debug +symbols within compiled binaries, which allows debuggers and profilers to work +correctly. Debugging symbols are also required for Redot's crash stacktraces to +display with references to source code files and lines. + +The downside is that debugging symbols are large files (significantly larger +than the binaries themselves). As a result, official binaries currently do not +include debugging symbols. This means you need to compile Redot yourself to have +access to debugging symbols. + +When using ``debug_symbols=yes``, you can also use +``separate_debug_symbols=yes`` to put debug information in a separate file with +a ``.debug`` suffix. This allows distributing both files independently. Note +that on Windows, when compiling with MSVC, debugging information is *always* +written to a separate ``.pdb`` file regardless of ``separate_debug_symbols``. + +:::tip + +Use the `[strip](path/to/binary)` command to remove debugging symbols from +a binary you've already compiled. + +::: + +## Optimization level + +Several compiler optimization levels can be chosen from: + +- ``optimize=speed_trace`` *(default when targeting non-Web platforms)*: Favors + execution speed at the cost of larger binary size. Optimizations may sometimes + negatively impact debugger usage (stack traces may be less accurate. If this + occurs to you, use ``optimize=debug`` instead. +- ``optimize=speed``: Favors even more execution speed, at the cost of even + larger binary size compared to ``optimize=speed_trace``. Even less friendly to + debugging compared to ``optimize=debug``, as this uses the most aggressive + optimizations available. +- ``optimize=size`` *(default when targeting the Web platform)*: Favors small + binaries at the cost of slower execution speed. +- ``optimize=debug``: Only enables optimizations that do not impact debugging in + any way. This results in faster binaries than ``optimize=none``, but slower + binaries than ``optimize=speed_trace``. +- ``optimize=none``: Do not perform any optimization. This provides the fastest + build times, but the slowest execution times. +- ``optimize=custom`` *(advanced users only)*: Do not pass optimization + arguments to the C/C++ compilers. You will have to pass arguments manually + using the ``cflags``, ``ccflags`` and ``cxxflags`` SCons options. + +## Architecture + +The ``arch`` option is meant to control the CPU or OS version intended to run the +binaries. It is focused mostly on desktop platforms and ignored everywhere +else. + +Supported values for the ``arch`` option are **auto**, **x86_32**, **x86_64**, +**arm32**, **arm64**, **rv64**, **ppc32**, **ppc64** and **wasm32**. + +``` +scons platform= arch={auto|x86_32|x86_64|arm32|arm64|rv64|ppc32|ppc64|wasm32} + +``` + +This flag appends the value of ``arch`` to resulting binaries when +relevant. The default value ``arch=auto`` detects the architecture +that matches the host platform. + +## Custom modules + +It's possible to compile modules residing outside of Redot's directory +tree, along with the built-in modules. + +A ``custom_modules`` build option can be passed to the command line before +compiling. The option represents a comma-separated list of directory paths +containing a collection of independent C++ modules that can be seen as C++ +packages, just like the built-in ``modules/`` directory. + +For instance, it's possible to provide both relative, absolute, and user +directory paths containing such modules: + +``` +scons custom_modules="../modules,/abs/path/to/modules,~/src/Redot_modules" + +``` + +:::note + +If there's any custom module with the exact directory name as a built-in +module, the engine will only compile the custom one. This logic can be used +to override built-in module implementations. + +::: + +:::info + +[doc_custom_modules_in_cpp](../core_and_modules/custom_modules_in_cpp.md) + +::: + +## Cleaning generated files + +Sometimes, you may encounter an error due to generated files being present. You +can remove them by using `[scons --clean](options)`, where ``<options>`` is the +list of build options you've used to build Redot previously. + +Alternatively, you can use ``git clean -fixd`` which will clean build artifacts +for all platforms and configurations. Beware, as this will remove all untracked +and ignored files in the repository. Don't run this command if you have +uncommitted work! + +## Other build options + +There are several other build options that you can use to configure the +way Redot should be built (compiler, debug options, etc.) as well as the +features to include/disable. + +Check the output of ``scons --help`` for details about each option for +the version you are willing to compile. + +### Overriding the build options + +#### Using a file + +The default ``custom.py`` file can be created at the root of the Redot Engine +source to initialize any SCons build options passed via the command line: + +```python +optimize = "size" +module_mono_enabled = "yes" +use_llvm = "yes" +extra_suffix = "game_title" + +``` + +You can also disable some of the built-in modules before compiling, saving some +time it takes to build the engine. See [doc_optimizing_for_size](optimizing_for_size.md) page for more details. + +:::info + +You can use the online +[Redot build options generator](https://Redot-build-options-generator.github.io/) +to generate a ``custom.py`` file containing SCons options. +You can then save this file and place it at the root of your Redot source directory. + +::: + +Another custom file can be specified explicitly with the ``profile`` command +line option, both overriding the default build configuration: + +```shell +scons profile=path/to/custom.py + +``` + +:::note +Build options set from the file can be overridden by the command line +options. + +::: + +It's also possible to override the options conditionally: + +```python +import version + +# Override options specific for Redot 3.x and 4.x versions. +if version.major == 3: + pass +elif version.major == 4: + pass + +``` + +#### Using the SCONSFLAGS + +``SCONSFLAGS`` is an environment variable which is used by the SCons to set the +options automatically without having to supply them via the command line. + +For instance, you may want to force a number of CPU threads with the +aforementioned ``-j`` option for all future builds: + + + + + +```bash +export SCONSFLAGS="-j4" + +``` + + + + + +```bat +set SCONSFLAGS=-j4 + +``` + + + + + +```powershell +$env:SCONSFLAGS="-j4" +``` + + + + + +#### SCU (single compilation unit) build + +Regular builds tend to be bottlenecked by including large numbers of headers +in each compilation translation unit. Primarily to speed up development (rather +than for production builds), Redot offers a "single compilation unit" build +(aka "Unity / Jumbo" build). + +For the folders accelerated by this option, multiple ``.cpp`` files are +compiled in each translation unit, so headers can be shared between multiple +files, which can dramatically decrease build times. + +To perform an SCU build, use the ``scu_build=yes`` SCons option. + +:::note +When developing a Pull Request using SCU builds, be sure to make a +regular build prior to submitting the PR. This is because SCU builds +by nature include headers from earlier ``.cpp`` files in the +translation unit, therefore won't catch all the includes you will +need in a regular build. The CI will catch these errors, but it will +usually be faster to catch them on a local build on your machine. + +::: + +## Export templates + +Official export templates are downloaded from the Redot Engine site: +[redotengine.org](https://redotengine.org/). However, you might want +to build them yourself (in case you want newer ones, you are using custom +modules, or simply don't trust your own shadow). + +If you download the official export templates package and unzip it, you +will notice that most files are optimized binaries or packages for each +platform: + +```none +android_debug.apk +android_release.apk +android_source.zip +ios.zip +linux_debug.arm32 +linux_debug.arm64 +linux_debug.x86_32 +linux_debug.x86_64 +linux_release.arm32 +linux_release.arm64 +linux_release.x86_32 +linux_release.x86_64 +macos.zip +version.txt +web_debug.zip +web_dlink_debug.zip +web_dlink_nothreads_debug.zip +web_dlink_nothreads_release.zip +web_dlink_release.zip +web_nothreads_debug.zip +web_nothreads_release.zip +web_release.zip +windows_debug_x86_32_console.exe +windows_debug_x86_32.exe +windows_debug_x86_64_console.exe +windows_debug_x86_64.exe +windows_debug_arm64_console.exe +windows_debug_arm64.exe +windows_release_x86_32_console.exe +windows_release_x86_32.exe +windows_release_x86_64_console.exe +windows_release_x86_64.exe +windows_release_arm64_console.exe +windows_release_arm64.exe + +``` + +To create those yourself, follow the instructions detailed for each +platform in this same tutorial section. Each platform explains how to +create its own template. + +The ``version.txt`` file should contain the corresponding Redot version +identifier. This file is used to install export templates in a version-specific +directory to avoid conflicts. For instance, if you are building export templates +for Redot 3.1.1, ``version.txt`` should contain ``3.1.1.stable`` on the first +line (and nothing else). This version identifier is based on the ``major``, +``minor``, ``patch`` (if present) and ``status`` lines of the +[version.py file in the Redot Git repository](https://github.com/redot-engine/redot-engine/blob/master/version.py). + +If you are developing for multiple platforms, macOS is definitely the most +convenient host platform for cross-compilation, since you can cross-compile for +every target. Linux and Windows come in second place, +but Linux has the advantage of being the easier platform to set this up. \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/compiling/optimizing_for_size.md b/Redot-Documentation/docs/Contributing/Development/compiling/optimizing_for_size.md new file mode 100644 index 0000000..3f40f96 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/compiling/optimizing_for_size.md @@ -0,0 +1,323 @@ + +# Optimizing a build for size + +.. highlight:: shell + +## Rationale + +Sometimes, it is desired to optimize a build for size rather than speed. +This means not compiling unused functions from the engine, as well as using +specific compiler flags to aid on decreasing build size. +Common situations include creating builds for mobile and Web platforms. + +This tutorial aims to give an overview on different methods to create +a smaller binary. Before continuing, it is recommended to read the previous tutorials +on compiling Redot for each platform. + +The options below are listed from the most important (greatest size savings) +to the least important (lowest size savings). + +## Stripping binaries + +- **Space savings:** Very high +- **Difficulty:** Easy +- **Performed in official builds:** Yes + +If you build Windows (MinGW), Linux or macOS binaries from source, remember to +strip debug symbols from binaries by installing the ``strip`` package from your +distribution then running: + +``` +strip path/to/Redot.binary + +``` + +On Windows, ``strip.exe`` is included in most MinGW toolchain setups. + +This will reduce the size of compiled binaries by a factor between 5× and 10×. +The downside is that crash backtraces will no longer provide accurate information +(which is useful for troubleshooting the cause of a crash). +[C++ profilers](../debugging/using_cpp_profilers.md) will also no longer be able to display +function names (this does not affect the built-in GDScript profiler). + +:::note + +The above command will not work on Windows binaries compiled with MSVC +and platforms such as Android and Web. Instead, pass ``debug_symbols=no`` +on the SCons command line when compiling. + +::: + +## Compiling with link-time optimization + +- **Space savings:** High +- **Difficulty:** Easy +- **Performed in official builds:** Yes + +Enabling link-time optimization produces more efficient binaries, both in +terms of performance and file size. It works by eliminating duplicate +template functions and unused code. It can currently be used with the GCC +and MSVC compilers: + +``` +scons target=template_release lto=full + +``` + +Linking becomes much slower and more RAM-consuming with this option, +so it should be used only for release builds: + +- When compiling the ``master`` branch, you need to have at least 8 GB of RAM + available for successful linking with LTO enabled. +- When compiling the ``3.x`` branch, you need to have at least 6 GB of RAM + available for successful linking with LTO enabled. + +## Optimizing for size instead of speed + +- **Space savings:** High +- **Difficulty:** Easy +- **Performed in official builds:** Yes, but only for web builds + +Redot 3.1 onwards allows compiling using size optimizations (instead of speed). +To enable this, set the ``optimize`` flag to ``size``: + +``` +scons target=template_release optimize=size + +``` + +Some platforms such as WebAssembly already use this mode by default. + +## Disabling advanced text server + +- **Space savings:** High +- **Difficulty:** Easy +- **Performed in official builds:** No + +By default, Redot uses an advanced text server with the support for the +following features: + +- Right-to-left typesetting and complex scripts, required to write languages + such as Arabic and Hebrew. +- Font ligatures and OpenType features (such as small capitals, fractions and + slashed zero). + +Redot provides a fallback text server that isn't compiled by default. This text +server can be used as a lightweight alternative to the default advanced text +server: + +``` +scons target=template_release module_text_server_adv_enabled=no module_text_server_fb_enabled=yes + +``` + +If you only intend on supporting Latin, Greek and Cyrillic-based languages in +your project, the fallback text server should suffice. + +This fallback text server can also process large amounts of text more quickly +than the advanced text server. This makes the fallback text server a good fit +for mobile/web projects. + +:::note + +Remember to always pass ``module_text_server_fb_enabled=yes`` when using +``module_text_server_adv_enabled=no``. Otherwise, the compiled binary won't +contain any text server, which means no text will be displayed at all when +running the project. + +::: + +## Disabling 3D + +- **Space savings:** Moderate +- **Difficulty:** Easy +- **Performed in official builds:** No + +For 2D games, having the whole 3D engine available usually makes no sense. +Because of this, there is a build flag to disable it: + +``` +scons target=template_release disable_3d=yes + +``` + +Tools must be disabled in order to use this flag, as the editor is not designed +to operate without 3D support. Without it, the binary size can be reduced +by about 15%. + +:::note + +Disabling 3D support also disables all navigation. This includes 2D navigation, +as it uses the 3D navigation system internally. + +::: + +## Disabling advanced GUI objects + +- **Space savings:** Moderate +- **Difficulty:** Easy +- **Performed in official builds:** No + +Most small games don't require complex GUI controls such as Tree, ItemList, +TextEdit or GraphEdit. They can be disabled using a build flag: + +``` +scons target=template_release disable_advanced_gui=yes + +``` + +This is everything that will be disabled: + +- [class_AcceptDialog](/docs/Classes/AcceptDialog) +- [class_CharFXTransform](/docs/Classes/CharFXTransform) +- [class_CodeEdit](/docs/Classes/CodeEdit) +- [class_CodeHighlighter](/docs/Classes/CodeHighlighter) +- [class_ColorPickerButton](/docs/Classes/ColorPickerButton) +- [class_ColorPicker](/docs/Classes/ColorPicker) +- [class_ConfirmationDialog](/docs/Classes/ConfirmationDialog) +- [class_FileDialog](/docs/Classes/FileDialog) +- [class_GraphEdit](/docs/Classes/GraphEdit) +- [class_GraphElement](/docs/Classes/GraphElement) +- [class_GraphFrame](/docs/Classes/GraphFrame) +- [class_GraphNode](/docs/Classes/GraphNode) +- [class_HSplitContainer](/docs/Classes/HSplitContainer) +- [class_MenuBar](/docs/Classes/MenuBar) +- [class_MenuButton](/docs/Classes/MenuButton) +- [class_OptionButton](/docs/Classes/OptionButton) +- [class_PopupMenu](/docs/Classes/PopupMenu) (will make all popup menus unavailable in code for classes that use them, + like [class_LineEdit](/docs/Classes/LineEdit), even though those classes are still available) +- [class_RichTextEffect](/docs/Classes/RichTextEffect) +- [class_RichTextLabel](/docs/Classes/RichTextLabel) +- [class_SpinBox](/docs/Classes/SpinBox) +- [class_SplitContainer](/docs/Classes/SplitContainer) +- [class_SubViewportContainer](/docs/Classes/SubViewportContainer) +- [class_SyntaxHighlighter](/docs/Classes/SyntaxHighlighter) +- [class_TextEdit](/docs/Classes/TextEdit) +- [class_TreeItem](/docs/Classes/TreeItem) +- [class_Tree](/docs/Classes/Tree) +- [class_VSplitContainer](/docs/Classes/VSplitContainer) + +## Disabling unwanted modules + +- **Space savings:** Very low to moderate depending on modules +- **Difficulty:** Medium to hard depending on modules +- **Performed in official builds:** No + +A lot of Redot's functions are offered as modules. +You can see a list of modules with the following command: + +``` +scons --help + +``` + +The list of modules that can be disabled will appear, together with all +build options. If you are working on a simple 2D game, you could disable +a lot of them: + +``` +scons target=template_release module_basis_universal_enabled=no module_bmp_enabled=no module_camera_enabled=no module_csg_enabled=no module_dds_enabled=no module_enet_enabled=no module_gridmap_enabled=no module_hdr_enabled=no module_jsonrpc_enabled=no module_ktx_enabled=no module_mbedtls_enabled=no module_meshoptimizer_enabled=no module_minimp3_enabled=no module_mobile_vr_enabled=no module_msdfgen_enabled=no module_multiplayer_enabled=no module_noise_enabled=no module_navigation_enabled=no module_ogg_enabled=no module_openxr_enabled=no module_raycast_enabled=no module_regex_enabled=no module_squish_enabled=no module_svg_enabled=no module_tga_enabled=no module_theora_enabled=no module_tinyexr_enabled=no module_upnp_enabled=no module_vhacd_enabled=no module_vorbis_enabled=no module_webrtc_enabled=no module_websocket_enabled=no module_webxr_enabled=no module_zip_enabled=no + +``` + +If this proves not to work for your use case, you should review the list of +modules and see which ones you actually still need for your game (e.g. you might +want to keep networking-related modules, regex support, +``minimp3``/``ogg``/``vorbis`` to play music, or ``theora`` to play videos). + +Alternatively, you can supply a list of disabled modules by creating +``custom.py`` at the root of the source, with the contents similar to the +following: + +```python +module_basis_universal_enabled = "no" +module_bmp_enabled = "no" +module_camera_enabled = "no" +module_csg_enabled = "no" +module_dds_enabled = "no" +module_enet_enabled = "no" +module_gridmap_enabled = "no" +module_hdr_enabled = "no" +module_jsonrpc_enabled = "no" +module_ktx_enabled = "no" +module_mbedtls_enabled = "no" +module_meshoptimizer_enabled = "no" +module_minimp3_enabled = "no" +module_mobile_vr_enabled = "no" +module_msdfgen_enabled= "no" +module_multiplayer_enabled = "no" +module_noise_enabled = "no" +module_navigation_enabled = "no" +module_ogg_enabled = "no" +module_openxr_enabled = "no" +module_raycast_enabled = "no" +module_regex_enabled = "no" +module_squish_enabled = "no" +module_svg_enabled = "no" +module_tga_enabled = "no" +module_theora_enabled = "no" +module_tinyexr_enabled = "no" +module_upnp_enabled = "no" +module_vhacd_enabled = "no" +module_vorbis_enabled = "no" +module_webrtc_enabled = "no" +module_websocket_enabled = "no" +module_webxr_enabled = "no" +module_zip_enabled = "no" + +``` + +:::info + +[doc_overriding_build_options](doc_overriding_build_options). + +::: + +## Optimizing the distribution of your project + +### Desktop + +:::note + +This section is only relevant when distributing the files on a desktop +platform that doesn't perform its own compression or packing. As such, this +advice is relevant when you distribute ZIP archives on itch.io or GitHub +Releases. + +Platforms like Steam already apply their own compression scheme, so you +don't need to create a ZIP archive to distribute files in the first place. + +::: + +As an aside, you can look into optimizing the distribution of your project itself. +This can be done even without recompiling the export template. + +[7-Zip](https://7-zip.org/) can be used to create ZIP archives that are more +efficient than usual, while remaining compatible with every ZIP extractor +(including Windows' own built-in extractor). ZIP size reduction in a large +project can reach dozens of megabytes compared to a typical ZIP compressor, +although average savings are in the 1-5 MB range. Creating this ZIP archive will +take longer than usual, but it will extract just as fast as any other ZIP +archive. + +When using the 7-Zip GUI, this is done by creating a ZIP archive with the Ultra +compression mode. When using the command line, this is done using the following +command: + +``` +7z a -mx9 my_project.zip folder_containing_executable_and_pck + +``` + +### Web + +Enabling gzip or Brotli compression for all file types from the web export +(especially the ``.wasm`` and ``.pck``) can reduce the download size +significantly, leading to faster loading times, especially on slow connections. + +Creating precompressed gzip or Brotli files with a high compression level can be +even more efficient, as long as the web server is configured to serve those +files when they exist. When supported, Brotli should be preferred over gzip as +it has a greater potential for file size reduction. + +See [doc_exporting_for_web_serving_the_files](doc_exporting_for_web_serving_the_files) for instructions. \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/android_studio.md b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/android_studio.md new file mode 100644 index 0000000..a955b2b --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/android_studio.md @@ -0,0 +1,103 @@ + +# Android Studio + +[Android Studio](https://developer.android.com/studio) is a free +IDE for Android development made by [Google](https://about.google/) and [JetBrains](https://www.jetbrains.com/). +It's based on [IntelliJ IDEA](https://www.jetbrains.com/idea/) and has a +feature-rich editor which supports Java and C/C++. It can be used to +work on Redot's core engine as well as the Android platform codebase. + +## Importing the project + +- From the Android Studio's welcome window select **Open**. + +
+ Android Studio's welcome window. +
+ Android Studio's welcome window. +
+
+ +- Navigate to ``<Redot root directory>/platform/android/java`` and select the ``settings.gradle`` file. +- Android Studio will import and index the project. + +## Android Studio project layout + +The project is organized using [Android Studio's modules](https://developer.android.com/studio/projects#ApplicationModules): + +- ``lib`` module: + - Located under ``<Redot root directory>/platform/android/java/lib``, this is a **library module** that organizes + the Redot java and native code and make it available as a reusable dependency / artifact. + - The artifact generated by this module is made available for other Android modules / projects to use as a dependency, via [MavenCentral](https://repo1.maven.org/maven2/org/Redotengine/Redot/). + +- ``editor`` module: + - Located under ``<Redot root directory>/platform/android/java/editor``, this is an **application module** that holds + the source code for the Android port of the Redot Editor. + - This module has a dependency on the ``lib`` module. + +- ``app`` module: + - Located under ``<Redot root directory>/platform/android/java/app``, this is an **application module** that holds + the source code for the Android build templates. + - This module has a dependency on the ``lib`` module. + +## Building & debugging the editor module + +- To build the ``editor`` module: + - Select the [Run/Debug Configurations drop down](https://developer.android.com/studio/run/rundebugconfig#running) and select ``editor``. + +
+ +
+ + - Select **Run > Run 'editor'** from the top menu or [click the Run icon](https://developer.android.com/studio/run/rundebugconfig#running). +- To debug the ``editor`` module: + - Open the **Build Variants** window using **View > Tools Windows > Build Variants** from the top menu. + - In the **Build Variants** window, make sure that in the **Active Build Variant** column, the ``:editor`` entry is set to **dev**. + +
+ +
+ + - Open the **Run/Debug Configurations** window by clicking on **Run > Edit Configurations...** on the top menu. + - In the **Run/Debug Configurations** window, select the ``editor`` entry, and under **Debugger** make sure the **Debug Type** is set to ``Dual (Java + Native)`` + +
+ +
+ + - Select **Run > Debug 'editor'** from the top menu or [click the Debug icon](https://developer.android.com/studio/run/rundebugconfig#running). + +## Building & debugging the app module + +The ``app`` module requires the presence of a Redot project in its ``assets`` directory (``<Redot root directory>/platform/android/java/app/assets``) to run. +This is usually handled by the Redot Editor during the export process. +While developing in Android Studio, it's necessary to manually add a Redot project under that directory to replicate the export process. +Once that's done, you can follow the instructions below to run/debug the ``app`` module: + +- To build the ``app`` module: + - Select the [Run/Debug Configurations drop down](https://developer.android.com/studio/run/rundebugconfig#running) and select ``app``. + +
+ +
+ + - Select **Run > Run 'app'** from the top menu or [click the Run icon](https://developer.android.com/studio/run/rundebugconfig#running). +- To debug the ``app`` module: + - Open the **Build Variants** window using **View > Tools Windows > Build Variants** from the top menu. + - In the **Build Variants** window, make sure that in the **Active Build Variant** column, the ``:app`` entry is set to **dev**. + +
+ +
+ + - Open the **Run/Debug Configurations** window by clicking on **Run > Edit Configurations...** on the top menu. + - In the **Run/Debug Configurations** window, select the ``app`` entry, and under **Debugger** make sure the **Debug Type** is set to ``Dual (Java + Native)`` + +
+ +
+ + - Select **Run > Debug 'app'** from the top menu or [click the Debug icon](https://developer.android.com/studio/run/rundebugconfig#running). + +If you run into any issues, ask for help in +[Redot's Android dev channel](https://chat.redotengine.org/channel/android). \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/clion.md b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/clion.md new file mode 100644 index 0000000..f9223f4 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/clion.md @@ -0,0 +1,94 @@ + +# CLion + +[CLion](https://www.jetbrains.com/clion/) is a commercial +[JetBrains](https://www.jetbrains.com/) IDE for C++. + +## Importing the project + +CLion can import a project's [compilation database file](https://clang.llvm.org/docs/JSONCompilationDatabase.html), commonly named ``compile_commands.json``. To generate the compilation database file, open the terminal, change to the Redot root directory, and run: + +``` +scons compiledb=yes + +``` + +Then, open the Redot root directory with CLion. CLion will import the compilation database, index the codebase, and provide autocompletion and other advanced code navigation and refactoring functionality. + +## Compiling and debugging the project + +CLion does not support compiling and debugging Redot via SCons out of the box. This can be achieved by creating a custom build target and run configuration in CLion. Before creating a custom build target, you must [compile Redot](toc-devel-compiling) once on the command line, to generate the Redot executable. Open the terminal, change into the Redot root directory, and execute: + +``` +scons dev_build=yes + +``` + +To add a custom build target that invokes SCons for compilation: + +- Open CLion and navigate to **Preferences > Build, Execution, Deployment > Custom Build Targets** + +
+ +
+ +- Click **Add target** and give the target a name, e.g. ``Redot debug``. + +
+ +
+ +- Click **...** next to the **Build:** selectbox, then click the **+** button in the **External Tools** dialog to add a new external tool. + +
+ +
+ +- Give the tool a name, e.g. ``Build Redot debug``, set **Program** to ``scons``, set **Arguments** to the compilation settings you want (see [compiling Redot](toc-devel-compiling)), and set the **Working directory** to ``$ProjectFileDir$``, which equals the Redot root directory. Click **OK** to create the tool. + +:::note +CLion does not expand shell commands like ``scons -j$(nproc)``. Use concrete values instead, e.g. ``scons -j8``. + +::: + +
+ +
+ +- Back in the **External Tools** dialog, click the **+** again to add a second external tool for cleaning the Redot build via SCons. Give the tool a name, e.g. ``Clean Redot debug``, set **Program** to ``scons``, set **Arguments** to ``-c`` (which will clean the build), and set the **Working directory** to ``$ProjectFileDir$``. Click **OK** to create the tool. + +
+ +
+ +- Close the **External Tools** dialog. In the **Custom Build Target** dialog for the custom ``Redot debug`` build target, select the **Build Redot debug** tool from the **Build** select box, and select the **Clean Redot debug** tool from the **Clean** select box. Click **OK** to create the custom build target. + +
+ +
+ +- In the main IDE window, click **Add Configuration**. + +
+ +
+ +- In the **Run/Debug Configuration** dialog, click **Add new...**, then select **Custom Build Application** to create a new custom run/debug configuration. + +
+ +
+ +- Give the run/debug configuration a name, e.g. ``Redot debug``, select the ``Redot debug`` custom build target as the **Target**. Select the Redot executable in the ``bin/`` folder as the **Executable**, and set the **Program arguments** to ``--editor --path path-to-your-project/``, where ``path-to-your-project/`` should be a path pointing to an existing Redot project. If you omit the ``--path`` argument, you will only be able to debug the Redot Project Manager window. Click **OK** to create the run/debug configuration. + +
+ +
+ +You can now build, run, debug, profile, and Valgrind check the Redot editor via the run configuration. + +
+ +
+ +When playing a scene, the Redot editor will spawn a separate process. You can debug this process in CLion by going to **Run > Attach to process...**, typing ``Redot``, and selecting the Redot process with the highest **pid** (process ID), which will usually be the running project. \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/code_blocks.md b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/code_blocks.md new file mode 100644 index 0000000..8d02717 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/code_blocks.md @@ -0,0 +1,122 @@ + +# Code::Blocks + +[Code::Blocks](https://codeblocks.org/) is a free, open-source, cross-platform IDE. + +## Creating a new project + +From Code::Blocks' main screen, click **Create a new project** or select **File > New > Project...**. + +
+ +
+ +In the **New from template** window, from **Projects**, select **Empty project**, and click **Go**. + +
+ +
+ +Click Next, to pass the welcome to the new empty project wizard. + +
+ +
+ +The project file should be created in the root of the cloned project folder. To achieve this, first, ensure that the **Project title** is the same as the folder name that Redot was cloned into. Unless you cloned the project into a folder with a different name, this will be ``Redot``. + +Second, ensure that the **Folder to create project in** is the folder you ran the Git clone command from, not the ``Redot`` project folder. Confirm that the **Resulting filename** field will create the project file in the root of the cloned project folder. + +
+ +
+ +The compiler and configuration settings are managed through **SCons** and will be configured later. However, it's worth deselecting the **Create "Release" configuration** option; so only a single build target is created before clicking **Finish**. + +
+ +
+ +## Configuring the build + +The first step is to change the project properties. Right-click on the new project and select **Properties...**. + +
+ +
+ +Check the **This is a custom Makefile** property. Click OK to save the changes. + +
+ +
+ +The next step is to change the build options. Right-click on the new project and select **Build Options...**. + +
+ +
+ +Select the **"Make" commands** tab and remove all the existing commands for all the build targets. For each build target enter the **SCons** command for creating the desired build in the **Build project/target** field. The minimum is ``scons``. For details on the **SCons** build options, see [doc_introduction_to_the_buildsystem](../compiling/introduction_to_the_buildsystem.md). It's also useful to add the ``scons --clean`` command in the **Clean project/target** field to the project's default commands. + +If you're using Windows, all the commands need to be preceded with ``cmd /c`` to initialize the command interpreter. + +
+ +
+ +
+ +
+ +Windows example: + +
+ +
+ +Code::Blocks should now be configured to build Redot; so either select **Build > Build**, click the gear button, or press `Ctrl + F9`. + +## Configuring the run + +Once **SCons** has successfully built the desired target, reopen the project **Properties...** and select the **Build targets** tab. In the **Output filename** field, browse to the ``bin`` folder and select the compiled file. + +Deselect the **Auto-generate filename prefix** and **Auto-generate filename extension** options. + +
+ +
+ +Code::Blocks should now be configured to run your compiled Redot executable; so either select **Build > Run**, click the green arrow button, or press `Ctrl + F10`. + +There are two additional points worth noting. First, if required, the **Execution working dir** field can be used to test specific projects, by setting it to the folder containing the ``project.Redot`` file. Second, the **Build targets** tab can be used to add and remove build targets for working with and creating different builds. + +## Adding files to the project + +To add all the Redot code files to the project, right-click on the new project and select **Add files recursively...**. + +
+ +
+ +It should automatically select the project folder; so simply click **Open**. By default, all code files are included, so simply click **OK**. + +
+ +
+ +## Code style configuration + +Before editing any files, remember that all code needs to comply with the [doc_code_style_guidelines](../code_style_guidelines.md). One important difference with Redot is the use of tabs for indents. Therefore, the key default editor setting that needs to be changed in Code::Blocks is to enable tabs for indents. This setting can be found by selecting **Settings > Editor**. + +
+ +
+ +Under **General Settings**, on the **Editor Settings** tab, under **Tab Options** check **Use TAB character**. + +
+ +
+ +That's it. You're ready to start contributing to Redot using the Code::Blocks IDE. Remember to save the project file and the **Workspace**. If you run into any issues, ask for help in one of [Redot's community channels](https://redotengine.org/community). \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/kdevelop.md b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/kdevelop.md new file mode 100644 index 0000000..e1e3699 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/kdevelop.md @@ -0,0 +1,82 @@ + +# KDevelop + +[KDevelop](https://www.kdevelop.org) is a free, open source IDE for all desktop platforms. + +## Importing the project + +- From the KDevelop's main screen select **Open Project**. + +
+ KDevelop's main screen. +
+ KDevelop's main screen. +
+
+ +- Navigate to the Redot root folder and select it. +- On the next screen, choose **Custom Build System** for the **Project Manager**. + +
+ +
+ +- After the project has been imported, open the project configuration by right-clicking + on it in the **Projects** panel and selecting **Open Configuration..** option. + +
+ +
+ +- Under **Language Support** open the **Includes/Imports** tab and add the following paths: + +```none +. // A dot, to indicate the root of the Redot project +core/ +core/os/ +core/math/ +drivers/ +platform// // Replace with a folder + corresponding to your current platform + +``` + +
+ +
+ +- Apply the changes. +- Under **Custom Build System** add a new build configuration with the following settings: + +| Build Directory | *blank* | +| --- | --- | +| Enable | **True** | +| Executable | **scons** | +| Arguments | See [doc_introduction_to_the_buildsystem](../compiling/introduction_to_the_buildsystem.md) for a full list of arguments. | + +
+ +
+ +- Apply the changes and close the configuration window. + +## Debugging the project + +- Select **Run > Configure Launches...** from the top menu. + +
+ +
+ +- Click **Add** to create a new launch configuration. +- Select **Executable** option and specify the path to your executable located in + the ``<Redot root directory>/bin`` folder. The name depends on your build configuration, + e.g. ``Redot.linuxbsd.editor.dev.x86_64`` for 64-bit LinuxBSD platform with + ``platform=editor`` and ``dev_build=yes``. + +
+ +
+ +If you run into any issues, ask for help in one of +[Redot's community channels](https://redotengine.org/community). \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/qt_creator.md b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/qt_creator.md new file mode 100644 index 0000000..811d929 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/qt_creator.md @@ -0,0 +1,110 @@ + +# Qt Creator + +[Qt Creator](https://doc.qt.io/qtcreator/index.html) is a free, open source IDE for all desktop platforms. + +## Importing the project + +- From the Qt Creator's main screen select **New Project > Import Project > Import Existing Project**. + +
+ +
+ +- Under **Location** select the Redot root folder. + +
+ +
+ +- Next, you can choose which folders and files will be visible to the project. + While C/C++ files are added automatically, other extensions can be potentially useful: + ``*.glsl`` for shader files, ``*.py`` for buildsystem files, + ``*.java`` for Android platform development, ``*.mm`` for macOS platform development. + +
+ +
+ +:::note +You can change this configuration later by right-clicking on your project +and selecting the **Edit Files...** option. + +
+ +
+ +::: + +- Finish the import. +- Open the ``project_name.includes`` file and add a line containing ``.`` to it + to correctly enable the code completion. + +
+ +
+ +- From the left-side menu select **Projects** and open the **Build** tab. +- Delete the predefined ``make`` build step. + +
+ +
+ +- Click **Add Build Step > Custom Process Step** to add a new build step + with the following settings: + +| Command | **scons** | +| --- | --- | +| Arguments | See [doc_introduction_to_the_buildsystem](../compiling/introduction_to_the_buildsystem.md) for a full list of arguments. | + +
+ +
+ +:::note +If the build fails with ``Could not start process "scons"``, it can mean that ``scons`` +is not in your ``PATH`` environment variable. In this case, you'll have to specify the +full path to the SCons binary. + +::: + +## Debugging the project + +- From the left-side menu select **Projects** and open the **Run** tab. +- Under **Executable** specify the path to your executable located in + the ``<Redot root directory>/bin`` folder. The name depends on your build configuration, + e.g. ``Redot.linuxbsd.editor.dev.x86_64`` for 64-bit LinuxBSD platform with + ``platform=editor`` and ``dev_build=yes``. + You can use ``%{buildDir}`` to reference the project root, e.g: ``%{buildDir}/bin/Redot.linuxbsd.editor.dev.x86_64``. +- If you want to run a specific project, specify its root folder under **Working directory**. +- If you want to run the editor, add ``-e`` to the **Command line arguments** field. + +
+ +
+ +To learn more about command line arguments, refer to the +[command line tutorial](../../../tutorials/editor/command_line_tutorial.md). + +## Code style configuration + +Developers must follow the project's [code style](../code_style_guidelines.md) +and the IDE should help them follow it. By default, Qt Creator uses spaces +for indentation which doesn't match the Redot code style guidelines. You can +change this behavior by changing the **Code Style** in **Tools > Options > C++**. + +
+ +
+ +Click on **Edit** to change the current settings, then click on +**Copy Built-in Code Style** button to set a new code style. Set a name for it +(e.g. Redot) and change the Tab policy to be **Tabs Only**. + +
+ +
+ +If you run into any issues, ask for help in one of +[Redot's community channels](https://redotengine.org/community). \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/rider.md b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/rider.md new file mode 100644 index 0000000..c6806bd --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/rider.md @@ -0,0 +1,99 @@ + +# JetBrains Rider + +[JetBrains Rider](https://www.jetbrains.com/rider/) is a commercial +[JetBrains](https://www.jetbrains.com/) IDE for C# and C++ that uses the same solution system as Visual Studio. + +:::note + +This documentation is for contributions to the game engine, and not using +JetBrains Rider as a C# or GDScript editor. To code C# or GDScript in an external editor, see +[the C# guide to configure an external editor](doc_c_sharp_setup_external_editor). + +::: + +## Importing the project + +You will need to install [Python](https://www.python.org/) in your development environment +along with [MinGW](https://www.mingw-w64.org/downloads/). You will also need the Visual Studio C++ Build Tools, which +you can install using the Visual Studio Installer. Ensure all dependencies are installed +before you continue to the next steps. + +:::tip +If you already use Visual Studio as your main IDE, you can use the same solution file in Rider. +Rider and Visual Studio use the same solution format, so you can switch between the two IDEs without rebuilding the solution file. +Debug configurations need to be changed when going from one IDE to another. + +::: + +Rider requires a solution file to work on a C++ project. While Redot does not come +with a solution file, it can be generated using SCons. + +- Navigate to the Redot root folder and open a Command Prompt or PowerShell window. +- Copy, paste and run the next command to generate the solution. + +``` +scons platform=windows vsproj=yes dev_build=yes + +``` + +The ``vsproj`` parameter signals that you want Visual Studio solution generated. +The ``dev_build`` parameter makes sure the debug symbols are included, allowing to e.g. step through code using breakpoints. + +- If you have Rider setup as your main IDE for .sln, you can now open the project by double-clicking on the ``Redot.sln`` in the project root + or by using the **Open** option inside of Rider. + +:::note +Rider could fail to build the solution. +If that is the case, try running `git clean -xdf` to remove all traces of the previous build artifacts +and regenerate the build files using the `scons` command again. Restarting the terminal and your +development environment may help. + +::: + +## Compiling and debugging the project +Rider comes with a built-in debugger that can be used to debug the Redot project. You can launch the debugger +by pressing the **Debug** icon at the top of the screen, this only works for the Project manager, +if you want to debug the editor, you need to configure the debugger first. + +
+ +
+ +- Click on the **Redot > Edit Configurations** option at the top of the screen. + +
+ +
+ +- Ensure the following values for the C++ Project Run Configuration: + + - Exe Path : ``$(LocalDebuggerCommand)`` + - Program Arguments: `[-e --path](path to the Redot project)` + - Working Directory: ``$(LocalDebuggerWorkingDirectory)`` + - Before Launch has a value of "Build Project" + +This will tell the executable to debug the specified project without using the project manager. +Use the root path to the project folder, not ``project.Redot`` file path. + +
+ +
+ +- Finally click on "Apply" and "OK" to save the changes. + +- When you press the **Debug** icon at the top of the screen, JetBrains Rider will launch the Redot editor with the debugger attached. + +Alternatively you can use **Run > Attach to Process** to attach the debugger to a running Redot instance. + +
+ +
+ +- You can find the Redot instance by searching for ``Redot.editor`` and then clicking ``Attach with LLDB`` + +
+ +
+ +Please consult the [JetBrains Rider documentation](https://www.jetbrains.com/rider/documentation/) for any specific information about the JetBrains IDE. \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/visual_studio.md b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/visual_studio.md new file mode 100644 index 0000000..b468a50 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/visual_studio.md @@ -0,0 +1,73 @@ + +# Visual Studio + +[Visual Studio Community](https://visualstudio.microsoft.com) is a Windows-only IDE +by [Microsoft](https://microsoft.com) that's free for individual use or non-commercial use within organizations. +It has many useful features, such as memory view, performance view, source +control and more. + +## Importing the project + +Visual Studio requires a solution file to work on a project. While Redot does not come +with the solution file, it can be generated using SCons. + +- Navigate to the Redot root folder and open a Command Prompt or PowerShell window. +- | Run ``scons platform=windows vsproj=yes dev_build=yes`` to generate the solution with debug symbols. + | The ``vsproj`` parameter signals that you want Visual Studio solution generated. + | The ``dev_build`` parameter makes sure the debug symbols are included, allowing to e.g. step through code using breakpoints. +- You can now open the project by double-clicking on the ``Redot.sln`` in the project root + or by using the **Open a project or solution** option inside of the Visual Studio. +- Use the **Build** top menu to build the project. + +:::warning +Visual Studio must be configured with the C++ package. It can be selected +in the installer: + +
+ +
+ +::: + +## Debugging the project + +Visual Studio features a powerful debugger. This allows the user to examine Redot's +source code, stop at specific points in the code, inspect the current execution context, +and make live changes to the codebase. + +You can launch the project with the debugger attached using the **Debug > Start Debugging** +option from the top menu. However, unless you want to debug the Project Manager specifically, +you'd need to configure debugging options first. This is due to the fact that when the Redot +Project Manager opens a project, the initial process is terminated and the debugger gets detached. + +- To configure the launch options to use with the debugger use **Project > Properties** + from the top menu: + +
+ +
+ +- Open the **Debugging** section and under **Command Arguments** add two new arguments: + the ``-e`` flag opens the editor instead of the Project Manager, and the ``--path`` argument + tells the executable to open the specified project (must be provided as an *absolute* path + to the project root, not the ``project.Redot`` file; if the path contains spaces be sure to pass it inside double quotation marks). + +
+ +
+ +To learn more about command line arguments, refer to the +[command line tutorial](../../../tutorials/editor/command_line_tutorial.md). + +Even if you start the project without a debugger attached it can still be connected to the running +process using **Debug > Attach to Process...** menu. + +To check that everything is working, put a breakpoint in ``main.cpp`` and press `F5` to +start debugging. + +
+ +
+ +If you run into any issues, ask for help in one of +[Redot's community channels](https://redotengine.org/community). \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/visual_studio_code.md b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/visual_studio_code.md new file mode 100644 index 0000000..00f13e3 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/visual_studio_code.md @@ -0,0 +1,270 @@ +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +# Visual Studio Code + +[Visual Studio Code](https://code.visualstudio.com) is a free cross-platform code editor +by [Microsoft](https://microsoft.com) (not to be confused with [doc_configuring_an_ide_vs](visual_studio.md)). + +## Importing the project + +- Make sure the C/C++ extension is installed. You can find instructions in + the [official documentation](https://code.visualstudio.com/docs/languages/cpp). + Alternatively, [clangd](https://open-vsx.org/extension/llvm-vs-code-extensions/vscode-clangd) + can be used instead. +- When using the clangd extension, run ``scons compiledb=yes``. +- From the Visual Studio Code's main screen open the Redot root folder with + **File > Open Folder...**. +- Press `Ctrl + Shift + P` to open the command prompt window and enter *Configure Task*. + +
+ +
+ +- Select the **Create tasks.json file from template** option. + +
+ +
+ +- Then select **Others**. + +
+ +
+ +- If there is no such option as **Create tasks.json file from template** available, either delete the file if it already exists in your folder or create a ``.vscode/tasks.json`` file manually. See [Tasks in Visual Studio Code](https://code.visualstudio.com/docs/editor/tasks#_custom-tasks) for more details on tasks. + +- Within the ``tasks.json`` file find the ``"tasks"`` array and add a new section to it: + +```js +{ + "label": "build", + "group": "build", + "type": "shell", + "command": "scons", + "args": [ + // enable for debugging with breakpoints + "dev_build=yes", + ], + "problemMatcher": "$msCompile" +} + +``` + +
+ An example of a filled out ``tasks.json``. +
+ An example of a filled out ``tasks.json``. +
+
+ +Arguments can be different based on your own setup and needs. See +[doc_introduction_to_the_buildsystem](../compiling/introduction_to_the_buildsystem.md) for a full list of arguments. + +## Debugging the project + +To run and debug the project you need to create a new configuration in the ``launch.json`` file. + +- Press `Ctrl + Shift + D` to open the Run panel. +- If ``launch.json`` file is missing you will be prompted to create a new one. + +
+ +
+ +- Select **C++ (GDB/LLDB)**. There may be another platform-specific option here. If selected, + adjust the configuration example provided accordingly. +- Within the ``launch.json`` file find the ``"configurations"`` array and add a new section to it: + + + + + +```js +{ + "name": "Launch Project", + "type": "lldb", + "request": "launch", + // Change to Redot.linuxbsd.editor.dev.x86_64.llvm for llvm-based builds. + "program": "${workspaceFolder}/bin/Redot.linuxbsd.editor.dev.x86_64", + // Change the arguments below for the project you want to test with. + // To run the project instead of editing it, remove the "--editor" argument. + "args": [ "--editor", "--path", "path-to-your-Redot-project-folder" ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "preLaunchTask": "build" +} +``` + + + + + +```js +{ + "name": "Launch Project", + "type": "cppdbg", + "request": "launch", + // Change to Redot.linuxbsd.editor.dev.x86_64.llvm for llvm-based builds. + "program": "${workspaceFolder}/bin/Redot.linuxbsd.editor.dev.x86_64", + // Change the arguments below for the project you want to test with. + // To run the project instead of editing it, remove the "--editor" argument. + "args": [ "--editor", "--path", "path-to-your-Redot-project-folder" ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "setupCommands": + [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Load custom pretty-printers for Redot types.", + "text": "source ${workspaceRoot}/misc/utility/Redot_gdb_pretty_print.py" + } + ], + "preLaunchTask": "build" +} + +``` + + + + + +```js +{ + "name": "Launch Project", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}/bin/Redot.windows.editor.dev.x86_64.exe", + // Change the arguments below for the project you want to test with. + // To run the project instead of editing it, remove the "--editor" argument. + "args": [ "--editor", "--path", "path-to-your-Redot-project-folder" ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "console": "internalConsole", + "visualizerFile": "${workspaceFolder}/platform/windows/Redot.natvis", + "preLaunchTask": "build" +} + +``` + + + + + +```js +{ + "name": "Launch Project", + "type": "lldb", + "request": "custom", + "targetCreateCommands": [ + "target create ${workspaceFolder}/bin/Redot.macos.editor.dev.x86_64" + ], + // Change the arguments below for the project you want to test with. + // To run the project instead of editing it, remove the "--editor" argument. + "processCreateCommands": [ + "process launch -- --editor --path path-to-your-Redot-project-folder" + ] +} +``` + + + + + +
+ An example of a filled out ``launch.json``. +
+ An example of a filled out ``launch.json``. +
+
+ +:::note + +Due to sporadic performance issues, it is recommended to use LLDB over GDB on Unix-based systems. +Make sure that the [CodeLLDB extension](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb) +is installed. + +If you encounter issues with lldb, you may consider using gdb (see the LinuxBSD_gdb configuration). + +Do note that lldb may work better with LLVM-based builds. See [doc_compiling_for_linuxbsd](../compiling/compiling_for_linuxbsd.md) for further information. + +::: + +The name under ``program`` depends on your build configuration, +e.g. ``Redot.linuxbsd.editor.dev.x86_64`` for 64-bit LinuxBSD platform with +``target=editor`` and ``dev_build=yes``. + +## Configuring Intellisense + +For the C/C++ extension: + +To fix include errors you may be having, you need to configure some settings in the ``c_cpp_properties.json`` file. + +- First, make sure to build the project since some files need to be generated. + +- Edit the C/C++ Configuration file either with the UI or with text: + +
+ +
+ +- Add an include path for your platform, for example, ``${workspaceFolder}/platform/windows``. + +- Add defines for the editor ``TOOLS_ENABLED``, debug builds ``DEBUG_ENABLED``, and tests ``TESTS_ENABLED``. + +- Make sure the compiler path is configured correctly to the compiler you are using. See [doc_introduction_to_the_buildsystem](../compiling/introduction_to_the_buildsystem.md) for further information on your platform. + +- The ``c_cpp_properties.json`` file should look similar to this for Windows: + +```js +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/platform/windows" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE", + "TOOLS_ENABLED", + "DEBUG_ENABLED", + "TESTS_ENABLED" + ], + "windowsSdkVersion": "10.0.22621.0", + "compilerPath": "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.39.33519/bin/Hostx64/x64/cl.exe", + "cStandard": "c17", + "cppStandard": "c++17", + "intelliSenseMode": "windows-msvc-x64" + } + ], + "version": 4 +} + +``` + +- Alternatively, you can use the scons argument ``compiledb=yes`` and set the compile commands setting ``compileCommands`` to ``compile_commands.json``, found in the advanced section of the C/C++ Configuration UI. + + - This argument can be added to your build task in ``tasks.json`` since it will need to be run whenever files are added or moved. + +If you run into any issues, ask for help in one of +[Redot's community channels](https://redotengine.org/community). + +:::tip + +To get linting on class reference XML files, install the +[vscode-xml extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-xml). + +::: diff --git a/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/xcode.md b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/xcode.md new file mode 100644 index 0000000..c8b3b2b --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/configuring_an_ide/xcode.md @@ -0,0 +1,107 @@ + +# Xcode + +[Xcode](https://developer.apple.com/xcode) is a free macOS-only IDE. You can +download it from the Mac App Store. + +## Importing the project + +- From Xcode's main screen create a new project using the **Other > External Build System** template. + +
+ +
+ +- Now choose a name for your project and set the path to scons executable in build tool (to find the path you can type ``where scons`` in a terminal). + +
+ +
+ +- Open the main target from the **Targets** section and select the **Info** tab. + +
+ +
+ +- Fill out the form with the following settings: + +| Arguments | See [doc_introduction_to_the_buildsystem](../compiling/introduction_to_the_buildsystem.md) for a full list of arguments. | +| --- | --- | +| Directory | A full path to the Redot root folder | + +- Add a Command Line Tool target which will be used for indexing the project by + choosing **File > New > Target...**. + +
+ +
+ +- Select **macOS > Application > Command Line Tool**. + +
+ +
+ +:::note +Name it something so you know not to compile with this target (e.g. ``RedotXcodeIndex``). + +::: + +- For this target open the **Build Settings** tab and look for **Header Search Paths**. +- Set **Header Search Paths** to the absolute path to the Redot root folder. You need to + include subdirectories as well. To achieve that, add two two asterisks (``**``) to the + end of the path, e.g. ``/Users/me/repos/Redot-source/**``. + +- Add the Redot source to the project by dragging and dropping it into the project file browser. +- Select **Create groups** for the **Added folders** option and check *only* + your command line indexing target in the **Add to targets** section. + +
+ +
+ +- Xcode will now index the files. This may take a few minutes. +- Once Xcode is done indexing, you should have jump-to-definition, + autocompletion, and full syntax highlighting. + +## Debugging the project + +To enable debugging support you need to edit the external build target's build and run schemes. + +- Open the scheme editor of the external build target. +- Locate the **Build > Post Actions** section. +- Add a new script run action +- Under **Provide build settings from** select your project. This allows to reference + the project directory within the script. +- Create a script that will give the binary a name that Xcode can recognize, e.g.: + +```shell +ln -f ${PROJECT_DIR}/Redot/bin/Redot.macos.tools.64 ${PROJECT_DIR}/Redot/bin/Redot + +``` + +
+ +
+ +- Build the external build target. + +- Open the scheme editor again and select **Run**. + +
+ +
+ +- Set the **Executable** to the file you linked in your post-build action script. +- Check **Debug executable**. +- You can add two arguments on the **Arguments** tab: + the ``-e`` flag opens the editor instead of the Project Manager, and the ``--path`` argument + tells the executable to open the specified project (must be provided as an *absolute* path + to the project root, not the ``project.Redot`` file). + +To check that everything is working, put a breakpoint in ``platform/macos/Redot_main_macos.mm`` and +run the project. + +If you run into any issues, ask for help in one of +[Redot's community channels](https://redotengine.org/community). \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/core_and_modules/2d_coordinate_systems.md b/Redot-Documentation/docs/Contributing/Development/core_and_modules/2d_coordinate_systems.md new file mode 100644 index 0000000..7482da6 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/core_and_modules/2d_coordinate_systems.md @@ -0,0 +1,138 @@ + +# 2D coordinate systems and 2D transforms + +## Introduction + +This is a detailed overview of the available 2D coordinate systems and 2D transforms that are +built in. The basic concepts are covered in [doc_viewport_and_canvas_transforms](../../../tutorials/2d/2d_transforms.md). + +[Transform2D](/docs/Classes/Transform2D) are matrices that convert coordinates from one coordinate +system to an other. In order to use them, it is beneficial to know which coordinate systems are +available in Redot. For a deeper understanding, the [doc_matrices_and_transforms](../../../tutorials/math/matrices_and_transforms.md) tutorial +offers insights to the underlying functionality. + +## Redot 2D coordinate systems + +The following graphic gives an overview of Redot 2D coordinate systems and the available +node-transforms, transform-functions and coordinate-system related functions. At the left +is the OS Window Manager screen, at the right are the [CanvasItems](/docs/Classes/CanvasItem). For +simplicity reasons this graphic doesn't include [SubViewport](/docs/Classes/SubViewport), +[SubViewportContainer](/docs/Classes/SubViewportContainer), [ParallaxLayer](/docs/Classes/ParallaxLayer) +and [ParallaxBackground](/docs/Classes/ParallaxBackground) all of which also influence transforms. + +The graphic is based on a node tree of the following form: ``Root Window (embed Windows)`` ⇒ +``Window (don't embed Windows)`` ⇒ ``CanvasLayer`` ⇒ ``CanvasItem`` ⇒ ``CanvasItem`` ⇒ +``CanvasItem``. There are more complex combinations possible, like deeply nested Window and +SubViewports, however this example intends to provide an overview of the methodology in general. + + + +Click graphic to enlarge. + +- **Item Coordinates** + This is the local coordinate system of a [CanvasItem](/docs/Classes/CanvasItem). + +- **Parent Item Coordinates** + This is the local coordinate system of the parent's *CanvasItem*. When positioning + *CanvasItems* in the *Canvas*, they usually inherit the transformations of their parent + *CanvasItems*. An exceptions is + [CanvasItems.top_level](/docs/Classes/CanvasItem_property_top_level). + +- **Canvas Coordinates** + As mentioned in the previous tutorial [doc_canvas_layers](../../../tutorials/2d/canvas_layers.md), there are two types of canvases + (*Viewport* canvas and *CanvasLayer* canvas) and both have a canvas coordinate system. These + are also called world coordinates. A *Viewport* can contain multiple *Canvases* with different + coordinate systems. + +- **Viewport Coordinates** + This is the coordinate system of the [Viewport](/docs/Classes/Viewport). + +- **Camera Coordinates** + This is only used internally for functionality like 3D-camera ray projections. + +- **Embedder Coordinates / Screen Coordinates** + Every *Viewport* (*Window* or *SubViewport*) in the scene tree is embedded either in a + different node or in the OS Window Manager. This coordinate system's origin is identical to the + top-left corner of the *Window* or *SubViewport* and its scale is the one of the embedder or + the OS Window Manager. + + If the embedder is the OS Window Manager, then they are also called Screen Coordinates. + +- **Absolute Embedder Coordinates / Absolute Screen Coordinates** + The origin of this coordinate system is the top-left corner of the embedding node or the OS + Window Manager screen. Its scale is the one of the embedder or the OS Window Manager. + + If the embedder is the OS Window Manager, then they are also called Absolute Screen + Coordinates. + +## Node transforms + +Each of the mentioned nodes have one or more transforms associated with them and the combination of +these nodes infer the transforms between the different coordinate systems. With a few exceptions, +the transforms are [Transform2D](/docs/Classes/Transform2D) and the following list shows details and +effects of each of them. + +- **CanvasItem transform** + *CanvasItems* are either *Control*-nodes or *Node2D*-nodes. + + For *Control* nodes this transform consists of a [position](/docs/Classes/Control_property_position) + relative to the parent's origin and a [scale](/docs/Classes/Control_property_scale) and + [rotation](/docs/Classes/Control_property_rotation) around a + [pivot point](/docs/Classes/Control_property_pivot_offset). + + For *Node2D* nodes [transform](/docs/Classes/Node2D_property_transform) consists of + [position](/docs/Classes/Node2D_property_position), [rotation](/docs/Classes/Node2D_property_rotation), + [scale](/docs/Classes/Node2D_property_scale) and [skew](/docs/Classes/Node2D_property_skew). + + The transform affects the item itself and usually also child-*CanvasItems* and in the case of a + *SubViewportContainer* it affects the contained *SubViewport*. + +- **CanvasLayer transform** + The *CanvasLayer's* [transform](/docs/Classes/CanvasLayer_property_transform) affects all + *CanvasItems* within the *CanvasLayer*. It doesn't affect other *CanvasLayers* or *Windows* in + its *Viewport*. + +- **CanvasLayer follow viewport transform** + The *follow viewport transform* is an automatically calculated transform, that is based on the + *Viewport's* [canvas transform](/docs/Classes/Viewport_property_canvas_transform) and the + *CanvasLayer's* [follow viewport scale](/docs/Classes/CanvasLayer_property_follow_viewport_scale) + and can be used, if [enabled](/docs/Classes/CanvasLayer_property_follow_viewport_enabled), to + achieve a pseudo-3D effect. It affects the same child nodes as the *CanvasLayer transform*. + +- **Viewport canvas transform** + The [canvas transform](/docs/Classes/Viewport_property_canvas_transform) affects all + *CanvasItems* in the *Viewport's* default canvas. It also affects *CanvasLayers*, that have + follow viewport transform enabled. The *Viewport's* active [Camera2D](/docs/Classes/Camera2D) + works by changing this transform. It doesn't affect this *Viewport's* embedded *Windows*. + +- **Viewport global canvas transform** + *Viewports* also have a [global canvas transform](/docs/Classes/Viewport_property_global_canvas_transform). + This is the master transform and affects all individual *Canvas Layer* and embedded *Window* + transforms. This is primarily used in Redot's CanvasItem Editor. + +- **Viewport stretch transform** + Finally, *Viewports* have a *stretch transform*, which is used when resizing or stretching the + viewport. This transform is used for [Windows](/docs/Classes/Window) as described in + [doc_multiple_resolutions](../../../tutorials/rendering/multiple_resolutions.md), but can also be manually set on *SubViewports* by means of + [size](/docs/Classes/SubViewport_property_size) and + [size_2d_override](/docs/Classes/SubViewport_property_size_2d_override). It's + [translation](/docs/Classes/Transform2D_method_get_origin), + [rotation](/docs/Classes/Transform2D_method_get_rotation) and + [skew](/docs/Classes/Transform2D_method_get_skew) are the default values and it can only have + non-default [scale](/docs/Classes/Transform2D_method_get_scale). + +- **Window transform** + In order to scale and position the *Window's* content as described in + [doc_multiple_resolutions](../../../tutorials/rendering/multiple_resolutions.md), each [Window](/docs/Classes/Window) contains a + *window transform*. It is for example responsible for the black bars at the *Window's* sides so + that the *Viewport* is displayed with a fixed aspect ratio. + +- **Window position** + Every *Window* also has a [position](/docs/Classes/Window_property_position) to describe its + position within its embedder. The embedder can be another *Viewport* or the OS Window Manager. + +- **SubViewportContainer shrink transform** + [stretch](/docs/Classes/SubViewportContainer_property_stretch) together with + [stretch_shrink](/docs/Classes/SubViewportContainer_property_stretch_shrink) declare for a + *SubViewportContainer* if and by what integer factor the contained *SubViewport* should be + scaled in comparison to the container's size. \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/core_and_modules/binding_to_external_libraries.md b/Redot-Documentation/docs/Contributing/Development/core_and_modules/binding_to_external_libraries.md new file mode 100644 index 0000000..dffff28 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/core_and_modules/binding_to_external_libraries.md @@ -0,0 +1,238 @@ + +# Binding to external libraries + +## Modules + +The Summator example in [doc_custom_modules_in_cpp](custom_modules_in_cpp.md) is great for small, +custom modules, but what if you want to use a larger, external library? +Let's look at an example using [Festival](https://www.cstr.ed.ac.uk/projects/festival/), +a speech synthesis (text-to-speech) library written in C++. + +To bind to an external library, set up a module directory similar to the Summator example: + +```none +Redot/modules/tts/ + +``` + +Next, you will create a header file with a TTS class: + +```cpp +#ifndef Redot_TTS_H +#define Redot_TTS_H + +#include "core/object/ref_counted.h" + +class TTS : public RefCounted { + GDCLASS(TTS, RefCounted); + +protected: + static void _bind_methods(); + +public: + bool say_text(String p_txt); + + TTS(); +}; + +#endif // Redot_TTS_H + +``` + +And then you'll add the cpp file. + +```cpp +#include "tts.h" + +#include + +bool TTS::say_text(String p_txt) { + + //convert Redot String to Redot CharString to C string + return festival_say_text(p_txt.ascii().get_data()); +} + +void TTS::_bind_methods() { + + ClassDB::bind_method(D_METHOD("say_text", "txt"), &TTS::say_text); +} + +TTS::TTS() { + festival_initialize(true, 210000); //not the best way to do it as this should only ever be called once. +} + +``` + +Just as before, the new class needs to be registered somehow, so two more files +need to be created: + +```none +register_types.h +register_types.cpp + +``` + +:::info + +These files must be in the top-level folder of your module (next to your +``SCsub`` and ``config.py`` files) for the module to be registered properly. + +::: + +These files should contain the following: + +```cpp +void initialize_tts_module(ModuleInitializationLevel p_level); +void uninitialize_tts_module(ModuleInitializationLevel p_level); +/* yes, the word in the middle must be the same as the module folder name */ + +``` + +```cpp +#include "register_types.h" + +#include "core/object/class_db.h" +#include "tts.h" + +void initialize_tts_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } + ClassDB::register_class(); +} + +void uninitialize_tts_module(ModuleInitializationLevel p_level) { + // Nothing to do here in this example. +} + +``` + +Next, you need to create an ``SCsub`` file so the build system compiles +this module: + +```python +Import('env') + +env_tts = env.Clone() +env_tts.add_source_files(env.modules_sources, "*.cpp") # Add all cpp files to the build + +``` + +You'll need to install the external library on your machine to get the .a library files. See the library's official +documentation for specific instructions on how to do this for your operation system. We've included the +installation commands for Linux below, for reference. + +```shell +sudo apt-get install festival festival-dev # Installs festival and speech_tools libraries +apt-cache search festvox-* # Displays list of voice packages +sudo apt-get install festvox-don festvox-rablpc16k festvox-kallpc16k festvox-kdlpc16k # Installs voices + +``` + +:::info + +The voices that Festival uses (and any other potential external/3rd-party +resource) all have varying licenses and terms of use; some (if not most) of them may be +be problematic with Redot, even if the Festival Library itself is MIT License compatible. +Please be sure to check the licenses and terms of use. + +::: + +The external library will also need to be installed inside your module to make the source +files accessible to the compiler, while also keeping the module code self-contained. The +festival and speech_tools libraries can be installed from the modules/tts/ directory via +git using the following commands: + +```shell +git clone https://github.com/festvox/festival +git clone https://github.com/festvox/speech_tools + +``` + +If you don't want the external repository source files committed to your repository, you +can link to them instead by adding them as submodules (from within the modules/tts/ directory), as seen below: + +```shell +git submodule add https://github.com/festvox/festival +git submodule add https://github.com/festvox/speech_tools + +``` + +:::info + +Please note that Git submodules are not used in the Redot repository. If +you are developing a module to be merged into the main Redot repository, you should not +use submodules. If your module doesn't get merged in, you can always try to implement +the external library as a GDExtension. + +::: + +To add include directories for the compiler to look at you can append it to the +environment's paths: + +```python +# These paths are relative to /modules/tts/ +env_tts.Append(CPPPATH=["speech_tools/include", "festival/src/include"]) + +# LIBPATH and LIBS need to be set on the real "env" (not the clone) +# to link the specified libraries to the Redot executable. + +# This is a path relative to /modules/tts/ where your .a libraries reside. +# If you are compiling the module externally (not in the Redot source tree), +# these will need to be full paths. +env.Append(LIBPATH=['libpath']) + +# Check with the documentation of the external library to see which library +# files should be included/linked. +env.Append(LIBS=['Festival', 'estools', 'estbase', 'eststring']) + +``` + +If you want to add custom compiler flags when building your module, you need to clone +`env` first, so it won't add those flags to whole Redot build (which can cause errors). +Example `SCsub` with custom flags: + +```python +Import('env') + +env_tts = env.Clone() +env_tts.add_source_files(env.modules_sources, "*.cpp") +# Append CCFLAGS flags for both C and C++ code. +env_tts.Append(CCFLAGS=['-O2']) +# If you need to, you can: +# - Append CFLAGS for C code only. +# - Append CXXFLAGS for C++ code only. + +``` + +The final module should look like this: + +```none +Redot/modules/tts/festival/ +Redot/modules/tts/libpath/libestbase.a +Redot/modules/tts/libpath/libestools.a +Redot/modules/tts/libpath/libeststring.a +Redot/modules/tts/libpath/libFestival.a +Redot/modules/tts/speech_tools/ +Redot/modules/tts/config.py +Redot/modules/tts/tts.h +Redot/modules/tts/tts.cpp +Redot/modules/tts/register_types.h +Redot/modules/tts/register_types.cpp +Redot/modules/tts/SCsub + +``` + +## Using the module + +You can now use your newly created module from any script: + +``` +var t = TTS.new() +var script = "Hello world. This is a test!" +var is_spoken = t.say_text(script) +print('is_spoken: ', is_spoken) + +``` + +And the output will be ``is_spoken: True`` if the text is spoken. \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/core_and_modules/common_engine_methods_and_macros.md b/Redot-Documentation/docs/Contributing/Development/core_and_modules/common_engine_methods_and_macros.md new file mode 100644 index 0000000..e2dc32f --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/core_and_modules/common_engine_methods_and_macros.md @@ -0,0 +1,267 @@ + +# Common engine methods and macros + +Redot's C++ codebase makes use of dozens of custom methods and macros which are +used in almost every file. This page is geared towards beginner contributors, +but it can also be useful for those writing custom C++ modules. + +## Print text + +```cpp +// Prints a message to standard output. +print_line("Message"); + +// Non-String arguments are automatically converted to String for printing. +// If passing several arguments, they will be concatenated together with a +// space between each argument. +print_line("There are", 123, "nodes"); + +// Prints a message to standard output, but only when the engine +// is started with the `--verbose` command line argument. +print_verbose("Message"); + +// Prints a rich-formatted message using BBCode to standard output. +// This supports a subset of BBCode tags supported by RichTextLabel +// and will also appear formatted in the editor Output panel. +// On Windows, this requires Windows 10 or later to work in the terminal. +print_line_rich("[b]Bold[/b], [color=red]Red text[/color]") + +// Prints a formatted error or warning message with a trace. +ERR_PRINT("Message"); +WARN_PRINT("Message"); + +// Prints an error or warning message only once per session. +// This can be used to avoid spamming the console output. +ERR_PRINT_ONCE("Message"); +WARN_PRINT_ONCE("Message"); + +``` + +If you need to add placeholders in your messages, use format strings as +described below. + +## Format a string + +The ``vformat()`` function returns a formatted [class_String](/docs/Classes/String). It behaves +in a way similar to C's ``sprintf()``: + +```cpp +vformat("My name is %s.", "Godette"); +vformat("%d bugs on the wall!", 1234); +vformat("Pi is approximately %f.", 3.1416); + +// Converts the resulting String into a `const char *`. +// You may need to do this if passing the result as an argument +// to a method that expects a `const char *` instead of a String. +vformat("My name is %s.", "Godette").c_str(); + +``` + +In most cases, try to use ``vformat()`` instead of string concatenation as it +makes for more readable code. + +## Convert an integer or float to a string + +This is not needed when printing numbers using ``print_line()``, but you may +still need to perform manual conversion for some other use cases. + +```cpp +// Stores the string "42" using integer-to-string conversion. +String int_to_string = itos(42); + +// Stores the string "123.45" using real-to-string conversion. +String real_to_string = rtos(123.45); + +``` + +## Internationalize a string + +There are two types of internationalization in Redot's codebase: + +- ``TTR()``: **Editor ("tools") translations** will only be processed in the + editor. If a user uses the same text in one of their projects, it won't be + translated if they provide a translation for it. When contributing to the + engine, this is generally the macro you should use for localizable strings. +- ``RTR()``: **Runtime translations** will be automatically localized in + projects if they provide a translation for the given string. This kind of + translation shouldn't be used in editor-only code. + +```cpp +// Returns the translated string that matches the user's locale settings. +// Translations are located in `editor/translations`. +// The localization template is generated automatically; don't modify it. +TTR("Exit the editor?"); + +``` + +To insert placeholders in localizable strings, wrap the localization macro in a +``vformat()`` call as follows: + +```cpp +String file_path = "example.txt"; +vformat(TTR("Couldn't open \"%s\" for reading."), file_path); + +``` + +:::note + +When using ``vformat()`` and a translation macro together, always wrap the +translation macro in ``vformat()``, not the other way around. Otherwise, the +string will never match the translation as it will have the placeholder +already replaced when it's passed to TranslationServer. + +::: + +## Clamp a value + +Redot provides macros for clamping a value with a lower bound (``MAX``), an +upper bound (``MIN``) or both (``CLAMP``): + +```cpp +int a = 3; +int b = 5; + +MAX(b, 6); // 6 +MIN(2, a); // 2 +CLAMP(a, 10, 30); // 10 + +``` + +This works with any type that can be compared to other values (like ``int`` and +``float``). + +## Microbenchmarking + +If you want to benchmark a piece of code but don't know how to use a profiler, +use this snippet: + +```cpp +uint64_t begin = Time::get_singleton()->get_ticks_usec(); + +// Your code here... + +uint64_t end = Time::get_singleton()->get_ticks_usec(); +print_line(vformat("Snippet took %d microseconds", end - begin)); + +``` + +This will print the time spent between the ``begin`` declaration and the ``end`` +declaration. + +:::note + +You may have to ``#include "core/os/os.h"`` if it's not present already. + +When opening a pull request, make sure to remove this snippet as well as the +include if it wasn't there previously. + +::: + +## Get project/editor settings + +There are four macros available for this: + +```cpp +// Returns the specified project setting's value, +// defaulting to `false` if it doesn't exist. +GLOBAL_DEF("section/subsection/value", false); + +// Returns the specified editor setting's value, +// defaulting to "Untitled" if it doesn't exist. +EDITOR_DEF("section/subsection/value", "Untitled"); + +``` + +If a default value has been specified elsewhere, don't specify it again to avoid +repetition: + +```cpp +// Returns the value of the project setting. +GLOBAL_GET("section/subsection/value"); +// Returns the value of the editor setting. +EDITOR_GET("section/subsection/value"); + +``` + +It's recommended to use ``GLOBAL_DEF``/``EDITOR_DEF`` only once per setting and +use ``GLOBAL_GET``/``EDITOR_GET`` in all other places where it's referenced. + +## Error macros + +Redot features many error macros to make error reporting more convenient. + +:::warning + +Conditions in error macros work in the **opposite** way of GDScript's +built-in ``assert()`` function. An error is reached if the condition inside +evaluates to ``true``, not ``false``. + +::: + +:::note + +Only variants with custom messages are documented here, as these should +always be used in new contributions. Make sure the custom message provided +includes enough information for people to diagnose the issue, even if they +don't know C++. In case a method was passed invalid arguments, you can print +the invalid value in question to ease debugging. + +For internal error checking where displaying a human-readable message isn't +necessary, remove ``_MSG`` at the end of the macro name and don't supply a +message argument. + +Also, always try to return processable data so the engine can keep running +well. + +::: + +```cpp +// Conditionally prints an error message and returns from the function. +// Use this in methods which don't return a value. +ERR_FAIL_COND_MSG(!mesh.is_valid(), vformat("Couldn't load mesh at: %s", path)); + +// Conditionally prints an error message and returns `0` from the function. +// Use this in methods which must return a value. +ERR_FAIL_COND_V_MSG(rect.x < 0 || rect.y < 0, 0, + "Couldn't calculate the rectangle's area."); + +// Prints an error message if `index` is < 0 or >= `SomeEnum::QUALITY_MAX`, +// then returns from the function. +ERR_FAIL_INDEX_MSG(index, SomeEnum::QUALITY_MAX, + vformat("Invalid quality: %d. See SomeEnum for allowed values.", index)); + +// Prints an error message if `index` is < 0 >= `some_array.size()`, +// then returns `-1` from the function. +ERR_FAIL_INDEX_V_MSG(index, some_array.size(), -1, + vformat("Item %d is out of bounds.", index)); + +// Unconditionally prints an error message and returns from the function. +// Only use this if you need to perform complex error checking. +if (!complex_error_checking_routine()) { + ERR_FAIL_MSG("Couldn't reload the filesystem cache."); +} + +// Unconditionally prints an error message and returns `false` from the function. +// Only use this if you need to perform complex error checking. +if (!complex_error_checking_routine()) { + ERR_FAIL_V_MSG(false, "Couldn't parse the input arguments."); +} + +// Crashes the engine. This should generally never be used +// except for testing crash handling code. Redot's philosophy +// is to never crash, both in the editor and in exported projects. +CRASH_NOW_MSG("Can't predict the future! Aborting."); + +``` + +:::info + +See [core/error/error_macros.h](https://github.com/redot-engine/redot-engine/blob/master/core/error/error_macros.h) +in Redot's codebase for more information about each error macro. + +Some functions return an error code (materialized by a return type of +``Error``). This value can be returned directly from an error macro. +See the list of available error codes in +[core/error/error_list.h](https://github.com/redot-engine/redot-engine/blob/master/core/error/error_list.h). + +::: diff --git a/Redot-Documentation/docs/Contributing/Development/core_and_modules/core_types.md b/Redot-Documentation/docs/Contributing/Development/core_and_modules/core_types.md new file mode 100644 index 0000000..4cbd0af --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/core_and_modules/core_types.md @@ -0,0 +1,195 @@ + +# Core types + +Redot has a rich set of classes and templates that compose its core, +and everything is built upon them. + +This reference will try to list them in order for their better +understanding. + +## Definitions + +Redot uses the standard C99 datatypes, such as ``uint8_t``, +``uint32_t``, ``int64_t``, etc. which are nowadays supported by every +compiler. Reinventing the wheel for those is not fun, as it makes code +more difficult to read. + +In general, care is not taken to use the most efficient datatype for a +given task unless using large structures or arrays. ``int`` is used +through most of the code unless necessary. This is done because nowadays +every device has at least a 32 bits bus and can do such operations in +one cycle. It makes code more readable too. + +For files or memory sizes, ``size_t`` is used, which is warranted to be +64 bits. + +For Unicode characters, CharType instead of wchar_t is used, because +many architectures have 4 bytes long wchar_t, where 2 bytes might be +desired. However, by default, this has not been forced and CharType maps +directly to wchar_t. + +### References: + +- [core/typedefs.h](https://github.com/redot-engine/redot-engine/blob/master/core/typedefs.h) + +## Memory model + +PC is a wonderful architecture. Computers often have gigabytes of RAM, +terabytes of storage and gigahertz of CPU, and when an application needs +more resources the OS will swap out the inactive ones. Other +architectures (like mobile or consoles) are in general more limited. + +The most common memory model is the heap, where an application will +request a region of memory, and the underlying OS will try to fit it +somewhere and return it. This often works best and is flexible, +but over time and with abuse, this can lead to segmentation. + +Segmentation slowly creates holes that are too small for most common +allocations, so that memory is wasted. There is a lot of literature +about heap and segmentation, so this topic will not be developed +further here. Modern operating systems use paged memory, which helps +mitigate the problem of segmentation but doesn't solve it. + +However, in many studies and tests, it is shown that given enough +memory, if the maximum allocation size is below a given threshold in +proportion to the maximum heap size and proportion of memory intended to +be unused, segmentation will not be a problem over time as it will +remain constant. In other words, leave 10-20% of your memory free +and perform all small allocations and you are fine. + +Redot ensures that all objects that can be allocated dynamically are +small (less than a few kb at most). But what happens if an allocation is +too large (like an image or mesh geometry or large array)? In this case +Redot has the option to use a dynamic memory pool. This memory needs to +be locked to be accessed, and if an allocation runs out of memory, the +pool will be rearranged and compacted on demand. Depending on the need +of the game, the programmer can configure the dynamic memory pool size. + +## Allocating memory + +Redot has many tools for tracking memory usage in a game, especially +during debug. Because of this, the regular C and C++ library calls +should not be used. Instead, a few other ones are provided. + +For C-style allocation, Redot provides a few macros: + +```none +memalloc() +memrealloc() +memfree() + +``` + +These are equivalent to the usual malloc, realloc, free of the standard C +library. + +For C++-style allocation, special macros are provided: + +```none +memnew( Class / Class(args) ) +memdelete( instance ) + +memnew_arr( Class , amount ) +memdelete_arr( pointer to array ) + +``` + +which are equivalent to new, delete, new[] and delete[]. + +memnew/memdelete also use a little C++ magic and notify Objects right +after they are created, and right before they are deleted. + +For dynamic memory, use Vector<>. + +### References: + +- [core/os/memory.h](https://github.com/redot-engine/redot-engine/blob/master/core/os/memory.h) + +## Containers + +Redot provides also a set of common containers: + +- Vector +- List +- Set +- Map + +They aim to be as minimal as possible, as templates +in C++ are often inlined and make the binary size much fatter, both in +debug symbols and code. List, Set and Map can be iterated using +pointers, like this: + +```cpp +for(List::Element *E=somelist.front();E;E=E->next()) { + print_line(E->get()); // print the element +} + +``` + +The Vector<> class also has a few nice features: + +- It does copy on write, so making copies of it is cheap as long as + they are not modified. +- It supports multi-threading, by using atomic operations on the + reference counter. + +### References: + +- [core/templates/vector.h](https://github.com/redot-engine/redot-engine/blob/master/core/templates/vector.h) +- [core/templates/list.h](https://github.com/redot-engine/redot-engine/blob/master/core/templates/list.h) +- [core/templates/set.h](https://github.com/redot-engine/redot-engine/blob/master/core/templates/hash_set.h) +- [core/templates/map.h](https://github.com/redot-engine/redot-engine/blob/master/core/templates/hash_map.h) + +## String + +Redot also provides a String class. This class has a huge amount of +features, full Unicode support in all the functions (like case +operations) and utf8 parsing/extracting, as well as helpers for +conversion and visualization. + +### References: + +- [core/string/ustring.h](https://github.com/redot-engine/redot-engine/blob/master/core/string/ustring.h) + +## StringName + +StringNames are like a String, but they are unique. Creating a +StringName from a string results in a unique internal pointer for all +equal strings. StringNames are useful for using strings as +identifier, as comparing them is basically comparing a pointer. + +Creation of a StringName (especially a new one) is slow, but comparison +is fast. + +### References: + +- [core/string/string_name.h](https://github.com/redot-engine/redot-engine/blob/master/core/string/string_name.h) + +## Math types + +There are several linear math types available in the core/math +directory. + +### References: + +- [core/math](https://github.com/redot-engine/redot-engine/tree/master/core/math) + +## NodePath + +This is a special datatype used for storing paths in a scene tree and +referencing them fast. + +### References: + +- [core/string/node_path.h](https://github.com/redot-engine/redot-engine/blob/master/core/string/node_path.h) + +## RID + +RIDs are resource IDs. Servers use these to reference data stored in +them. RIDs are opaque, meaning that the data they reference can't be +accessed directly. RIDs are unique, even for different types of +referenced data. + +### References: + +- [core/templates/rid.h](https://github.com/redot-engine/redot-engine/blob/master/core/templates/rid.h) \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/core_and_modules/custom_audiostreams.md b/Redot-Documentation/docs/Contributing/Development/core_and_modules/custom_audiostreams.md new file mode 100644 index 0000000..139a4e8 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/core_and_modules/custom_audiostreams.md @@ -0,0 +1,338 @@ + +# Custom AudioStreams + +## Introduction + +AudioStream is the base class of all audio emitting objects. +AudioStreamPlayer binds onto an AudioStream to emit PCM data +into an AudioServer which manages audio drivers. + +All audio resources require two audio based classes: AudioStream +and AudioStreamPlayback. As a data container, AudioStream contains +the resource and exposes itself to GDScript. AudioStream references +its own internal custom AudioStreamPlayback which translates +AudioStream into PCM data. + +This guide assumes the reader knows how to create C++ modules. If not, refer to this guide +[doc_custom_modules_in_cpp](custom_modules_in_cpp.md). + +### References: + +- [servers/audio/audio_stream.h](https://github.com/redot-engine/redot-engine/blob/master/servers/audio/audio_stream.h) +- [scene/audio/audio_stream_player.cpp](https://github.com/redot-engine/redot-engine/blob/master/scene/audio/audio_stream_player.cpp) + +## What for? + +- Binding external libraries (like Wwise, FMOD, etc). +- Adding custom audio queues +- Adding support for more audio formats + +## Create an AudioStream + +An AudioStream consists of three components: data container, stream name, +and an AudioStreamPlayback friend class generator. Audio data can be +loaded in a number of ways such as with an internal counter for a tone generator, +internal/external buffer, or a file reference. + +Some AudioStreams need to be stateless such as objects loaded from +ResourceLoader. ResourceLoader loads once and references the same +object regardless how many times ``load`` is called on a specific resource. +Therefore, playback state must be self-contained in AudioStreamPlayback. + +```cpp +#include "core/reference.h" +#include "core/resource.h" +#include "servers/audio/audio_stream.h" + +class AudioStreamMyTone : public AudioStream { + GDCLASS(AudioStreamMyTone, AudioStream) + +private: + friend class AudioStreamPlaybackMyTone; + uint64_t pos; + int mix_rate; + bool stereo; + int hz; + +public: + void reset(); + void set_position(uint64_t pos); + virtual Ref instance_playback(); + virtual String get_stream_name() const; + void gen_tone(int16_t *pcm_buf, int size); + virtual float get_length() const { return 0; } // if supported, otherwise return 0 + AudioStreamMyTone(); + +protected: + static void _bind_methods(); +}; + +``` + +```cpp +#include "audiostream_mytone.h" + +AudioStreamMyTone::AudioStreamMyTone() + : mix_rate(44100), stereo(false), hz(639) { +} + +Ref AudioStreamMyTone::instance_playback() { + Ref talking_tree; + talking_tree.instantiate(); + talking_tree->base = Ref(this); + return talking_tree; +} + +String AudioStreamMyTone::get_stream_name() const { + return "MyTone"; +} +void AudioStreamMyTone::reset() { + set_position(0); +} +void AudioStreamMyTone::set_position(uint64_t p) { + pos = p; +} +void AudioStreamMyTone::gen_tone(int16_t *pcm_buf, int size) { + for (int i = 0; i < size; i++) { + pcm_buf[i] = 32767.0 * sin(2.0 * Math_PI * double(pos + i) / (double(mix_rate) / double(hz))); + } + pos += size; +} +void AudioStreamMyTone::_bind_methods() { + ClassDB::bind_method(D_METHOD("reset"), &AudioStreamMyTone::reset); + ClassDB::bind_method(D_METHOD("get_stream_name"), &AudioStreamMyTone::get_stream_name); +} + +``` + +### References: + +- [servers/audio/audio_stream.h](https://github.com/redot-engine/redot-engine/blob/master/servers/audio/audio_stream.h) + +## Create an AudioStreamPlayback + +AudioStreamPlayer uses ``mix`` callback to obtain PCM data. The callback must match sample rate and fill the buffer. + +Since AudioStreamPlayback is controlled by the audio thread, i/o and dynamic memory allocation are forbidden. + +```cpp +#include "core/reference.h" +#include "core/resource.h" +#include "servers/audio/audio_stream.h" + +class AudioStreamPlaybackMyTone : public AudioStreamPlayback { + GDCLASS(AudioStreamPlaybackMyTone, AudioStreamPlayback) + friend class AudioStreamMyTone; + +private: + enum { + PCM_BUFFER_SIZE = 4096 + }; + enum { + MIX_FRAC_BITS = 13, + MIX_FRAC_LEN = (1 << MIX_FRAC_BITS), + MIX_FRAC_MASK = MIX_FRAC_LEN - 1, + }; + void *pcm_buffer; + Ref base; + bool active; + +public: + virtual void start(float p_from_pos = 0.0); + virtual void stop(); + virtual bool is_playing() const; + virtual int get_loop_count() const; // times it looped + virtual float get_playback_position() const; + virtual void seek(float p_time); + virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames); + virtual float get_length() const; // if supported, otherwise return 0 + AudioStreamPlaybackMyTone(); + ~AudioStreamPlaybackMyTone(); +}; + +``` + +```cpp +#include "audiostreamplayer_mytone.h" + +#include "core/math/math_funcs.h" +#include "core/print_string.h" + +AudioStreamPlaybackMyTone::AudioStreamPlaybackMyTone() + : active(false) { + AudioServer::get_singleton()->lock(); + pcm_buffer = AudioServer::get_singleton()->audio_data_alloc(PCM_BUFFER_SIZE); + zeromem(pcm_buffer, PCM_BUFFER_SIZE); + AudioServer::get_singleton()->unlock(); +} +AudioStreamPlaybackMyTone::~AudioStreamPlaybackMyTone() { + if(pcm_buffer) { + AudioServer::get_singleton()->audio_data_free(pcm_buffer); + pcm_buffer = NULL; + } +} +void AudioStreamPlaybackMyTone::stop() { + active = false; + base->reset(); +} +void AudioStreamPlaybackMyTone::start(float p_from_pos) { + seek(p_from_pos); + active = true; +} +void AudioStreamPlaybackMyTone::seek(float p_time) { + float max = get_length(); + if (p_time < 0) { + p_time = 0; + } + base->set_position(uint64_t(p_time * base->mix_rate) << MIX_FRAC_BITS); +} +void AudioStreamPlaybackMyTone::mix(AudioFrame *p_buffer, float p_rate, int p_frames) { + ERR_FAIL_COND(!active); + if (!active) { + return; + } + zeromem(pcm_buffer, PCM_BUFFER_SIZE); + int16_t *buf = (int16_t *)pcm_buffer; + base->gen_tone(buf, p_frames); + + for(int i = 0; i < p_frames; i++) { + float sample = float(buf[i]) / 32767.0; + p_buffer[i] = AudioFrame(sample, sample); + } +} +int AudioStreamPlaybackMyTone::get_loop_count() const { + return 0; +} +float AudioStreamPlaybackMyTone::get_playback_position() const { + return 0.0; +} +float AudioStreamPlaybackMyTone::get_length() const { + return 0.0; +} +bool AudioStreamPlaybackMyTone::is_playing() const { + return active; +} + +``` + +### Resampling + +Redot's AudioServer currently uses 44100 Hz sample rate. When other sample rates are +needed such as 48000, either provide one or use AudioStreamPlaybackResampled. +Redot provides cubic interpolation for audio resampling. + +Instead of overloading ``mix``, AudioStreamPlaybackResampled uses ``_mix_internal`` to +query AudioFrames and ``get_stream_sampling_rate`` to query current mix rate. + +```cpp +#include "core/reference.h" +#include "core/resource.h" +#include "servers/audio/audio_stream.h" + +class AudioStreamMyToneResampled; + +class AudioStreamPlaybackResampledMyTone : public AudioStreamPlaybackResampled { + GDCLASS(AudioStreamPlaybackResampledMyTone, AudioStreamPlaybackResampled) + friend class AudioStreamMyToneResampled; + +private: + enum { + PCM_BUFFER_SIZE = 4096 + }; + enum { + MIX_FRAC_BITS = 13, + MIX_FRAC_LEN = (1 << MIX_FRAC_BITS), + MIX_FRAC_MASK = MIX_FRAC_LEN - 1, + }; + void *pcm_buffer; + Ref base; + bool active; + +protected: + virtual void _mix_internal(AudioFrame *p_buffer, int p_frames); + +public: + virtual void start(float p_from_pos = 0.0); + virtual void stop(); + virtual bool is_playing() const; + virtual int get_loop_count() const; // times it looped + virtual float get_playback_position() const; + virtual void seek(float p_time); + virtual float get_length() const; // if supported, otherwise return 0 + virtual float get_stream_sampling_rate(); + AudioStreamPlaybackResampledMyTone(); + ~AudioStreamPlaybackResampledMyTone(); +}; + +``` + +```cpp +#include "mytone_audiostream_resampled.h" + +#include "core/math/math_funcs.h" +#include "core/print_string.h" + +AudioStreamPlaybackResampledMyTone::AudioStreamPlaybackResampledMyTone() + : active(false) { + AudioServer::get_singleton()->lock(); + pcm_buffer = AudioServer::get_singleton()->audio_data_alloc(PCM_BUFFER_SIZE); + zeromem(pcm_buffer, PCM_BUFFER_SIZE); + AudioServer::get_singleton()->unlock(); +} +AudioStreamPlaybackResampledMyTone::~AudioStreamPlaybackResampledMyTone() { + if (pcm_buffer) { + AudioServer::get_singleton()->audio_data_free(pcm_buffer); + pcm_buffer = NULL; + } +} +void AudioStreamPlaybackResampledMyTone::stop() { + active = false; + base->reset(); +} +void AudioStreamPlaybackResampledMyTone::start(float p_from_pos) { + seek(p_from_pos); + active = true; +} +void AudioStreamPlaybackResampledMyTone::seek(float p_time) { + float max = get_length(); + if (p_time < 0) { + p_time = 0; + } + base->set_position(uint64_t(p_time * base->mix_rate) << MIX_FRAC_BITS); +} +void AudioStreamPlaybackResampledMyTone::_mix_internal(AudioFrame *p_buffer, int p_frames) { + ERR_FAIL_COND(!active); + if (!active) { + return; + } + zeromem(pcm_buffer, PCM_BUFFER_SIZE); + int16_t *buf = (int16_t *)pcm_buffer; + base->gen_tone(buf, p_frames); + + for(int i = 0; i < p_frames; i++) { + float sample = float(buf[i]) / 32767.0; + p_buffer[i] = AudioFrame(sample, sample); + } +} +float AudioStreamPlaybackResampledMyTone::get_stream_sampling_rate() { + return float(base->mix_rate); +} +int AudioStreamPlaybackResampledMyTone::get_loop_count() const { + return 0; +} +float AudioStreamPlaybackResampledMyTone::get_playback_position() const { + return 0.0; +} +float AudioStreamPlaybackResampledMyTone::get_length() const { + return 0.0; +} +bool AudioStreamPlaybackResampledMyTone::is_playing() const { + return active; +} + +``` + +### References: +- [core/math/audio_frame.h](https://github.com/redot-engine/redot-engine/blob/master/core/math/audio_frame.h) +- [servers/audio/audio_stream.h](https://github.com/redot-engine/redot-engine/blob/master/servers/audio/audio_stream.h) +- [scene/audio/audio_stream_player.cpp](https://github.com/redot-engine/redot-engine/blob/master/scene/audio/audio_stream_player.cpp) \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/core_and_modules/custom_godot_servers.md b/Redot-Documentation/docs/Contributing/Development/core_and_modules/custom_godot_servers.md new file mode 100644 index 0000000..9a98444 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/core_and_modules/custom_godot_servers.md @@ -0,0 +1,496 @@ + +# Custom Redot servers + +## Introduction + +Redot implements multi-threading as servers. Servers are daemons which +manage data, process it, and push the result. Servers implement the +mediator pattern which interprets resource ID and process data for the +engine and other modules. In addition, the server claims ownership for +its RID allocations. + +This guide assumes the reader knows how to create C++ modules and Redot +data types. If not, refer to [doc_custom_modules_in_cpp](custom_modules_in_cpp.md). + +### References + +- [Why does Redot use servers and RIDs?](https://godotengine.org/article/why-does-godot-use-servers-and-rids) +- [Singleton pattern](https://en.wikipedia.org/wiki/Singleton_pattern) +- [Mediator pattern](https://en.wikipedia.org/wiki/Mediator_pattern) + +## What for? + +- Adding artificial intelligence. +- Adding custom asynchronous threads. +- Adding support for a new input device. +- Adding writing threads. +- Adding a custom VoIP protocol. +- And more... + +## Creating a Redot server + +At minimum, a server must have a static instance, a sleep timer, a thread loop, +an initialization state and a cleanup procedure. + +```cpp +#ifndef HILBERT_HOTEL_H +#define HILBERT_HOTEL_H + +#include "core/object/object.h" +#include "core/os/thread.h" +#include "core/os/mutex.h" +#include "core/templates/list.h" +#include "core/templates/rid.h" +#include "core/templates/set.h" +#include "core/variant/variant.h" + +class HilbertHotel : public Object { + GDCLASS(HilbertHotel, Object); + + static HilbertHotel *singleton; + static void thread_func(void *p_udata); + +private: + bool thread_exited; + mutable bool exit_thread; + Thread *thread; + Mutex *mutex; + +public: + static HilbertHotel *get_singleton(); + Error init(); + void lock(); + void unlock(); + void finish(); + +protected: + static void _bind_methods(); + +private: + uint64_t counter; + RID_Owner bus_owner; + // https://github.com/redot-engine/redot-engine/blob/master/core/templates/rid.h + Set buses; + void _emit_occupy_room(uint64_t room, RID rid); + +public: + RID create_bus(); + Variant get_bus_info(RID id); + bool empty(); + bool delete_bus(RID id); + void clear(); + void register_rooms(); + HilbertHotel(); +}; + +#endif + +``` + +```cpp +#include "hilbert_hotel.h" + +#include "core/variant/dictionary.h" +#include "core/os/os.h" + +#include "prime_225.h" + +void HilbertHotel::thread_func(void *p_udata) { + + HilbertHotel *ac = (HilbertHotel *) p_udata; + uint64_t msdelay = 1000; + + while (!ac->exit_thread) { + if (!ac->empty()) { + ac->lock(); + ac->register_rooms(); + ac->unlock(); + } + OS::get_singleton()->delay_usec(msdelay * 1000); + } +} + +Error HilbertHotel::init() { + thread_exited = false; + counter = 0; + mutex = Mutex::create(); + thread = Thread::create(HilbertHotel::thread_func, this); + return OK; +} + +HilbertHotel *HilbertHotel::singleton = NULL; + +HilbertHotel *HilbertHotel::get_singleton() { + return singleton; +} + +void HilbertHotel::register_rooms() { + for (Set::Element *e = buses.front(); e; e = e->next()) { + auto bus = bus_owner.getornull(e->get()); + + if (bus) { + uint64_t room = bus->next_room(); + _emit_occupy_room(room, bus->get_self()); + } + } +} + +void HilbertHotel::unlock() { + if (!thread || !mutex) { + return; + } + + mutex->unlock(); +} + +void HilbertHotel::lock() { + if (!thread || !mutex) { + return; + } + + mutex->lock(); +} + +void HilbertHotel::_emit_occupy_room(uint64_t room, RID rid) { + _HilbertHotel::get_singleton()->_occupy_room(room, rid); +} + +Variant HilbertHotel::get_bus_info(RID id) { + InfiniteBus *bus = bus_owner.getornull(id); + + if (bus) { + Dictionary d; + d["prime"] = bus->get_bus_num(); + d["current_room"] = bus->get_current_room(); + return d; + } + + return Variant(); +} + +void HilbertHotel::finish() { + if (!thread) { + return; + } + + exit_thread = true; + Thread::wait_to_finish(thread); + + memdelete(thread); + + if (mutex) { + memdelete(mutex); + } + + thread = NULL; +} + +RID HilbertHotel::create_bus() { + lock(); + InfiniteBus *ptr = memnew(InfiniteBus(PRIME[counter++])); + RID ret = bus_owner.make_rid(ptr); + ptr->set_self(ret); + buses.insert(ret); + unlock(); + + return ret; +} + +// https://github.com/redot-engine/redot-engine/blob/master/core/templates/rid.h +bool HilbertHotel::delete_bus(RID id) { + if (bus_owner.owns(id)) { + lock(); + InfiniteBus *b = bus_owner.get(id); + bus_owner.free(id); + buses.erase(id); + memdelete(b); + unlock(); + return true; + } + + return false; +} + +void HilbertHotel::clear() { + for (Set::Element *e = buses.front(); e; e = e->next()) { + delete_bus(e->get()); + } +} + +bool HilbertHotel::empty() { + return buses.size() <= 0; +} + +void HilbertHotel::_bind_methods() { +} + +HilbertHotel::HilbertHotel() { + singleton = this; +} + +``` + +```cpp +const uint64_t PRIME[225] = { + 2,3,5,7,11,13,17,19,23, + 29,31,37,41,43,47,53,59,61, + 67,71,73,79,83,89,97,101,103, + 107,109,113,127,131,137,139,149,151, + 157,163,167,173,179,181,191,193,197, + 199,211,223,227,229,233,239,241,251, + 257,263,269,271,277,281,283,293,307, + 311,313,317,331,337,347,349,353,359, + 367,373,379,383,389,397,401,409,419, + 421,431,433,439,443,449,457,461,463, + 467,479,487,491,499,503,509,521,523, + 541,547,557,563,569,571,577,587,593, + 599,601,607,613,617,619,631,641,643, + 647,653,659,661,673,677,683,691,701, + 709,719,727,733,739,743,751,757,761, + 769,773,787,797,809,811,821,823,827, + 829,839,853,857,859,863,877,881,883, + 887,907,911,919,929,937,941,947,953, + 967,971,977,983,991,997,1009,1013,1019, + 1021,1031,1033,1039,1049,1051,1061,1063,1069, + 1087,1091,1093,1097,1103,1109,1117,1123,1129, + 1151,1153,1163,1171,1181,1187,1193,1201,1213, + 1217,1223,1229,1231,1237,1249,1259,1277,1279, + 1283,1289,1291,1297,1301,1303,1307,1319,1321, + 1327,1361,1367,1373,1381,1399,1409,1423,1427 +}; + +``` + +## Custom managed resource data + +Redot servers implement a mediator pattern. All data types inherit ``RID_Data``. +`RID_Owner` owns the object when ``make_rid`` is called. During debug mode only, +RID_Owner maintains a list of RIDs. In practice, RIDs are similar to writing +object-oriented C code. + +```cpp +class InfiniteBus : public RID_Data { + RID self; + +private: + uint64_t prime_num; + uint64_t num; + +public: + uint64_t next_room() { + return prime_num * num++; + } + + uint64_t get_bus_num() const { + return prime_num; + } + + uint64_t get_current_room() const { + return prime_num * num; + } + + _FORCE_INLINE_ void set_self(const RID &p_self) { + self = p_self; + } + + _FORCE_INLINE_ RID get_self() const { + return self; + } + + InfiniteBus(uint64_t prime) : prime_num(prime), num(1) {}; + ~InfiniteBus() {}; +} + +``` + +### References + +- [RID](/docs/Classes/rid) +- [core/templates/rid.h](https://github.com/redot-engine/redot-engine/blob/master/core/templates/rid.h) + +## Registering the class in GDScript + +Servers are allocated in ``register_types.cpp``. The constructor sets the static +instance and ``init()`` creates the managed thread; ``unregister_types.cpp`` +cleans up the server. + +Since a Redot server class creates an instance and binds it to a static singleton, +binding the class might not reference the correct instance. Therefore, a dummy +class must be created to reference the proper Redot server. + +In ``register_server_types()``, ``Engine::get_singleton()->add_singleton`` +is used to register the dummy class in GDScript. + +```cpp +/* Yes, the word in the middle must be the same as the module folder name */ +void register_hilbert_hotel_types(); +void unregister_hilbert_hotel_types(); + +``` + +```cpp +#include "register_types.h" + +#include "core/object/class_db.h" +#include "core/config/engine.h" + +#include "hilbert_hotel.h" + +static HilbertHotel *hilbert_hotel = NULL; +static _HilbertHotel *_hilbert_hotel = NULL; + +void register_hilbert_hotel_types() { + hilbert_hotel = memnew(HilbertHotel); + hilbert_hotel->init(); + _hilbert_hotel = memnew(_HilbertHotel); + ClassDB::register_class<_HilbertHotel>(); + Engine::get_singleton()->add_singleton(Engine::Singleton("HilbertHotel", _HilbertHotel::get_singleton())); +} + +void unregister_hilbert_hotel_types() { + if (hilbert_hotel) { + hilbert_hotel->finish(); + memdelete(hilbert_hotel); + } + + if (_hilbert_hotel) { + memdelete(_hilbert_hotel); + } +} + +``` + +- [servers/register_server_types.cpp](https://github.com/redot-engine/redot-engine/blob/master/servers/register_server_types.cpp) + +### Bind methods + +The dummy class binds singleton methods to GDScript. In most cases, the dummy class methods wraps around. + +```cpp +Variant _HilbertHotel::get_bus_info(RID id) { + return HilbertHotel::get_singleton()->get_bus_info(id); +} + +``` + +Binding Signals + +It is possible to emit signals to GDScript by calling the GDScript dummy object. + +```cpp +void HilbertHotel::_emit_occupy_room(uint64_t room, RID rid) { + _HilbertHotel::get_singleton()->_occupy_room(room, rid); +} + +``` + +```cpp +class _HilbertHotel : public Object { + GDCLASS(_HilbertHotel, Object); + + friend class HilbertHotel; + static _HilbertHotel *singleton; + +protected: + static void _bind_methods(); + +private: + void _occupy_room(int room_number, RID bus); + +public: + RID create_bus(); + void connect_signals(); + bool delete_bus(RID id); + static _HilbertHotel *get_singleton(); + Variant get_bus_info(RID id); + + _HilbertHotel(); + ~_HilbertHotel(); +}; + +#endif + +``` + +```cpp +_HilbertHotel *_HilbertHotel::singleton = NULL; +_HilbertHotel *_HilbertHotel::get_singleton() { return singleton; } + +RID _HilbertHotel::create_bus() { + return HilbertHotel::get_singleton()->create_bus(); +} + +bool _HilbertHotel::delete_bus(RID rid) { + return HilbertHotel::get_singleton()->delete_bus(rid); +} + +void _HilbertHotel::_occupy_room(int room_number, RID bus) { + emit_signal("occupy_room", room_number, bus); +} + +Variant _HilbertHotel::get_bus_info(RID id) { + return HilbertHotel::get_singleton()->get_bus_info(id); +} + +void _HilbertHotel::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_bus_info", "r_id"), &_HilbertHotel::get_bus_info); + ClassDB::bind_method(D_METHOD("create_bus"), &_HilbertHotel::create_bus); + ClassDB::bind_method(D_METHOD("delete_bus"), &_HilbertHotel::delete_bus); + ADD_SIGNAL(MethodInfo("occupy_room", PropertyInfo(Variant::INT, "room_number"), PropertyInfo(Variant::_RID, "r_id"))); +} + +void _HilbertHotel::connect_signals() { + HilbertHotel::get_singleton()->connect("occupy_room", _HilbertHotel::get_singleton(), "_occupy_room"); +} + +_HilbertHotel::_HilbertHotel() { + singleton = this; +} + +_HilbertHotel::~_HilbertHotel() { +} + +``` + +## MessageQueue + +In order to send commands into SceneTree, MessageQueue is a thread-safe buffer +to queue set and call methods for other threads. To queue a command, obtain +the target object RID and use either ``push_call``, ``push_set``, or ``push_notification`` +to execute the desired behavior. The queue will be flushed whenever either +``SceneTree::idle`` or ``SceneTree::iteration`` is executed. + +### References: + +- [core/object/message_queue.cpp](https://github.com/redot-engine/redot-engine/blob/master/core/object/message_queue.cpp) + +## Summing it up + +Here is the GDScript sample code: + +``` +extends Node + +func _ready(): + print("Start debugging") + HilbertHotel.occupy_room.connect(_print_occupy_room) + var rid = HilbertHotel.create_bus() + OS.delay_msec(2000) + HilbertHotel.create_bus() + OS.delay_msec(2000) + HilbertHotel.create_bus() + OS.delay_msec(2000) + print(HilbertHotel.get_bus_info(rid)) + HilbertHotel.delete_bus(rid) + print("Ready done") + +func _print_occupy_room(room_number, r_id): + print("Room number: " + str(room_number) + ", RID: " + str(r_id)) + print(HilbertHotel.get_bus_info(r_id)) + +``` + +### Notes + +- The actual [Hilbert Hotel](https://en.wikipedia.org/wiki/Hilbert%27s_paradox_of_the_Grand_Hotel) is impossible. +- Connecting signal example code is pretty hacky. \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/core_and_modules/custom_modules_in_cpp.md b/Redot-Documentation/docs/Contributing/Development/core_and_modules/custom_modules_in_cpp.md new file mode 100644 index 0000000..8ad5988 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/core_and_modules/custom_modules_in_cpp.md @@ -0,0 +1,752 @@ +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +# Custom modules in C++ + +## Modules + +Redot allows extending the engine in a modular way. New modules can be +created and then enabled/disabled. This allows for adding new engine +functionality at every level without modifying the core, which can be +split for use and reuse in different modules. + +Modules are located in the ``modules/`` subdirectory of the build system. +By default, dozens of modules are enabled, such as GDScript (which, yes, +is not part of the base engine), the Mono runtime, a regular expressions +module, and others. As many new modules as desired can be +created and combined. The SCons build system will take care of it +transparently. + +## What for? + +While it's recommended that most of a game be written in scripting (as +it is an enormous time saver), it's perfectly possible to use C++ +instead. Adding C++ modules can be useful in the following scenarios: + +- Binding an external library to Redot (like PhysX, FMOD, etc). +- Optimize critical parts of a game. +- Adding new functionality to the engine and/or editor. +- Porting an existing game to Redot. +- Write a whole, new game in C++ because you can't live without C++. + +## Creating a new module + +Before creating a module, make sure to :ref:`download the source code of Redot +and compile it <toc-devel-compiling>`. + +To create a new module, the first step is creating a directory inside +``modules/``. If you want to maintain the module separately, you can checkout +a different VCS into modules and use it. + +The example module will be called "summator" (``Redot/modules/summator``). +Inside we will create a summator class: + +```cpp +#ifndef SUMMATOR_H +#define SUMMATOR_H + +#include "core/object/ref_counted.h" + +class Summator : public RefCounted { + GDCLASS(Summator, RefCounted); + + int count; + +protected: + static void _bind_methods(); + +public: + void add(int p_value); + void reset(); + int get_total() const; + + Summator(); +}; + +#endif // SUMMATOR_H + +``` + +And then the cpp file. + +```cpp +#include "summator.h" + +void Summator::add(int p_value) { + count += p_value; +} + +void Summator::reset() { + count = 0; +} + +int Summator::get_total() const { + return count; +} + +void Summator::_bind_methods() { + ClassDB::bind_method(D_METHOD("add", "value"), &Summator::add); + ClassDB::bind_method(D_METHOD("reset"), &Summator::reset); + ClassDB::bind_method(D_METHOD("get_total"), &Summator::get_total); +} + +Summator::Summator() { + count = 0; +} + +``` + +Then, the new class needs to be registered somehow, so two more files +need to be created: + +```none +register_types.h +register_types.cpp + +``` + +:::info + +These files must be in the top-level folder of your module (next to your +``SCsub`` and ``config.py`` files) for the module to be registered properly. + +::: + +These files should contain the following: + +```cpp +#include "modules/register_module_types.h" + +void initialize_summator_module(ModuleInitializationLevel p_level); +void uninitialize_summator_module(ModuleInitializationLevel p_level); +/* yes, the word in the middle must be the same as the module folder name */ + +``` + +```cpp +#include "register_types.h" + +#include "core/object/class_db.h" +#include "summator.h" + +void initialize_summator_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } + ClassDB::register_class(); +} + +void uninitialize_summator_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } + // Nothing to do here in this example. +} + +``` + +Next, we need to create an ``SCsub`` file so the build system compiles +this module: + +```python +# SCsub + +Import('env') + +env.add_source_files(env.modules_sources, "*.cpp") # Add all cpp files to the build + +``` + +With multiple sources, you can also add each file individually to a Python +string list: + +```python +src_list = ["summator.cpp", "other.cpp", "etc.cpp"] +env.add_source_files(env.modules_sources, src_list) + +``` + +This allows for powerful possibilities using Python to construct the file list +using loops and logic statements. Look at some modules that ship with Redot by +default for examples. + +To add include directories for the compiler to look at you can append it to the +environment's paths: + +```python +env.Append(CPPPATH=["mylib/include"]) # this is a relative path +env.Append(CPPPATH=["#myotherlib/include"]) # this is an 'absolute' path + +``` + +If you want to add custom compiler flags when building your module, you need to clone +``env`` first, so it won't add those flags to whole Redot build (which can cause errors). +Example ``SCsub`` with custom flags: + +```python +Import('env') + +module_env = env.Clone() +module_env.add_source_files(env.modules_sources, "*.cpp") +# Append CCFLAGS flags for both C and C++ code. +module_env.Append(CCFLAGS=['-O2']) +# If you need to, you can: +# - Append CFLAGS for C code only. +# - Append CXXFLAGS for C++ code only. + +``` + +And finally, the configuration file for the module, this is a +Python script that must be named ``config.py``: + +```python +# config.py + +def can_build(env, platform): + return True + +def configure(env): + pass + +``` + +The module is asked if it's OK to build for the specific platform (in +this case, ``True`` means it will build for every platform). + +And that's it. Hope it was not too complex! Your module should look like +this: + +```none +Redot/modules/summator/config.py +Redot/modules/summator/summator.h +Redot/modules/summator/summator.cpp +Redot/modules/summator/register_types.h +Redot/modules/summator/register_types.cpp +Redot/modules/summator/SCsub + +``` + +You can then zip it and share the module with everyone else. When +building for every platform (instructions in the previous sections), +your module will be included. + +:::note +There is a parameter limit of 5 in C++ modules for things such +as subclasses. This can be raised to 13 by including the header +file ``core/method_bind_ext.gen.inc``. + +::: + +## Using the module + +You can now use your newly created module from any script: + + + + + +```gdscript +var s = Summator.new() +s.add(10) +s.add(20) +s.add(30) +print(s.get_total()) +s.reset() +``` + + + + + +The output will be ``60``. + +:::info +The previous Summator example is great for small, custom modules, +but what if you want to use a larger, external library? Refer to +[doc_binding_to_external_libraries](binding_to_external_libraries.md) for details about binding to +external libraries. + +::: + +:::warning +If your module is meant to be accessed from the running project +(not just from the editor), you must also recompile every export +template you plan to use, then specify the path to the custom +template in each export preset. Otherwise, you'll get errors when +running the project as the module isn't compiled in the export +template. See the [Compiling](toc-devel-compiling) pages +for more information. + +::: + +## Compiling a module externally + +Compiling a module involves moving the module's sources directly under the +engine's ``modules/`` directory. While this is the most straightforward way to +compile a module, there are a couple of reasons as to why this might not be a +practical thing to do: + +1. Having to manually copy modules sources every time you want to compile the + engine with or without the module, or taking additional steps needed to + manually disable a module during compilation with a build option similar to + ``module_summator_enabled=no``. Creating symbolic links may also be a solution, + but you may additionally need to overcome OS restrictions like needing the + symbolic link privilege if doing this via script. + +2. Depending on whether you have to work with the engine's source code, the + module files added directly to ``modules/`` changes the working tree to the + point where using a VCS (like ``git``) proves to be cumbersome as you need to + make sure that only the engine-related code is committed by filtering + changes. + +So if you feel like the independent structure of custom modules is needed, lets +take our "summator" module and move it to the engine's parent directory: + +```shell +mkdir ../modules +mv modules/summator ../modules + +``` + +Compile the engine with our module by providing ``custom_modules`` build option +which accepts a comma-separated list of directory paths containing custom C++ +modules, similar to the following: + +```shell +scons custom_modules=../modules + +``` + +The build system shall detect all modules under the ``../modules`` directory +and compile them accordingly, including our "summator" module. + +:::warning + +Any path passed to ``custom_modules`` will be converted to an absolute path +internally as a way to distinguish between custom and built-in modules. It +means that things like generating module documentation may rely on a +specific path structure on your machine. + +::: + +:::info + +[Introduction to the buildsystem - Custom modules build option](doc_buildsystem_custom_modules). + +::: + +## Customizing module types initialization + +Modules can interact with other built-in engine classes during runtime and even +affect the way core types are initialized. So far, we've been using +``register_summator_types`` as a way to bring in module classes to be available +within the engine. + +A crude order of the engine setup can be summarized as a list of the following +type registration methods: + +```cpp +preregister_module_types(); +preregister_server_types(); +register_core_singletons(); +register_server_types(); +register_scene_types(); +EditorNode::register_editor_types(); +register_platform_apis(); +register_module_types(); +initialize_physics(); +initialize_navigation_server(); +register_server_singletons(); +register_driver_types(); +ScriptServer::init_languages(); + +``` + +Our ``Summator`` class is initialized during the ``register_module_types()`` +call. Imagine that we need to satisfy some common module runtime dependency +(like singletons), or allow us to override existing engine method callbacks +before they can be assigned by the engine itself. In that case, we want to +ensure that our module classes are registered *before* any other built-in type. + +This is where we can define an optional ``preregister_summator_types()`` +method which will be called before anything else during the +``preregister_module_types()`` engine setup stage. + +We now need to add this method to ``register_types`` header and source files: + +```cpp +#define MODULE_SUMMATOR_HAS_PREREGISTER +void preregister_summator_types(); + +void register_summator_types(); +void unregister_summator_types(); + +``` + +:::note +Unlike other register methods, we have to explicitly define +``MODULE_SUMMATOR_HAS_PREREGISTER`` to let the build system know what +relevant method calls to include at compile time. The module's name +has to be converted to uppercase as well. + +::: + +```cpp +#include "register_types.h" + +#include "core/object/class_db.h" +#include "summator.h" + +void preregister_summator_types() { + // Called before any other core types are registered. + // Nothing to do here in this example. +} + +void register_summator_types() { + ClassDB::register_class(); +} + +void unregister_summator_types() { + // Nothing to do here in this example. +} + +``` + +## Improving the build system for development + +:::warning + +This shared library support is not designed to support distributing a module +to other users without recompiling the engine. For that purpose, use +a GDExtension instead. + +::: + +So far, we defined a clean SCsub that allows us to add the sources +of our new module as part of the Redot binary. + +This static approach is fine when we want to build a release version of our +game, given we want all the modules in a single binary. + +However, the trade-off is that every single change requires a full recompilation of the +game. Even though SCons is able to detect and recompile only the file that was +changed, finding such files and eventually linking the final binary takes a long time. + +The solution to avoid such a cost is to build our own module as a shared +library that will be dynamically loaded when starting our game's binary. + +```python +Import('env') + +sources = [ + "register_types.cpp", + "summator.cpp" +] + +# First, create a custom env for the shared library. +module_env = env.Clone() + +# Position-independent code is required for a shared library. +module_env.Append(CCFLAGS=['-fPIC']) + +# Don't inject Redot's dependencies into our shared library. +module_env['LIBS'] = [] + +# Define the shared library. By default, it would be built in the module's +# folder, however it's better to output it into `bin` next to the +# Redot binary. +shared_lib = module_env.SharedLibrary(target='#bin/summator', source=sources) + +# Finally, notify the main build environment it now has our shared library +# as a new dependency. + +# LIBPATH and LIBS need to be set on the real "env" (not the clone) +# to link the specified libraries to the Redot executable. + +env.Append(LIBPATH=['#bin']) + +# SCons wants the name of the library with it custom suffixes +# (e.g. ".linuxbsd.tools.64") but without the final ".so". +shared_lib_shim = shared_lib[0].name.rsplit('.', 1)[0] +env.Append(LIBS=[shared_lib_shim]) + +``` + +Once compiled, we should end up with a ``bin`` directory containing both the +``Redot*`` binary and our ``libsummator*.so``. However given the .so is not in +a standard directory (like ``/usr/lib``), we have to help our binary find it +during runtime with the ``LD_LIBRARY_PATH`` environment variable: + +```shell +export LD_LIBRARY_PATH="$PWD/bin/" +./bin/Redot* + +``` + +:::note + +You have to ``export`` the environment variable. Otherwise, +you won't be able to run your project from the editor. + +::: + +On top of that, it would be nice to be able to select whether to compile our +module as shared library (for development) or as a part of the Redot binary +(for release). To do that we can define a custom flag to be passed to SCons +using the ``ARGUMENT`` command: + +```python +Import('env') + +sources = [ + "register_types.cpp", + "summator.cpp" +] + +module_env = env.Clone() +module_env.Append(CCFLAGS=['-O2']) + +if ARGUMENTS.get('summator_shared', 'no') == 'yes': + # Shared lib compilation + module_env.Append(CCFLAGS=['-fPIC']) + module_env['LIBS'] = [] + shared_lib = module_env.SharedLibrary(target='#bin/summator', source=sources) + shared_lib_shim = shared_lib[0].name.rsplit('.', 1)[0] + env.Append(LIBS=[shared_lib_shim]) + env.Append(LIBPATH=['#bin']) +else: + # Static compilation + module_env.add_source_files(env.modules_sources, sources) + +``` + +Now by default ``scons`` command will build our module as part of Redot's binary +and as a shared library when passing ``summator_shared=yes``. + +Finally, you can even speed up the build further by explicitly specifying your +shared module as target in the SCons command: + +```shell +scons summator_shared=yes platform=linuxbsd bin/libsummator.linuxbsd.tools.64.so + +``` + +## Writing custom documentation + +Writing documentation may seem like a boring task, but it is highly recommended +to document your newly created module to make it easier for users to benefit +from it. Not to mention that the code you've written one year ago may become +indistinguishable from the code that was written by someone else, so be kind to +your future self! + +There are several steps in order to setup custom docs for the module: + +1. Make a new directory in the root of the module. The directory name can be + anything, but we'll be using the ``doc_classes`` name throughout this section. + +2. Now, we need to edit ``config.py``, add the following snippet: + +```python +def get_doc_path(): + return "doc_classes" + +def get_doc_classes(): + return [ + "Summator", + ] + +``` + +The ``get_doc_path()`` function is used by the build system to determine +the location of the docs. In this case, they will be located in the +``modules/summator/doc_classes`` directory. If you don't define this, +the doc path for your module will fall back to the main ``doc/classes`` +directory. + +The ``get_doc_classes()`` method is necessary for the build system to +know which registered classes belong to the module. You need to list all of your +classes here. The classes that you don't list will end up in the +main ``doc/classes`` directory. + +:::tip + +You can use Git to check if you have missed some of your classes by checking the +untracked files with ``git status``. For example + +``` +git status + +``` + +Example output + +``` +Untracked files: + (use "git add ..." to include in what will be committed) + + doc/classes/MyClass2D.xml + doc/classes/MyClass4D.xml + doc/classes/MyClass5D.xml + doc/classes/MyClass6D.xml + ... + +``` + +::: + +3. Now we can generate the documentation: + +We can do this via running Redot's doctool i.e. `[Redot --doctool](path)`, +which will dump the engine API reference to the given ``<path>`` in XML format. + +In our case we'll point it to the root of the cloned repository. You can point it +to an another folder, and just copy over the files that you need. + +Run command: + +``` +bin/ --doctool . + +``` + +Now if you go to the ``Redot/modules/summator/doc_classes`` folder, you will see +that it contains a ``Summator.xml`` file, or any other classes, that you referenced +in your ``get_doc_classes`` function. + +Edit the file(s) following [doc_class_reference_primer](../../Documentation/class_reference_primer.md) and recompile the engine. + +Once the compilation process is finished, the docs will become accessible within +the engine's built-in documentation system. + +In order to keep documentation up-to-date, all you'll have to do is simply modify +one of the XML files and recompile the engine from now on. + +If you change your module's API, you can also re-extract the docs, they will contain +the things that you previously added. Of course if you point it to your Redot +folder, make sure you don't lose work by extracting older docs from an older engine build +on top of the newer ones. + +Note that if you don't have write access rights to your supplied ``<path>``, +you might encounter an error similar to the following: + +```console +ERROR: Can't write doc file: docs/doc/classes/@GDScript.xml + At: editor/doc/doc_data.cpp:956 + +``` + +## Writing custom unit tests + +It's possible to write self-contained unit tests as part of a C++ module. If you +are not familiar with the unit testing process in Redot yet, please refer to +[doc_unit_testing](unit_testing.md). + +The procedure is the following: + +1. Create a new directory named ``tests/`` under your module's root: + +```console +cd modules/summator +mkdir tests +cd tests + +``` + +2. Create a new test suite: ``test_summator.h``. The header must be prefixed + with ``test_`` so that the build system can collect it and include it as part + of the ``tests/test_main.cpp`` where the tests are run. + +3. Write some test cases. Here's an example: + +```cpp +#ifndef TEST_SUMMATOR_H +#define TEST_SUMMATOR_H + +#include "tests/test_macros.h" + +#include "modules/summator/summator.h" + +namespace TestSummator { + +TEST_CASE("[Modules][Summator] Adding numbers") { + Ref s = memnew(Summator); + CHECK(s->get_total() == 0); + + s->add(10); + CHECK(s->get_total() == 10); + + s->add(20); + CHECK(s->get_total() == 30); + + s->add(30); + CHECK(s->get_total() == 60); + + s->reset(); + CHECK(s->get_total() == 0); +} + +} // namespace TestSummator + +#endif // TEST_SUMMATOR_H + +``` + +4. Compile the engine with ``scons tests=yes``, and run the tests with the + following command: + +```console +./bin/ --test --source-file="*test_summator*" --success + +``` + +You should see the passing assertions now. + +## Adding custom editor icons + +Similarly to how you can write self-contained documentation within a module, +you can also create your own custom icons for classes to appear in the editor. + +For the actual process of creating editor icons to be integrated within the engine, +please refer to [doc_editor_icons](../editor/creating_icons.md) first. + +Once you've created your icon(s), proceed with the following steps: + +1. Make a new directory in the root of the module named ``icons``. This is the + default path for the engine to look for module's editor icons. + +2. Move your newly created ``svg`` icons (optimized or not) into that folder. + +3. Recompile the engine and run the editor. Now the icon(s) will appear in + editor's interface where appropriate. + +If you'd like to store your icons somewhere else within your module, +add the following code snippet to ``config.py`` to override the default path: + +```python +def get_icons_path(): + return "path/to/icons" + +``` + +## Summing up + +Remember to: + +- Use ``GDCLASS`` macro for inheritance, so Redot can wrap it. +- Use ``_bind_methods`` to bind your functions to scripting, and to + allow them to work as callbacks for signals. +- **Avoid multiple inheritance for classes exposed to Redot**, as ``GDCLASS`` + doesn't support this. You can still use multiple inheritance in your own + classes as long as they're not exposed to Redot's scripting API. + +But this is not all, depending what you do, you will be greeted with +some (hopefully positive) surprises. + +- If you inherit from [class_Node](/docs/Classes/Node) (or any derived node type, such as + Sprite2D), your new class will appear in the editor, in the inheritance + tree in the "Add Node" dialog. +- If you inherit from [class_Resource](/docs/Classes/Resource), it will appear in the resource + list, and all the exposed properties can be serialized when + saved/loaded. +- By this same logic, you can extend the Editor and almost any area of + the engine. \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/core_and_modules/custom_platform_ports.md b/Redot-Documentation/docs/Contributing/Development/core_and_modules/custom_platform_ports.md new file mode 100644 index 0000000..36aafb4 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/core_and_modules/custom_platform_ports.md @@ -0,0 +1,189 @@ + +# Custom platform ports + +Similar to [doc_custom_modules_in_cpp](custom_modules_in_cpp.md), Redot's multi-platform architecture +is designed in a way that allows creating platform ports without modifying any +existing source code. + +An example of a custom platform port distributed independently from the engine +is [FRT](https://github.com/efornara/frt), which targets single-board +computers. Note that this platform port currently targets Redot 3.x; therefore, +it does not use the [class_DisplayServer](/docs/Classes/DisplayServer) abstraction that is new in Redot 4. + +Some reasons to create custom platform ports might be: + +- You want to [port your game to consoles](../../../tutorials/platform/consoles.md), but wish to + write the platform layer yourself. This is a long and arduous process, as it + requires signing NDAs with console manufacturers, but it allows you to have + full control over the console porting process. +- You want to port Redot to an exotic platform that isn't currently supported. + +If you have questions about creating a custom platform port, feel free to ask in +the ``#platforms`` channel of the +[Redot Contributors Chat](https://chat.redotengine.org/channel/platforms). + +:::note + +Redot is a modern engine with modern requirements. Even if you only +intend to run simple 2D projects on the target platform, it still requires +an amount of memory that makes it unviable to run on most retro consoles. +For reference, in Redot 4, an empty project with nothing visible requires +about 100 MB of RAM to run on Linux (50 MB in headless mode). + +If you want to run Redot on heavily memory-constrained platforms, older +Redot versions have lower memory requirements. The porting process is +similar, with the exception of [class_DisplayServer](/docs/Classes/DisplayServer) not being split +from the [class_OS](/docs/Classes/OS) singleton. + +::: + +## Official platform ports + +The official platform ports can be used as a reference when creating a custom platform port: + +- [Windows](https://github.com/redot-engine/redot-engine/tree/master/platform/windows) +- [macOS](https://github.com/redot-engine/redot-engine/tree/master/platform/macos) +- [Linux/\*BSD](https://github.com/redot-engine/redot-engine/tree/master/platform/linuxbsd) +- [Android](https://github.com/redot-engine/redot-engine/tree/master/platform/android) +- [iOS](https://github.com/redot-engine/redot-engine/tree/master/platform/ios) +- [Web](https://github.com/redot-engine/redot-engine/tree/master/platform/web) + +While platform code is usually self-contained, there are exceptions to this +rule. For instance, audio drivers that are shared across several platforms and +rendering drivers are located in the +[drivers/ folder](https://github.com/redot-engine/redot-engine/tree/master/drivers) +of the Redot source code. + +## Creating a custom platform port + +Creating a custom platform port is a large undertaking which requires prior +knowledge of the platform's SDKs. Depending on what features you need, the +amount of work needed varies: + +### Required features of a platform port + +At the very least, a platform port must have methods from the [class_OS](/docs/Classes/OS) +singleton implemented to be buildable and usable for headless operation. +A ``logo.svg`` (32×32) vector image must also be present within the platform +folder. This logo is displayed in the Export dialog for each export preset +targeting the platform in question. + +See [this implementation](https://github.com/redot-engine/redot-engine/blob/master/platform/linuxbsd/os_linuxbsd.cpp) +for the Linux/\*BSD platform as an example. See also the +[OS singleton header](https://github.com/redot-engine/redot-engine/blob/master/core/os/os.h) +for reference. + +:::note + +If your target platform is UNIX-like, consider inheriting from the ``OS_Unix`` +class to get much of the work done automatically. + +If the platform is not UNIX-like, you might use the +[Windows port](https://github.com/redot-engine/redot-engine/blob/master/platform/windows/os_windows.cpp) +as a reference. + +::: + +**detect.py file** + +A ``detect.py`` file must be created within the platform's folder with all +methods implemented. This file is required for SCons to detect the platform as a +valid option for compiling. See the +[detect.py file](https://github.com/redot-engine/redot-engine/blob/master/platform/linuxbsd/detect.py) +for the Linux/\*BSD platform as an example. + +All methods should be implemented within ``detect.py`` as follows: + +- ``is_active()``: Can be used to temporarily disable building for a platform. + This should generally always return ``True``. +- ``get_name()``: Returns the platform's user-visible name as a string. +- ``can_build()``: Return ``True`` if the host system is able to build for the + target platform, ``False`` otherwise. Do not put slow checks here, as this is + queried when the list of platforms is requested by the user. Use + ``configure()`` for extensive dependency checks instead. +- ``get_opts()``: Returns the list of SCons build options that can be defined by + the user for this platform. +- ``get_flags()``: Returns the list of overridden SCons flags for this platform. +- ``configure()``: Perform build configuration, such as selecting compiler + options depending on SCons options chosen. + +### Optional features of a platform port + +In practice, headless operation doesn't suffice if you want to see anything on +screen and handle input devices. You may also want audio output for most +games. + +*Some links on this list point to the Linux/\*BSD platform implementation as a reference.* + +- One or more [DisplayServers](https://github.com/redot-engine/redot-engine/blob/master/platform/linuxbsd/x11/display_server_x11.cpp), + with the windowing methods implemented. DisplayServer also covers features such + as mouse support, touchscreen support and tablet driver (for pen input). + See the + [DisplayServer singleton header](https://github.com/redot-engine/redot-engine/blob/master/servers/display_server.h) + for reference. + + - For platforms not featuring full windowing support (or if it's not relevant + for the port you are making), most windowing functions can be left mostly + unimplemented. These functions can be made to only check if the window ID is + ``MAIN_WINDOW_ID`` and specific operations like resizing may be tied to the + platform's screen resolution feature (if relevant). Any attempt to create + or manipulate other window IDs can be rejected. +- *If the target platform supports the graphics APIs in question:* Rendering + context for [Vulkan](https://github.com/redot-engine/redot-engine/blob/master/platform/linuxbsd/x11/rendering_context_driver_vulkan_x11.cpp), + [Direct3D 12](https://github.com/redot-engine/redot-engine/blob/master/drivers/d3d12/rendering_context_driver_d3d12.cpp) + [OpenGL 3.3 or OpenGL ES 3.0](https://github.com/redot-engine/redot-engine/blob/master/platform/linuxbsd/x11/gl_manager_x11.cpp). +- Input handlers for [keyboard](https://github.com/redot-engine/redot-engine/blob/master/platform/linuxbsd/x11/key_mapping_x11.cpp) + and [controller](https://github.com/redot-engine/redot-engine/blob/master/platform/linuxbsd/joypad_linux.cpp). +- One or more [audio drivers](https://github.com/redot-engine/redot-engine/blob/master/drivers/pulseaudio/audio_driver_pulseaudio.cpp). + The audio driver can be located in the ``platform/`` folder (this is done for + the Android and Web platforms), or in the ``drivers/`` folder if multiple + platforms may be using this audio driver. See the + [AudioServer singleton header](https://github.com/redot-engine/redot-engine/blob/master/servers/audio_server.h) + for reference. +- [Crash handler](https://github.com/redot-engine/redot-engine/blob/master/platform/linuxbsd/crash_handler_linuxbsd.cpp), + for printing crash backtraces when the game crashes. This allows for easier + troubleshooting on platforms where logs aren't readily accessible. +- [Text-to-speech driver](https://github.com/redot-engine/redot-engine/blob/master/platform/linuxbsd/tts_linux.cpp) + (for accessibility). +- [Export handler](https://github.com/redot-engine/redot-engine/tree/master/platform/linuxbsd/export) + (for exporting from the editor, including [doc_one-click_deploy](../../../tutorials/export/one-click_deploy.md)). + Not required if you intend to export only a PCK from the editor, then run the + export template binary directly by renaming it to match the PCK file. See the + [EditorExportPlatform header](https://github.com/redot-engine/redot-engine/blob/master/editor/export/editor_export_platform.h) + for reference. + ``run_icon.svg`` (16×16) should be present within the platform folder if + [doc_one-click_deploy](../../../tutorials/export/one-click_deploy.md) is implemented for the target platform. This icon + is displayed at the top of the editor when one-click deploy is set up for the + target platform. + +If the target platform doesn't support running Vulkan, Direct3D 12, OpenGL 3.3, +or OpenGL ES 3.0, you have two options: + +- Use a library at runtime to translate Vulkan or OpenGL calls to another graphics API. + For example, [MoltenVK](https://moltengl.com/moltenvk/) is used on macOS + to translate Vulkan to Metal at runtime. +- Create a new renderer from scratch. This is a large undertaking, especially if + you want to support both 2D and 3D rendering with advanced features. + +## Distributing a custom platform port + +:::danger + +Before distributing a custom platform port, make sure you're allowed to +distribute all the code that is being linked against. Console SDKs are +typically under NDAs which prevent redistribution to the public. + +::: + +Platform ports are designed to be as self-contained as possible. Most of the +code can be kept within a single folder located in ``platform/``. Like +[doc_custom_modules_in_cpp](custom_modules_in_cpp.md), this allows for streamlining the build process +by making it possible to ``git clone`` a platform folder within a Redot repository +clone's ``platform/`` folder, then run `[scons platform=](name)`. No other steps are +necessary for building, unless third-party platform-specific dependencies need +to be installed first. + +However, when a custom rendering driver is needed, another folder must be added +in ``drivers/``. In this case, the platform port can be distributed as a fork of +the Redot repository, or as a collection of several folders that can be added +over a Redot Git repository clone. \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/core_and_modules/custom_resource_format_loaders.md b/Redot-Documentation/docs/Contributing/Development/core_and_modules/custom_resource_format_loaders.md new file mode 100644 index 0000000..cdcd0ff --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/core_and_modules/custom_resource_format_loaders.md @@ -0,0 +1,366 @@ + +# Custom resource format loaders + +## Introduction + +ResourceFormatLoader is a factory interface for loading file assets. +Resources are primary containers. When load is called on the same file +path again, the previous loaded Resource will be referenced. Naturally, +loaded resources must be stateless. + +This guide assumes the reader knows how to create C++ modules and Redot +data types. If not, refer to this guide: [doc_custom_modules_in_cpp](custom_modules_in_cpp.md) + +### References + +- [ResourceLoader](/docs/Classes/resourceloader) +- [core/io/resource_loader.cpp](https://github.com/redot-engine/redot-engine/blob/master/core/io/resource_loader.cpp) + +## What for? + +- Adding new support for many file formats +- Audio formats +- Video formats +- Machine learning models + +## What not? + +- Raster images + +ImageFormatLoader should be used to load images. + +### References + +- [core/io/image_loader.h](https://github.com/redot-engine/redot-engine/blob/master/core/io/image_loader.h) + +## Creating a ResourceFormatLoader + +Each file format consist of a data container and a ``ResourceFormatLoader``. + +ResourceFormatLoaders are classes which return all the +necessary metadata for supporting new extensions in Redot. The +class must return the format name and the extension string. + +In addition, ResourceFormatLoaders must convert file paths into +resources with the ``load`` function. To load a resource, ``load`` must +read and handle data serialization. + +```cpp +#ifndef RESOURCE_LOADER_JSON_H +#define RESOURCE_LOADER_JSON_H + +#include "core/io/resource_loader.h" + +class ResourceFormatLoaderJson : public ResourceFormatLoader { + GDCLASS(ResourceFormatLoaderJson, ResourceFormatLoader); +public: + virtual RES load(const String &p_path, const String &p_original_path, Error *r_error = NULL); + virtual void get_recognized_extensions(List *r_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; +#endif // RESOURCE_LOADER_JSON_H + +``` + +```cpp +#include "resource_loader_json.h" + +#include "resource_json.h" + +RES ResourceFormatLoaderJson::load(const String &p_path, const String &p_original_path, Error *r_error) { +Ref json = memnew(JsonResource); + if (r_error) { + *r_error = OK; + } + Error err = json->load_file(p_path); + return json; +} + +void ResourceFormatLoaderJson::get_recognized_extensions(List *r_extensions) const { + if (!r_extensions->find("json")) { + r_extensions->push_back("json"); + } +} + +String ResourceFormatLoaderJson::get_resource_type(const String &p_path) const { + return "Resource"; +} + +bool ResourceFormatLoaderJson::handles_type(const String &p_type) const { + return ClassDB::is_parent_class(p_type, "Resource"); +} + +``` + +## Creating a ResourceFormatSaver + +If you'd like to be able to edit and save a resource, you can implement a +``ResourceFormatSaver``: + +```cpp +#ifndef RESOURCE_SAVER_JSON_H +#define RESOURCE_SAVER_JSON_H + +#include "core/io/resource_saver.h" + +class ResourceFormatSaverJson : public ResourceFormatSaver { + GDCLASS(ResourceFormatSaverJson, ResourceFormatSaver); +public: + virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); + virtual bool recognize(const RES &p_resource) const; + virtual void get_recognized_extensions(const RES &p_resource, List *r_extensions) const; +}; +#endif // RESOURCE_SAVER_JSON_H + +``` + +```cpp +#include "resource_saver_json.h" + +#include "resource_json.h" +#include "scene/resources/resource_format_text.h" + +Error ResourceFormatSaverJson::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { + Ref json = memnew(JsonResource); + Error error = json->save_file(p_path, p_resource); + return error; +} + +bool ResourceFormatSaverJson::recognize(const RES &p_resource) const { + return Object::cast_to(*p_resource) != NULL; +} + +void ResourceFormatSaverJson::get_recognized_extensions(const RES &p_resource, List *r_extensions) const { + if (Object::cast_to(*p_resource)) { + r_extensions->push_back("json"); + } +} + +``` + +## Creating custom data types + +Redot may not have a proper substitute within its [doc_core_types](core_types.md) +or managed resources. Redot needs a new registered data type to +understand additional binary formats such as machine learning models. + +Here is an example of creating a custom datatype: + +```cpp +#ifndef RESOURCE_JSON_H +#define RESOURCE_JSON_H + +#include "core/io/json.h" +#include "core/variant_parser.h" + +class JsonResource : public Resource { + GDCLASS(JsonResource, Resource); + +protected: + static void _bind_methods() { + ClassDB::bind_method(D_METHOD("set_dict", "dict"), &JsonResource::set_dict); + ClassDB::bind_method(D_METHOD("get_dict"), &JsonResource::get_dict); + + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "content"), "set_dict", "get_dict"); + } + +private: + Dictionary content; + +public: + Error load_file(const String &p_path); + Error save_file(const String &p_path, const RES &p_resource); + + void set_dict(const Dictionary &p_dict); + Dictionary get_dict(); +}; +#endif // RESOURCE_JSON_H + +``` + +```cpp +#include "resource_json.h" + +Error JsonResource::load_file(const String &p_path) { + Error error; + FileAccess *file = FileAccess::open(p_path, FileAccess::READ, &error); + if (error != OK) { + if (file) { + file->close(); + } + return error; + } + + String json_string = String(""); + while (!file->eof_reached()) { + json_string += file->get_line(); + } + String error_string; + int error_line; + JSON json; + Variant result; + error = json.parse(json_string, result, error_string, error_line); + if (error != OK) { + return error; + } + + content = Dictionary(result); + file->close(); + return error; + } + + content = Dictionary(result); + return OK; +} + +Error JsonResource::save_file(const String &p_path, const RES &p_resource) { + Error error; + FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &error); + if (error != OK) { + if (file) { + file->close(); + } + return error; + } + + Ref json_ref = p_resource.get_ref_ptr(); + JSON json; + + file->store_string(json.print(json_ref->get_dict(), " ")); + file->close(); + return OK; +} + +void JsonResource::set_dict(const Dictionary &p_dict) { + content = p_dict; +} + +Dictionary JsonResource::get_dict() { + return content; +} + +``` + +### Considerations + +Some libraries may not define certain common routines such as IO handling. +Therefore, Redot call translations are required. + +For example, here is the code for translating ``FileAccess`` +calls into ``std::istream``. + +```cpp +#include "core/io/file_access.h" + +#include +#include + +class RedotFileInStreamBuf : public std::streambuf { + +public: + RedotFileInStreamBuf(FileAccess *fa) { + _file = fa; + } + int underflow() { + if (_file->eof_reached()) { + return EOF; + } else { + size_t pos = _file->get_position(); + uint8_t ret = _file->get_8(); + _file->seek(pos); // Required since get_8() advances the read head. + return ret; + } + } + int uflow() { + return _file->eof_reached() ? EOF : _file->get_8(); + } + +private: + FileAccess *_file; +}; + +``` + +### References + +- [istream](https://cplusplus.com/reference/istream/istream/) +- [streambuf](https://cplusplus.com/reference/streambuf/streambuf/?kw=streambuf) +- [core/io/file_access.h](https://github.com/redot-engine/redot-engine/blob/master/core/io/file_access.h) + +## Registering the new file format + +Redot registers ``ResourcesFormatLoader`` with a ``ResourceLoader`` +handler. The handler selects the proper loader automatically +when ``load`` is called. + +```cpp +void register_json_types(); +void unregister_json_types(); + +``` + +```cpp +#include "register_types.h" + +#include "core/class_db.h" +#include "resource_loader_json.h" +#include "resource_saver_json.h" +#include "resource_json.h" + +static Ref json_loader; +static Ref json_saver; + +void register_json_types() { + ClassDB::register_class(); + + json_loader.instantiate(); + ResourceLoader::add_resource_format_loader(json_loader); + + json_saver.instantiate(); + ResourceSaver::add_resource_format_saver(json_saver); +} + +void unregister_json_types() { + ResourceLoader::remove_resource_format_loader(json_loader); + json_loader.unref(); + + ResourceSaver::remove_resource_format_saver(json_saver); + json_saver.unref(); +} + +``` + +### References + +- [core/io/resource_loader.cpp](https://github.com/redot-engine/redot-engine/blob/master/core/io/resource_loader.cpp) + +## Loading it on GDScript + +Save a file called ``demo.json`` with the following contents and place it in the +project's root folder: + +```json +{ + "savefilename": "demo.json", + "demo": [ + "welcome", + "to", + "Redot", + "resource", + "loaders" + ] +} + +``` + +Then attach the following script to any node + +``` +extends Node + +@onready var json_resource = load("res://demo.json") + +func _ready(): + print(json_resource.get_dict()) +``` diff --git a/Redot-Documentation/docs/Contributing/Development/core_and_modules/files/class_tree.zip b/Redot-Documentation/docs/Contributing/Development/core_and_modules/files/class_tree.zip new file mode 100644 index 0000000..99ccef0 Binary files /dev/null and b/Redot-Documentation/docs/Contributing/Development/core_and_modules/files/class_tree.zip differ diff --git a/Redot-Documentation/docs/Contributing/Development/core_and_modules/godot_architecture_diagram.md b/Redot-Documentation/docs/Contributing/Development/core_and_modules/godot_architecture_diagram.md new file mode 100644 index 0000000..9dfe374 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/core_and_modules/godot_architecture_diagram.md @@ -0,0 +1,8 @@ + +# Redot's architecture diagram + +The following diagram describes the architecture used by Redot, from the +core components down to the abstracted drivers, via the scene +structure and the servers. + +![Image](img/architecture_diagram.jpg) \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/core_and_modules/inheritance_class_tree.md b/Redot-Documentation/docs/Contributing/Development/core_and_modules/inheritance_class_tree.md new file mode 100644 index 0000000..1e44119 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/core_and_modules/inheritance_class_tree.md @@ -0,0 +1,23 @@ +# Inheritance class tree + +## Object + +![Image](img/Object.webp) + +## Reference + +![Image](img/RefCounted.webp) + +## Control + +![Image](img/Control.webp) + +## Node2D + +![Image](img/Node2D.webp) + +## Node3D + +![Image](img/Node3D.webp) + +Source files: :download:[class_tree.zip](files/class_tree.zip). \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/core_and_modules/internal_rendering_architecture.md b/Redot-Documentation/docs/Contributing/Development/core_and_modules/internal_rendering_architecture.md new file mode 100644 index 0000000..e1e1f4d --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/core_and_modules/internal_rendering_architecture.md @@ -0,0 +1,815 @@ + +# Internal rendering architecture + +This page is a high-level overview of Redot 4's internal renderer design. +It does not apply to previous Redot versions. + +The goal of this page is to document design decisions taken to best suit +[Redot's design philosophy](doc_best_practices_for_engine_contributors), +while providing a starting point for new rendering contributors. + +If you have questions about rendering internals not answered here, feel free to +ask in the ``#rendering`` channel of the +[Redot Contributors Chat](https://chat.redotengine.org/channel/rendering). + +:::note + +If you have difficulty understanding concepts on this page, it is +recommended to go through an OpenGL tutorial such as +[LearnOpenGL](https://learnopengl.com/). + +Modern low-level APIs (Vulkan/Direct3D 12/Metal) require intermediate +knowledge of higher-level APIs (OpenGL/Direct3D 11) to be used +effectively. Thankfully, contributors rarely need to work directly with +low-level APIs. Redot's renderers are built entirely on OpenGL and +RenderingDevice, which is our abstraction over Vulkan/Direct3D 12/Metal. + +::: + +## Rendering methods + +### Forward+ + +This is a forward renderer that uses a *clustered* approach to lighting. + +Clustered lighting uses a compute shader to group lights into a 3D frustum +aligned grid. Then, at render time, pixels can lookup what lights affect the +grid cell they are in and only run light calculations for lights that might +affect that pixel. + +This approach can greatly speed up rendering performance on desktop hardware, +but is substantially less efficient on mobile. + +### Mobile + +This is a forward renderer that uses a traditional single-pass approach to lighting. +Internally, it is called **Forward Mobile**. + +Intended for mobile platforms, but can also run on desktop platforms. This +rendering method is optimized to perform well on mobile GPUs. Mobile GPUs have a +very different architecture compared to desktop GPUs due to their unique +constraints around battery usage, heat, and overall bandwidth limitations of +reading and writing data. Compute shaders also have very limited support or +aren't supported at all. As a result, the mobile renderer purely uses +raster-based shaders (fragment/vertex). + +Unlike desktop GPUs, mobile GPUs perform *tile-based rendering*. Instead of +rendering the whole image as a single unit, the image is divided in smaller +tiles that fit within the faster internal memory of the mobile GPU. Each tile is +rendered and then written out to the destination texture. This all happens +automatically on the graphics driver. + +The problem is that this introduces bottlenecks in our traditional approach. For +desktop rendering, we render all opaque geometry, then handle the background, +then transparent geometry, then post-processing. Each pass will need to read the +current result into tile memory, perform its operations and then write it out +again. We then wait for all tiles to be completed before moving on to the next +pass. + +The first important change in the mobile renderer is that the mobile renderer +does not use the RGBA16F texture formats that the desktop renderer does. +Instead, it is using an R10G10B10A2 UNORM texture format. This halves the bandwidth +required and has further improvements as mobile hardware often further optimizes +for 32-bit formats. The tradeoff is that the mobile renderer has limited HDR +capabilities due to the reduced precision and maximum values in the color data. + +The second important change is the use of sub-passes whenever possible. +Sub-passes allows us to perform the rendering steps end-to-end per tile saving +on the overhead introduced by reading from and writing to the tiles between each +rendering pass. The ability to use sub-passes is limited by the inability to +read neighboring pixels, as we're constrained to working within a single tile. + +This limitation of subpasses results in not being able to implement features +such as glow and depth of field efficiently. Similarly, if there is a +requirement to read from the screen texture or depth texture, we must fully +write out the rendering result limiting our ability to use sub-passes. When such +features are enabled, a mix of sub-passes and normal passes are used, and these +features result in a notable performance penalty. + +On desktop platforms, the use of sub-passes won't have any impact on +performance. However, this rendering method can still perform better than +Forward+ in simple scenes thanks to its lower complexity and lower +bandwidth usage. This is especially noticeable on low-end GPUs, integrated +graphics or in VR applications. + +Given its low-end focus, this rendering method does not provide high-end +rendering features such as SDFGI and [doc_volumetric_fog](doc_volumetric_fog). Several +post-processing effects are also not available. + +### Compatibility + +:::note + +This is the only rendering method available when using the OpenGL driver. +This rendering method is not available when using Vulkan, Direct3D 12, or Metal. + +::: + +This is a traditional (non-clustered) forward renderer. Internally, it is called +**GL Compatibility**. It's intended for old GPUs that don't have Vulkan support, +but still works very efficiently on newer hardware. Specifically, it is optimized +for older and lower-end mobile devices. However, many optimizations carry over +making it a good choice for older and lower-end desktop as well. + +Like the Mobile renderer, the Compatibility renderer uses an R10G10B10A2 UNORM +texture for 3D rendering. Unlike the mobile renderer, colors are tonemapped and +stored in sRGB format so there is no HDR support. This avoids the need for a +tonemapping pass and allows us to use the lower bit texture without substantial +banding. + +The Compatibility renderer uses a traditional forward single-pass approach to +drawing objects with lights, but it uses a multi-pass approach to draw lights +with shadows. Specifically, in the first pass, it can draw multiple lights +without shadows and up to one DirectionalLight3D with shadows. In each +subsequent pass, it can draw up to one OmniLight3D, one SpotLight3D and one +DirectionalLight3D with shadows. Lights with shadows will affect the scene +differently than lights without shadows, as the lighting is blended in sRGB space +instead of linear space. This difference in lighting will impact how the scene +looks and needs to be kept in mind when designing scenes for the Compatibility +renderer. + +Given its low-end focus, this rendering method does not provide high-end +rendering features (even less so compared to Mobile). Most +post-processing effects are not available. + +### Why not deferred rendering? + +Forward rendering generally provides a better tradeoff for performance versus +flexibility, especially when a clustered approach to lighting is used. While +deferred rendering can be faster in some cases, it's also less flexible and +requires using hacks to be able to use MSAA. Since games with a less realistic +art style can benefit a lot from MSAA, we chose to go with forward rendering for +Redot 4 (like Redot 3). + +That said, parts of the forward renderer *are* performed with a deferred approach +to allow for some optimizations when possible. This applies to VoxelGI and SDFGI +in particular. + +A clustered deferred renderer may be developed in the future. This renderer +could be used in situations where performance is favored over flexibility. + +## Rendering drivers + +Redot 4 supports the following graphics APIs: + +### Vulkan + +This is the main driver in Redot 4, with most of the development focus going +towards this driver. + +Vulkan 1.0 is required as a baseline, with optional Vulkan 1.1 and 1.2 features +used when available. [volk](https://github.com/zeux/volk) is used as a Vulkan +loader, and +[Vulkan Memory Allocator](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator) +is used for memory management. + +Both the Forward+ and Mobile +[doc_internal_rendering_architecture_methods](doc_internal_rendering_architecture_methods) are supported when using the +Vulkan driver. + +**Vulkan context creation:** + +- [drivers/vulkan/vulkan_context.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/drivers/vulkan/vulkan_context.cpp) + +**Direct3D 12 context creation:** + +- [drivers/d3d12/d3d12_context.cpp](https://github.com/redot-engine/redot-engine/blob/master/drivers/d3d12/d3d12_context.cpp) + +### Direct3D 12 + +Like Vulkan, the Direct3D 12 driver targets modern platforms only. It is +designed to target both Windows and Xbox (whereas Vulkan can't be used directly on Xbox). + +Both the Forward+ and Mobile [doc_internal_rendering_architecture_methods](doc_internal_rendering_architecture_methods) can be +used with Direct3D 12. + +[doc_internal_rendering_architecture_core_shaders](doc_internal_rendering_architecture_core_shaders) are shared with the +Vulkan renderer. Shaders are transpiled from +:abbr:`SPIR-V (Standard Portable Intermediate Representation)` to +:abbr:`DXIL (DirectX Intermediate Language)` using +Mesa NIR ([more information](https://godotengine.org/article/d3d12-adventures-in-shaderland/)). + +**This driver is still experimental and only available in Redot 4.3 and later.** +While Direct3D 12 allows supporting Direct3D-exclusive features on Windows 11 such +as windowed optimizations and Auto HDR, Vulkan is still recommended for most projects. +See the [pull request that introduced Direct3D 12 support](https://github.com/redot-engine/redot-engine/pull/70315) +for more information. + +### Metal + +Redot provides a native Metal driver that works on all Apple Silicon hardware +(macOS ARM). Compared to using the MoltenVK translation layer, this is +significantly faster, particularly in CPU-bound scenarios. + +Both the Forward+ and Mobile [doc_internal_rendering_architecture_methods](doc_internal_rendering_architecture_methods) can be +used with Metal. + +[doc_internal_rendering_architecture_core_shaders](doc_internal_rendering_architecture_core_shaders) are shared with the +Vulkan renderer. Shaders are transpiled from GLSL to :abbr:`MSL (Metal Shading Language)` +using SPIRV-Cross. + +Redot also supports Metal rendering via [MoltenVK](https://github.com/KhronosGroup/MoltenVK), +which is used as a fallback when native Metal support is not available (e.g. on x86 macOS). + +**This driver is still experimental and only available in Redot 4.4 and later.** +See the [pull request that introduced Metal support](https://github.com/redot-engine/redot-engine/pull/88199) +for more information. + +### OpenGL + +This driver uses OpenGL ES 3.0 and targets legacy and low-end devices that don't +support Vulkan. OpenGL 3.3 Core Profile is used on desktop platforms to run this +driver, as most graphics drivers on desktop don't support OpenGL ES. +WebGL 2.0 is used for web exports. + +It is possible to use OpenGL ES 3.0 directly on desktop platforms +by passing the ``--rendering-driver opengl3_es`` command line argument, although this +will only work on graphics drivers that feature native OpenGL ES support (such +as Mesa). + +Only the [doc_internal_rendering_architecture_compatibility](doc_internal_rendering_architecture_compatibility) rendering +method can be used with the OpenGL driver. + +[doc_internal_rendering_architecture_core_shaders](doc_internal_rendering_architecture_core_shaders) are entirely different +from the Vulkan renderer. + +Many advanced features are not supported with this driver, as it targets low-end +devices first and foremost. + +### Summary of rendering drivers/methods + +The following rendering API + rendering method combinations are currently possible: + +- Vulkan + Forward+ (optionally through MoltenVK on macOS and iOS) +- Vulkan + Mobile (optionally through MoltenVK on macOS and iOS) +- Direct3D 12 + Forward+ +- Direct3D 12 + Mobile +- Metal + Forward+ +- Metal + Mobile +- OpenGL + Compatibility (optionally through ANGLE on Windows and macOS) + +Each combination has its own limitations and performance characteristics. Make +sure to test your changes on all rendering methods if possible before opening a +pull request. + +## RenderingDevice abstraction + +:::note + +The OpenGL driver does not use the RenderingDevice abstraction. + +::: + +To make the complexity of modern low-level graphics APIs more manageable, +Redot uses its own abstraction called RenderingDevice. + +This means that when writing code for modern rendering methods, you don't +actually use the Vulkan, Direct3D 12, or Metal APIs directly. While this is still +lower-level than an API like OpenGL, this makes working on the renderer easier, +as RenderingDevice will abstract many API-specific quirks for you. The +RenderingDevice presents a similar level of abstraction as WebGPU. + +**Vulkan RenderingDevice implementation:** + +- [drivers/vulkan/rendering_device_driver_vulkan.cpp](https://github.com/redot-engine/redot-engine/blob/master/drivers/vulkan/rendering_device_driver_vulkan.cpp) + +**Direct3D 12 RenderingDevice implementation:** + +- [drivers/d3d12/rendering_device_driver_d3d12.cpp](https://github.com/redot-engine/redot-engine/blob/master/drivers/d3d12/rendering_device_driver_d3d12.cpp) + +**Metal RenderingDevice implementation:** + +- [drivers/metal/rendering_device_driver_metal.mm](https://github.com/redot-engine/redot-engine/blob/master/drivers/metal/rendering_device_driver_metal.mm) + +## Core rendering classes architecture + +This diagram represents the structure of rendering classes in Redot, including the RenderingDevice abstraction: + +![Image](img/rendering_architecture_diagram.webp) + +[View at full size](https://raw.githubusercontent.com/Redot-engine/Redot-docs/master/contributing/development/core_and_modules/img/rendering_architecture_diagram.webp) + +## Core shaders + +While shaders in Redot projects are written using a +[custom language inspired by GLSL](doc_shading_language), core shaders are +written directly in GLSL. + +These core shaders are embedded in the editor and export template binaries at +compile-time. To see any changes you've made to those GLSL shaders, you need to +recompile the editor or export template binary. + +Some material features such as height mapping, refraction and proximity fade are +not part of core shaders, and are performed in the default BaseMaterial3D using +the Redot shader language instead (not GLSL). This is done by procedurally +generating the required shader code depending on the features enabled in the +material. + +By convention, shader files with ``_inc`` in their name are included in other +GLSL files for better code reuse. Standard GLSL preprocessing is used to achieve +this. + +:::warning + +Core material shaders will be used by every material in the scene – both +with the default BaseMaterial3D and custom shaders. As a result, these +shaders must be kept as simple as possible to avoid performance issues and +ensure shader compilation doesn't become too slow. + +If you use ``if`` branching in a shader, performance may decrease as +:abbr:`VGPR (Vector General-Purpose Register)` usage will increase in the +shader. This happens even if all pixels evaluate to ``true`` or ``false`` in +a given frame. + +If you use ``#if`` preprocessor branching, the number of required shader +versions will increase in the scene. In a worst-case scenario, adding a +single boolean ``#define`` can *double* the number of shader versions that +may need to be compiled in a given scene. In some cases, Vulkan +specialization constants can be used as a faster (but more limited) +alternative. + +This means there is a high barrier to adding new built-in material features +in Redot, both in the core shaders and BaseMaterial3D. While BaseMaterial3D +can make use of dynamic code generation to only include the shader code if +the feature is enabled, it'll still require generating more shader versions +when these features are used in a project. This can make shader compilation +stutter more noticeable in complex 3D scenes. + +See +[The Shader Permutation Problem](https://therealmjp.github.io/posts/shader-permutations-part1/) +and +[Branching on a GPU](https://medium.com/@jasonbooth_86226/branching-on-a-gpu-18bfc83694f2) +blog posts for more information. + +::: + +**Core GLSL material shaders:** + +- Forward+: [servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl) +- Mobile: [servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl) +- Compatibility: [drivers/gles3/shaders/scene.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/drivers/gles3/shaders/scene.glsl) + +**Material shader generation:** + +- [scene/resources/material.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/scene/resources/material.cpp) + +**Other GLSL shaders for Forward+ and Mobile rendering methods:** + +- [servers/rendering/renderer_rd/shaders/](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/) +- [modules/lightmapper_rd/](https://github.com/redot-engine/redot-engine/blob/4.2/modules/lightmapper_rd) + +**Other GLSL shaders for the Compatibility rendering method:** + +- [drivers/gles3/shaders/](https://github.com/redot-engine/redot-engine/blob/4.2/drivers/gles3/shaders/) + +## 2D and 3D rendering separation + +:::note + +The following is only applicable in the Forward+ and Mobile +rendering methods, not in Compatibility. Multiple Viewports can be used to +emulate this when using the Compatibility renderer, or to perform 2D +resolution scaling. + +::: + +2D and 3D are rendered to separate buffers, as 2D rendering in Redot is performed +in :abbr:`LDR (Low Dynamic Range)` sRGB-space while 3D rendering uses +:abbr:`HDR (High Dynamic Range)` linear space. + +The color format used for 2D rendering is RGB8 (RGBA8 if the **Transparent** +property on the Viewport is enabled). 3D rendering uses a 24-bit unsigned +normalized integer depth buffer, or 32-bit signed floating-point if a 24-bit +depth buffer is not supported by the hardware. 2D rendering does not use a depth +buffer. + +3D resolution scaling is performed differently depending on whether bilinear or +FSR 1.0 scaling is used. When bilinear scaling is used, no special upscaling +shader is run. Instead, the viewport's texture is stretched and displayed with a +linear sampler (which makes the filtering happen directly on the hardware). This +allows maximizing the performance of bilinear 3D scaling. + +The ``configure()`` function in RenderSceneBuffersRD reallocates the 2D/3D +buffers when the resolution or scaling changes. + +Dynamic resolution scaling isn't supported yet, but is planned in a future Redot +release. + +**2D and 3D rendering buffer configuration C++ code:** + +- [servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp) + +**FSR 1.0:** + +- [servers/rendering/renderer_rd/effects/fsr.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/effects/fsr.cpp) +- [thirdparty/amd-fsr/](https://github.com/redot-engine/redot-engine/tree/master/thirdparty/amd-fsr) + +## 2D rendering techniques + +2D light rendering is performed in a single pass to allow for better performance +with large amounts of lights. + +The Forward+ and Mobile rendering methods don't feature 2D batching yet, but +it's planned for a future release. + +The Compatibility renderer features 2D batching to improve performance, which is +especially noticeable with lots of text on screen. + +MSAA can be enabled in 2D to provide "automatic" line and polygon antialiasing, +but FXAA does not affect 2D rendering as it's calculated before 2D rendering +begins. Redot's 2D drawing methods such as the Line2D node or some CanvasItem +``draw_*()`` methods provide their own way of antialiasing based on triangle +strips and vertex colors, which don't require MSAA to work. + +A 2D signed distance field representing LightOccluder2D nodes in the viewport is +automatically generated if a user shader requests it. This can be used for +various effects in custom shaders, such as 2D global illumination. It is also +used to calculate particle collisions in 2D. + +**2D SDF generation GLSL shader:** + +- [servers/rendering/renderer_rd/shaders/canvas_sdf.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/canvas_sdf.glsl) + +## 3D rendering techniques + +### Batching and instancing + +In the Forward+ renderer, Vulkan instancing is used to group rendering +of identical objects for performance. This is not as fast as static mesh +merging, but it still allows instances to be culled individually. + +### Light, decal and reflection probe rendering + +:::note + +Decal rendering is currently not available in the Compatibility renderer. + +::: + +The Forward+ renderer uses clustered lighting. This +allows using as many lights as you want; performance largely depends on screen +coverage. Shadow-less lights can be almost free if they don't occupy much space +on screen. + +All rendering methods also support rendering up to 8 directional lights at the +same time (albeit with lower shadow quality when more than one light has shadows +enabled). + +The Mobile renderer uses a single-pass lighting approach, with a +limitation of 8 OmniLights + 8 SpotLights affecting each Mesh *resource* (plus a +limitation of 256 OmniLights + 256 SpotLights in the camera view). These limits +are hardcoded and can't be adjusted in the project settings. + +The Compatibility renderer uses a hybrid single-pass + multi-pass lighting +approach. Lights without shadows are rendered in a single pass. Lights with +shadows are rendered in multiple passes. This is required for performance +reasons on mobile devices. As a result, performance does not scale well with +many shadow-casting lights. It is recommended to only have a handful of lights +with shadows in the camera frustum at a time and for those lights to be spread +apart so that each object is only touched by 1 or 2 shadowed lights at a time. +The maximum number of lights visible at once can be adjusted in the project +settings. + +In all 3 methods, lights without shadows are much cheaper than lights with +shadows. To improve performance, lights are only updated when the light is +modified or when objects in its radius are modified. Redot currently doesn't +separate static shadow rendering from dynamic shadow rendering, but this is +planned in a future release. + +Clustering is also used for reflection probes and decal rendering in the +Forward+ renderer. + +### Shadow mapping + +Both Forward+ and Mobile methods use +:abbr:`PCF (Percentage Closer Filtering)` to filter shadow maps and create a +soft penumbra. Instead of using a fixed PCF pattern, these methods use a vogel +disk pattern which allows for changing the number of samples and smoothly +changing the quality. + +Redot also supports percentage-closer soft shadows (PCSS) for more realistic +shadow penumbra rendering. PCSS shadows are limited to the Forward+ renderer +as they're too demanding to be usable in the Mobile renderer. +PCSS also uses a vogel-disk shaped kernel. + +Additionally, both shadow-mapping techniques rotate the kernel on a per-pixel +basis to help soften under-sampling artifacts. + +The Compatibility renderer supports shadow mapping for DirectionalLight3D, +OmniLight3D, and SpotLight3D lights. + +### Temporal antialiasing + +:::note + +Only available in the Forward+ renderer, not the Mobile or Compatibility renderers. + +::: + +Redot uses a custom TAA implementation based on the old TAA implementation from +[Spartan Engine](https://github.com/PanosK92/SpartanEngine). + +Temporal antialiasing requires motion vectors to work. If motion vectors +are not correctly generated, ghosting will occur when the camera or objects move. + +Motion vectors are generated on the GPU in the main material shader. This is +done by running the vertex shader corresponding to the previous rendered frame +(with the previous camera transform) in addition to the vertex shader for the +current rendered frame, then storing the difference between them in a color buffer. + +Alternatively, FSR 2.2 can be used as an upscaling solution that also provides +its own temporal antialiasing algorithm. FSR 2.2 is implemented on top of the +RenderingDevice abstraction as opposed to using AMD's reference code directly. + +**TAA resolve:** + +- [servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl) + +**FSR 2.2:** + +- [servers/rendering/renderer_rd/effects/fsr2.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/effects/fsr2.cpp) +- [servers/rendering/renderer_rd/shaders/effects/fsr2/](https://github.com/redot-engine/redot-engine/tree/master/servers/rendering/renderer_rd/shaders/effects/fsr2) +- [thirdparty/amd-fsr2/](https://github.com/redot-engine/redot-engine/tree/master/thirdparty/amd-fsr2) + +### Global illumination + +:::note + +VoxelGI and SDFGI are only available in the Forward+ renderer, not the +Mobile or Compatibility renderers. + +LightmapGI *baking* is only available in the Forward+ and Mobile renderers, +and can only be performed within the editor (not in an exported +project). LightmapGI *rendering* is supported by the Compatibility renderer. + +::: + +Redot supports voxel-based GI (VoxelGI), signed distance field GI (SDFGI) and +lightmap baking and rendering (LightmapGI). These techniques can be used +simultaneously if desired. + +Lightmap baking happens on the GPU using Vulkan compute shaders. The GPU-based +lightmapper is implemented in the LightmapperRD class, which inherits from the +Lightmapper class. This allows for implementing additional lightmappers, paving +the way for a future port of the CPU-based lightmapper present in Redot 3.x. +This would allow baking lightmaps while using the Compatibility renderer. + +**Core GI C++ code:** + +- [servers/rendering/renderer_rd/environment/gi.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/environment/gi.cpp) +- [scene/3d/voxel_gi.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/scene/3d/voxel_gi.cpp) - VoxelGI node +- [editor/plugins/voxel_gi_editor_plugin.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/editor/plugins/voxel_gi_editor_plugin.cpp) - Editor UI for the VoxelGI node + +**Core GI GLSL shaders:** + +- [servers/rendering/renderer_rd/shaders/environment/voxel_gi.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/environment/voxel_gi.glsl) +- [servers/rendering/renderer_rd/shaders/environment/voxel_gi_debug.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/environment/voxel_gi_debug.glsl) - VoxelGI debug draw mode +- [servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl) - SDFGI Cascades debug draw mode +- [servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl) - SDFGI Probes debug draw mode +- [servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl) +- [servers/rendering/renderer_rd/shaders/environment/sdfgi_preprocess.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/environment/sdfgi_preprocess.glsl) +- [servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl) + +**Lightmapper C++ code:** + +- [scene/3d/lightmap_gi.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/scene/3d/lightmap_gi.cpp) - LightmapGI node +- [editor/plugins/lightmap_gi_editor_plugin.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/editor/plugins/lightmap_gi_editor_plugin.cpp) - Editor UI for the LightmapGI node +- [scene/3d/lightmapper.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/scene/3d/lightmapper.cpp) - Abstract class +- [modules/lightmapper_rd/lightmapper_rd.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/modules/lightmapper_rd/lightmapper_rd.cpp) - GPU-based lightmapper implementation + +**Lightmapper GLSL shaders:** + +- [modules/lightmapper_rd/lm_raster.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/modules/lightmapper_rd/lm_raster.glsl) +- [modules/lightmapper_rd/lm_compute.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/modules/lightmapper_rd/lm_compute.glsl) +- [modules/lightmapper_rd/lm_blendseams.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/modules/lightmapper_rd/lm_blendseams.glsl) + +### Depth of field + +:::note + +Only available in the Forward+ and Mobile renderers, not the +Compatibility renderer. + +::: + +The Forward+ and Mobile renderers use different approaches to DOF rendering, with +different visual results. This is done to best match the performance characteristics +of the target hardware. In Forward+, DOF is performed using a compute shader. In +Mobile, DOF is performed using a fragment shader (raster). + +Box, hexagon and circle bokeh shapes are available (from fastest to slowest). +Depth of field can optionally be jittered every frame to improve its appearance +when temporal antialiasing is enabled. + +**Depth of field C++ code:** + +- [servers/rendering/renderer_rd/effects/bokeh_dof.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/effects/bokeh_dof.cpp) + +**Depth of field GLSL shader (compute - used for Forward+):** + +- [servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl) + +**Depth of field GLSL shader (raster - used for Mobile):** + +- [servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl) + +### Screen-space effects (SSAO, SSIL, SSR, SSS) + +:::note + +Only available in the Forward+ renderer, not the Mobile or Compatibility renderers. + +::: + +The Forward+ renderer supports screen-space ambient occlusion, +screen-space indirect lighting, screen-space reflections and subsurface scattering. + +SSAO uses an implementation derived from Intel's +[ASSAO](https://www.intel.com/content/www/us/en/developer/articles/technical/adaptive-screen-space-ambient-occlusion.html) +(converted to Vulkan). SSIL is derived from SSAO to provide high-performance +indirect lighting. + +When both SSAO and SSIL are enabled, parts of SSAO and SSIL are shared to reduce +the performance impact. + +SSAO and SSIL are performed at half resolution by default to improve performance. +SSR is always performed at half resolution to improve performance. + +**Screen-space effects C++ code:** + +- [servers/rendering/renderer_rd/effects/ss_effects.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/effects/ss_effects.cpp) + +**Screen-space ambient occlusion GLSL shader:** + +- [servers/rendering/renderer_rd/shaders/effects/ssao.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/effects/ssao.glsl) +- [servers/rendering/renderer_rd/shaders/effects/ssao_blur.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/effects/ssao_blur.glsl) +- [servers/rendering/renderer_rd/shaders/effects/ssao_interleave.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/effects/ssao_interleave.glsl) +- [servers/rendering/renderer_rd/shaders/effects/ssao_importance_map.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/effects/ssao_importance_map.glsl) + +**Screen-space indirect lighting GLSL shader:** + +- [servers/rendering/renderer_rd/shaders/effects/ssil.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/effects/ssil.glsl) +- [servers/rendering/renderer_rd/shaders/effects/ssil_blur.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/effects/ssil_blur.glsl) +- [servers/rendering/renderer_rd/shaders/effects/ssil_interleave.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/effects/ssil_interleave.glsl) +- [servers/rendering/renderer_rd/shaders/effects/ssil_importance_map.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/effects/ssil_importance_map.glsl) + +**Screen-space reflections GLSL shader:** + +- [servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl) +- [servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_scale.glsl) +- [servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_filter.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_filter.glsl) + +**Subsurface scattering GLSL:** + +- [servers/rendering/renderer_rd/shaders/effects/subsurface_scattering.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/effects/subsurface_scattering.glsl) + +### Sky rendering + +:::info + +[doc_sky_shader](doc_sky_shader) + +::: + +Redot supports using shaders to render the sky background. The radiance map +(which is used to provide ambient light and reflections for PBR materials) is +automatically updated based on the sky shader. + +The SkyMaterial resources such as ProceduralSkyMaterial, PhysicalSkyMaterial and +PanoramaSkyMaterial generate a built-in shader for sky rendering. This is +similar to what BaseMaterial3D provides for 3D scene materials. + +A detailed technical implementation can be found in the +[Custom sky shaders in Redot 4.0](https://godotengine.org/article/custom-sky-shaders-godot-4-0) +article. + +**Sky rendering C++ code:** + +- [servers/rendering/renderer_rd/environment/sky.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/environment/sky.cpp) - Sky rendering +- [scene/resources/sky.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/scene/resources/sky.cpp) - Sky resource (not to be confused with sky rendering) +- [scene/resources/sky_material.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/scene/resources/sky_material.cpp) SkyMaterial resources (used in the Sky resource) + +**Sky rendering GLSL shader:** + +### Volumetric fog + +:::note + +Only available in the Forward+ renderer, not the Mobile or Compatibility renderers. + +::: + +:::info + +[doc_fog_shader](doc_fog_shader) + +::: + +Redot supports a frustum-aligned voxel (froxel) approach to volumetric fog +rendering. As opposed to a post-processing filter, this approach is more +general-purpose as it can work with any light type. Fog can also use shaders for +custom behavior, which allows animating the fog or using a 3D texture to +represent density. + +The FogMaterial resource generates a built-in shader for FogVolume nodes. This is +similar to what BaseMaterial3D provides for 3D scene materials. + +A detailed technical explanation can be found in the +[Fog Volumes arrive in Redot 4.0](https://godotengine.org/article/fog-volumes-arrive-in-godot-4) +article. + +**Volumetric fog C++ code:** + +- [servers/rendering/renderer_rd/environment/fog.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/environment/fog.cpp) - General volumetric fog +- [scene/3d/fog_volume.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/scene/3d/fog_volume.cpp) - FogVolume node +- [scene/resources/fog_material.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/scene/resources/fog_material.cpp) - FogMaterial resource (used by FogVolume) + +**Volumetric fog GLSL shaders:** + +- [servers/rendering/renderer_rd/shaders/environment/volumetric_fog.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/environment/volumetric_fog.glsl) +- [servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl) + +### Occlusion culling + +While modern GPUs can handle drawing a lot of triangles, the number of draw +calls in complex scenes can still be a bottleneck (even with Vulkan, Direct3D 12, +and Metal). + +Redot 4 supports occlusion culling to reduce overdraw (when the depth prepass +is disabled) and reduce vertex throughput. +This is done by rasterizing a low-resolution buffer on the CPU using +[Embree](https://github.com/embree/embree). The buffer's resolution depends +on the number of CPU threads on the system, as this is done in parallel. +This buffer includes occluder shapes that were baked in the editor or created at +runtime. + +As complex occluders can introduce a lot of strain on the CPU, baked occluders +can be simplified automatically when generated in the editor. + +Redot's occlusion culling doesn't support dynamic occluders yet, but +OccluderInstance3D nodes can still have their visibility toggled or be moved. +However, this will be slow when updating complex occluders this way. Therefore, +updating occluders at runtime is best done only on simple occluder shapes such +as quads or cuboids. + +This CPU-based approach has a few advantages over other solutions, such as +portals and rooms or a GPU-based culling solution: + +- No manual setup required (but can be tweaked manually for best performance). +- No frame delay, which is problematic in cutscenes during camera cuts or when + the camera moves fast behind a wall. +- Works the same on all rendering drivers and methods, with no unpredictable + behavior depending on the driver or GPU hardware. + +Occlusion culling is performed by registering occluder meshes, which is done +using OccluderInstance3D *nodes* (which themselves use Occluder3D *resources*). +RenderingServer then performs occlusion culling by calling Embree in +RendererSceneOcclusionCull. + +**Occlusion culling C++ code:** + +- [scene/3d/occluder_instance_3d.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/scene/3d/occluder_instance_3d.cpp) +- [servers/rendering/renderer_scene_occlusion_cull.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_scene_occlusion_cull.cpp) + +### Visibility range (LOD) + +Redot supports manually authored hierarchical level of detail (HLOD), with +distances specified by the user in the inspector. + +In RenderingSceneCull, the ``_scene_cull()`` and ``_render_scene()`` functions +are where most of the LOD determination happens. Each viewport can render the +same mesh with different LODs (to allow for split screen rendering to look correct). + +**Visibility range C++ code:** + +- [servers/rendering/renderer_scene_cull.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_scene_cull.cpp) + +### Automatic mesh LOD + +The ImporterMesh class is used for the 3D mesh import workflow in the editor. +Its ``generate_lods()`` function handles generating using the +[meshoptimizer](https://meshoptimizer.org/) library. + +LOD mesh generation also generates shadow meshes at the same time. These are +meshes that have their vertices welded regardless of smoothing and materials. +This is used to improve shadow rendering performance by lowering the vertex +throughput required to render shadows. + +The RenderingSceneCull class's ``_render_scene()`` function determines which +mesh LOD should be used when rendering. Each viewport can render the +same mesh with different LODs (to allow for split screen rendering to look correct). + +The mesh LOD is automatically chosen based on a screen coverage metric. This +takes resolution and camera FOV changes into account without requiring user +intervention. The threshold multiplier can be adjusted in the project settings. + +To improve performance, shadow rendering and reflection probe rendering also choose +their own mesh LOD thresholds (which can be different from the main scene rendering). + +**Mesh LOD generation on import C++ code:** + +- [scene/resources/importer_mesh.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/scene/resources/importer_mesh.cpp) + +**Mesh LOD determination C++ code:** + +- [servers/rendering/renderer_scene_cull.cpp](https://github.com/redot-engine/redot-engine/blob/4.2/servers/rendering/renderer_scene_cull.cpp) \ No newline at end of file diff --git a/Redot-Documentation/docs/Contributing/Development/core_and_modules/object_class.md b/Redot-Documentation/docs/Contributing/Development/core_and_modules/object_class.md new file mode 100644 index 0000000..635a879 --- /dev/null +++ b/Redot-Documentation/docs/Contributing/Development/core_and_modules/object_class.md @@ -0,0 +1,328 @@ + +# Object class + +:::info + +This page describes the C++ implementation of objects in Redot. +Looking for the Object class reference? [Have a look here.](/docs/Classes/Object) + +::: + +## General definition + +[Object](/docs/Classes/object) is the base class for almost everything. Most classes in Redot +inherit directly or indirectly from it. Objects provide reflection and +editable properties, and declaring them is a matter of using a single +macro like this: + +```cpp +class CustomObject : public Object { + + GDCLASS(CustomObject, Object); // this is required to inherit +}; + +``` + +This adds a lot of functionality to Objects. For example: + +```cpp +obj = memnew(CustomObject); +print_line("Object class: ", obj->get_class()); // print object class + +obj2 = Object::cast_to(obj); // converting between classes, this also works without RTTI enabled. + +``` + +### References: + +- [core/object/object.h](https://github.com/redot-engine/redot-engine/blob/master/core/object/object.h) + +## Registering an Object + +ClassDB is a static class that holds the entire list of registered +classes that inherit from Object, as well as dynamic bindings to all +their methods properties and integer constants. + +Classes are registered by calling: + +```cpp +ClassDB::register_class() + +``` + +Registering it will allow the class to be instanced by scripts, code, or +creating them again when deserializing. + +Registering as virtual is the same but it can't be instanced. + +```cpp +ClassDB::register_virtual_class() + +``` + +Object-derived classes can override the static function +``static void _bind_methods()``. When one class is registered, this +static function is called to register all the object methods, +properties, constants, etc. It's only called once. If an Object derived +class is instanced but has not been registered, it will be registered as +virtual automatically. + +Inside ``_bind_methods``, there are a couple of things that can be done. +Registering functions is one: + +```cpp +ClassDB::bind_method(D_METHOD("methodname", "arg1name", "arg2name", "arg3name"), &MyCustomType::method); + +``` + +Default values for arguments can be passed as parameters at the end: + +```cpp +ClassDB::bind_method(D_METHOD("methodname", "arg1name", "arg2name", "arg3name"), &MyCustomType::method, DEFVAL(-1), DEFVAL(-2)); // Default values for arg2name (-1) and arg3name (-2). + +``` + +Default values must be provided in the same order as they are declared, +skipping required arguments and then providing default values for the optional ones. +This matches the syntax for declaring methods in C++. + +``D_METHOD`` is a macro that converts "methodname" to a StringName for more +efficiency. Argument names are used for introspection, but when +compiling on release, the macro ignores them, so the strings are unused +and optimized away. + +Check ``_bind_methods`` of Control or Object for more examples. + +If just adding modules and functionality that is not expected to be +documented as thoroughly, the ``D_METHOD()`` macro can safely be ignored and a +string passing the name can be passed for brevity. + +### References: + +- [core/object/class_db.h](https://github.com/redot-engine/redot-engine/blob/master/core/object/class_db.h) + +## Constants + +Classes often have enums such as: + +```cpp +enum SomeMode { + MODE_FIRST, + MODE_SECOND +}; + +``` + +For these to work when binding to methods, the enum must be declared +convertible to int. A macro is provided to help with this: + +```cpp +VARIANT_ENUM_CAST(MyClass::SomeMode); // now functions that take SomeMode can be bound. + +``` + +The constants can also be bound inside ``_bind_methods``, by using: + +```cpp +BIND_CONSTANT(MODE_FIRST); +BIND_CONSTANT(MODE_SECOND); + +``` + +## Properties (set/get) + +Objects export properties, properties are useful for the following: + +- Serializing and deserializing the object. +- Creating a list of editable values for the Object derived class. + +Properties are usually defined by the PropertyInfo() class and +constructed as: + +```cpp +PropertyInfo(type, name, hint, hint_string, usage_flags) + +``` + +For example: + +```cpp +PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "0,49,1", PROPERTY_USAGE_EDITOR) + +``` + +This is an integer property named "amount". The hint is a range, and the range +goes from 0 to 49 in steps of 1 (integers). It is only usable for the editor +(editing the value visually) but won't be serialized. + +Another example: + +```cpp +PropertyInfo(Variant::STRING, "modes", PROPERTY_HINT_ENUM, "Enabled,Disabled,Turbo") + +``` + +This is a string property, can take any string but the editor will only +allow the defined hint ones. Since no usage flags were specified, the +default ones are PROPERTY_USAGE_STORAGE and PROPERTY_USAGE_EDITOR. + +There are plenty of hints and usage flags available in object.h, give them a +check. + +Properties can also work like C# properties and be accessed from script +using indexing, but this usage is generally discouraged, as using +functions is preferred for legibility. Many properties are also bound +with categories, such as "animation/frame" which also make indexing +impossible unless using operator []. + +From ``_bind_methods()``, properties can be created and bound as long as +set/get functions exist. Example: + +```cpp +ADD_PROPERTY(PropertyInfo(Variant::INT, "amount"), "set_amount", "get_amount") + +``` + +This creates the property using the setter and the getter. + +## Binding properties using ``_set``/``_get``/``_get_property_list`` + +An additional method of creating properties exists when more flexibility +is desired (i.e. adding or removing properties on context). + +The following functions can be overridden in an Object derived class, +they are NOT virtual, DO NOT make them virtual, they are called for +every override and the previous ones are not invalidated (multilevel +call). + +```cpp +protected: + void _get_property_list(List *r_props) const; // return list of properties + bool _get(const StringName &p_property, Variant &r_value) const; // return true if property was found + bool _set(const StringName &p_property, const Variant &p_value); // return true if property was found + +``` + +This is also a little less efficient since ``p_property`` must be +compared against the desired names in serial order. + +## Dynamic casting + +Redot provides dynamic casting between Object-derived classes, for +example: + +```cpp +void somefunc(Object *some_obj) { + + Button *button = Object::cast_to