Skip to content

Commit

Permalink
Merge pull request #87391 from markdibarry/new_node_parallax_2d
Browse files Browse the repository at this point in the history
Add new Parallax2D node
  • Loading branch information
akien-mga committed Mar 4, 2024
2 parents a195bc4 + a628709 commit 1a9c0ee
Show file tree
Hide file tree
Showing 22 changed files with 751 additions and 53 deletions.
46 changes: 46 additions & 0 deletions doc/classes/Parallax2D.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Parallax2D" inherits="Node2D" experimental="This node is meant to replace [ParallaxBackground] and [ParallaxLayer]. The implementation may change in the future." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
A node used to create a parallax scrolling background.
</brief_description>
<description>
A [Parallax2D] is used to create a parallax effect. It can move at a different speed relative to the camera movement using [member scroll_scale]. This creates an illusion of depth in a 2D game. If manual scrolling is desired, the [Camera2D] position can be ignored with [member ignore_camera_scroll].
[b]Note:[/b] Any changes to this node's position made after it enters the scene tree will be overridden if [member ignore_camera_scroll] is [code]false[/code] or [member screen_offset] is modified.
</description>
<tutorials>
</tutorials>
<members>
<member name="autoscroll" type="Vector2" setter="set_autoscroll" getter="get_autoscroll" default="Vector2(0, 0)">
Velocity at which the offset scrolls automatically, in pixels per second.
</member>
<member name="follow_viewport" type="bool" setter="set_follow_viewport" getter="get_follow_viewport" default="true">
If [code]true[/code], this [Parallax2D] is offset by the current camera's position. If the [Parallax2D] is in a [CanvasLayer] separate from the current camera, it may be desired to match the value with [member CanvasLayer.follow_viewport_enabled].
</member>
<member name="ignore_camera_scroll" type="bool" setter="set_ignore_camera_scroll" getter="is_ignore_camera_scroll" default="false">
If [code]true[/code], [Parallax2D]'s position is not affected by the position of the camera.
</member>
<member name="limit_begin" type="Vector2" setter="set_limit_begin" getter="get_limit_begin" default="Vector2(-1e+07, -1e+07)">
Top-left limits for scrolling to begin. If the camera is outside of this limit, the [Parallax2D] stops scrolling. Must be lower than [member limit_end] minus the viewport size to work.
</member>
<member name="limit_end" type="Vector2" setter="set_limit_end" getter="get_limit_end" default="Vector2(1e+07, 1e+07)">
Bottom-right limits for scrolling to end. If the camera is outside of this limit, the [Parallax2D] will stop scrolling. Must be higher than [member limit_begin] and the viewport size combined to work.
</member>
<member name="repeat_size" type="Vector2" setter="set_repeat_size" getter="get_repeat_size" default="Vector2(0, 0)">
Repeats the [Texture2D] of each of this node's children and offsets them by this value. When scrolling, the node's position loops, giving the illusion of an infinite scrolling background if the values are larger than the screen size. If an axis is set to [code]0[/code], the [Texture2D] will not be repeated.
</member>
<member name="repeat_times" type="int" setter="set_repeat_times" getter="get_repeat_times" default="1">
Overrides the amount of times the texture repeats. Each texture copy spreads evenly from the original by [member repeat_size]. Useful for when zooming out with a camera.
</member>
<member name="screen_offset" type="Vector2" setter="set_screen_offset" getter="get_screen_offset" default="Vector2(0, 0)">
Offset used to scroll this [Parallax2D]. This value is updated automatically unless [member ignore_camera_scroll] is [code]true[/code].
</member>
<member name="scroll_offset" type="Vector2" setter="set_scroll_offset" getter="get_scroll_offset" default="Vector2(0, 0)">
The [Parallax2D]'s offset. Similar to [member screen_offset] and [member Node2D.position], but will not be overridden.
[b]Note:[/b] Values will loop if [member repeat_size] is set higher than [code]0[/code].
</member>
<member name="scroll_scale" type="Vector2" setter="set_scroll_scale" getter="get_scroll_scale" default="Vector2(1, 1)">
Multiplier to the final [Parallax2D]'s offset. Can be used to simulate distance from the camera.
For example, a value of [code]1[/code] scrolls at the same speed as the camera. A value greater than [code]1[/code] scrolls faster, making objects appear closer. Less than [code]1[/code] scrolls slower, making object appear closer and a value of [code]0[/code] stops the objects completely.
</member>
</members>
</class>
9 changes: 9 additions & 0 deletions doc/classes/RenderingServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,15 @@
A copy of the canvas item will be drawn with a local offset of the mirroring [Vector2].
</description>
</method>
<method name="canvas_set_item_repeat">
<return type="void" />
<param index="0" name="item" type="RID" />
<param index="1" name="repeat_size" type="Vector2" />
<param index="2" name="repeat_times" type="int" />
<description>
A copy of the canvas item will be drawn with a local offset of the [param repeat_size] by the number of times of the [param repeat_times]. As the [param repeat_times] increases, the copies will spread away from the origin texture.
</description>
</method>
<method name="canvas_set_modulate">
<return type="void" />
<param index="0" name="canvas" type="RID" />
Expand Down
25 changes: 23 additions & 2 deletions drivers/gles3/rasterizer_canvas_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,23 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou

