Fix mixed use of tabs and spaces in indented blocks

4-space is our convention for indented blocks and should be used consistently.
The only exception is for C++ code which is tab-indented, as the engine code.
This commit is contained in:
Rémi Verschelde
2018-11-20 11:02:11 +01:00
parent 6274c53536
commit a0e32ac017
31 changed files with 477 additions and 489 deletions

View File

@@ -261,8 +261,6 @@ Description
Resource for environment nodes (like :ref:`WorldEnvironment<class_WorldEnvironment>`) that define multiple environment operations (such as background :ref:`Sky<class_Sky>` or :ref:`Color<class_Color>`, ambient light, fog, depth-of-field...). These parameters affect the final render of the scene. The order of these operations is:
- DOF Blur
- Motion Blur

View File

@@ -85,17 +85,15 @@ Description
This stylebox can be used to achieve all kinds of looks without the need of a texture. Those properties are customizable:
- Color
- Color
- Border width (individual width for each border)
- Border width (individual width for each border)
- Rounded corners (individual radius for each corner)
- Rounded corners (individual radius for each corner)
- Shadow
- Shadow
About corner radius:
Setting corner radius to high values is allowed. As soon as corners would overlap the stylebox will switch to a relative system. Example:
Setting corner radius to high values is allowed. As soon as corners would overlap the stylebox will switch to a relative system. Example:
::
@@ -235,7 +233,7 @@ This sets the amount of vertices used for each corner. Higher values result in r
For corner radius smaller than 10: 4-5 should be enough
For corner radius smaller than 30: 8-12 should be enough ...
For corner radius smaller than 30: 8-12 should be enough
.. _class_StyleBoxFlat_corner_radius_bottom_left:

View File

@@ -171,8 +171,7 @@ Return whether the custom node has an input **sequence** port.
Execute the custom node's logic, returning the index of the output sequence port to use or a :ref:`String<class_String>` when there is an error.
The ``inputs`` array contains the values of the input ports.
The ``inputs`` array contains the values of the input ports.
``outputs`` is an array whose indices should be set to the respective outputs.
@@ -180,6 +179,5 @@ The ``start_mode`` is usually ``START_MODE_BEGIN_SEQUENCE``, unless you have use
``working_mem`` is an array which can be used to persist information between runs of the custom node.
When returning, you can mask the returned value with one of the STEP\_\* constants.
When returning, you can mask the returned value with one of the STEP\_\* constants.

View File

@@ -53,7 +53,7 @@ import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
if on_rtd:
using_rtd_theme = True
using_rtd_theme = True
# Theme options
html_theme_options = {

View File

@@ -38,31 +38,31 @@ This is a simple example of a shader for leaves:
.. code-block:: glsl
shader_type spatial;
render_mode depth_draw_alpha_prepass, cull_disabled, world_vertex_coords;
shader_type spatial;
render_mode depth_draw_alpha_prepass, cull_disabled, world_vertex_coords;
This is a spatial shader. There is no front/back culling (so leaves can be seen from both sides), and alpha prepass is used, so there are less depth arctifacts that result from using transparency (and leaves cast shadow). Finally, for the sway effect, world coordinates are recommended, so the tree can be duplicated, moved, etc. and it will still work together with other trees.
.. code-block:: glsl
uniform sampler2D texture_albedo : hint_albedo;
uniform vec4 transmission : hint_color;
uniform sampler2D texture_albedo : hint_albedo;
uniform vec4 transmission : hint_color;
Here, the texture is read, as well as a transmission color, which is used to add some back-lighting to the leaves, simulating subsurface scattering.
.. code-block:: glsl
uniform float sway_speed = 1.0;
uniform float sway_strength = 0.05;
uniform float sway_phase_len = 8.0;
uniform float sway_speed = 1.0;
uniform float sway_strength = 0.05;
uniform float sway_phase_len = 8.0;
void vertex() {
float strength = COLOR.r * sway_strength;
VERTEX.x += sin(VERTEX.x * sway_phase_len * 1.123 + TIME * sway_speed) * strength;
VERTEX.y += sin(VERTEX.y * sway_phase_len + TIME * sway_speed * 1.12412) * strength;
VERTEX.z += sin(VERTEX.z * sway_phase_len * 0.9123 + TIME * sway_speed * 1.3123) * strength;
}
void vertex() {
float strength = COLOR.r * sway_strength;
VERTEX.x += sin(VERTEX.x * sway_phase_len * 1.123 + TIME * sway_speed) * strength;
VERTEX.y += sin(VERTEX.y * sway_phase_len + TIME * sway_speed * 1.12412) * strength;
VERTEX.z += sin(VERTEX.z * sway_phase_len * 0.9123 + TIME * sway_speed * 1.3123) * strength;
}
This is the code to create the sway of the leaves. It's basic (just uses a sinewave multiplying by the time and axis position, but works well). Notice that the strength is multiplied by the color. Every axis uses a different small near 1.0 multiplication factor so axes don't appear in sync.
@@ -71,14 +71,14 @@ Finally all that is left is the fragment shader:
.. code-block:: glsl
void fragment() {
vec4 albedo_tex = texture(texture_albedo,UV);
ALBEDO = albedo_tex.rgb;
ALPHA = albedo_tex.a;
METALLIC = 0.0;
ROUGHNESS = 1.0;
TRANSMISSION = transmission.rgb;
}
void fragment() {
vec4 albedo_tex = texture(texture_albedo,UV);
ALBEDO = albedo_tex.rgb;
ALPHA = albedo_tex.a;
METALLIC = 0.0;
ROUGHNESS = 1.0;
TRANSMISSION = transmission.rgb;
}
And this is pretty much it.

View File

@@ -24,7 +24,7 @@ For 2D games, having the whole 3D engine available usually makes no sense. Becau
::
scons p=windows target=release tools=no disable_3d=yes
scons p=windows target=release tools=no 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
@@ -38,7 +38,7 @@ TextEditor or GraphEdit. They can be disabled using a build flag:
::
scons p=windows target=release tools=no disable_advanced_gui=yes
scons p=windows target=release tools=no disable_advanced_gui=yes
Disabling unwanted modules
--------------------------
@@ -48,7 +48,7 @@ You can see a list of modules with the following command:
::
scons --help
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
@@ -56,7 +56,7 @@ a lot of them:
::
scons p=windows target=release tools=no module_bmp_enabled=no module_bullet_enabled=no module_csg_enabled=no module_dds_enabled=no module_enet_enabled=no module_etc_enabled=no module_gdnative_enabled=no module_gridmap_enabled=no module_hdr_enabled=no module_mbedtls_enabled=no module_mobile_vr_enabled=no module_opus_enabled=no module_pvr_enabled=no module_recast_enabled=no module_regex_enabled=no module_squish_enabled=no module_tga_enabled=no module_thekla_unwrap_enabled=no module_theora_enabled=no module_tinyexr_enabled=no module_vorbis_enabled=no module_webm_enabled=no module_websocket_enabled=no
scons p=windows target=release tools=no module_bmp_enabled=no module_bullet_enabled=no module_csg_enabled=no module_dds_enabled=no module_enet_enabled=no module_etc_enabled=no module_gdnative_enabled=no module_gridmap_enabled=no module_hdr_enabled=no module_mbedtls_enabled=no module_mobile_vr_enabled=no module_opus_enabled=no module_pvr_enabled=no module_recast_enabled=no module_regex_enabled=no module_squish_enabled=no module_tga_enabled=no module_thekla_unwrap_enabled=no module_theora_enabled=no module_tinyexr_enabled=no module_vorbis_enabled=no module_webm_enabled=no module_websocket_enabled=no
Optimizing for size instead of speed
------------------------------------
@@ -66,7 +66,7 @@ To enable this, just set the ``optimize`` flag to ``size``:
::
scons p=windows target=release tools=no optimize=size
scons p=windows target=release tools=no optimize=size
Some platforms such as WebAssembly already use this mode by default.
@@ -80,7 +80,7 @@ and MSVC compilers:
::
scons p=windows target=release tools=no use_lto=yes
scons p=windows target=release tools=no use_lto=yes
Linking becomes much slower with this option, so it should be used only for
release builds.
@@ -92,7 +92,7 @@ If you build from source, remember to strip debug symbols from binaries:
::
strip godot.64
strip godot.64
Using UPX to compress binaries
------------------------------

View File

@@ -201,8 +201,8 @@ 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)
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.

