Add new Parallax2D node

This commit is contained in:
markdibarry 2024-01-17 12:09:52 -05:00
parent f2045ba822
commit a62870956a
22 changed files with 751 additions and 53 deletions

View File

@ -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>

View File

@ -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" />

View File

@ -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) {
@ -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) {
@ -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;

View File

@ -357,7 +357,7 @@ public:
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);

View File

@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 1a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-10a2 2 0 00-2-2zm10 2v10h-10v-10zM7 5v6l-3-3zm2 0v6l3-3z" fill="#8da5f3"/></svg>

After

Width:  |  Height:  |  Size: 201 B

View File

@ -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);
}

View File

@ -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

View File

@ -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"
@ -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>();

View File

@ -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() {

290
scene/2d/parallax_2d.cpp Normal file
View File

@ -0,0 +1,290 @@
/**************************************************************************/
/* parallax_2d.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_2d.h"
#include "core/config/project_settings.h"
void Parallax2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
group_name = "__cameras_" + itos(get_viewport_rid().get_id());
add_to_group(group_name);
_update_repeat();
_update_scroll();
} break;
case NOTIFICATION_READY: {
_update_process();
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
autoscroll_offset += autoscroll * get_process_delta_time();
autoscroll_offset = autoscroll_offset.posmodv(repeat_size);
_update_scroll();
} break;
case NOTIFICATION_EXIT_TREE: {
remove_from_group(group_name);
} break;
}
}
#ifdef TOOLS_ENABLED
void Parallax2D::_edit_set_position(const Point2 &p_position) {
set_scroll_offset(p_position);
}
#endif // TOOLS_ENABLED
void Parallax2D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "position") {
p_property.usage = PROPERTY_USAGE_NONE;
}
}
void Parallax2D::_camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset, const Point2 &p_adj_screen_pos) {
if (!ignore_camera_scroll) {
set_screen_offset(p_adj_screen_pos);
}
}
void Parallax2D::_update_process() {
set_process_internal(!Engine::get_singleton()->is_editor_hint() && (repeat_size.x || repeat_size.y) && (autoscroll.x || autoscroll.y));
}
void Parallax2D::_update_scroll() {
if (!is_inside_tree()) {
return;
}
Point2 scroll_ofs = screen_offset;
Size2 vps = get_viewport_rect().size;
if (Engine::get_singleton()->is_editor_hint()) {
vps = Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height"));
} else {
if (limit_begin.x <= limit_end.x - vps.x) {
scroll_ofs.x = CLAMP(scroll_ofs.x, limit_begin.x, limit_end.x - vps.x);
}
if (limit_begin.y <= limit_end.y - vps.y) {
scroll_ofs.y = CLAMP(scroll_ofs.y, limit_begin.y, limit_end.y - vps.y);
}
}
scroll_ofs *= scroll_scale;
if (repeat_size.x) {
real_t mod = Math::fposmod(scroll_ofs.x - scroll_offset.x - autoscroll_offset.x, repeat_size.x);
scroll_ofs.x = screen_offset.x - mod;
} else {
scroll_ofs.x = screen_offset.x + scroll_offset.x - scroll_ofs.x;
}
if (repeat_size.y) {
real_t mod = Math::fposmod(scroll_ofs.y - scroll_offset.y - autoscroll_offset.y, repeat_size.y);
scroll_ofs.y = screen_offset.y - mod;
} else {
scroll_ofs.y = screen_offset.y + scroll_offset.y - scroll_ofs.y;
}
if (!follow_viewport) {
scroll_ofs -= screen_offset;
}
set_position(scroll_ofs);
}
void Parallax2D::_update_repeat() {
if (!is_inside_tree()) {
return;
}
Point2 repeat_scale = repeat_size * get_scale();
RenderingServer::get_singleton()->canvas_set_item_repeat(get_canvas_item(), repeat_scale, repeat_times);
}
void Parallax2D::set_scroll_scale(const Size2 &p_scale) {
scroll_scale = p_scale;
}
Size2 Parallax2D::get_scroll_scale() const {
return scroll_scale;
}
void Parallax2D::set_repeat_size(const Size2 &p_repeat_size) {
if (p_repeat_size == repeat_size) {
return;
}
repeat_size = p_repeat_size.max(Vector2(0, 0));
_update_process();
_update_repeat();
_update_scroll();
}
Size2 Parallax2D::get_repeat_size() const {
return repeat_size;
}
void Parallax2D::set_repeat_times(int p_repeat_times) {
if (p_repeat_times == repeat_times) {
return;
}
repeat_times = MAX(p_repeat_times, 1);
_update_repeat();
}
int Parallax2D::get_repeat_times() const {
return repeat_times;
}
void Parallax2D::set_scroll_offset(const Point2 &p_offset) {
if (p_offset == scroll_offset) {
return;
}
scroll_offset = p_offset;
_update_scroll();
}
Point2 Parallax2D::get_scroll_offset() const {
return scroll_offset;
}
void Parallax2D::set_autoscroll(const Point2 &p_autoscroll) {
if (p_autoscroll == autoscroll) {
return;
}
autoscroll = p_autoscroll;
autoscroll_offset = Point2();
_update_process();
_update_scroll();
}
Point2 Parallax2D::get_autoscroll() const {
return autoscroll;
}
void Parallax2D::set_screen_offset(const Point2 &p_offset) {
if (p_offset == screen_offset) {
return;
}
screen_offset = p_offset;
_update_scroll();
}
Point2 Parallax2D::get_screen_offset() const {
return screen_offset;
}
void Parallax2D::set_limit_begin(const Point2 &p_offset) {
limit_begin = p_offset;
}
Point2 Parallax2D::get_limit_begin() const {
return limit_begin;
}
void Parallax2D::set_limit_end(const Point2 &p_offset) {
limit_end = p_offset;
}
Point2 Parallax2D::get_limit_end() const {
return limit_end;
}
void Parallax2D::set_follow_viewport(bool p_follow) {
follow_viewport = p_follow;
}
bool Parallax2D::get_follow_viewport() {
return follow_viewport;
}
void Parallax2D::set_ignore_camera_scroll(bool p_ignore) {
ignore_camera_scroll = p_ignore;
}
bool Parallax2D::is_ignore_camera_scroll() {
return ignore_camera_scroll;
}
void Parallax2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_camera_moved", "transform", "screen_offset", "adj_screen_offset"), &Parallax2D::_camera_moved);
ClassDB::bind_method(D_METHOD("set_scroll_scale", "scale"), &Parallax2D::set_scroll_scale);
ClassDB::bind_method(D_METHOD("get_scroll_scale"), &Parallax2D::get_scroll_scale);
ClassDB::bind_method(D_METHOD("set_repeat_size", "repeat_size"), &Parallax2D::set_repeat_size);
ClassDB::bind_method(D_METHOD("get_repeat_size"), &Parallax2D::get_repeat_size);
ClassDB::bind_method(D_METHOD("set_repeat_times", "repeat_times"), &Parallax2D::set_repeat_times);
ClassDB::bind_method(D_METHOD("get_repeat_times"), &Parallax2D::get_repeat_times);
ClassDB::bind_method(D_METHOD("set_autoscroll", "autoscroll"), &Parallax2D::set_autoscroll);
ClassDB::bind_method(D_METHOD("get_autoscroll"), &Parallax2D::get_autoscroll);
ClassDB::bind_method(D_METHOD("set_scroll_offset", "offset"), &Parallax2D::set_scroll_offset);
ClassDB::bind_method(D_METHOD("get_scroll_offset"), &Parallax2D::get_scroll_offset);
ClassDB::bind_method(D_METHOD("set_screen_offset", "offset"), &Parallax2D::set_screen_offset);
ClassDB::bind_method(D_METHOD("get_screen_offset"), &Parallax2D::get_screen_offset);
ClassDB::bind_method(D_METHOD("set_limit_begin", "offset"), &Parallax2D::set_limit_begin);
ClassDB::bind_method(D_METHOD("get_limit_begin"), &Parallax2D::get_limit_begin);
ClassDB::bind_method(D_METHOD("set_limit_end", "offset"), &Parallax2D::set_limit_end);
ClassDB::bind_method(D_METHOD("get_limit_end"), &Parallax2D::get_limit_end);
ClassDB::bind_method(D_METHOD("set_follow_viewport", "follow"), &Parallax2D::set_follow_viewport);
ClassDB::bind_method(D_METHOD("get_follow_viewport"), &Parallax2D::get_follow_viewport);
ClassDB::bind_method(D_METHOD("set_ignore_camera_scroll", "ignore"), &Parallax2D::set_ignore_camera_scroll);
ClassDB::bind_method(D_METHOD("is_ignore_camera_scroll"), &Parallax2D::is_ignore_camera_scroll);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scroll_scale", PROPERTY_HINT_LINK), "set_scroll_scale", "get_scroll_scale");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scroll_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_scroll_offset", "get_scroll_offset");
ADD_GROUP("Repeat", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "repeat_size"), "set_repeat_size", "get_repeat_size");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "autoscroll", PROPERTY_HINT_NONE, "suffix:px/s"), "set_autoscroll", "get_autoscroll");
ADD_PROPERTY(PropertyInfo(Variant::INT, "repeat_times"), "set_repeat_times", "get_repeat_times");
ADD_GROUP("Limit", "limit_");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "limit_begin", PROPERTY_HINT_NONE, "suffix:px"), "set_limit_begin", "get_limit_begin");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "limit_end", PROPERTY_HINT_NONE, "suffix:px"), "set_limit_end", "get_limit_end");
ADD_GROUP("Override", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_viewport"), "set_follow_viewport", "get_follow_viewport");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_camera_scroll"), "set_ignore_camera_scroll", "is_ignore_camera_scroll");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "screen_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_screen_offset", "get_screen_offset");
}
Parallax2D::Parallax2D() {
}

101
scene/2d/parallax_2d.h Normal file
View File

@ -0,0 +1,101 @@
/**************************************************************************/
/* parallax_2d.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_2D_H
#define PARALLAX_2D_H
#include "scene/2d/node_2d.h"
class Parallax2D : public Node2D {
GDCLASS(Parallax2D, Node2D);
static constexpr real_t DEFAULT_LIMIT = 10000000;
String group_name;
Size2 scroll_scale = Size2(1, 1);
Point2 scroll_offset;
Point2 screen_offset;
Vector2 repeat_size;
int repeat_times = 1;
Point2 limit_begin = Point2(-DEFAULT_LIMIT, -DEFAULT_LIMIT);
Point2 limit_end = Point2(DEFAULT_LIMIT, DEFAULT_LIMIT);
Point2 autoscroll;
Point2 autoscroll_offset;
bool follow_viewport = true;
bool ignore_camera_scroll = false;
void _update_process();
void _update_repeat();
void _update_scroll();
protected:
#ifdef TOOLS_ENABLED
void _edit_set_position(const Point2 &p_position) override;
#endif // TOOLS_ENABLED
void _validate_property(PropertyInfo &p_property) const;
void _camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset, const Point2 &p_adj_screen_offset);
void _notification(int p_what);
static void _bind_methods();
public:
void set_scroll_scale(const Size2 &p_scale);
Size2 get_scroll_scale() const;
void set_repeat_size(const Size2 &p_repeat_size);
Size2 get_repeat_size() const;
void set_repeat_times(int p_repeat_times);
int get_repeat_times() const;
void set_autoscroll(const Point2 &p_autoscroll);
Point2 get_autoscroll() const;
void set_scroll_offset(const Point2 &p_offset);
Point2 get_scroll_offset() const;
void set_screen_offset(const Point2 &p_offset);
Point2 get_screen_offset() const;
void set_limit_begin(const Point2 &p_offset);
Point2 get_limit_begin() const;
void set_limit_end(const Point2 &p_offset);
Point2 get_limit_end() const;
void set_follow_viewport(bool p_follow);
bool get_follow_viewport();
void set_ignore_camera_scroll(bool p_ignore);
bool is_ignore_camera_scroll();
Parallax2D();
};
#endif // PARALLAX_2D_H

View File

@ -45,7 +45,7 @@ void ParallaxBackground::_notification(int p_what) {
}
}
void ParallaxBackground::_camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset) {
void ParallaxBackground::_camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset, const Point2 &p_adj_screen_offset) {
screen_offset = p_screen_offset;
set_scroll_scale(p_transform.get_scale().dot(Vector2(0.5, 0.5)));

View File

@ -50,7 +50,7 @@ class ParallaxBackground : public CanvasLayer {
void _update_scroll();
protected:
void _camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset);
void _camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset, const Point2 &p_adj_screen_offset);
void _notification(int p_what);
static void _bind_methods();

View File

@ -52,6 +52,7 @@
#include "scene/2d/navigation_link_2d.h"
#include "scene/2d/navigation_obstacle_2d.h"
#include "scene/2d/navigation_region_2d.h"
#include "scene/2d/parallax_2d.h"
#include "scene/2d/parallax_background.h"
#include "scene/2d/parallax_layer.h"
#include "scene/2d/path_2d.h"
@ -813,6 +814,7 @@ void register_scene_types() {
GDREGISTER_CLASS(TileData);
GDREGISTER_CLASS(TileMap);
GDREGISTER_ABSTRACT_CLASS(TileMapLayerGroup);
GDREGISTER_CLASS(Parallax2D);
GDREGISTER_CLASS(ParallaxBackground);
GDREGISTER_CLASS(ParallaxLayer);
GDREGISTER_CLASS(TouchScreenButton);

View File

@ -37,17 +37,14 @@
#include "rendering_server_globals.h"
#include "servers/rendering/storage/texture_storage.h"
void RendererCanvasCull::_render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, uint32_t p_canvas_cull_mask, RenderingMethod::RenderInfo *r_render_info) {
void RendererCanvasCull::_render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, uint32_t p_canvas_cull_mask, RenderingMethod::RenderInfo *r_render_info) {
RENDER_TIMESTAMP("Cull CanvasItem Tree");
memset(z_list, 0, z_range * sizeof(RendererCanvasRender::Item *));
memset(z_last_list, 0, z_range * sizeof(RendererCanvasRender::Item *));
for (int i = 0; i < p_child_item_count; i++) {
_cull_canvas_item(p_child_items[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, p_canvas_cull_mask);
}
if (p_canvas_item) {
_cull_canvas_item(p_canvas_item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, p_canvas_cull_mask);
_cull_canvas_item(p_child_items[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, p_canvas_cull_mask, p_child_items[i].mirror, 1);
}
RendererCanvasRender::Item *list = nullptr;
@ -223,7 +220,7 @@ void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item *
}
}
void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask) {
void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times) {
Item *ci = p_canvas_item;
if (!ci->visible) {
@ -250,6 +247,22 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
Transform2D xform = ci->xform;
Transform2D parent_xform = p_parent_xform;
Point2 repeat_size = p_repeat_size;
int repeat_times = p_repeat_times;
if (ci->repeat_source) {
repeat_size = ci->repeat_size;
repeat_times = ci->repeat_times;
} else {
ci->repeat_size = repeat_size;
ci->repeat_times = repeat_times;
if (repeat_size.x || repeat_size.y) {
rect.size += repeat_size * repeat_times / ci->xform.get_scale();
rect.position -= repeat_size * (repeat_times / 2);
}
}
if (snapping_2d_transforms_to_pixel) {
xform.columns[2] = xform.columns[2].round();
parent_xform.columns[2] = parent_xform.columns[2].round();
@ -324,7 +337,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
sorter.sort(child_items, child_item_count);
for (i = 0; i < child_item_count; i++) {
_cull_canvas_item(child_items[i], xform * child_items[i]->ysort_xform, p_clip_rect, modulate * child_items[i]->ysort_modulate, child_items[i]->ysort_parent_abs_z_index, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner, false, p_canvas_cull_mask);
_cull_canvas_item(child_items[i], xform * child_items[i]->ysort_xform, p_clip_rect, modulate * child_items[i]->ysort_modulate, child_items[i]->ysort_parent_abs_z_index, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner, false, p_canvas_cull_mask, repeat_size, repeat_times);
}
} else {
RendererCanvasRender::Item *canvas_group_from = nullptr;
@ -348,14 +361,14 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
if (!child_items[i]->behind && !use_canvas_group) {
continue;
}
_cull_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask);
_cull_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times);
}
_attach_canvas_item_for_draw(ci, p_canvas_clip, r_z_list, r_z_last_list, xform, p_clip_rect, global_rect, modulate, p_z, p_material_owner, use_canvas_group, canvas_group_from);
for (int i = 0; i < child_item_count; i++) {
if (child_items[i]->behind || use_canvas_group) {
continue;
}
_cull_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask);
_cull_canvas_item(child_items[i], xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times);
}
}
}
@ -374,38 +387,7 @@ void RendererCanvasCull::render_canvas(RID p_render_target, Canvas *p_canvas, co
int l = p_canvas->child_items.size();
Canvas::ChildItem *ci = p_canvas->child_items.ptrw();
bool has_mirror = false;
for (int i = 0; i < l; i++) {
if (ci[i].mirror.x || ci[i].mirror.y) {
has_mirror = true;
break;
}
}
if (!has_mirror) {
_render_canvas_item_tree(p_render_target, ci, l, nullptr, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask, r_render_info);
} else {
//used for parallaxlayer mirroring
for (int i = 0; i < l; i++) {
const Canvas::ChildItem &ci2 = p_canvas->child_items[i];
_render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask, r_render_info);
//mirroring (useful for scrolling backgrounds)
if (ci2.mirror.x != 0) {
Transform2D xform2 = p_transform * Transform2D(0, Vector2(ci2.mirror.x, 0));
_render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask, r_render_info);
}
if (ci2.mirror.y != 0) {
Transform2D xform2 = p_transform * Transform2D(0, Vector2(0, ci2.mirror.y));
_render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask, r_render_info);
}
if (ci2.mirror.y != 0 && ci2.mirror.x != 0) {
Transform2D xform2 = p_transform * Transform2D(0, ci2.mirror);
_render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask, r_render_info);
}
}
}
_render_canvas_item_tree(p_render_target, ci, l, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel, canvas_cull_mask, r_render_info);
RENDER_TIMESTAMP("< Render Canvas");
}
@ -432,6 +414,15 @@ void RendererCanvasCull::canvas_set_item_mirroring(RID p_canvas, RID p_item, con
canvas->child_items.write[idx].mirror = p_mirroring;
}
void RendererCanvasCull::canvas_set_item_repeat(RID p_item, const Point2 &p_repeat_size, int p_repeat_times) {
Item *canvas_item = canvas_item_owner.get_or_null(p_item);
ERR_FAIL_NULL(canvas_item);
canvas_item->repeat_source = true;
canvas_item->repeat_size = p_repeat_size;
canvas_item->repeat_times = p_repeat_times;
}
void RendererCanvasCull::canvas_set_modulate(RID p_canvas, const Color &p_color) {
Canvas *canvas = canvas_owner.get_or_null(p_canvas);
ERR_FAIL_NULL(canvas);

View File

@ -187,8 +187,8 @@ public:
_FORCE_INLINE_ void _attach_canvas_item_for_draw(Item *ci, Item *p_canvas_clip, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, const Transform2D &p_transform, const Rect2 &p_clip_rect, Rect2 p_global_rect, const Color &modulate, int p_z, RendererCanvasCull::Item *p_material_owner, bool p_use_canvas_group, RendererCanvasRender::Item *r_canvas_group_from);
private:
void _render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, uint32_t p_canvas_cull_mask, RenderingMethod::RenderInfo *r_render_info = nullptr);
void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask);
void _render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, uint32_t p_canvas_cull_mask, RenderingMethod::RenderInfo *r_render_info = nullptr);
void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times);
static constexpr int z_range = RS::CANVAS_ITEM_Z_MAX - RS::CANVAS_ITEM_Z_MIN + 1;
@ -204,6 +204,7 @@ public:
void canvas_initialize(RID p_rid);
void canvas_set_item_mirroring(RID p_canvas, RID p_item, const Point2 &p_mirroring);
void canvas_set_item_repeat(RID p_item, const Point2 &p_repeat_size, int p_repeat_times);
void canvas_set_modulate(RID p_canvas, const Color &p_color);
void canvas_set_parent(RID p_canvas, RID p_parent, float p_scale);
void canvas_set_disable_scale(bool p_disable);

View File

@ -350,6 +350,9 @@ public:
ViewportRender *vp_render = nullptr;
bool distance_field;
bool light_masked;
bool repeat_source;
Point2 repeat_size;
int repeat_times = 1;
Rect2 global_rect_cache;
@ -468,6 +471,7 @@ public:
z_final = 0;
texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT;
texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT;
repeat_source = false;
}
virtual ~Item() {
clear();

View File

@ -406,7 +406,7 @@ _FORCE_INLINE_ static uint32_t _indices_to_primitives(RS::PrimitiveType p_primit
return (p_indices - subtractor[p_primitive]) / divisor[p_primitive];
}
void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RD::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info) {
void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RD::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_offset, RenderingMethod::RenderInfo *r_render_info) {
//create an empty push constant
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
@ -425,6 +425,11 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
PushConstant push_constant;
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;
_update_transform_2d_to_mat2x3(base_transform, push_constant.world);
@ -1240,7 +1245,23 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co
}
}
_render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, r_render_info);
if (!ci->repeat_size.x && !ci->repeat_size.y) {
_render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, Point2(), r_render_info);
} 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 {
_render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, pos, r_render_info);
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);
}
prev_material = material;
}

View File

@ -424,7 +424,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
double debug_redraw_time = 1.0;
inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data = false); //recursive, so regular inline used instead.
void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info = nullptr);
void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_offset, RenderingMethod::RenderInfo *r_render_info = nullptr);
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);
_FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4);

View File

@ -852,6 +852,7 @@ public:
FUNCRIDSPLIT(canvas)
FUNC3(canvas_set_item_mirroring, RID, RID, const Point2 &)
FUNC3(canvas_set_item_repeat, RID, const Point2 &, int)
FUNC2(canvas_set_modulate, RID, const Color &)
FUNC3(canvas_set_parent, RID, RID, float)
FUNC1(canvas_set_disable_scale, bool)

View File

@ -3186,6 +3186,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("canvas_create"), &RenderingServer::canvas_create);
ClassDB::bind_method(D_METHOD("canvas_set_item_mirroring", "canvas", "item", "mirroring"), &RenderingServer::canvas_set_item_mirroring);
ClassDB::bind_method(D_METHOD("canvas_set_item_repeat", "item", "repeat_size", "repeat_times"), &RenderingServer::canvas_set_item_repeat);
ClassDB::bind_method(D_METHOD("canvas_set_modulate", "canvas", "color"), &RenderingServer::canvas_set_modulate);
ClassDB::bind_method(D_METHOD("canvas_set_disable_scale", "disable"), &RenderingServer::canvas_set_disable_scale);

View File

@ -1378,6 +1378,7 @@ public:
virtual RID canvas_create() = 0;
virtual void canvas_set_item_mirroring(RID p_canvas, RID p_item, const Point2 &p_mirroring) = 0;
virtual void canvas_set_item_repeat(RID p_item, const Point2 &p_repeat_size, int p_repeat_times) = 0;
virtual void canvas_set_modulate(RID p_canvas, const Color &p_color) = 0;
virtual void canvas_set_parent(RID p_canvas, RID p_parent, float p_scale) = 0;