GLES3::CanvasShaderData::BlendMode blend_mode = shader_data_cache ? shader_data_cache->blend_mode : GLES3::CanvasShaderData::BLEND_MODE_MIX;

_record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used);
if (!ci->repeat_size.x && !ci->repeat_size.y) {
_record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used, Point2());
} else {
Point2 start_pos = ci->repeat_size * -(ci->repeat_times / 2);
Point2 end_pos = ci->repeat_size * ci->repeat_times + ci->repeat_size + start_pos;
Point2 pos = start_pos;

do {
do {
_record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used, pos);
pos.y += ci->repeat_size.y;
} while (pos.y < end_pos.y);

pos.x += ci->repeat_size.x;
pos.y = start_pos.y;
} while (pos.x < end_pos.x);
}
}

if (index == 0) {
Expand Down Expand Up @@ -784,7 +800,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
state.last_item_index += index;
}

void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used) {
void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, const Point2 &p_offset) {
RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? state.default_filter : p_item->texture_filter;

if (texture_filter != state.canvas_instance_batches[state.current_batch_index].filter) {
Expand All @@ -802,6 +818,11 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
}

Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform;

if (p_offset.x || p_offset.y) {
base_transform *= Transform2D(0, p_offset / p_item->xform.get_scale());
}

Transform2D draw_transform; // Used by transform command

Color base_color = p_item->final_modulate;
Expand Down
2 changes: 1 addition & 1 deletion drivers/gles3/rasterizer_canvas_gles3.h
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender {

void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info = nullptr) override;
void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr);
void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used);
void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used, const Point2 &p_offset);
void _render_batch(Light *p_lights, uint32_t p_index, RenderingMethod::RenderInfo *r_render_info = nullptr);
bool _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization);
void _new_batch(bool &r_batch_broken);
Expand Down
1 change: 1 addition & 0 deletions editor/icons/Parallax2D.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
137 changes: 137 additions & 0 deletions editor/plugins/parallax_background_editor_plugin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/**************************************************************************/
/* parallax_background_editor_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot 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 "parallax_background_editor_plugin.h"

#include "canvas_item_editor_plugin.h"
#include "editor/editor_node.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/scene_tree_dock.h"
#include "scene/2d/parallax_2d.h"
#include "scene/2d/parallax_background.h"
#include "scene/2d/parallax_layer.h"
#include "scene/gui/box_container.h"
#include "scene/gui/menu_button.h"

void ParallaxBackgroundEditorPlugin::edit(Object *p_object) {
parallax_background = Object::cast_to<ParallaxBackground>(p_object);
}

bool ParallaxBackgroundEditorPlugin::handles(Object *p_object) const {
return Object::cast_to<ParallaxBackground>(p_object) != nullptr;
}

void ParallaxBackgroundEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
toolbar->show();
} else {
toolbar->hide();
}
}

void ParallaxBackgroundEditorPlugin::_menu_callback(int p_idx) {
if (p_idx == MENU_CONVERT_TO_PARALLAX_2D) {
convert_to_parallax2d();
}
}

void ParallaxBackgroundEditorPlugin::convert_to_parallax2d() {
ParallaxBackground *parallax_bg = parallax_background;
TypedArray<Node> children = parallax_bg->get_children();

EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to Parallax2D"), UndoRedo::MERGE_DISABLE, parallax_bg);

for (int i = 0; i < children.size(); i++) {
ParallaxLayer *parallax_layer = Object::cast_to<ParallaxLayer>(children[i]);

if (!parallax_layer) {
continue;
}

Parallax2D *parallax2d = memnew(Parallax2D);

Point2 offset = parallax_bg->get_scroll_base_offset() * parallax_layer->get_motion_scale();
offset += parallax_layer->get_motion_offset() + parallax_layer->get_position();
parallax2d->set_scroll_offset(offset);

Point2 limit_begin = parallax2d->get_limit_begin();
Point2 limit_end = parallax2d->get_limit_end();

if (parallax_bg->get_limit_begin().x != 0 || parallax_bg->get_limit_end().x != 0) {
limit_begin.x = parallax_bg->get_limit_begin().x;
limit_end.x = parallax_bg->get_limit_end().x;
}

if (parallax_bg->get_limit_begin().y != 0 || parallax_bg->get_limit_end().y != 0) {
limit_begin.y = parallax_bg->get_limit_begin().y;
limit_end.y = parallax_bg->get_limit_end().y;
}

parallax2d->set_limit_begin(limit_begin);
parallax2d->set_limit_end(limit_end);
parallax2d->set_follow_viewport(!parallax_bg->is_ignore_camera_zoom());
parallax2d->set_repeat_size(parallax_layer->get_mirroring());
parallax2d->set_scroll_scale(parallax_bg->get_scroll_base_scale() * parallax_layer->get_motion_scale());

SceneTreeDock::get_singleton()->replace_node(parallax_layer, parallax2d);
}

if (parallax_bg->is_ignore_camera_zoom()) {
CanvasLayer *canvas_layer = memnew(CanvasLayer);
SceneTreeDock::get_singleton()->replace_node(parallax_bg, canvas_layer);
} else {
Node2D *node2d = memnew(Node2D);
SceneTreeDock::get_singleton()->replace_node(parallax_bg, node2d);
}

ur->commit_action(false);
}

void ParallaxBackgroundEditorPlugin::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
menu->get_popup()->connect("id_pressed", callable_mp(this, &ParallaxBackgroundEditorPlugin::_menu_callback));
menu->set_icon(menu->get_editor_theme_icon(SNAME("ParallaxBackground")));
} break;
}
}

ParallaxBackgroundEditorPlugin::ParallaxBackgroundEditorPlugin() {
toolbar = memnew(HBoxContainer);
toolbar->hide();
add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, toolbar);

menu = memnew(MenuButton);
menu->get_popup()->add_item(TTR("Convert to Parallax2D"), MENU_CONVERT_TO_PARALLAX_2D);
menu->set_text(TTR("ParallaxBackground"));
menu->set_switch_on_hover(true);
toolbar->add_child(menu);
}
67 changes: 67 additions & 0 deletions editor/plugins/parallax_background_editor_plugin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**************************************************************************/
/* parallax_background_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot 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 PARALLAX_BACKGROUND_EDITOR_PLUGIN_H
#define PARALLAX_BACKGROUND_EDITOR_PLUGIN_H

#include "editor/editor_plugin.h"

class HBoxContainer;
class MenuButton;
class ParallaxBackground;

class ParallaxBackgroundEditorPlugin : public EditorPlugin {
GDCLASS(ParallaxBackgroundEditorPlugin, EditorPlugin);

enum {
MENU_CONVERT_TO_PARALLAX_2D,
};

ParallaxBackground *parallax_background = nullptr;
HBoxContainer *toolbar = nullptr;
MenuButton *menu = nullptr;

void _menu_callback(int p_idx);
void convert_to_parallax2d();

protected:
void _notification(int p_what);

public:
virtual String get_name() const override { return "ParallaxBackground"; }
bool has_main_screen() const override { return false; }
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;

ParallaxBackgroundEditorPlugin();
};

#endif // PARALLAX_BACKGROUND_EDITOR_PLUGIN_H
2 changes: 2 additions & 0 deletions editor/register_editor_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
#include "editor/plugins/node_3d_editor_gizmos.h"
#include "editor/plugins/occluder_instance_3d_editor_plugin.h"
#include "editor/plugins/packed_scene_editor_plugin.h"
#include "editor/plugins/parallax_background_editor_plugin.h"
#include "editor/plugins/path_2d_editor_plugin.h"
#include "editor/plugins/path_3d_editor_plugin.h"
#include "editor/plugins/physical_bone_3d_editor_plugin.h"
Expand Down Expand Up @@ -252,6 +253,7 @@ void register_editor_types() {
EditorPlugins::add_by_type<NavigationLink2DEditorPlugin>();
EditorPlugins::add_by_type<NavigationObstacle2DEditorPlugin>();
EditorPlugins::add_by_type<NavigationPolygonEditorPlugin>();
EditorPlugins::add_by_type<ParallaxBackgroundEditorPlugin>();
EditorPlugins::add_by_type<Path2DEditorPlugin>();
EditorPlugins::add_by_type<Polygon2DEditorPlugin>();
EditorPlugins::add_by_type<Cast2DEditorPlugin>();
Expand Down
6 changes: 4 additions & 2 deletions scene/2d/camera_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@ void Camera2D::_update_scroll() {

Size2 screen_size = _get_camera_screen_size();
Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5) : Point2());
Point2 adj_screen_pos = camera_screen_center - (screen_size * 0.5);

get_tree()->call_group(group_name, "_camera_moved", xform, screen_offset);
};
// TODO: Remove xform and screen_offset when ParallaxBackground/ParallaxLayer is removed.
get_tree()->call_group(group_name, SNAME("_camera_moved"), xform, screen_offset, adj_screen_pos);
}
}

void Camera2D::_update_process_callback() {
Expand Down
Loading

0 comments on commit 1a9c0ee

Please sign in to comment.