View File

@@ -6,17 +6,17 @@ Custom AudioStreams
Introduction
------------
AudioStream is the base class of all audio emitting objects.
AudioStreamPlayer binds onto an AudioStream to emit PCM data
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
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
This guide assumes the reader knows how to create C++ modules. If not, refer to this guide
:ref:`doc_custom_modules_in_c++`.
References:
@@ -25,8 +25,6 @@ References:
- `servers/audio/audio_stream.h <https://github.com/godotengine/godot/blob/master/servers/audio/audio_stream.h>`__
- `scene/audio/audioplayer.cpp <https://github.com/godotengine/godot/blob/master/scene/audio/audio_player.cpp>`__
What for?
---------
@@ -37,57 +35,58 @@ What for?
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,
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.
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.
.. code:: cpp
/* audiostream_mytone.h */
#include "reference.h"
#include "resource.h"
#include "servers/audio/audio_stream.h
#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<AudioStreamPlayback> instance_playback();
virtual String get_stream_name() const;
void gen_tone(int16_t *, int frames);
virtual float get_length() const { return 0; } //if supported, otherwise return 0
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();
};
.. code:: cpp
/* audiostream_mytone.cpp */
#include "audiostream_mytone.h"
AudioStreamMyTone::AudioStreamMyTone()
: mix_rate(44100), stereo(false), hz(639) {
: mix_rate(44100), stereo(false), hz(639) {
}
Ref<AudioStreamPlayback> AudioStreamMyTone::instance_playback(){
Ref<AudioStreamPlayback> AudioStreamMyTone::instance_playback() {
Ref<AudioStreamPlaybackMyTone> talking_tree;
talking_tree.instance();
talking_tree->base = Ref<AudioStreamMyTone>(this);
@@ -103,16 +102,15 @@ Therefore, playback state must be self contained in AudioStreamPlayback.
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)));
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(){
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:
@@ -124,23 +122,24 @@ References:
Create an AudioStreamPlayback
-----------------------------
AudioStreamPlayer uses ``mix`` callback to obtain PCM data. The callback must match sample rate and fill the buffer.
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.
.. code:: cpp
/* audiostreamplayer_mytone.h */
#include "reference.h"
#include "resource.h"
#include "servers/audio/audio_stream.h"
#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{
enum {
PCM_BUFFER_SIZE = 4096
};
enum {
@@ -148,34 +147,34 @@ Since AudioStreamPlayback is controlled by the audio thread, i/o and dynamic mem
MIX_FRAC_LEN = (1 << MIX_FRAC_BITS),
MIX_FRAC_MASK = MIX_FRAC_LEN - 1,
};
void * pcm_buffer;
void *pcm_buffer;
Ref<AudioStreamMyTone> 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 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
virtual float get_length() const; // if supported, otherwise return 0
AudioStreamPlaybackMyTone();
~AudioStreamPlaybackMyTone();
};
.. code:: cpp
/* audiostreamplayer_mytone.cpp */
#include "audiostreamplayer_mytone.h"
#include "math/math_funcs.h"
#include "print_string.h"
AudioStreamPlaybackMyTone::AudioStreamPlaybackMyTone()
: active(false){
#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);
@@ -187,33 +186,32 @@ Since AudioStreamPlayback is controlled by the audio thread, i/o and dynamic mem
pcm_buffer = NULL;
}
}
void AudioStreamPlaybackMyTone::stop(){
void AudioStreamPlaybackMyTone::stop() {
active = false;
base->reset();
}
void AudioStreamPlaybackMyTone::start(float p_from_pos){
void AudioStreamPlaybackMyTone::start(float p_from_pos) {
seek(p_from_pos);
active = true;
}
void AudioStreamPlaybackMyTone::seek(float p_time){
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){
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;
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;
for(int i = 0; i < p_frames; i++) {
float sample = float(buf[i]) / 32767.0;
p_buffer[i] = AudioFrame(sample, sample);
}
}
@@ -230,23 +228,20 @@ Since AudioStreamPlayback is controlled by the audio thread, i/o and dynamic mem
return active;
}
Resampling
~~~~~~~~~~
Godots AudioServer currently uses 44100 Hz sample rate. When other sample rates are
needed such as 48000, either provide one or use AudioStreamPlaybackResampled.
Godots AudioServer currently uses 44100 Hz sample rate. When other sample rates are
needed such as 48000, either provide one or use AudioStreamPlaybackResampled.
Godot provides cubic interpolation for audio resampling.
Instead of overloading ``mix``, AudioStreamPlaybackResampled uses ``_mix_internal`` to
Instead of overloading ``mix``, AudioStreamPlaybackResampled uses ``_mix_internal`` to
query AudioFrames and ``get_stream_sampling_rate`` to query current mix rate.
.. code:: cpp
#include "reference.h"
#include "resource.h"
#include "core/reference.h"
#include "core/resource.h"
#include "servers/audio/audio_stream.h"
class AudioStreamMyToneResampled;
@@ -254,8 +249,9 @@ query AudioFrames and ``get_stream_sampling_rate`` to query current mix rate.
class AudioStreamPlaybackResampledMyTone : public AudioStreamPlaybackResampled {
GDCLASS(AudioStreamPlaybackResampledMyTone, AudioStreamPlaybackResampled)
friend class AudioStreamMyToneResampled;
private:
enum{
enum {
PCM_BUFFER_SIZE = 4096
};
enum {
@@ -263,35 +259,35 @@ query AudioFrames and ``get_stream_sampling_rate`` to query current mix rate.
MIX_FRAC_LEN = (1 << MIX_FRAC_BITS),
MIX_FRAC_MASK = MIX_FRAC_LEN - 1,
};
void * pcm_buffer;
void *pcm_buffer;
Ref<AudioStreamMyToneResampled> 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 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_length() const; // if supported, otherwise return 0
virtual float get_stream_sampling_rate();
AudioStreamPlaybackResampledMyTone();
~AudioStreamPlaybackResampledMyTone();
};
.. code:: cpp
#include "mytone_audiostream_resampled.h"
#include "math/math_funcs.h"
#include "print_string.h"
AudioStreamPlaybackResampledMyTone::AudioStreamPlaybackResampledMyTone()
: active(false){
#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);
@@ -303,37 +299,36 @@ query AudioFrames and ``get_stream_sampling_rate`` to query current mix rate.
pcm_buffer = NULL;
}
}
void AudioStreamPlaybackResampledMyTone::stop(){
void AudioStreamPlaybackResampledMyTone::stop() {
active = false;
base->reset();
}
void AudioStreamPlaybackResampledMyTone::start(float p_from_pos){
void AudioStreamPlaybackResampledMyTone::start(float p_from_pos) {
seek(p_from_pos);
active = true;
}
void AudioStreamPlaybackResampledMyTone::seek(float p_time){
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){
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;
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;
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(){
float AudioStreamPlaybackResampledMyTone::get_stream_sampling_rate() {
return float(base->mix_rate);
}
int AudioStreamPlaybackResampledMyTone::get_loop_count() const {
@@ -349,13 +344,8 @@ query AudioFrames and ``get_stream_sampling_rate`` to query current mix rate.
return active;
}
References:
~~~~~~~~~~~
- `core/math/audio_frame.h <https://github.com/godotengine/godot/blob/master/core/math/audio_frame.h>`__
- `servers/audio/audio_stream.h <https://github.com/godotengine/godot/blob/master/servers/audio/audio_stream.h>`__
- `scene/audio/audioplayer.cpp <https://github.com/godotengine/godot/blob/master/scene/audio/audio_player.cpp>`__

View File

@@ -19,9 +19,7 @@ References
~~~~~~~~~~~
- `Why does Godot 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?
@@ -45,13 +43,13 @@ an initialization state and a cleanup procedure.
#ifndef HILBERT_HOTEL_H
#define HILBERT_HOTEL_H
#include "object.h"
#include "list.h"
#include "rid.h"
#include "set.h"
#include "variant.h"
#include "os/thread.h"
#include "os/mutex.h"
#include "core/list.h"
#include "core/object.h"
#include "core/os/thread.h"
#include "core/os/mutex.h"
#include "core/rid.h"
#include "core/set.h"
#include "core/variant.h"
class HilbertHotel : public Object {
GDCLASS(HilbertHotel, Object);
@@ -97,19 +95,21 @@ an initialization state and a cleanup procedure.
.. code:: cpp
#include "hilbert_hotel.h"
#include "variant.h"
#include "os/os.h"
#include "list.h"
#include "dictionary.h"
#include "core/dictionary.h"
#include "core/list.h"
#include "core/os/os.h"
#include "core/variant.h"
#include "prime_225.h"
oid HilbertHotel::thread_func(void *p_udata) {
void HilbertHotel::thread_func(void *p_udata) {
HilbertHotel *ac = (HilbertHotel *) p_udata;
uint64_t msdelay = 1000;
while (!ac -> exit_thread) {
if (!ac -> empty()) {
while (!ac->exit_thread) {
if (!ac->empty()) {
ac->lock();
ac->register_rooms();
ac->unlock();
@@ -163,8 +163,8 @@ an initialization state and a cleanup procedure.
_HilbertHotel::get_singleton()->_occupy_room(room, rid);
}
Variant HilbertHotel::get_bus_info(RID id){
InfiniteBus * bus = bus_owner.getornull(id);
Variant HilbertHotel::get_bus_info(RID id) {
InfiniteBus *)bus = bus_owner.getornull(id);
if (bus) {
Dictionary d;
@@ -240,33 +240,35 @@ an initialization state and a cleanup procedure.
/* prime_225.h */
#include "int_types.h"
#include "core/int_types.h"
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};
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
----------------------------
@@ -335,9 +337,11 @@ is used to register the dummy class in GDScript.
/* register_types.cpp */
#include "register_types.h"
#include "class_db.h"
#include "core/class_db.h"
#include "core/engine.h"
#include "hilbert_hotel.h"
#include "engine.h"
static HilbertHotel *hilbert_hotel = NULL;
static _HilbertHotel *_hilbert_hotel = NULL;
@@ -432,7 +436,7 @@ It is possible to emit signals to GDScript by calling the GDScript dummy object.
return HilbertHotel::get_singleton()->delete_bus(rid);
}
void _HilbertHotel::_occupy_room(int room_number, RID bus){
void _HilbertHotel::_occupy_room(int room_number, RID bus) {
emit_signal("occupy_room", room_number, bus);
}
@@ -479,24 +483,24 @@ Here is the GDScript sample code:
.. code::
extends Node
extends Node
func _ready():
print("start Debugging")
HilbertHotel.connect("occupy_room", self, "_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 _ready():
print("Start debugging")
HilbertHotel.connect("occupy_room", self, "_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_num: " + str(room_number) + " rid: " + str(r_id))
print(HilbertHotel.get_bus_info(r_id))
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
~~~~~

View File

@@ -60,7 +60,7 @@ read and handle data serialization.
#ifndef MY_JSON_LOADER_H
#define MY_JSON_LOADER_H
#include "io/resource_loader.h"
#include "core/io/resource_loader.h"
class ResourceFormatLoaderMyJson : public ResourceFormatLoader {
public:
@@ -120,42 +120,43 @@ Here is an example of how to create a custom datatype
#ifndef MY_JSON_H
#define MY_JSON_H
#include "core/dictionary.h"
#include "core/io/json.h"
#include "core/reference.h"
#include "core/variant.h"
#include "reference.h"
#include "variant_parser.h"
#include "io/json.h"
#include "dictionary.h"
#include "core/variant_parser.h"
class MyJson : public Resource{
class MyJson : public Resource {
GDCLASS(MyJson, Resource);
protected:
static void _bind_methods() {
ClassDB::bind_method(D_METHOD("toString"), &MyJson::toString);
ClassDB::bind_method(D_METHOD("to_string"), &MyJson::to_string);
}
private:
Dictionary dict;
public:
Error set_file(const String &p_path){
Error set_file(const String &p_path) {
Error error_file;
FileAccess *file = FileAccess::open(p_path, FileAccess::READ, &error_file);
String buf = String("");
while(!file->eof_reached()){
while (!file->eof_reached()) {
buf += file->get_line();
}
String err_string;
int err_line;
JSON cmd;
Variant ret;
Error err = cmd.parse( buf, ret, err_string, err_line);
Error err = cmd.parse(buf, ret, err_string, err_line);
dict = Dictionary(ret);
file -> close();
file->close();
return OK;
}
String toString() const {
String to_string() const {
return String(*this);
}
@@ -164,15 +165,15 @@ Here is an example of how to create a custom datatype
return a.print(dict);
}
MyJson() {};
~MyJson() {};
MyJson() {};
~MyJson() {};
};
#endif
#endif // MY_JSON_H
Considerations
~~~~~~~~~~~~~~
Some libraries may not define certain common routines such as i/o handling.
Some libraries may not define certain common routines such as IO handling.
Therefore, Godot call translations are required.
For example, here is the code for translating ``FileAccess``
@@ -183,9 +184,10 @@ calls into ``std::istream``.
#include <istream>
#include <streambuf>
class GodotFileInStreamBuf : public std::streambuf{
class GodotFileInStreamBuf : public std::streambuf {
public:
GodotFileInStreamBuf(FileAccess * fa) {
GodotFileInStreamBuf(FileAccess *fa) {
_file = fa;
}
int underflow() {
@@ -194,15 +196,16 @@ calls into ``std::istream``.
} else {
size_t pos = _file->get_position();
uint8_t ret = _file->get_8();
_file->seek(pos); //required since get_8() advances the read head
_file->seek(pos); // required since get_8() advances the read head
return ret;
}
}
int uflow() {
return _file->eof_reached() ? EOF : _file -> get_8();
return _file->eof_reached() ? EOF : _file->get_8();
}
private:
FileAccess * _file;
FileAccess *_file;
};
@@ -224,7 +227,7 @@ when ``load`` is called.
/* register_types.cpp */
#include "register_types.h"
#include "class_db.h"
#include "core/class_db.h"
#include "my_json_loader.h"
#include "my_json.h"
@@ -251,21 +254,21 @@ Loading it on GDScript
.. code::
{
"savefilename" : "demo.mjson",
"demo": [
"welcome",
"to",
"godot",
"resource",
"loaders"
]
}
{
"savefilename" : "demo.mjson",
"demo": [
"welcome",
"to",
"godot",
"resource",
"loaders"
]
}
.. code::
extends Node
extends Node
func _ready():
var myjson = load("res://demo.mjson")
print( myjson.toString())
func _ready():
var myjson = load("res://demo.mjson")
print(myjson.to_string())

View File

@@ -1595,12 +1595,12 @@ into an invalid state, for example:
::
func my_func():
yield(button_func(), "completed")
print("All buttons were pressed, hurray!")
yield(button_func(), "completed")
print("All buttons were pressed, hurray!")
func button_func():
yield($Button0, "pressed")
yield($Button1, "pressed")
yield($Button1, "pressed")
``my_func`` will only continue execution once both the buttons are pressed.

View File

@@ -82,14 +82,14 @@ changed:
velocity = Vector2()
# Remove keyboard controls.
# if Input.is_action_pressed("ui_right"):
# if Input.is_action_pressed("ui_right"):
# velocity.x += 1
# if Input.is_action_pressed("ui_left"):
# velocity.x -= 1
# if Input.is_action_pressed("ui_down"):
# velocity.y += 1
# if Input.is_action_pressed("ui_up"):
# velocity.y -= 1
# if Input.is_action_pressed("ui_left"):
# velocity.x -= 1
# if Input.is_action_pressed("ui_down"):
# velocity.y += 1
# if Input.is_action_pressed("ui_up"):
# velocity.y -= 1
if velocity.length() > 0:
velocity = velocity.normalized() * speed
@@ -99,7 +99,7 @@ changed:
$AnimatedSprite.stop()
$Trail.emitting = false
position += velocity * delta
position += velocity * delta
# We don't need to clamp the player's position
# because you can't click outside the screen.
# position.x = clamp(position.x, 0, screensize.x)

View File

@@ -149,7 +149,7 @@ function:
.. code-tab:: gdscript GDScript
func _my_level_was_completed():
get_tree().change_scene("res://levels/level2.tscn")
get_tree().change_scene("res://levels/level2.tscn")
.. code-tab:: csharp

View File

@@ -40,10 +40,10 @@ Blender. Currently only the diffuse color and a few flags (eg unshaded) are
exported.
.. warning::
Export of Blender materials is currently very primitive. However, it is the
focus of a current GSOC project
Export of Blender materials is currently very primitive. However, it is the
focus of a current GSOC project
.. warning::
Materials are currently exported using their "Blender Render" settings.
When Blender 2.8 is released, this will be removed and this part of the
exporter will change.
Materials are currently exported using their "Blender Render" settings.
When Blender 2.8 is released, this will be removed and this part of the
exporter will change.

View File

@@ -38,9 +38,9 @@ There are the following caveats:
what you want.
.. important::
To build compound physics shapes, parent together multiple objects with
rigid body enabled. The physics properties are taken from the parent-most
rigid body, and the rest are used as collision shapes.
To build compound physics shapes, parent together multiple objects with
rigid body enabled. The physics properties are taken from the parent-most
rigid body, and the rest are used as collision shapes.
Collision Geometry Only

View File

@@ -21,7 +21,7 @@ Features can be queried at run-time from the singleton API by calling:
::
OS.has_feature(name)
OS.has_feature(name)
Default features

View File

@@ -5,7 +5,7 @@ pushd %~dp0
REM Command file for Sphinx documentation on Windows
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
@@ -15,15 +15,15 @@ if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%

View File

@@ -11,7 +11,6 @@ In this part we're going to add grenades to the player, give the player the abil
.. image:: img/PartFiveFinished.png
.. note:: You are assumed to have finished :ref:`doc_fps_tutorial_part_four` before moving on to this part of the tutorial.
The finished project from :ref:`doc_fps_tutorial_part_four` will be the starting project for part 5
Let's get started!
@@ -564,7 +563,7 @@ ______
The last thing we do is check to see whether or not ``grabbed_object`` is equal to ``null``, outside all of the grabbing/throwing related code.
.. note:: While technically not input related, it's easy enough to place the code moving the grabbed object here
because it's only two lines, and then all of the grabbing/throwing code is in one place
because it's only two lines, and then all of the grabbing/throwing code is in one place
If the player is holding a object, we set its global position to the camera's position plus ``OBJECT_GRAB_DISTANCE`` in the direction the camera is facing.
@@ -579,13 +578,13 @@ want the player to be able to change weapons or reload, so change ``_physics_pro
process_input(delta)
process_view_input(delta)
process_movement(delta)
if grabbed_object == null:
process_changing_weapons(delta)
process_reloading(delta)
# Process the UI
process_UI(delta)
# Process the UI
process_UI(delta)
Now the player cannot change weapons or reload while holding an object.
@@ -717,40 +716,40 @@ Add the following to ``Turret.gd``:
func fire_bullet():
if use_raycast == true:
node_raycast.look_at(current_target.global_transform.origin + Vector3(0, PLAYER_HEIGHT, 0), Vector3(0,1,0))
node_raycast.force_raycast_update()
if node_raycast.is_colliding():
var body = node_raycast.get_collider()
if body.has_method("bullet_hit"):
body.bullet_hit(TURRET_DAMAGE_RAYCAST, node_raycast.get_collision_point())
ammo_in_turret -= 1
else:
var clone = bullet_scene.instance()
var scene_root = get_tree().root.get_children()[0]
scene_root.add_child(clone)
clone.global_transform = $Head/Barrel_End.global_transform
clone.scale = Vector3(8, 8, 8)
clone.BULLET_DAMAGE = TURRET_DAMAGE_BULLET
clone.BULLET_SPEED = 60
ammo_in_turret -= 1
node_flash_one.visible = true
node_flash_two.visible = true
flash_timer = FLASH_TIME
fire_timer = FIRE_TIME
if ammo_in_turret <= 0:
ammo_reload_timer = AMMO_RELOAD_TIME
if use_raycast == true:
node_raycast.look_at(current_target.global_transform.origin + Vector3(0, PLAYER_HEIGHT, 0), Vector3(0,1,0))
node_raycast.force_raycast_update()
if node_raycast.is_colliding():
var body = node_raycast.get_collider()
if body.has_method("bullet_hit"):
body.bullet_hit(TURRET_DAMAGE_RAYCAST, node_raycast.get_collision_point())
ammo_in_turret -= 1
else:
var clone = bullet_scene.instance()
var scene_root = get_tree().root.get_children()[0]
scene_root.add_child(clone)
clone.global_transform = $Head/Barrel_End.global_transform
clone.scale = Vector3(8, 8, 8)
clone.BULLET_DAMAGE = TURRET_DAMAGE_BULLET
clone.BULLET_SPEED = 60
ammo_in_turret -= 1
node_flash_one.visible = true
node_flash_two.visible = true
flash_timer = FLASH_TIME
fire_timer = FIRE_TIME
if ammo_in_turret <= 0:
ammo_reload_timer = AMMO_RELOAD_TIME
func body_entered_vision(body):
if current_target == null:

View File

@@ -11,7 +11,6 @@ In this part we will be adding health pick ups, ammo pick ups, targets the playe
.. image:: img/PartFourFinished.png
.. note:: You are assumed to have finished :ref:`doc_fps_tutorial_part_three` before moving on to this part of the tutorial.
The finished project from :ref:`doc_fps_tutorial_part_three` will be the starting project for part 4
Let's get started!

View File

@@ -14,7 +14,6 @@ This is the last part of the FPS tutorial, by the end of this you will have a so
.. image:: img/FinishedTutorialPicture.png
.. note:: You are assumed to have finished :ref:`doc_fps_tutorial_part_five` before moving on to this part of the tutorial.
The finished project from :ref:`doc_fps_tutorial_part_four` will be the starting project for part 6
Let's get started!
@@ -495,9 +494,9 @@ Instead of:
# Capturing/Freeing cursor
if Input.is_action_just_pressed("ui_cancel"):
if Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
else:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
else:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
You will leave only:
@@ -561,7 +560,7 @@ Next we need to make a few changes to ``physics_process``. Change ``physics_proc
::
func _physics_process(delta):
if !is_dead:
process_input(delta)
process_view_input(delta)
@@ -841,8 +840,8 @@ First, open up ``SimpleAudioPlayer.gd`` and change it to the following:
audio_node.stream = audio_stream
# If you are using a AudioPlayer3D, then uncomment these lines to set the position.
# if position != null:
# audio_node.global_transform.origin = position
#if position != null:
# audio_node.global_transform.origin = position
audio_node.play(0.0)

View File

@@ -13,7 +13,6 @@ weapons fire.
.. image:: img/PartThreeFinished.png
.. note:: You are assumed to have finished :ref:`doc_fps_tutorial_part_two` before moving on to this part of the tutorial.
The finished project from :ref:`doc_fps_tutorial_part_two` will be the starting project for part 3
Let's get started!
@@ -516,8 +515,8 @@ and insert the following code:
return
# If you are using a AudioPlayer3D, then uncomment these lines to set the position.
# if position != null:
# audio_node.global_transform.origin = position
#if position != null:
# audio_node.global_transform.origin = position
audio_node.play()

View File

@@ -15,7 +15,6 @@ rifle, and attack using a knife. The player will also now have animations with t
and the weapons will interact with objects in the environment.
.. note:: You are assumed to have finished :ref:`doc_fps_tutorial_part_one` before moving on to this part of the tutorial.
The finished project from :ref:`doc_fps_tutorial_part_one` will be the starting project for part 2
Let's get started!
@@ -1056,7 +1055,7 @@ Now lets add all of the player input code for the weapons in ``process_input``.
if current_weapon != null:
if animation_manager.current_state == current_weapon.IDLE_ANIM_NAME:
animation_manager.set_animation(current_weapon.FIRE_ANIM_NAME)
# ----------------------------------
# ----------------------------------
Lets go over the additions, starting with how we're changing weapons.

View File

@@ -26,30 +26,30 @@ Because of the large difference in performance it often makes sense to re-build
In summary you can use the low-level networking API for maximum control and implement everything on top of bare network protocols or use the high-level API based on :ref:`SceneTree <class_SceneTree>` that does most of the heavy lifting behind the scenes in a generally optimized way.
.. note:: Most of Godot's supported platforms offer all or most of the mentioned high- and low-level networking
features. As networking is always largely hardware and operating system dependent, however,
some features may change or not be available on some target platforms. Most notably,
the HTML5 platform currently only offers WebSocket support and lacks some of the higher level features as
well as raw access to low-level protocols like TCP and UDP.
features. As networking is always largely hardware and operating system dependent, however,
some features may change or not be available on some target platforms. Most notably,
the HTML5 platform currently only offers WebSocket support and lacks some of the higher level features as
well as raw access to low-level protocols like TCP and UDP.
.. note:: More about TCP/IP, UDP, and networking:
https://gafferongames.com/post/udp_vs_tcp/
Gaffer On Games has a lot of useful articles about networking in Games
(`here <https://gafferongames.com/tags/networking>`__), including the comprehensive
`introduction to networking models in games <https://gafferongames.com/post/what_every_programmer_needs_to_know_about_game_networking/>`__.
If you want to use your low-level networking library of choice instead of Godot's built-in networking,
see here for an example:
https://github.com/PerduGames/gdnet3
https://gafferongames.com/post/udp_vs_tcp/
Gaffer On Games has a lot of useful articles about networking in Games
(`here <https://gafferongames.com/tags/networking>`__), including the comprehensive
`introduction to networking models in games <https://gafferongames.com/post/what_every_programmer_needs_to_know_about_game_networking/>`__.
If you want to use your low-level networking library of choice instead of Godot's built-in networking,
see here for an example:
https://github.com/PerduGames/gdnet3
.. warning:: Adding networking to your game comes with some responsibility.
It can make your application vulnerable if done wrong and may lead to cheats or exploits.
It may even allow an attacker to compromise the machines your application runs on
and use your servers to send spam, attack others or steal your users data if they play your game.
This is always the case when networking is involved and has nothing to do with Godot.
You can of course experiment, but when you release a networked application,
always take care of any possible security concerns.
It can make your application vulnerable if done wrong and may lead to cheats or exploits.
It may even allow an attacker to compromise the machines your application runs on
and use your servers to send spam, attack others or steal your users data if they play your game.
This is always the case when networking is involved and has nothing to do with Godot.
You can of course experiment, but when you release a networked application,
always take care of any possible security concerns.
Mid level abstraction
---------------------
@@ -276,7 +276,7 @@ every peer and RPC will work great! Here is an example:
::
remote func pre_configure_game():
var selfPeerID = get_tree().get_network_unique_id()
var selfPeerID = get_tree().get_network_unique_id()
# Load world
var world = load(which_level).instance()
@@ -367,7 +367,7 @@ If you have paid attention to the previous example, it's possible you noticed th
var player = preload("res://player.tscn").instance()
player.set_name(str(p))
get_node("/root/world/players").add_child(player)
[...]
[...]
Each time this piece of code is executed on each peer, the peer makes itself master on the node it controls, and all other nodes remain as puppets with the server being their network master.

View File

@@ -28,14 +28,14 @@ Below is all the code we need to make it work. The URL points to an online API m
extends CanvasLayer
func _ready():
pass
pass
func _on_Button_pressed():
$HTTPRequest.request("http://www.mocky.io/v2/5185415ba171ea3a00704eed")
$HTTPRequest.request("http://www.mocky.io/v2/5185415ba171ea3a00704eed")
func _on_HTTPRequest_request_completed( result, response_code, headers, body ):
var json = JSON.parse(body.get_string_from_utf8())
print(json.result)
var json = JSON.parse(body.get_string_from_utf8())
print(json.result)
With this, you should see ``(hello:world)`` printed on the console; hello being a key, and world being a value, both of them strings.

View File

@@ -398,22 +398,22 @@ the ground (including slopes) and jump when standing on the ground:
var velocity = Vector2()
func get_input():
velocity.x = 0
var right = Input.is_action_pressed('ui_right')
var left = Input.is_action_pressed('ui_left')
var jump = Input.is_action_just_pressed('ui_select')
if is_on_floor() and jump:
velocity.y = jump_speed
if right:
velocity.x += run_speed
if left:
velocity.x -= run_speed
velocity.x = 0
var right = Input.is_action_pressed('ui_right')
var left = Input.is_action_pressed('ui_left')
var jump = Input.is_action_just_pressed('ui_select')
if is_on_floor() and jump:
velocity.y = jump_speed
if right:
velocity.x += run_speed
if left:
velocity.x -= run_speed
func _physics_process(delta):
velocity.y += gravity * delta
get_input()
velocity = move_and_slide(velocity, Vector2(0, -1))
velocity.y += gravity * delta
get_input()
velocity = move_and_slide(velocity, Vector2(0, -1))
.. code-tab:: csharp

View File

@@ -33,16 +33,16 @@ Here is a custom ``look_at()`` function that will work reliably with rigid bodie
extends RigidBody
func look_follow(state, current_transform, target_position):
var up_dir = Vector3(0, 1, 0)
var cur_dir = current_transform.basis.xform(Vector3(0, 0, 1))
var target_dir = (target_position - current_transform.origin).normalized()
var rotation_angle = acos(cur_dir.x) - acos(target_dir.x)
var up_dir = Vector3(0, 1, 0)
var cur_dir = current_transform.basis.xform(Vector3(0, 0, 1))
var target_dir = (target_position - current_transform.origin).normalized()
var rotation_angle = acos(cur_dir.x) - acos(target_dir.x)
state.set_angular_velocity(up_dir * (rotation_angle / state.get_step()))
state.set_angular_velocity(up_dir * (rotation_angle / state.get_step()))
func _integrate_forces(state):
var target_position = $my_target_spatial_node.get_global_transform().origin
look_follow(state, get_global_transform(), target_position)
var target_position = $my_target_spatial_node.get_global_transform().origin
look_follow(state, get_global_transform(), target_position)
.. code-tab:: csharp

View File

@@ -326,8 +326,8 @@ it sets the color as a pure red instead.
::
var material = SpatialMaterial.new()
material.albedo_color = color
var material = SpatialMaterial.new()
material.albedo_color = color
This part makes a new :ref:`SpatialMaterial<class_SpatialMaterial>` that is the
imported resource. We create a new instance of it and then set its albedo color

View File

@@ -27,9 +27,9 @@ the current fragment. As a result, this simple 2D fragment shader:
.. code-block:: glsl
void fragment() {}
COLOR=textureLod( SCREEN_TEXTURE, SCREEN_UV, 0.0);
}
void fragment() {}
COLOR = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0);
}
results in an invisible object, because it just shows what lies behind.
@@ -49,21 +49,21 @@ and saturation:
.. code-block:: glsl
shader_type canvas_item;
shader_type canvas_item;
uniform float brightness = 1.0;
uniform float contrast = 1.0;
uniform float saturation = 1.0;
uniform float brightness = 1.0;
uniform float contrast = 1.0;
uniform float saturation = 1.0;
void fragment() {
vec3 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0).rgb;
void fragment() {
vec3 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0).rgb;
c.rgb = mix(vec3(0.0), c.rgb, brightness);
c.rgb = mix(vec3(0.5), c.rgb, contrast);
c.rgb = mix(vec3(dot(vec3(1.0), c.rgb)*0.33333), c.rgb, saturation);
c.rgb = mix(vec3(0.0), c.rgb, brightness);
c.rgb = mix(vec3(0.5), c.rgb, contrast);
c.rgb = mix(vec3(dot(vec3(1.0), c.rgb) * 0.33333), c.rgb, saturation);
COLOR.rgb = c;
}
COLOR.rgb = c;
}
Behind the scenes
~~~~~~~~~~~~~~~~~
@@ -130,9 +130,9 @@ converted via the inverse projection matrix.
The following code retrieves the 3D position below the pixel being drawn:
.. code-block:: glsl
void fragment() {
float depth = textureLod(DEPTH_TEXTURE,SCREEN_UV,0.0).r;
vec4 upos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV*2.0-1.0,depth*2.0-1.0,1.0);
vec3 pixel_position = upos.xyz/upos.w;
}
void fragment() {
float depth = textureLod(DEPTH_TEXTURE, SCREEN_UV, 0.0).r;
vec4 upos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
vec3 pixel_position = upos.xyz / upos.w;
}

View File

@@ -157,10 +157,10 @@ If a larger matrix is constructed from a smaller matrix, the additional rows and
If a smaller matrix is constructed from a larger matrix, the top, left submatrix of the larger matrix is chosen.
.. code-block:: glsl
mat3 basis = mat3(WORLD_MATRIX);
mat4 m4 = mat4(basis);
mat2 m2 = mat2(m4);
mat3 basis = mat3(WORLD_MATRIX);
mat4 m4 = mat4(basis);
mat2 m2 = mat2(m4);
Swizzling
~~~~~~~~~

View File

@@ -77,16 +77,16 @@ Copy the following code to your shader. The above code is a single pass edge det
shader_type canvas_item;
void fragment() {
vec3 col = -8.0 * texture(TEXTURE, SCREEN_UV).xyz;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz;
col += texture(TEXTURE, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz;
col += texture(TEXTURE, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz;
col += texture(TEXTURE, SCREEN_UV + SCREEN_PIXEL_SIZE.xy).xyz;
col += texture(TEXTURE, SCREEN_UV + SCREEN_PIXEL_SIZE.xy).xyz;
col += texture(TEXTURE, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, SCREEN_PIXEL_SIZE.y)).xyz;
col += texture(TEXTURE, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, -SCREEN_PIXEL_SIZE.y)).xyz;
COLOR.xyz = col;
vec3 col = -8.0 * texture(TEXTURE, SCREEN_UV).xyz;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz;
col += texture(TEXTURE, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz;
col += texture(TEXTURE, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz;
col += texture(TEXTURE, SCREEN_UV + SCREEN_PIXEL_SIZE.xy).xyz;
col += texture(TEXTURE, SCREEN_UV + SCREEN_PIXEL_SIZE.xy).xyz;
col += texture(TEXTURE, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, SCREEN_PIXEL_SIZE.y)).xyz;
col += texture(TEXTURE, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, -SCREEN_PIXEL_SIZE.y)).xyz;
COLOR.xyz = col;
}
.. note::
@@ -135,16 +135,16 @@ does not matter:
//Blurs the screen in the X-direction
void fragment() {
vec3 col = texture(TEXTURE, SCREEN_UV).xyz * 0.16;
col += texture(TEXTURE, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
col += texture(TEXTURE, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
col += texture(TEXTURE, SCREEN_UV + vec2(2.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
col += texture(TEXTURE, SCREEN_UV + vec2(2.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
col += texture(TEXTURE, SCREEN_UV + vec2(3.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
col += texture(TEXTURE, SCREEN_UV + vec2(3.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
col += texture(TEXTURE, SCREEN_UV + vec2(4.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
col += texture(TEXTURE, SCREEN_UV + vec2(4.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
COLOR.xyz = col;
vec3 col = texture(TEXTURE, SCREEN_UV).xyz * 0.16;
col += texture(TEXTURE, SCREEN_UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
col += texture(TEXTURE, SCREEN_UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
col += texture(TEXTURE, SCREEN_UV + vec2(2.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
col += texture(TEXTURE, SCREEN_UV + vec2(2.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
col += texture(TEXTURE, SCREEN_UV + vec2(3.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
col += texture(TEXTURE, SCREEN_UV + vec2(3.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
col += texture(TEXTURE, SCREEN_UV + vec2(4.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
col += texture(TEXTURE, SCREEN_UV + vec2(4.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
COLOR.xyz = col;
}
::
@@ -153,16 +153,16 @@ does not matter:
//Blurs the screen in the Y-direction
void fragment() {
vec3 col = texture(TEXTURE, SCREEN_UV).xyz * 0.16;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 2.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 2.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 3.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 3.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 4.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 4.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
COLOR.xyz = col;
vec3 col = texture(TEXTURE, SCREEN_UV).xyz * 0.16;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 2.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 2.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 3.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 3.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 4.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
col += texture(TEXTURE, SCREEN_UV + vec2(0.0, 4.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
COLOR.xyz = col;
}
Using the above code you should end up with a full screen blur effect like below.

View File

@@ -52,16 +52,16 @@ will ensure that the :ref:`ColorRect <class_ColorRect>` takes up the entire :ref
Next, we add a :ref:`Shader Material <class_ShaderMaterial>` to the :ref:`ColorRect <class_ColorRect>`.
.. note:: I'm assuming you are familiar with the basics of shading for this tutorial. Even if you aren't, all the code
.. note:: We are assuming you are familiar with the basics of shading for this tutorial. Even if you aren't, all the code
will still be provided so you should have no problem following along.
::
shader_type canvas_item
shader_type canvas_item
void fragment() {
COLOR = vec4(UV.x, UV.y, 0.5, 1.0);
}
void fragment() {
COLOR = vec4(UV.x, UV.y, 0.5, 1.0);
}
The above code renders a gradient like the one below.
@@ -107,7 +107,7 @@ the sphere in a nice way? One solution is to use a function that repeats on the
::
COLOR.xyz = vec3(sin(UV.x * 3.14159 * 4.0) * cos(UV.y * 3.14159 * 4.0) * 0.5 + 0.5);
COLOR.xyz = vec3(sin(UV.x * 3.14159 * 4.0) * cos(UV.y * 3.14159 * 4.0) * 0.5 + 0.5);
.. image:: img/planet_sincos.png
@@ -118,8 +118,8 @@ projection. Which translates a spherical map onto a 2D plane.
.. note:: If you are interested in a little extra information on the technique, we will be converting from
spherical coordinates into Cartesian coordinates. Spherical coordinates map the longitude and
latitude of the sphere, while Cartesian coordinates are for all intents and purposes a
vector from the center of the sphere to the point.
latitude of the sphere, while Cartesian coordinates are for all intents and purposes a
vector from the center of the sphere to the point.
For each pixel we will calculate its 3D position on the sphere. From that we will use
3D noise to determine a color value. By calculating the noise in 3D we solve the problem
@@ -131,14 +131,14 @@ coordinates.
::
float theta = UV.y * 3.14159;
float phi = UV.x * 3.14159 * 2.0;
vec3 unit = vec3(0.0, 0.0, 0.0);
float theta = UV.y * 3.14159;
float phi = UV.x * 3.14159 * 2.0;
vec3 unit = vec3(0.0, 0.0, 0.0);
unit.x = sin(phi) * sin(theta);
unit.y = cos(theta) * -1.0;
unit.z = cos(phi) * sin(theta);
unit = normalize(unit);
unit.x = sin(phi) * sin(theta);
unit.y = cos(theta) * -1.0;
unit.z = cos(phi) * sin(theta);
unit = normalize(unit);
And if we use ``unit`` as an output ``COLOR`` value we get.
@@ -149,62 +149,64 @@ to make the planet. We will be using this noise function directly from a `Shader
::
vec3 hash( vec3 p ) {
p = vec3( dot(p,vec3(127.1, 311.7, 74.7)),
dot(p,vec3(269.5, 183.3, 246.1)),
dot(p,vec3(113.5, 271.9, 124.6)));
vec3 hash(vec3 p) {
p = vec3(dot(p, vec3(127.1, 311.7, 74.7)),
dot(p, vec3(269.5, 183.3, 246.1)),
dot(p, vec3(113.5, 271.9, 124.6)));
return -1.0 + 2.0 * fract(sin(p) * 43758.5453123);
}
return -1.0 + 2.0 * fract(sin(p) * 43758.5453123);
}
// return value noise (in x) and its derivatives (in yzw)
vec4 noised( in vec3 x ) {
// grid
vec3 p = floor(x);
vec3 w = fract(x);
// quintic interpolant
vec3 u = w * w * w * (w * (w * 6.0 - 15.0) + 10.0);
vec3 du = 30.0 * w * w * (w * (w - 2.0) + 1.0);
// gradients
vec3 ga = hash( p + vec3(0.0, 0.0, 0.0) );
vec3 gb = hash( p + vec3(1.0, 0.0, 0.0) );
vec3 gc = hash( p + vec3(0.0, 1.0, 0.0) );
vec3 gd = hash( p + vec3(1.0, 1.0, 0.0) );
vec3 ge = hash( p + vec3(0.0, 0.0, 1.0) );
vec3 gf = hash( p + vec3(1.0, 0.0, 1.0) );
vec3 gg = hash( p + vec3(0.0, 1.0, 1.0) );
vec3 gh = hash( p + vec3(1.0, 1.0, 1.0) );
// projections
float va = dot( ga, w - vec3(0.0, 0.0, 0.0) );
float vb = dot( gb, w - vec3(1.0, 0.0, 0.0) );
float vc = dot( gc, w - vec3(0.0, 1.0, 0.0) );
float vd = dot( gd, w - vec3(1.0, 1.0, 0.0) );
float ve = dot( ge, w - vec3(0.0, 0.0, 1.0) );
float vf = dot( gf, w - vec3(1.0, 0.0, 1.0) );
float vg = dot( gg, w - vec3(0.0, 1.0, 1.0) );
float vh = dot( gh, w - vec3(1.0, 1.0, 1.0) );
// interpolations
return vec4( va + u.x*(vb-va) + u.y*(vc-va) + u.z*(ve-va) + u.x*u.y*(va-vb-vc+vd) + u.y*u.z*(va-vc-ve+vg) + u.z*u.x*(va-vb-ve+vf) + (-va+vb+vc-vd+ve-vf-vg+vh)*u.x*u.y*u.z, // value
ga + u.x*(gb-ga) + u.y*(gc-ga) + u.z*(ge-ga) + u.x*u.y*(ga-gb-gc+gd) + u.y*u.z*(ga-gc-ge+gg) + u.z*u.x*(ga-gb-ge+gf) + (-ga+gb+gc-gd+ge-gf-gg+gh)*u.x*u.y*u.z + // derivatives
du * (vec3(vb,vc,ve) - va + u.yzx*vec3(va-vb-vc+vd,va-vc-ve+vg,va-vb-ve+vf) + u.zxy*vec3(va-vb-ve+vf,va-vb-vc+vd,va-vc-ve+vg) + u.yzx*u.zxy*(-va+vb+vc-vd+ve-vf-vg+vh) ));
}
// return value noise (in x) and its derivatives (in yzw)
vec4 noised(in vec3 x) {
// grid
vec3 p = floor(x);
vec3 w = fract(x);
// quintic interpolant
vec3 u = w * w * w * (w * (w * 6.0 - 15.0) + 10.0);
vec3 du = 30.0 * w * w * (w * (w - 2.0) + 1.0);
// gradients
vec3 ga = hash(p + vec3(0.0, 0.0, 0.0));
vec3 gb = hash(p + vec3(1.0, 0.0, 0.0));
vec3 gc = hash(p + vec3(0.0, 1.0, 0.0));
vec3 gd = hash(p + vec3(1.0, 1.0, 0.0));
vec3 ge = hash(p + vec3(0.0, 0.0, 1.0));
vec3 gf = hash(p + vec3(1.0, 0.0, 1.0));
vec3 gg = hash(p + vec3(0.0, 1.0, 1.0));
vec3 gh = hash(p + vec3(1.0, 1.0, 1.0));
// projections
float va = dot(ga, w - vec3(0.0, 0.0, 0.0));
float vb = dot(gb, w - vec3(1.0, 0.0, 0.0));
float vc = dot(gc, w - vec3(0.0, 1.0, 0.0));
float vd = dot(gd, w - vec3(1.0, 1.0, 0.0));
float ve = dot(ge, w - vec3(0.0, 0.0, 1.0));
float vf = dot(gf, w - vec3(1.0, 0.0, 1.0));
float vg = dot(gg, w - vec3(0.0, 1.0, 1.0));
float vh = dot(gh, w - vec3(1.0, 1.0, 1.0));
// interpolations
return vec4(
va + u.x*(vb-va) + u.y*(vc-va) + u.z*(ve-va) + u.x*u.y*(va-vb-vc+vd) + u.y*u.z*(va-vc-ve+vg) + u.z*u.x*(va-vb-ve+vf) + (-va+vb+vc-vd+ve-vf-vg+vh)*u.x*u.y*u.z, // value
ga + u.x*(gb-ga) + u.y*(gc-ga) + u.z*(ge-ga) + u.x*u.y*(ga-gb-gc+gd) + u.y*u.z*(ga-gc-ge+gg) + u.z*u.x*(ga-gb-ge+gf) + (-ga+gb+gc-gd+ge-gf-gg+gh)*u.x*u.y*u.z + // derivatives
du * (vec3(vb,vc,ve) - va + u.yzx*vec3(va-vb-vc+vd,va-vc-ve+vg,va-vb-ve+vf) + u.zxy*vec3(va-vb-ve+vf,va-vb-vc+vd,va-vc-ve+vg) + u.yzx*u.zxy*(-va+vb+vc-vd+ve-vf-vg+vh))
);
}
.. note:: All credit goes to the initial author Inigo Quilez. It is published with the ``MIT`` licence.
Now to use ``noised``, add the following to the ``fragment`` function:
Now to use ``noised``, add the following to the ``fragment`` function:
::
vec4 n = noised(unit * 5.0);
COLOR.xyz = vec3(n.x * 0.5 + 0.5);
vec4 n = noised(unit * 5.0);
COLOR.xyz = vec3(n.x * 0.5 + 0.5);
.. image:: img/planet_noise.png
.. note:: In order to highlight the texture, I have set the material to unshaded.
.. note:: In order to highlight the texture, we set the material to unshaded.
You can see now that the noise indeed wraps seamlessly around the sphere. Although this
looks nothing like the planet you were promised. So lets move onto something more colorful.
@@ -224,7 +226,7 @@ values whether it be floats or vector types.
::
COLOR.xyz = mix(vec3(0.05, 0.3, 0.5), vec3(0.9, 0.4, 0.1), n.x * 0.5 + 0.5);
COLOR.xyz = mix(vec3(0.05, 0.3, 0.5), vec3(0.9, 0.4, 0.1), n.x * 0.5 + 0.5);
The first color is blue for the ocean. The second color is a kind of reddish color (because
all alien planets need red terrain). And finally they are mixed together by ``n.x * 0.5 + 0.5``.
@@ -239,7 +241,7 @@ And thus the whole line becomes:
::
COLOR.xyz = mix(vec3(0.05, 0.3, 0.5), vec3(0.9, 0.4, 0.1), smoothstep(-0.1, 0.0, n.x));
COLOR.xyz = mix(vec3(0.05, 0.3, 0.5), vec3(0.9, 0.4, 0.1), smoothstep(-0.1, 0.0, n.x));
What ``smoothstep`` does is return ``0`` if the third parameter is below the first and return 1 if the
third parameter is larger than the second and smoothly blends between ``0`` and ``1`` if the third number
@@ -257,10 +259,10 @@ instead of just one. ``n`` becomes:
::
vec4 n = noised(unit * 5.0) * 0.5;
n += noised(unit * 10.0) * 0.25;
n += noised(unit * 20.0) * 0.125;
n += noised(unit * 40.0) * 0.0625;
vec4 n = noised(unit * 5.0) * 0.5;
n += noised(unit * 10.0) * 0.25;
n += noised(unit * 20.0) * 0.125;
n += noised(unit * 40.0) * 0.0625;
And now the planet looks like:
@@ -279,7 +281,7 @@ into the ``alpha`` channel of our output ``COLOR`` and using it as a Roughness m
::
COLOR.a = 0.3 + 0.7 * smoothstep(-0.1, 0.0, n.x);
COLOR.a = 0.3 + 0.7 * smoothstep(-0.1, 0.0, n.x);
This line returns ``0.3`` for water and ``1.0`` for land. This means that the land is going to be quite
rough while the water will be quite smooth.
@@ -306,7 +308,7 @@ rendering one transparent object on top of another we want to enable ``blend_pre
::
render_mode blend_premul_alpha;
render_mode blend_premul_alpha;
This pre-multiplies the colors by the ``alpha`` value and then blends them correctly together. Typically
when blending one transparent color on top of another, even if the background has an ``alpha`` of ``0`` (as it