mirror of
https://github.com/godotengine/godot.git
synced 2025-12-08 06:09:55 +00:00
Editor: Restructure editor code
Moving various editor files into sub folders to reduce clutter
This commit is contained in:
parent
3954b2459d
commit
f11aff3841
601 changed files with 1195 additions and 1019 deletions
|
|
@ -4,7 +4,3 @@ from misc.utility.scons_hints import *
|
|||
Import("env")
|
||||
|
||||
env.add_source_files(env.editor_sources, "*.cpp")
|
||||
|
||||
SConscript("gizmos/SCsub")
|
||||
SConscript("shader_baker/SCsub")
|
||||
SConscript("tiles/SCsub")
|
||||
|
|
|
|||
|
|
@ -1,953 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* abstract_polygon_2d_editor.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 "abstract_polygon_2d_editor.h"
|
||||
|
||||
#include "canvas_item_editor_plugin.h"
|
||||
#include "core/math/geometry_2d.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
|
||||
bool AbstractPolygon2DEditor::Vertex::operator==(const AbstractPolygon2DEditor::Vertex &p_vertex) const {
|
||||
return polygon == p_vertex.polygon && vertex == p_vertex.vertex;
|
||||
}
|
||||
|
||||
bool AbstractPolygon2DEditor::Vertex::operator!=(const AbstractPolygon2DEditor::Vertex &p_vertex) const {
|
||||
return !(*this == p_vertex);
|
||||
}
|
||||
|
||||
bool AbstractPolygon2DEditor::Vertex::valid() const {
|
||||
return vertex >= 0;
|
||||
}
|
||||
|
||||
bool AbstractPolygon2DEditor::_is_empty() const {
|
||||
if (!_get_node()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const int n = _get_polygon_count();
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
Vector<Vector2> vertices = _get_polygon(i);
|
||||
|
||||
if (vertices.size() != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AbstractPolygon2DEditor::_is_line() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AbstractPolygon2DEditor::_has_uv() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
int AbstractPolygon2DEditor::_get_polygon_count() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
Variant AbstractPolygon2DEditor::_get_polygon(int p_idx) const {
|
||||
return _get_node()->get("polygon");
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::_set_polygon(int p_idx, const Variant &p_polygon) const {
|
||||
_get_node()->set("polygon", p_polygon);
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::_action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) {
|
||||
Node2D *node = _get_node();
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->add_do_method(node, "set_polygon", p_polygon);
|
||||
undo_redo->add_undo_method(node, "set_polygon", p_previous);
|
||||
}
|
||||
|
||||
Vector2 AbstractPolygon2DEditor::_get_offset(int p_idx) const {
|
||||
return Vector2(0, 0);
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::_commit_action() {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::_action_add_polygon(const Variant &p_polygon) {
|
||||
_action_set_polygon(0, p_polygon);
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::_action_remove_polygon(int p_idx) {
|
||||
_action_set_polygon(p_idx, _get_polygon(p_idx), Vector<Vector2>());
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::_action_set_polygon(int p_idx, const Variant &p_polygon) {
|
||||
_action_set_polygon(p_idx, _get_polygon(p_idx), p_polygon);
|
||||
}
|
||||
|
||||
bool AbstractPolygon2DEditor::_has_resource() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::_create_resource() {
|
||||
}
|
||||
|
||||
Vector2 AbstractPolygon2DEditor::_get_geometric_center() const {
|
||||
int n_polygons = _get_polygon_count();
|
||||
|
||||
double cx = 0.0;
|
||||
double cy = 0.0;
|
||||
int n_subs = 0;
|
||||
for (int i = 0; i < n_polygons; i++) {
|
||||
const Vector<Vector2> &vertices = _get_polygon(i);
|
||||
Vector<Vector<Point2>> decomp = ::Geometry2D::decompose_polygon_in_convex(vertices);
|
||||
if (decomp.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
for (const Vector<Vector2> &sub : decomp) {
|
||||
int sub_n_points = sub.size();
|
||||
double sub_area2x = 0.0;
|
||||
double sub_cx = 0.0;
|
||||
double sub_cy = 0.0;
|
||||
for (int n = 0; n < sub_n_points; n++) {
|
||||
int next = (n + 1 < sub_n_points) ? n + 1 : 0;
|
||||
sub_area2x += (sub[n].x * sub[next].y) - (sub[next].x * sub[n].y);
|
||||
sub_cx += (sub[n].x + sub[next].x) * (sub[n].x * sub[next].y - sub[next].x * sub[n].y);
|
||||
sub_cy += (sub[n].y + sub[next].y) * (sub[n].x * sub[next].y - sub[next].x * sub[n].y);
|
||||
}
|
||||
sub_cx /= (sub_area2x * 3);
|
||||
sub_cy /= (sub_area2x * 3);
|
||||
|
||||
cx += sub_cx;
|
||||
cy += sub_cy;
|
||||
}
|
||||
n_subs += decomp.size();
|
||||
}
|
||||
cx /= n_subs;
|
||||
cy /= n_subs;
|
||||
|
||||
return Vector2(cx, cy);
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::_menu_option(int p_option) {
|
||||
switch (p_option) {
|
||||
case MODE_CREATE: {
|
||||
mode = MODE_CREATE;
|
||||
button_create->set_pressed(true);
|
||||
button_edit->set_pressed(false);
|
||||
button_delete->set_pressed(false);
|
||||
} break;
|
||||
case MODE_EDIT: {
|
||||
_wip_close();
|
||||
mode = MODE_EDIT;
|
||||
button_create->set_pressed(false);
|
||||
button_edit->set_pressed(true);
|
||||
button_delete->set_pressed(false);
|
||||
} break;
|
||||
case MODE_DELETE: {
|
||||
_wip_close();
|
||||
mode = MODE_DELETE;
|
||||
button_create->set_pressed(false);
|
||||
button_edit->set_pressed(false);
|
||||
button_delete->set_pressed(true);
|
||||
} break;
|
||||
case CENTER_POLY: {
|
||||
_wip_close();
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Move Origin to Geometric Center"));
|
||||
|
||||
Vector2 center = _get_geometric_center();
|
||||
|
||||
int n_polygons = _get_polygon_count();
|
||||
for (int i = 0; i < n_polygons; i++) {
|
||||
const Vector<Vector2> &vertices = _get_polygon(i);
|
||||
int n_points = vertices.size();
|
||||
|
||||
Vector<Vector2> new_vertices;
|
||||
new_vertices.resize(n_points);
|
||||
for (int n = 0; n < n_points; n++) {
|
||||
new_vertices.write[n] = vertices[n] - center;
|
||||
}
|
||||
_action_set_polygon(i, vertices, new_vertices);
|
||||
}
|
||||
Node2D *node = _get_node();
|
||||
Vector2 node_pos = node->get_position();
|
||||
undo_redo->add_do_method(node, "set_position", node_pos + node->get_transform().basis_xform(center));
|
||||
undo_redo->add_undo_method(node, "set_position", node_pos);
|
||||
|
||||
_commit_action();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
button_create->set_button_icon(get_editor_theme_icon(SNAME("CurveCreate")));
|
||||
button_edit->set_button_icon(get_editor_theme_icon(SNAME("CurveEdit")));
|
||||
button_delete->set_button_icon(get_editor_theme_icon(SNAME("CurveDelete")));
|
||||
button_center->set_button_icon(get_editor_theme_icon(SNAME("CurveCenter")));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_READY: {
|
||||
disable_polygon_editing(false, String());
|
||||
|
||||
button_edit->set_pressed(true);
|
||||
|
||||
get_tree()->connect("node_removed", callable_mp(this, &AbstractPolygon2DEditor::_node_removed));
|
||||
create_resource->connect(SceneStringName(confirmed), callable_mp(this, &AbstractPolygon2DEditor::_create_resource));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::_node_removed(Node *p_node) {
|
||||
if (p_node == _get_node()) {
|
||||
edit(nullptr);
|
||||
hide();
|
||||
|
||||
canvas_item_editor->update_viewport();
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::_wip_changed() {
|
||||
if (wip_active && _is_line()) {
|
||||
_set_polygon(0, wip);
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::_wip_cancel() {
|
||||
wip.clear();
|
||||
wip_active = false;
|
||||
|
||||
edited_point = PosVertex();
|
||||
hover_point = Vertex();
|
||||
selected_point = Vertex();
|
||||
center_drag = false;
|
||||
|
||||
canvas_item_editor->update_viewport();
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::_wip_close() {
|
||||
if (!wip_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_is_line()) {
|
||||
_set_polygon(0, wip);
|
||||
} else if (wip.size() >= (_is_line() ? 2 : 3)) {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Create Polygon"));
|
||||
_action_add_polygon(wip);
|
||||
if (_has_uv()) {
|
||||
undo_redo->add_do_method(_get_node(), "set_uv", Vector<Vector2>());
|
||||
undo_redo->add_undo_method(_get_node(), "set_uv", _get_node()->get("uv"));
|
||||
}
|
||||
_commit_action();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
mode = MODE_EDIT;
|
||||
button_edit->set_pressed(true);
|
||||
button_create->set_pressed(false);
|
||||
button_delete->set_pressed(false);
|
||||
|
||||
wip.clear();
|
||||
wip_active = false;
|
||||
|
||||
edited_point = PosVertex();
|
||||
hover_point = Vertex();
|
||||
selected_point = Vertex();
|
||||
center_drag = false;
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::disable_polygon_editing(bool p_disable, const String &p_reason) {
|
||||
_polygon_editing_enabled = !p_disable;
|
||||
|
||||
button_create->set_disabled(p_disable);
|
||||
button_edit->set_disabled(p_disable);
|
||||
button_delete->set_disabled(p_disable);
|
||||
button_center->set_disabled(p_disable);
|
||||
|
||||
if (p_disable) {
|
||||
button_create->set_tooltip_text(p_reason);
|
||||
button_edit->set_tooltip_text(p_reason);
|
||||
button_delete->set_tooltip_text(p_reason);
|
||||
button_center->set_tooltip_text(p_reason);
|
||||
} else {
|
||||
button_create->set_tooltip_text(TTRC("Create points."));
|
||||
button_edit->set_tooltip_text(TTRC("Edit points.\nLMB: Move Point\nRMB: Erase Point"));
|
||||
button_delete->set_tooltip_text(TTRC("Erase points."));
|
||||
button_center->set_tooltip_text(TTRC("Move center of gravity to geometric center."));
|
||||
}
|
||||
}
|
||||
|
||||
bool AbstractPolygon2DEditor::_commit_drag() {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
|
||||
center_drag = false;
|
||||
int n_polygons = _get_polygon_count();
|
||||
ERR_FAIL_COND_V(pre_center_move_edit.size() != n_polygons, false);
|
||||
undo_redo->create_action(TTR("Move Geometric Center"));
|
||||
for (int i = 0; i < n_polygons; i++) {
|
||||
_action_set_polygon(i, pre_center_move_edit[i], _get_polygon(i));
|
||||
}
|
||||
pre_center_move_edit.clear();
|
||||
_commit_action();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
|
||||
if (!_get_node() || !_polygon_editing_enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_get_node()->is_visible_in_tree()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Viewport *vp = _get_node()->get_viewport();
|
||||
if (vp && !vp->is_visible_subviewport()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
Ref<InputEventMouseButton> mb = p_event;
|
||||
|
||||
if (!_has_resource()) {
|
||||
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
|
||||
create_resource->set_text(String("No polygon resource on this node.\nCreate and assign one?"));
|
||||
create_resource->popup_centered();
|
||||
}
|
||||
return (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT);
|
||||
}
|
||||
|
||||
CanvasItemEditor::Tool tool = CanvasItemEditor::get_singleton()->get_current_tool();
|
||||
if (tool != CanvasItemEditor::TOOL_SELECT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mb.is_valid()) {
|
||||
Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_screen_transform();
|
||||
|
||||
Vector2 gpoint = mb->get_position();
|
||||
Vector2 cpoint = canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint));
|
||||
cpoint = _get_node()->get_screen_transform().affine_inverse().xform(cpoint);
|
||||
|
||||
if (mode == MODE_EDIT || (_is_line() && mode == MODE_CREATE)) {
|
||||
if (mb->get_button_index() == MouseButton::LEFT) {
|
||||
if (mb->is_pressed()) {
|
||||
if (mb->is_meta_pressed() || mb->is_ctrl_pressed() || mb->is_shift_pressed() || mb->is_alt_pressed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const PosVertex closest = closest_point(gpoint);
|
||||
if (closest.valid()) {
|
||||
original_mouse_pos = gpoint;
|
||||
pre_move_edit = _get_polygon(closest.polygon);
|
||||
edited_point = PosVertex(closest, xform.affine_inverse().xform(closest.pos));
|
||||
selected_point = closest;
|
||||
edge_point = PosVertex();
|
||||
canvas_item_editor->update_viewport();
|
||||
return true;
|
||||
} else {
|
||||
selected_point = Vertex();
|
||||
|
||||
const PosVertex insert = closest_edge_point(gpoint);
|
||||
if (insert.valid()) {
|
||||
Vector<Vector2> vertices = _get_polygon(insert.polygon);
|
||||
|
||||
if (vertices.size() < (_is_line() ? 2 : 3)) {
|
||||
vertices.push_back(cpoint);
|
||||
undo_redo->create_action(TTR("Edit Polygon"));
|
||||
selected_point = Vertex(insert.polygon, vertices.size());
|
||||
_action_set_polygon(insert.polygon, vertices);
|
||||
_commit_action();
|
||||
return true;
|
||||
} else {
|
||||
edited_point = PosVertex(insert.polygon, insert.vertex + 1, xform.affine_inverse().xform(insert.pos));
|
||||
vertices.insert(edited_point.vertex, edited_point.pos);
|
||||
pre_move_edit = vertices;
|
||||
selected_point = Vertex(edited_point.polygon, edited_point.vertex);
|
||||
edge_point = PosVertex();
|
||||
|
||||
undo_redo->create_action(TTR("Insert Point"));
|
||||
_action_set_polygon(insert.polygon, vertices);
|
||||
_commit_action();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (edited_point.valid()) {
|
||||
if (original_mouse_pos != gpoint) {
|
||||
Vector<Vector2> vertices = _get_polygon(edited_point.polygon);
|
||||
ERR_FAIL_INDEX_V(edited_point.vertex, vertices.size(), false);
|
||||
vertices.write[edited_point.vertex] = edited_point.pos - _get_offset(edited_point.polygon);
|
||||
|
||||
undo_redo->create_action(TTR("Edit Polygon"));
|
||||
_action_set_polygon(edited_point.polygon, pre_move_edit, vertices);
|
||||
_commit_action();
|
||||
}
|
||||
|
||||
edited_point = PosVertex();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && !edited_point.valid()) {
|
||||
const PosVertex closest = closest_point(gpoint);
|
||||
|
||||
if (closest.valid()) {
|
||||
remove_point(closest);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (mode == MODE_DELETE) {
|
||||
if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
|
||||
const PosVertex closest = closest_point(gpoint);
|
||||
|
||||
if (closest.valid()) {
|
||||
remove_point(closest);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == MODE_CREATE) {
|
||||
if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
|
||||
if (_is_line()) {
|
||||
// for lines, we don't have a wip mode, and we can undo each single add point.
|
||||
Vector<Vector2> vertices = _get_polygon(0);
|
||||
vertices.push_back(cpoint);
|
||||
undo_redo->create_action(TTR("Insert Point"));
|
||||
_action_set_polygon(0, vertices);
|
||||
_commit_action();
|
||||
return true;
|
||||
} else if (!wip_active) {
|
||||
wip.clear();
|
||||
wip.push_back(cpoint);
|
||||
wip_active = true;
|
||||
_wip_changed();
|
||||
edited_point = PosVertex(-1, 1, cpoint);
|
||||
canvas_item_editor->update_viewport();
|
||||
hover_point = Vertex();
|
||||
selected_point = Vertex(0);
|
||||
edge_point = PosVertex();
|
||||
return true;
|
||||
} else {
|
||||
const real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
|
||||
|
||||
if (!_is_line() && wip.size() > 1 && xform.xform(wip[0]).distance_to(xform.xform(cpoint)) < grab_threshold) {
|
||||
//wip closed
|
||||
_wip_close();
|
||||
|
||||
return true;
|
||||
} else {
|
||||
//add wip point
|
||||
wip.push_back(cpoint);
|
||||
_wip_changed();
|
||||
edited_point = PosVertex(-1, wip.size(), cpoint);
|
||||
selected_point = Vertex(wip.size() - 1);
|
||||
canvas_item_editor->update_viewport();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && wip_active) {
|
||||
_wip_cancel();
|
||||
}
|
||||
}
|
||||
|
||||
// Center drag.
|
||||
if (edit_origin_and_center) {
|
||||
real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
|
||||
|
||||
if (mb->get_button_index() == MouseButton::LEFT) {
|
||||
if (mb->is_meta_pressed() || mb->is_ctrl_pressed() || mb->is_shift_pressed() || mb->is_alt_pressed()) {
|
||||
return false;
|
||||
}
|
||||
if (mb->is_pressed() && !center_drag) {
|
||||
Vector2 center_point = xform.xform(_get_geometric_center());
|
||||
if ((gpoint - center_point).length() < grab_threshold) {
|
||||
pre_center_move_edit.clear();
|
||||
int n_polygons = _get_polygon_count();
|
||||
for (int i = 0; i < n_polygons; i++) {
|
||||
pre_center_move_edit.push_back(_get_polygon(i));
|
||||
}
|
||||
center_drag_origin = cpoint;
|
||||
center_drag = true;
|
||||
return true;
|
||||
}
|
||||
} else if (center_drag) {
|
||||
return _commit_drag();
|
||||
}
|
||||
} else if (mb->get_button_index() == MouseButton::RIGHT && center_drag) {
|
||||
_commit_drag();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
|
||||
if (mm.is_valid()) {
|
||||
Vector2 gpoint = mm->get_position();
|
||||
|
||||
if (center_drag) {
|
||||
Vector2 cpoint = canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint));
|
||||
cpoint = _get_node()->get_screen_transform().affine_inverse().xform(cpoint);
|
||||
Vector2 delta = center_drag_origin - cpoint;
|
||||
|
||||
int n_polygons = _get_polygon_count();
|
||||
for (int i = 0; i < n_polygons; i++) {
|
||||
const Vector<Vector2> &vertices = _get_polygon(i);
|
||||
int n_points = vertices.size();
|
||||
|
||||
Vector<Vector2> new_vertices;
|
||||
new_vertices.resize(n_points);
|
||||
for (int n = 0; n < n_points; n++) {
|
||||
new_vertices.write[n] = vertices[n] - delta;
|
||||
}
|
||||
_set_polygon(i, new_vertices);
|
||||
}
|
||||
center_drag_origin = cpoint;
|
||||
} else if (edited_point.valid() && (wip_active || mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
|
||||
Vector2 cpoint = canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint));
|
||||
cpoint = _get_node()->get_screen_transform().affine_inverse().xform(cpoint);
|
||||
|
||||
//Move the point in a single axis. Should only work when editing a polygon and while holding shift.
|
||||
if (mode == MODE_EDIT && mm->is_shift_pressed()) {
|
||||
Vector2 old_point = pre_move_edit.get(selected_point.vertex);
|
||||
if (Math::abs(cpoint.x - old_point.x) > Math::abs(cpoint.y - old_point.y)) {
|
||||
cpoint.y = old_point.y;
|
||||
} else {
|
||||
cpoint.x = old_point.x;
|
||||
}
|
||||
}
|
||||
|
||||
edited_point = PosVertex(edited_point, cpoint);
|
||||
|
||||
if (!wip_active) {
|
||||
Vector<Vector2> vertices = _get_polygon(edited_point.polygon);
|
||||
ERR_FAIL_INDEX_V(edited_point.vertex, vertices.size(), false);
|
||||
vertices.write[edited_point.vertex] = cpoint - _get_offset(edited_point.polygon);
|
||||
_set_polygon(edited_point.polygon, vertices);
|
||||
}
|
||||
|
||||
canvas_item_editor->update_viewport();
|
||||
} else if (mode == MODE_EDIT || (_is_line() && mode == MODE_CREATE)) {
|
||||
const PosVertex new_hover_point = closest_point(gpoint);
|
||||
if (hover_point != new_hover_point) {
|
||||
hover_point = new_hover_point;
|
||||
canvas_item_editor->update_viewport();
|
||||
}
|
||||
|
||||
bool edge_hover = false;
|
||||
if (!hover_point.valid()) {
|
||||
const PosVertex on_edge_vertex = closest_edge_point(gpoint);
|
||||
|
||||
if (on_edge_vertex.valid()) {
|
||||
hover_point = Vertex();
|
||||
edge_point = on_edge_vertex;
|
||||
canvas_item_editor->update_viewport();
|
||||
edge_hover = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!edge_hover && edge_point.valid()) {
|
||||
edge_point = PosVertex();
|
||||
canvas_item_editor->update_viewport();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ref<InputEventKey> k = p_event;
|
||||
|
||||
if (k.is_valid() && k->is_pressed()) {
|
||||
if (k->get_keycode() == Key::KEY_DELETE || k->get_keycode() == Key::BACKSPACE) {
|
||||
if (wip_active && selected_point.polygon == -1) {
|
||||
if (wip.size() > selected_point.vertex) {
|
||||
wip.remove_at(selected_point.vertex);
|
||||
_wip_changed();
|
||||
selected_point = wip.size() - 1;
|
||||
canvas_item_editor->update_viewport();
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
const Vertex active_point = get_active_point();
|
||||
|
||||
if (active_point.valid()) {
|
||||
remove_point(active_point);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (wip_active && k->get_keycode() == Key::ENTER) {
|
||||
_wip_close();
|
||||
} else if (wip_active && k->get_keycode() == Key::ESCAPE) {
|
||||
_wip_cancel();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
|
||||
if (!_get_node()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_get_node()->is_visible_in_tree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Viewport *vp = _get_node()->get_viewport();
|
||||
if (vp && !vp->is_visible_subviewport()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_screen_transform();
|
||||
// All polygon points are sharp, so use the sharp handle icon
|
||||
const Ref<Texture2D> handle = get_editor_theme_icon(SNAME("EditorPathSharpHandle"));
|
||||
const Ref<Texture2D> nhandle = get_editor_theme_icon(SNAME("EditorPathNullHandle"));
|
||||
|
||||
Ref<Font> font = get_theme_font(SNAME("bold"), EditorStringName(EditorFonts));
|
||||
int font_size = 1.3 * get_theme_font_size(SNAME("bold_size"), EditorStringName(EditorFonts));
|
||||
const float outline_size = 4 * EDSCALE;
|
||||
Color font_color = get_theme_color(SceneStringName(font_color), EditorStringName(Editor));
|
||||
Color outline_color = font_color.inverted();
|
||||
|
||||
const Vertex active_point = get_active_point();
|
||||
const int n_polygons = _get_polygon_count();
|
||||
const bool is_closed = !_is_line();
|
||||
|
||||
if (edit_origin_and_center) {
|
||||
const Vector2 ¢er = _get_geometric_center();
|
||||
if (!center.is_zero_approx()) {
|
||||
const Vector2 point = xform.xform(center);
|
||||
p_overlay->draw_texture(nhandle, point - nhandle->get_size() * 0.5, Color(1, 1, 0.4));
|
||||
Size2 lbl_size = font->get_string_size("c", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size);
|
||||
p_overlay->draw_string_outline(font, point - lbl_size * 0.5, "c", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color);
|
||||
p_overlay->draw_string(font, point - lbl_size * 0.5, "c", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
|
||||
}
|
||||
{
|
||||
const Vector2 point = xform.xform(Vector2());
|
||||
p_overlay->draw_texture(nhandle, point - nhandle->get_size() * 0.5, center.is_equal_approx(Vector2()) ? Color(1, 1, 0.4) : Color(1, 0.4, 1));
|
||||
Size2 lbl_size = font->get_string_size("o", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size);
|
||||
p_overlay->draw_string_outline(font, point - lbl_size * 0.5, "o", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color);
|
||||
p_overlay->draw_string(font, point - lbl_size * 0.5, "o", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = -1; j < n_polygons; j++) {
|
||||
if (wip_active && wip_destructive && j != -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector<Vector2> points;
|
||||
Vector2 offset;
|
||||
|
||||
if (wip_active && j == edited_point.polygon) {
|
||||
points = Variant(wip);
|
||||
offset = Vector2(0, 0);
|
||||
} else {
|
||||
if (j == -1) {
|
||||
continue;
|
||||
}
|
||||
points = _get_polygon(j);
|
||||
offset = _get_offset(j);
|
||||
}
|
||||
|
||||
if (!wip_active && j == edited_point.polygon && EDITOR_GET("editors/polygon_editor/show_previous_outline")) {
|
||||
const Color col = Color(0.5, 0.5, 0.5); // FIXME polygon->get_outline_color();
|
||||
const int n = pre_move_edit.size();
|
||||
for (int i = 0; i < n - (is_closed ? 0 : 1); i++) {
|
||||
Vector2 p, p2;
|
||||
p = pre_move_edit[i] + offset;
|
||||
p2 = pre_move_edit[(i + 1) % n] + offset;
|
||||
|
||||
Vector2 point = xform.xform(p);
|
||||
Vector2 next_point = xform.xform(p2);
|
||||
|
||||
p_overlay->draw_line(point, next_point, col, Math::round(2 * EDSCALE));
|
||||
}
|
||||
}
|
||||
|
||||
const int n_points = points.size();
|
||||
const Color col = Color(1, 0.3, 0.1, 0.8);
|
||||
|
||||
for (int i = 0; i < n_points; i++) {
|
||||
const Vertex vertex(j, i);
|
||||
|
||||
const Vector2 p = (vertex == edited_point) ? edited_point.pos : (points[i] + offset);
|
||||
const Vector2 point = xform.xform(p);
|
||||
|
||||
if (is_closed || i < n_points - 1) {
|
||||
Vector2 p2;
|
||||
if (j == edited_point.polygon &&
|
||||
((wip_active && i == n_points - 1) || (((i + 1) % n_points) == edited_point.vertex))) {
|
||||
p2 = edited_point.pos;
|
||||
} else {
|
||||
p2 = points[(i + 1) % n_points] + offset;
|
||||
}
|
||||
|
||||
const Vector2 next_point = xform.xform(p2);
|
||||
p_overlay->draw_line(point, next_point, col, Math::round(2 * EDSCALE));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < n_points; i++) {
|
||||
const Vertex vertex(j, i);
|
||||
|
||||
const Vector2 p = (vertex == edited_point) ? edited_point.pos : (points[i] + offset);
|
||||
const Vector2 point = xform.xform(p);
|
||||
|
||||
const Color overlay_modulate = vertex == active_point ? Color(0.4, 1, 1) : Color(1, 1, 1);
|
||||
p_overlay->draw_texture(handle, point - handle->get_size() * 0.5, overlay_modulate);
|
||||
|
||||
if (vertex == hover_point) {
|
||||
String num = String::num_int64(vertex.vertex);
|
||||
Size2 num_size = font->get_string_size(num, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size);
|
||||
p_overlay->draw_string_outline(font, point - num_size * 0.5, num, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color);
|
||||
p_overlay->draw_string(font, point - num_size * 0.5, num, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (edge_point.valid()) {
|
||||
Ref<Texture2D> add_handle = get_editor_theme_icon(SNAME("EditorHandleAdd"));
|
||||
p_overlay->draw_texture(add_handle, edge_point.pos - add_handle->get_size() * 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::set_edit_origin_and_center(bool p_enabled) {
|
||||
edit_origin_and_center = p_enabled;
|
||||
if (button_center) {
|
||||
button_center->set_visible(edit_origin_and_center);
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::edit(Node *p_polygon) {
|
||||
if (!canvas_item_editor) {
|
||||
canvas_item_editor = CanvasItemEditor::get_singleton();
|
||||
}
|
||||
|
||||
if (p_polygon) {
|
||||
_set_node(p_polygon);
|
||||
|
||||
// Enable the pencil tool if the polygon is empty.
|
||||
if (_is_empty()) {
|
||||
_menu_option(MODE_CREATE);
|
||||
} else {
|
||||
_menu_option(MODE_EDIT);
|
||||
}
|
||||
|
||||
wip.clear();
|
||||
wip_active = false;
|
||||
edited_point = PosVertex();
|
||||
hover_point = Vertex();
|
||||
selected_point = Vertex();
|
||||
center_drag = false;
|
||||
} else {
|
||||
_set_node(nullptr);
|
||||
}
|
||||
|
||||
canvas_item_editor->update_viewport();
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::remove_point(const Vertex &p_vertex) {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
Vector<Vector2> vertices = _get_polygon(p_vertex.polygon);
|
||||
|
||||
if (vertices.size() > (_is_line() ? 2 : 3)) {
|
||||
vertices.remove_at(p_vertex.vertex);
|
||||
|
||||
undo_redo->create_action(TTR("Edit Polygon (Remove Point)"));
|
||||
_action_set_polygon(p_vertex.polygon, vertices);
|
||||
_commit_action();
|
||||
} else {
|
||||
undo_redo->create_action(TTR("Remove Polygon And Point"));
|
||||
_action_remove_polygon(p_vertex.polygon);
|
||||
_commit_action();
|
||||
}
|
||||
|
||||
if (_is_empty()) {
|
||||
_menu_option(MODE_CREATE);
|
||||
}
|
||||
|
||||
hover_point = Vertex();
|
||||
if (selected_point == p_vertex) {
|
||||
selected_point = Vertex();
|
||||
}
|
||||
}
|
||||
|
||||
AbstractPolygon2DEditor::Vertex AbstractPolygon2DEditor::get_active_point() const {
|
||||
return hover_point.valid() ? hover_point : selected_point;
|
||||
}
|
||||
|
||||
AbstractPolygon2DEditor::PosVertex AbstractPolygon2DEditor::closest_point(const Vector2 &p_pos) const {
|
||||
const real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
|
||||
|
||||
const int n_polygons = _get_polygon_count();
|
||||
const Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_screen_transform();
|
||||
|
||||
PosVertex closest;
|
||||
real_t closest_dist = 1e10;
|
||||
|
||||
for (int j = 0; j < n_polygons; j++) {
|
||||
Vector<Vector2> points = _get_polygon(j);
|
||||
const Vector2 offset = _get_offset(j);
|
||||
const int n_points = points.size();
|
||||
|
||||
for (int i = 0; i < n_points; i++) {
|
||||
Vector2 cp = xform.xform(points[i] + offset);
|
||||
|
||||
real_t d = cp.distance_to(p_pos);
|
||||
if (d < closest_dist && d < grab_threshold) {
|
||||
closest_dist = d;
|
||||
closest = PosVertex(j, i, cp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
AbstractPolygon2DEditor::PosVertex AbstractPolygon2DEditor::closest_edge_point(const Vector2 &p_pos) const {
|
||||
const real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
|
||||
const real_t eps = grab_threshold * 2;
|
||||
const real_t eps2 = eps * eps;
|
||||
|
||||
const int n_polygons = _get_polygon_count();
|
||||
const Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_screen_transform();
|
||||
|
||||
PosVertex closest;
|
||||
real_t closest_dist = 1e10;
|
||||
|
||||
for (int j = 0; j < n_polygons; j++) {
|
||||
Vector<Vector2> points = _get_polygon(j);
|
||||
const Vector2 offset = _get_offset(j);
|
||||
const int n_points = points.size();
|
||||
const int n_segments = n_points - (_is_line() ? 1 : 0);
|
||||
|
||||
for (int i = 0; i < n_segments; i++) {
|
||||
const Vector2 segment_a = xform.xform(points[i] + offset);
|
||||
const Vector2 segment_b = xform.xform(points[(i + 1) % n_points] + offset);
|
||||
|
||||
Vector2 cp = Geometry2D::get_closest_point_to_segment(p_pos, segment_a, segment_b);
|
||||
|
||||
if (cp.distance_squared_to(segment_a) < eps2 || cp.distance_squared_to(segment_b) < eps2) {
|
||||
continue; //not valid to reuse point
|
||||
}
|
||||
|
||||
real_t d = cp.distance_to(p_pos);
|
||||
if (d < closest_dist && d < grab_threshold) {
|
||||
closest_dist = d;
|
||||
closest = PosVertex(j, i, cp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
AbstractPolygon2DEditor::AbstractPolygon2DEditor(bool p_wip_destructive) {
|
||||
edited_point = PosVertex();
|
||||
center_drag = false;
|
||||
wip_destructive = p_wip_destructive;
|
||||
|
||||
hover_point = Vertex();
|
||||
selected_point = Vertex();
|
||||
edge_point = PosVertex();
|
||||
|
||||
button_create = memnew(Button);
|
||||
button_create->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
button_create->set_accessibility_name(TTRC("Create Polygon Points"));
|
||||
add_child(button_create);
|
||||
button_create->connect(SceneStringName(pressed), callable_mp(this, &AbstractPolygon2DEditor::_menu_option).bind(MODE_CREATE));
|
||||
button_create->set_toggle_mode(true);
|
||||
|
||||
button_edit = memnew(Button);
|
||||
button_edit->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
button_edit->set_accessibility_name(TTRC("Edit Polygon Points"));
|
||||
add_child(button_edit);
|
||||
button_edit->connect(SceneStringName(pressed), callable_mp(this, &AbstractPolygon2DEditor::_menu_option).bind(MODE_EDIT));
|
||||
button_edit->set_toggle_mode(true);
|
||||
|
||||
button_delete = memnew(Button);
|
||||
button_delete->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
button_delete->set_accessibility_name(TTRC("Delete Polygon Points"));
|
||||
add_child(button_delete);
|
||||
button_delete->connect(SceneStringName(pressed), callable_mp(this, &AbstractPolygon2DEditor::_menu_option).bind(MODE_DELETE));
|
||||
button_delete->set_toggle_mode(true);
|
||||
|
||||
button_center = memnew(Button);
|
||||
button_center->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
add_child(button_center);
|
||||
button_center->connect(SceneStringName(pressed), callable_mp(this, &AbstractPolygon2DEditor::_menu_option).bind(CENTER_POLY));
|
||||
button_center->set_visible(edit_origin_and_center);
|
||||
|
||||
create_resource = memnew(ConfirmationDialog);
|
||||
add_child(create_resource);
|
||||
create_resource->set_ok_button_text(TTR("Create"));
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditorPlugin::edit(Object *p_object) {
|
||||
Node *polygon_node = Object::cast_to<Node>(p_object);
|
||||
polygon_editor->edit(polygon_node);
|
||||
make_visible(polygon_node != nullptr);
|
||||
}
|
||||
|
||||
bool AbstractPolygon2DEditorPlugin::handles(Object *p_object) const {
|
||||
return p_object->is_class(klass);
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditorPlugin::make_visible(bool p_visible) {
|
||||
if (p_visible) {
|
||||
polygon_editor->show();
|
||||
} else {
|
||||
polygon_editor->hide();
|
||||
polygon_editor->edit(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
AbstractPolygon2DEditorPlugin::AbstractPolygon2DEditorPlugin(AbstractPolygon2DEditor *p_polygon_editor, const String &p_class) :
|
||||
polygon_editor(p_polygon_editor),
|
||||
klass(p_class) {
|
||||
CanvasItemEditor::get_singleton()->add_control_to_menu_panel(polygon_editor);
|
||||
polygon_editor->hide();
|
||||
}
|
||||
|
|
@ -1,177 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* abstract_polygon_2d_editor.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/2d/node_2d.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
|
||||
class Button;
|
||||
class CanvasItemEditor;
|
||||
class ConfirmationDialog;
|
||||
|
||||
class AbstractPolygon2DEditor : public HBoxContainer {
|
||||
GDCLASS(AbstractPolygon2DEditor, HBoxContainer);
|
||||
|
||||
Button *button_create = nullptr;
|
||||
Button *button_edit = nullptr;
|
||||
Button *button_delete = nullptr;
|
||||
Button *button_center = nullptr;
|
||||
|
||||
struct Vertex {
|
||||
Vertex() {}
|
||||
Vertex(int p_vertex) :
|
||||
vertex(p_vertex) {}
|
||||
Vertex(int p_polygon, int p_vertex) :
|
||||
polygon(p_polygon),
|
||||
vertex(p_vertex) {}
|
||||
|
||||
bool operator==(const Vertex &p_vertex) const;
|
||||
bool operator!=(const Vertex &p_vertex) const;
|
||||
|
||||
bool valid() const;
|
||||
|
||||
int polygon = -1;
|
||||
int vertex = -1;
|
||||
};
|
||||
|
||||
struct PosVertex : public Vertex {
|
||||
PosVertex() {}
|
||||
PosVertex(const Vertex &p_vertex, const Vector2 &p_pos) :
|
||||
Vertex(p_vertex.polygon, p_vertex.vertex),
|
||||
pos(p_pos) {}
|
||||
PosVertex(int p_polygon, int p_vertex, const Vector2 &p_pos) :
|
||||
Vertex(p_polygon, p_vertex),
|
||||
pos(p_pos) {}
|
||||
|
||||
Vector2 pos;
|
||||
};
|
||||
|
||||
PosVertex edited_point;
|
||||
Vertex hover_point; // point under mouse cursor
|
||||
Vertex selected_point; // currently selected
|
||||
PosVertex edge_point; // adding an edge point?
|
||||
Vector2 original_mouse_pos;
|
||||
|
||||
Vector<Vector2> pre_move_edit;
|
||||
Vector<Vector2> wip;
|
||||
bool wip_active = false;
|
||||
bool wip_destructive = false;
|
||||
|
||||
Vector<Vector<Vector2>> pre_center_move_edit;
|
||||
bool center_drag = false;
|
||||
Vector2 center_drag_origin;
|
||||
bool edit_origin_and_center = false;
|
||||
|
||||
bool _polygon_editing_enabled = false;
|
||||
|
||||
CanvasItemEditor *canvas_item_editor = nullptr;
|
||||
Panel *panel = nullptr;
|
||||
ConfirmationDialog *create_resource = nullptr;
|
||||
|
||||
protected:
|
||||
enum {
|
||||
MODE_CREATE,
|
||||
MODE_EDIT,
|
||||
MODE_DELETE,
|
||||
MODE_CONT,
|
||||
CENTER_POLY,
|
||||
};
|
||||
|
||||
int mode = MODE_EDIT;
|
||||
|
||||
virtual void _menu_option(int p_option);
|
||||
void _wip_changed();
|
||||
void _wip_close();
|
||||
void _wip_cancel();
|
||||
|
||||
void _notification(int p_what);
|
||||
void _node_removed(Node *p_node);
|
||||
|
||||
bool _commit_drag();
|
||||
|
||||
void remove_point(const Vertex &p_vertex);
|
||||
Vertex get_active_point() const;
|
||||
PosVertex closest_point(const Vector2 &p_pos) const;
|
||||
PosVertex closest_edge_point(const Vector2 &p_pos) const;
|
||||
|
||||
bool _is_empty() const;
|
||||
|
||||
virtual Node2D *_get_node() const = 0;
|
||||
virtual void _set_node(Node *p_polygon) = 0;
|
||||
|
||||
virtual bool _is_line() const;
|
||||
virtual bool _has_uv() const;
|
||||
virtual int _get_polygon_count() const;
|
||||
virtual Vector2 _get_offset(int p_idx) const;
|
||||
virtual Variant _get_polygon(int p_idx) const;
|
||||
virtual void _set_polygon(int p_idx, const Variant &p_polygon) const;
|
||||
|
||||
virtual void _action_add_polygon(const Variant &p_polygon);
|
||||
virtual void _action_remove_polygon(int p_idx);
|
||||
virtual void _action_set_polygon(int p_idx, const Variant &p_polygon);
|
||||
virtual void _action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon);
|
||||
virtual void _commit_action();
|
||||
|
||||
virtual Vector2 _get_geometric_center() const;
|
||||
|
||||
virtual bool _has_resource() const;
|
||||
virtual void _create_resource();
|
||||
|
||||
public:
|
||||
void disable_polygon_editing(bool p_disable, const String &p_reason);
|
||||
|
||||
bool forward_gui_input(const Ref<InputEvent> &p_event);
|
||||
void forward_canvas_draw_over_viewport(Control *p_overlay);
|
||||
void set_edit_origin_and_center(bool p_enabled);
|
||||
|
||||
void edit(Node *p_polygon);
|
||||
AbstractPolygon2DEditor(bool p_wip_destructive = true);
|
||||
};
|
||||
|
||||
class AbstractPolygon2DEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(AbstractPolygon2DEditorPlugin, EditorPlugin);
|
||||
|
||||
AbstractPolygon2DEditor *polygon_editor = nullptr;
|
||||
String klass;
|
||||
|
||||
public:
|
||||
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override { return polygon_editor->forward_gui_input(p_event); }
|
||||
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { polygon_editor->forward_canvas_draw_over_viewport(p_overlay); }
|
||||
|
||||
bool has_main_screen() const override { return false; }
|
||||
virtual String get_plugin_name() const override { return klass; }
|
||||
virtual void edit(Object *p_object) override;
|
||||
virtual bool handles(Object *p_object) const override;
|
||||
virtual void make_visible(bool p_visible) override;
|
||||
|
||||
AbstractPolygon2DEditorPlugin(AbstractPolygon2DEditor *p_polygon_editor, const String &p_class);
|
||||
};
|
||||
|
|
@ -1,821 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* animation_blend_space_1d_editor.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 "animation_blend_space_1d_editor.h"
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/gui/editor_file_dialog.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/animation/animation_blend_tree.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/check_box.h"
|
||||
#include "scene/gui/line_edit.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
#include "scene/gui/panel_container.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/gui/spin_box.h"
|
||||
|
||||
StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const {
|
||||
StringName path = AnimationTreeEditor::get_singleton()->get_base_path() + "blend_position";
|
||||
return path;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEvent> &p_event) {
|
||||
AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();
|
||||
if (!tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<InputEventKey> k = p_event;
|
||||
|
||||
if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_keycode() == Key::KEY_DELETE && !k->is_echo()) {
|
||||
if (selected_point != -1) {
|
||||
if (!read_only) {
|
||||
_erase_selected();
|
||||
}
|
||||
accept_event();
|
||||
}
|
||||
}
|
||||
|
||||
Ref<InputEventMouseButton> mb = p_event;
|
||||
|
||||
if (mb.is_valid() && mb->is_pressed() && ((tool_select->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) || (mb->get_button_index() == MouseButton::LEFT && tool_create->is_pressed()))) {
|
||||
if (!read_only) {
|
||||
menu->clear(false);
|
||||
animations_menu->clear();
|
||||
animations_to_add.clear();
|
||||
|
||||
LocalVector<StringName> classes;
|
||||
ClassDB::get_inheriters_from_class("AnimationRootNode", classes);
|
||||
classes.sort_custom<StringName::AlphCompare>();
|
||||
|
||||
menu->add_submenu_node_item(TTR("Add Animation"), animations_menu);
|
||||
|
||||
List<StringName> names;
|
||||
tree->get_animation_list(&names);
|
||||
|
||||
for (const StringName &E : names) {
|
||||
animations_menu->add_icon_item(get_editor_theme_icon(SNAME("Animation")), E);
|
||||
animations_to_add.push_back(E);
|
||||
}
|
||||
|
||||
for (const StringName &E : classes) {
|
||||
String name = String(E).replace_first("AnimationNode", "");
|
||||
if (name == "Animation" || name == "StartState" || name == "EndState") {
|
||||
continue;
|
||||
}
|
||||
|
||||
int idx = menu->get_item_count();
|
||||
menu->add_item(vformat(TTR("Add %s"), name), idx);
|
||||
menu->set_item_metadata(idx, E);
|
||||
}
|
||||
|
||||
Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard();
|
||||
if (clipb.is_valid()) {
|
||||
menu->add_separator();
|
||||
menu->add_item(TTR("Paste"), MENU_PASTE);
|
||||
}
|
||||
menu->add_separator();
|
||||
menu->add_item(TTR("Load..."), MENU_LOAD_FILE);
|
||||
|
||||
menu->set_position(blend_space_draw->get_screen_position() + mb->get_position());
|
||||
menu->reset_size();
|
||||
menu->popup();
|
||||
|
||||
add_point_pos = (mb->get_position() / blend_space_draw->get_size()).x;
|
||||
add_point_pos *= (blend_space->get_max_space() - blend_space->get_min_space());
|
||||
add_point_pos += blend_space->get_min_space();
|
||||
|
||||
if (snap->is_pressed()) {
|
||||
add_point_pos = Math::snapped(add_point_pos, blend_space->get_snap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mb.is_valid() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
|
||||
blend_space_draw->queue_redraw(); // why not
|
||||
|
||||
// try to see if a point can be selected
|
||||
selected_point = -1;
|
||||
_update_tool_erase();
|
||||
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
if (Math::abs(float(points[i] - mb->get_position().x)) < 10 * EDSCALE) {
|
||||
selected_point = i;
|
||||
|
||||
Ref<AnimationNode> node = blend_space->get_blend_point_node(i);
|
||||
EditorNode::get_singleton()->push_item(node.ptr(), "", true);
|
||||
dragging_selected_attempt = true;
|
||||
drag_from = mb->get_position();
|
||||
_update_tool_erase();
|
||||
_update_edited_point_pos();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mb.is_valid() && !mb->is_pressed() && dragging_selected_attempt && mb->get_button_index() == MouseButton::LEFT) {
|
||||
if (!read_only) {
|
||||
if (dragging_selected) {
|
||||
// move
|
||||
float point = blend_space->get_blend_point_position(selected_point);
|
||||
point += drag_ofs.x;
|
||||
|
||||
if (snap->is_pressed()) {
|
||||
point = Math::snapped(point, blend_space->get_snap());
|
||||
}
|
||||
|
||||
updating = true;
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Move Node Point"));
|
||||
undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point);
|
||||
undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point));
|
||||
undo_redo->add_do_method(this, "_update_space");
|
||||
undo_redo->add_undo_method(this, "_update_space");
|
||||
undo_redo->add_do_method(this, "_update_edited_point_pos");
|
||||
undo_redo->add_undo_method(this, "_update_edited_point_pos");
|
||||
undo_redo->commit_action();
|
||||
updating = false;
|
||||
_update_edited_point_pos();
|
||||
}
|
||||
|
||||
dragging_selected_attempt = false;
|
||||
dragging_selected = false;
|
||||
blend_space_draw->queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
// *set* the blend
|
||||
if (mb.is_valid() && !mb->is_pressed() && tool_blend->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
|
||||
float blend_pos = mb->get_position().x / blend_space_draw->get_size().x;
|
||||
blend_pos *= blend_space->get_max_space() - blend_space->get_min_space();
|
||||
blend_pos += blend_space->get_min_space();
|
||||
|
||||
tree->set(get_blend_position_path(), blend_pos);
|
||||
blend_space_draw->queue_redraw();
|
||||
}
|
||||
|
||||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
|
||||
if (mm.is_valid() && !blend_space_draw->has_focus()) {
|
||||
blend_space_draw->grab_focus();
|
||||
blend_space_draw->queue_redraw();
|
||||
}
|
||||
|
||||
if (mm.is_valid() && dragging_selected_attempt) {
|
||||
dragging_selected = true;
|
||||
drag_ofs = ((mm->get_position() - drag_from) / blend_space_draw->get_size()) * ((blend_space->get_max_space() - blend_space->get_min_space()) * Vector2(1, 0));
|
||||
blend_space_draw->queue_redraw();
|
||||
_update_edited_point_pos();
|
||||
}
|
||||
|
||||
if (mm.is_valid() && tool_blend->is_pressed() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
|
||||
float blend_pos = mm->get_position().x / blend_space_draw->get_size().x;
|
||||
blend_pos *= blend_space->get_max_space() - blend_space->get_min_space();
|
||||
blend_pos += blend_space->get_min_space();
|
||||
|
||||
tree->set(get_blend_position_path(), blend_pos);
|
||||
|
||||
blend_space_draw->queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::_blend_space_draw() {
|
||||
AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();
|
||||
if (!tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
Color linecolor = get_theme_color(SceneStringName(font_color), SNAME("Label"));
|
||||
Color linecolor_soft = linecolor;
|
||||
linecolor_soft.a *= 0.5;
|
||||
|
||||
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
|
||||
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
|
||||
Ref<Texture2D> icon = get_editor_theme_icon(SNAME("KeyValue"));
|
||||
Ref<Texture2D> icon_selected = get_editor_theme_icon(SNAME("KeySelected"));
|
||||
|
||||
Size2 s = blend_space_draw->get_size();
|
||||
|
||||
if (blend_space_draw->has_focus()) {
|
||||
Color color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
|
||||
blend_space_draw->draw_rect(Rect2(Point2(), s), color, false);
|
||||
}
|
||||
|
||||
blend_space_draw->draw_line(Point2(1, s.height - 1), Point2(s.width - 1, s.height - 1), linecolor, Math::round(EDSCALE));
|
||||
|
||||
if (blend_space->get_min_space() < 0) {
|
||||
float point = 0.0;
|
||||
point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
|
||||
point *= s.width;
|
||||
|
||||
float x = point;
|
||||
|
||||
blend_space_draw->draw_line(Point2(x, s.height - 1), Point2(x, s.height - 5 * EDSCALE), linecolor, Math::round(EDSCALE));
|
||||
blend_space_draw->draw_string(font, Point2(x + 2 * EDSCALE, s.height - 2 * EDSCALE - font->get_height(font_size) + font->get_ascent(font_size)), "0", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, linecolor);
|
||||
blend_space_draw->draw_line(Point2(x, s.height - 5 * EDSCALE), Point2(x, 0), linecolor_soft, Math::round(EDSCALE));
|
||||
}
|
||||
|
||||
if (snap->is_pressed()) {
|
||||
linecolor_soft.a = linecolor.a * 0.1;
|
||||
|
||||
if (blend_space->get_snap() > 0) {
|
||||
int prev_idx = -1;
|
||||
|
||||
for (int i = 0; i < s.x; i++) {
|
||||
float v = blend_space->get_min_space() + i * (blend_space->get_max_space() - blend_space->get_min_space()) / s.x;
|
||||
int idx = int(v / blend_space->get_snap());
|
||||
|
||||
if (i > 0 && prev_idx != idx) {
|
||||
blend_space_draw->draw_line(Point2(i, 0), Point2(i, s.height), linecolor_soft, Math::round(EDSCALE));
|
||||
}
|
||||
|
||||
prev_idx = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
points.clear();
|
||||
|
||||
for (int i = 0; i < blend_space->get_blend_point_count(); i++) {
|
||||
float point = blend_space->get_blend_point_position(i);
|
||||
|
||||
if (!read_only) {
|
||||
if (dragging_selected && selected_point == i) {
|
||||
point += drag_ofs.x;
|
||||
if (snap->is_pressed()) {
|
||||
point = Math::snapped(point, blend_space->get_snap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
|
||||
point *= s.width;
|
||||
|
||||
points.push_back(point);
|
||||
|
||||
Vector2 gui_point = Vector2(point, s.height / 2.0);
|
||||
|
||||
gui_point -= (icon->get_size() / 2.0);
|
||||
|
||||
gui_point = gui_point.floor();
|
||||
|
||||
if (i == selected_point) {
|
||||
blend_space_draw->draw_texture(icon_selected, gui_point);
|
||||
} else {
|
||||
blend_space_draw->draw_texture(icon, gui_point);
|
||||
}
|
||||
}
|
||||
|
||||
// blend position
|
||||
{
|
||||
Color color;
|
||||
if (tool_blend->is_pressed()) {
|
||||
color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
|
||||
} else {
|
||||
color = linecolor;
|
||||
color.a *= 0.5;
|
||||
}
|
||||
|
||||
float point = tree->get(get_blend_position_path());
|
||||
|
||||
point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
|
||||
point *= s.width;
|
||||
|
||||
Vector2 gui_point = Vector2(point, s.height / 2.0);
|
||||
|
||||
float mind = 5 * EDSCALE;
|
||||
float maxd = 15 * EDSCALE;
|
||||
blend_space_draw->draw_line(gui_point + Vector2(mind, 0), gui_point + Vector2(maxd, 0), color, Math::round(2 * EDSCALE));
|
||||
blend_space_draw->draw_line(gui_point + Vector2(-mind, 0), gui_point + Vector2(-maxd, 0), color, Math::round(2 * EDSCALE));
|
||||
blend_space_draw->draw_line(gui_point + Vector2(0, mind), gui_point + Vector2(0, maxd), color, Math::round(2 * EDSCALE));
|
||||
blend_space_draw->draw_line(gui_point + Vector2(0, -mind), gui_point + Vector2(0, -maxd), color, Math::round(2 * EDSCALE));
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::_update_space() {
|
||||
if (updating) {
|
||||
return;
|
||||
}
|
||||
|
||||
updating = true;
|
||||
|
||||
max_value->set_value(blend_space->get_max_space());
|
||||
min_value->set_value(blend_space->get_min_space());
|
||||
|
||||
sync->set_pressed(blend_space->is_using_sync());
|
||||
interpolation->select(blend_space->get_blend_mode());
|
||||
|
||||
label_value->set_text(blend_space->get_value_label());
|
||||
|
||||
snap_value->set_value(blend_space->get_snap());
|
||||
|
||||
blend_space_draw->queue_redraw();
|
||||
|
||||
updating = false;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::_config_changed(double) {
|
||||
if (updating) {
|
||||
return;
|
||||
}
|
||||
|
||||
updating = true;
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Change BlendSpace1D Config"));
|
||||
undo_redo->add_do_method(blend_space.ptr(), "set_max_space", max_value->get_value());
|
||||
undo_redo->add_undo_method(blend_space.ptr(), "set_max_space", blend_space->get_max_space());
|
||||
undo_redo->add_do_method(blend_space.ptr(), "set_min_space", min_value->get_value());
|
||||
undo_redo->add_undo_method(blend_space.ptr(), "set_min_space", blend_space->get_min_space());
|
||||
undo_redo->add_do_method(blend_space.ptr(), "set_snap", snap_value->get_value());
|
||||
undo_redo->add_undo_method(blend_space.ptr(), "set_snap", blend_space->get_snap());
|
||||
undo_redo->add_do_method(blend_space.ptr(), "set_use_sync", sync->is_pressed());
|
||||
undo_redo->add_undo_method(blend_space.ptr(), "set_use_sync", blend_space->is_using_sync());
|
||||
undo_redo->add_do_method(blend_space.ptr(), "set_blend_mode", interpolation->get_selected());
|
||||
undo_redo->add_undo_method(blend_space.ptr(), "set_blend_mode", blend_space->get_blend_mode());
|
||||
undo_redo->add_do_method(this, "_update_space");
|
||||
undo_redo->add_undo_method(this, "_update_space");
|
||||
undo_redo->commit_action();
|
||||
updating = false;
|
||||
|
||||
blend_space_draw->queue_redraw();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::_labels_changed(String) {
|
||||
if (updating) {
|
||||
return;
|
||||
}
|
||||
|
||||
updating = true;
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Change BlendSpace1D Labels"), UndoRedo::MERGE_ENDS);
|
||||
undo_redo->add_do_method(blend_space.ptr(), "set_value_label", label_value->get_text());
|
||||
undo_redo->add_undo_method(blend_space.ptr(), "set_value_label", blend_space->get_value_label());
|
||||
undo_redo->add_do_method(this, "_update_space");
|
||||
undo_redo->add_undo_method(this, "_update_space");
|
||||
undo_redo->commit_action();
|
||||
updating = false;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::_snap_toggled() {
|
||||
blend_space_draw->queue_redraw();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::_file_opened(const String &p_file) {
|
||||
file_loaded = ResourceLoader::load(p_file);
|
||||
if (file_loaded.is_valid()) {
|
||||
_add_menu_type(MENU_LOAD_FILE_CONFIRM);
|
||||
} else {
|
||||
EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only animation nodes are allowed."));
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::_add_menu_type(int p_index) {
|
||||
Ref<AnimationRootNode> node;
|
||||
if (p_index == MENU_LOAD_FILE) {
|
||||
open_file->clear_filters();
|
||||
List<String> filters;
|
||||
ResourceLoader::get_recognized_extensions_for_type("AnimationRootNode", &filters);
|
||||
for (const String &E : filters) {
|
||||
open_file->add_filter("*." + E);
|
||||
}
|
||||
open_file->popup_file_dialog();
|
||||
return;
|
||||
} else if (p_index == MENU_LOAD_FILE_CONFIRM) {
|
||||
node = file_loaded;
|
||||
file_loaded.unref();
|
||||
} else if (p_index == MENU_PASTE) {
|
||||
node = EditorSettings::get_singleton()->get_resource_clipboard();
|
||||
} else {
|
||||
String type = menu->get_item_metadata(p_index);
|
||||
|
||||
Object *obj = ClassDB::instantiate(type);
|
||||
ERR_FAIL_NULL(obj);
|
||||
AnimationNode *an = Object::cast_to<AnimationNode>(obj);
|
||||
ERR_FAIL_NULL(an);
|
||||
|
||||
node = Ref<AnimationNode>(an);
|
||||
}
|
||||
|
||||
if (node.is_null()) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only root nodes are allowed."));
|
||||
return;
|
||||
}
|
||||
|
||||
updating = true;
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Add Node Point"));
|
||||
undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", node, add_point_pos);
|
||||
undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count());
|
||||
undo_redo->add_do_method(this, "_update_space");
|
||||
undo_redo->add_undo_method(this, "_update_space");
|
||||
undo_redo->commit_action();
|
||||
updating = false;
|
||||
|
||||
blend_space_draw->queue_redraw();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::_add_animation_type(int p_index) {
|
||||
Ref<AnimationNodeAnimation> anim;
|
||||
anim.instantiate();
|
||||
|
||||
anim->set_animation(animations_to_add[p_index]);
|
||||
|
||||
updating = true;
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Add Animation Point"));
|
||||
undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", anim, add_point_pos);
|
||||
undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count());
|
||||
undo_redo->add_do_method(this, "_update_space");
|
||||
undo_redo->add_undo_method(this, "_update_space");
|
||||
undo_redo->commit_action();
|
||||
updating = false;
|
||||
|
||||
blend_space_draw->queue_redraw();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::_tool_switch(int p_tool) {
|
||||
if (p_tool == 0) {
|
||||
tool_erase->show();
|
||||
tool_erase_sep->show();
|
||||
} else {
|
||||
tool_erase->hide();
|
||||
tool_erase_sep->hide();
|
||||
}
|
||||
|
||||
_update_tool_erase();
|
||||
blend_space_draw->queue_redraw();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::_update_edited_point_pos() {
|
||||
if (updating) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) {
|
||||
float pos = blend_space->get_blend_point_position(selected_point);
|
||||
|
||||
if (dragging_selected) {
|
||||
pos += drag_ofs.x;
|
||||
|
||||
if (snap->is_pressed()) {
|
||||
pos = Math::snapped(pos, blend_space->get_snap());
|
||||
}
|
||||
}
|
||||
|
||||
updating = true;
|
||||
edit_value->set_value(pos);
|
||||
updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::_update_tool_erase() {
|
||||
bool point_valid = selected_point >= 0 && selected_point < blend_space->get_blend_point_count();
|
||||
tool_erase->set_disabled(!point_valid || read_only);
|
||||
|
||||
if (point_valid) {
|
||||
Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point);
|
||||
|
||||
if (AnimationTreeEditor::get_singleton()->can_edit(an)) {
|
||||
open_editor->show();
|
||||
} else {
|
||||
open_editor->hide();
|
||||
}
|
||||
|
||||
if (!read_only) {
|
||||
edit_hb->show();
|
||||
} else {
|
||||
edit_hb->hide();
|
||||
}
|
||||
} else {
|
||||
edit_hb->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::_erase_selected() {
|
||||
if (selected_point != -1) {
|
||||
updating = true;
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Remove BlendSpace1D Point"));
|
||||
undo_redo->add_do_method(blend_space.ptr(), "remove_blend_point", selected_point);
|
||||
undo_redo->add_undo_method(blend_space.ptr(), "add_blend_point", blend_space->get_blend_point_node(selected_point), blend_space->get_blend_point_position(selected_point), selected_point);
|
||||
undo_redo->add_do_method(this, "_update_space");
|
||||
undo_redo->add_undo_method(this, "_update_space");
|
||||
undo_redo->commit_action();
|
||||
|
||||
updating = false;
|
||||
|
||||
blend_space_draw->queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::_edit_point_pos(double) {
|
||||
if (updating) {
|
||||
return;
|
||||
}
|
||||
|
||||
updating = true;
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Move BlendSpace1D Node Point"));
|
||||
undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, edit_value->get_value());
|
||||
undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point));
|
||||
undo_redo->add_do_method(this, "_update_space");
|
||||
undo_redo->add_undo_method(this, "_update_space");
|
||||
undo_redo->add_do_method(this, "_update_edited_point_pos");
|
||||
undo_redo->add_undo_method(this, "_update_edited_point_pos");
|
||||
undo_redo->commit_action();
|
||||
updating = false;
|
||||
|
||||
blend_space_draw->queue_redraw();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::_open_editor() {
|
||||
if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) {
|
||||
Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point);
|
||||
ERR_FAIL_COND(an.is_null());
|
||||
AnimationTreeEditor::get_singleton()->enter_editor(itos(selected_point));
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
error_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
|
||||
error_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
|
||||
panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
|
||||
tool_blend->set_button_icon(get_editor_theme_icon(SNAME("EditPivot")));
|
||||
tool_select->set_button_icon(get_editor_theme_icon(SNAME("ToolSelect")));
|
||||
tool_create->set_button_icon(get_editor_theme_icon(SNAME("EditKey")));
|
||||
tool_erase->set_button_icon(get_editor_theme_icon(SNAME("Remove")));
|
||||
snap->set_button_icon(get_editor_theme_icon(SNAME("SnapGrid")));
|
||||
open_editor->set_button_icon(get_editor_theme_icon(SNAME("Edit")));
|
||||
interpolation->clear();
|
||||
interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackContinuous")), TTR("Continuous"), 0);
|
||||
interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackDiscrete")), TTR("Discrete"), 1);
|
||||
interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackCapture")), TTR("Capture"), 2);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_PROCESS: {
|
||||
AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();
|
||||
if (!tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
String error;
|
||||
|
||||
if (!tree->is_active()) {
|
||||
error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails.");
|
||||
} else if (tree->is_state_invalid()) {
|
||||
error = tree->get_invalid_state_reason();
|
||||
}
|
||||
|
||||
if (error != error_label->get_text()) {
|
||||
error_label->set_text(error);
|
||||
if (!error.is_empty()) {
|
||||
error_panel->show();
|
||||
} else {
|
||||
error_panel->hide();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
set_process(is_visible_in_tree());
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::_bind_methods() {
|
||||
ClassDB::bind_method("_update_space", &AnimationNodeBlendSpace1DEditor::_update_space);
|
||||
ClassDB::bind_method("_update_tool_erase", &AnimationNodeBlendSpace1DEditor::_update_tool_erase);
|
||||
|
||||
ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpace1DEditor::_update_edited_point_pos);
|
||||
}
|
||||
|
||||
bool AnimationNodeBlendSpace1DEditor::can_edit(const Ref<AnimationNode> &p_node) {
|
||||
Ref<AnimationNodeBlendSpace1D> b1d = p_node;
|
||||
return b1d.is_valid();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendSpace1DEditor::edit(const Ref<AnimationNode> &p_node) {
|
||||
blend_space = p_node;
|
||||
read_only = false;
|
||||
|
||||
if (blend_space.is_valid()) {
|
||||
read_only = EditorNode::get_singleton()->is_resource_read_only(blend_space);
|
||||
|
||||
_update_space();
|
||||
}
|
||||
|
||||
tool_create->set_disabled(read_only);
|
||||
edit_value->set_editable(!read_only);
|
||||
label_value->set_editable(!read_only);
|
||||
min_value->set_editable(!read_only);
|
||||
max_value->set_editable(!read_only);
|
||||
sync->set_disabled(read_only);
|
||||
interpolation->set_disabled(read_only);
|
||||
}
|
||||
|
||||
AnimationNodeBlendSpace1DEditor *AnimationNodeBlendSpace1DEditor::singleton = nullptr;
|
||||
|
||||
AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() {
|
||||
singleton = this;
|
||||
|
||||
HBoxContainer *top_hb = memnew(HBoxContainer);
|
||||
add_child(top_hb);
|
||||
|
||||
Ref<ButtonGroup> bg;
|
||||
bg.instantiate();
|
||||
|
||||
tool_blend = memnew(Button);
|
||||
tool_blend->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
tool_blend->set_toggle_mode(true);
|
||||
tool_blend->set_button_group(bg);
|
||||
top_hb->add_child(tool_blend);
|
||||
tool_blend->set_pressed(true);
|
||||
tool_blend->set_tooltip_text(TTR("Set the blending position within the space"));
|
||||
tool_blend->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_tool_switch).bind(3));
|
||||
|
||||
tool_select = memnew(Button);
|
||||
tool_select->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
tool_select->set_toggle_mode(true);
|
||||
tool_select->set_button_group(bg);
|
||||
top_hb->add_child(tool_select);
|
||||
tool_select->set_tooltip_text(TTR("Select and move points, create points with RMB."));
|
||||
tool_select->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_tool_switch).bind(0));
|
||||
|
||||
tool_create = memnew(Button);
|
||||
tool_create->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
tool_create->set_toggle_mode(true);
|
||||
tool_create->set_button_group(bg);
|
||||
top_hb->add_child(tool_create);
|
||||
tool_create->set_tooltip_text(TTR("Create points."));
|
||||
tool_create->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_tool_switch).bind(1));
|
||||
|
||||
tool_erase_sep = memnew(VSeparator);
|
||||
top_hb->add_child(tool_erase_sep);
|
||||
tool_erase = memnew(Button);
|
||||
tool_erase->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
top_hb->add_child(tool_erase);
|
||||
tool_erase->set_tooltip_text(TTR("Erase points."));
|
||||
tool_erase->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_erase_selected));
|
||||
|
||||
top_hb->add_child(memnew(VSeparator));
|
||||
|
||||
snap = memnew(Button);
|
||||
snap->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
snap->set_toggle_mode(true);
|
||||
top_hb->add_child(snap);
|
||||
snap->set_pressed(true);
|
||||
snap->set_tooltip_text(TTR("Enable snap and show grid."));
|
||||
snap->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_snap_toggled));
|
||||
|
||||
snap_value = memnew(SpinBox);
|
||||
top_hb->add_child(snap_value);
|
||||
snap_value->set_min(0.01);
|
||||
snap_value->set_step(0.01);
|
||||
snap_value->set_max(1000);
|
||||
snap_value->set_accessibility_name(TTRC("Grid Step"));
|
||||
|
||||
top_hb->add_child(memnew(VSeparator));
|
||||
top_hb->add_child(memnew(Label(TTR("Sync:"))));
|
||||
sync = memnew(CheckBox);
|
||||
top_hb->add_child(sync);
|
||||
sync->connect(SceneStringName(toggled), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed));
|
||||
|
||||
top_hb->add_child(memnew(VSeparator));
|
||||
|
||||
top_hb->add_child(memnew(Label(TTR("Blend:"))));
|
||||
interpolation = memnew(OptionButton);
|
||||
top_hb->add_child(interpolation);
|
||||
interpolation->connect(SceneStringName(item_selected), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed));
|
||||
|
||||
edit_hb = memnew(HBoxContainer);
|
||||
top_hb->add_child(edit_hb);
|
||||
edit_hb->add_child(memnew(VSeparator));
|
||||
edit_hb->add_child(memnew(Label(TTR("Point"))));
|
||||
|
||||
edit_value = memnew(SpinBox);
|
||||
edit_hb->add_child(edit_value);
|
||||
edit_value->set_min(-1000);
|
||||
edit_value->set_max(1000);
|
||||
edit_value->set_step(0.01);
|
||||
edit_value->set_accessibility_name(TTRC("Blend Value"));
|
||||
edit_value->connect(SceneStringName(value_changed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_edit_point_pos));
|
||||
|
||||
open_editor = memnew(Button);
|
||||
edit_hb->add_child(open_editor);
|
||||
open_editor->set_text(TTR("Open Editor"));
|
||||
open_editor->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_open_editor), CONNECT_DEFERRED);
|
||||
|
||||
edit_hb->hide();
|
||||
open_editor->hide();
|
||||
|
||||
VBoxContainer *main_vb = memnew(VBoxContainer);
|
||||
add_child(main_vb);
|
||||
main_vb->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
|
||||
panel = memnew(PanelContainer);
|
||||
panel->set_clip_contents(true);
|
||||
main_vb->add_child(panel);
|
||||
panel->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
panel->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
|
||||
blend_space_draw = memnew(Control);
|
||||
blend_space_draw->connect(SceneStringName(gui_input), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_blend_space_gui_input));
|
||||
blend_space_draw->connect(SceneStringName(draw), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_blend_space_draw));
|
||||
blend_space_draw->set_focus_mode(FOCUS_ALL);
|
||||
|
||||
panel->add_child(blend_space_draw);
|
||||
|
||||
{
|
||||
HBoxContainer *bottom_hb = memnew(HBoxContainer);
|
||||
main_vb->add_child(bottom_hb);
|
||||
bottom_hb->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
|
||||
min_value = memnew(SpinBox);
|
||||
min_value->set_min(-10000);
|
||||
min_value->set_max(0);
|
||||
min_value->set_step(0.01);
|
||||
min_value->set_accessibility_name(TTRC("Min"));
|
||||
|
||||
max_value = memnew(SpinBox);
|
||||
max_value->set_min(0.01);
|
||||
max_value->set_max(10000);
|
||||
max_value->set_step(0.01);
|
||||
max_value->set_accessibility_name(TTRC("Max"));
|
||||
|
||||
label_value = memnew(LineEdit);
|
||||
label_value->set_expand_to_text_length_enabled(true);
|
||||
label_value->set_accessibility_name(TTRC("Value"));
|
||||
|
||||
// now add
|
||||
|
||||
bottom_hb->add_child(min_value);
|
||||
bottom_hb->add_spacer();
|
||||
bottom_hb->add_child(label_value);
|
||||
bottom_hb->add_spacer();
|
||||
bottom_hb->add_child(max_value);
|
||||
}
|
||||
|
||||
snap_value->connect(SceneStringName(value_changed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed));
|
||||
min_value->connect(SceneStringName(value_changed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed));
|
||||
max_value->connect(SceneStringName(value_changed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed));
|
||||
label_value->connect(SceneStringName(text_changed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_labels_changed));
|
||||
|
||||
error_panel = memnew(PanelContainer);
|
||||
add_child(error_panel);
|
||||
|
||||
error_label = memnew(Label);
|
||||
error_label->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
error_panel->add_child(error_label);
|
||||
|
||||
menu = memnew(PopupMenu);
|
||||
add_child(menu);
|
||||
menu->connect(SceneStringName(id_pressed), callable_mp(this, &AnimationNodeBlendSpace1DEditor::_add_menu_type));
|
||||
|
||||
animations_menu = memnew(PopupMenu);
|
||||
animations_menu->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
menu->add_child(animations_menu);
|
||||
animations_menu->connect("index_pressed", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_add_animation_type));
|
||||
|
||||
open_file = memnew(EditorFileDialog);
|
||||
add_child(open_file);
|
||||
open_file->set_title(TTR("Open Animation Node"));
|
||||
open_file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
|
||||
open_file->connect("file_selected", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_file_opened));
|
||||
|
||||
set_custom_minimum_size(Size2(0, 150 * EDSCALE));
|
||||
}
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* animation_blend_space_1d_editor.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/animation_tree_editor_plugin.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/animation/animation_blend_space_1d.h"
|
||||
#include "scene/gui/graph_edit.h"
|
||||
|
||||
class Button;
|
||||
class CheckBox;
|
||||
class LineEdit;
|
||||
class OptionButton;
|
||||
class PanelContainer;
|
||||
class SpinBox;
|
||||
class VSeparator;
|
||||
|
||||
class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin {
|
||||
GDCLASS(AnimationNodeBlendSpace1DEditor, AnimationTreeNodeEditorPlugin);
|
||||
|
||||
Ref<AnimationNodeBlendSpace1D> blend_space;
|
||||
bool read_only = false;
|
||||
|
||||
HBoxContainer *goto_parent_hb = nullptr;
|
||||
Button *goto_parent = nullptr;
|
||||
|
||||
PanelContainer *panel = nullptr;
|
||||
Button *tool_blend = nullptr;
|
||||
Button *tool_select = nullptr;
|
||||
Button *tool_create = nullptr;
|
||||
VSeparator *tool_erase_sep = nullptr;
|
||||
Button *tool_erase = nullptr;
|
||||
Button *snap = nullptr;
|
||||
SpinBox *snap_value = nullptr;
|
||||
|
||||
LineEdit *label_value = nullptr;
|
||||
SpinBox *max_value = nullptr;
|
||||
SpinBox *min_value = nullptr;
|
||||
|
||||
CheckBox *sync = nullptr;
|
||||
OptionButton *interpolation = nullptr;
|
||||
|
||||
HBoxContainer *edit_hb = nullptr;
|
||||
SpinBox *edit_value = nullptr;
|
||||
Button *open_editor = nullptr;
|
||||
|
||||
int selected_point = -1;
|
||||
|
||||
Control *blend_space_draw = nullptr;
|
||||
|
||||
PanelContainer *error_panel = nullptr;
|
||||
Label *error_label = nullptr;
|
||||
|
||||
bool updating = false;
|
||||
|
||||
static AnimationNodeBlendSpace1DEditor *singleton;
|
||||
|
||||
void _blend_space_gui_input(const Ref<InputEvent> &p_event);
|
||||
void _blend_space_draw();
|
||||
|
||||
void _update_space();
|
||||
|
||||
void _config_changed(double);
|
||||
void _labels_changed(String);
|
||||
void _snap_toggled();
|
||||
|
||||
PopupMenu *menu = nullptr;
|
||||
PopupMenu *animations_menu = nullptr;
|
||||
Vector<String> animations_to_add;
|
||||
float add_point_pos = 0.0f;
|
||||
Vector<real_t> points;
|
||||
|
||||
bool dragging_selected_attempt = false;
|
||||
bool dragging_selected = false;
|
||||
Vector2 drag_from;
|
||||
Vector2 drag_ofs;
|
||||
|
||||
void _add_menu_type(int p_index);
|
||||
void _add_animation_type(int p_index);
|
||||
|
||||
void _tool_switch(int p_tool);
|
||||
void _update_edited_point_pos();
|
||||
void _update_tool_erase();
|
||||
void _erase_selected();
|
||||
void _edit_point_pos(double);
|
||||
void _open_editor();
|
||||
|
||||
EditorFileDialog *open_file = nullptr;
|
||||
Ref<AnimationNode> file_loaded;
|
||||
void _file_opened(const String &p_file);
|
||||
|
||||
enum {
|
||||
MENU_LOAD_FILE = 1000,
|
||||
MENU_PASTE = 1001,
|
||||
MENU_LOAD_FILE_CONFIRM = 1002
|
||||
};
|
||||
|
||||
StringName get_blend_position_path() const;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static AnimationNodeBlendSpace1DEditor *get_singleton() { return singleton; }
|
||||
virtual bool can_edit(const Ref<AnimationNode> &p_node) override;
|
||||
virtual void edit(const Ref<AnimationNode> &p_node) override;
|
||||
AnimationNodeBlendSpace1DEditor();
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,148 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* animation_blend_space_2d_editor.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/animation_tree_editor_plugin.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/animation/animation_blend_space_2d.h"
|
||||
#include "scene/gui/graph_edit.h"
|
||||
|
||||
class Button;
|
||||
class CheckBox;
|
||||
class LineEdit;
|
||||
class OptionButton;
|
||||
class PanelContainer;
|
||||
class SpinBox;
|
||||
class VSeparator;
|
||||
|
||||
class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin {
|
||||
GDCLASS(AnimationNodeBlendSpace2DEditor, AnimationTreeNodeEditorPlugin);
|
||||
|
||||
Ref<AnimationNodeBlendSpace2D> blend_space;
|
||||
bool read_only = false;
|
||||
|
||||
PanelContainer *panel = nullptr;
|
||||
Button *tool_blend = nullptr;
|
||||
Button *tool_select = nullptr;
|
||||
Button *tool_create = nullptr;
|
||||
Button *tool_triangle = nullptr;
|
||||
VSeparator *tool_erase_sep = nullptr;
|
||||
Button *tool_erase = nullptr;
|
||||
Button *snap = nullptr;
|
||||
SpinBox *snap_x = nullptr;
|
||||
SpinBox *snap_y = nullptr;
|
||||
CheckBox *sync = nullptr;
|
||||
OptionButton *interpolation = nullptr;
|
||||
|
||||
Button *auto_triangles = nullptr;
|
||||
|
||||
LineEdit *label_x = nullptr;
|
||||
LineEdit *label_y = nullptr;
|
||||
SpinBox *max_x_value = nullptr;
|
||||
SpinBox *min_x_value = nullptr;
|
||||
SpinBox *max_y_value = nullptr;
|
||||
SpinBox *min_y_value = nullptr;
|
||||
|
||||
HBoxContainer *edit_hb = nullptr;
|
||||
SpinBox *edit_x = nullptr;
|
||||
SpinBox *edit_y = nullptr;
|
||||
Button *open_editor = nullptr;
|
||||
|
||||
int selected_point;
|
||||
int selected_triangle;
|
||||
|
||||
Control *blend_space_draw = nullptr;
|
||||
|
||||
PanelContainer *error_panel = nullptr;
|
||||
Label *error_label = nullptr;
|
||||
|
||||
bool updating;
|
||||
|
||||
static AnimationNodeBlendSpace2DEditor *singleton;
|
||||
|
||||
void _blend_space_gui_input(const Ref<InputEvent> &p_event);
|
||||
void _blend_space_draw();
|
||||
|
||||
void _update_space();
|
||||
|
||||
void _config_changed(double);
|
||||
void _labels_changed(String);
|
||||
void _snap_toggled();
|
||||
|
||||
PopupMenu *menu = nullptr;
|
||||
PopupMenu *animations_menu = nullptr;
|
||||
Vector<String> animations_to_add;
|
||||
Vector2 add_point_pos;
|
||||
Vector<Vector2> points;
|
||||
|
||||
bool dragging_selected_attempt;
|
||||
bool dragging_selected;
|
||||
Vector2 drag_from;
|
||||
Vector2 drag_ofs;
|
||||
|
||||
Vector<int> making_triangle;
|
||||
|
||||
void _add_menu_type(int p_index);
|
||||
void _add_animation_type(int p_index);
|
||||
|
||||
void _tool_switch(int p_tool);
|
||||
void _update_edited_point_pos();
|
||||
void _update_tool_erase();
|
||||
void _erase_selected();
|
||||
void _edit_point_pos(double);
|
||||
void _open_editor();
|
||||
|
||||
void _auto_triangles_toggled();
|
||||
|
||||
StringName get_blend_position_path() const;
|
||||
|
||||
EditorFileDialog *open_file = nullptr;
|
||||
Ref<AnimationNode> file_loaded;
|
||||
void _file_opened(const String &p_file);
|
||||
|
||||
enum {
|
||||
MENU_LOAD_FILE = 1000,
|
||||
MENU_PASTE = 1001,
|
||||
MENU_LOAD_FILE_CONFIRM = 1002
|
||||
};
|
||||
|
||||
void _blend_space_changed();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static AnimationNodeBlendSpace2DEditor *get_singleton() { return singleton; }
|
||||
virtual bool can_edit(const Ref<AnimationNode> &p_node) override;
|
||||
virtual void edit(const Ref<AnimationNode> &p_node) override;
|
||||
AnimationNodeBlendSpace2DEditor();
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,210 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* animation_blend_tree_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/object/script_language.h"
|
||||
#include "editor/editor_inspector.h"
|
||||
#include "editor/plugins/animation_tree_editor_plugin.h"
|
||||
#include "scene/animation/animation_blend_tree.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/graph_edit.h"
|
||||
#include "scene/gui/panel_container.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
class AcceptDialog;
|
||||
class CheckBox;
|
||||
class ProgressBar;
|
||||
class EditorFileDialog;
|
||||
class EditorProperty;
|
||||
class MenuButton;
|
||||
class PanelContainer;
|
||||
class EditorInspectorPluginAnimationNodeAnimation;
|
||||
|
||||
class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin {
|
||||
GDCLASS(AnimationNodeBlendTreeEditor, AnimationTreeNodeEditorPlugin);
|
||||
|
||||
Ref<AnimationNodeBlendTree> blend_tree;
|
||||
|
||||
bool read_only = false;
|
||||
|
||||
GraphEdit *graph = nullptr;
|
||||
MenuButton *add_node = nullptr;
|
||||
Vector2 position_from_popup_menu;
|
||||
bool use_position_from_popup_menu;
|
||||
|
||||
PanelContainer *error_panel = nullptr;
|
||||
Label *error_label = nullptr;
|
||||
|
||||
AcceptDialog *filter_dialog = nullptr;
|
||||
Tree *filters = nullptr;
|
||||
CheckBox *filter_enabled = nullptr;
|
||||
Button *filter_fill_selection = nullptr;
|
||||
Button *filter_invert_selection = nullptr;
|
||||
Button *filter_clear_selection = nullptr;
|
||||
|
||||
HashMap<StringName, ProgressBar *> animations;
|
||||
Vector<EditorProperty *> visible_properties;
|
||||
|
||||
String to_node = "";
|
||||
int to_slot = -1;
|
||||
String from_node = "";
|
||||
|
||||
struct AddOption {
|
||||
String name;
|
||||
String type;
|
||||
Ref<Script> script;
|
||||
int input_port_count;
|
||||
AddOption(const String &p_name = String(), const String &p_type = String(), int p_input_port_count = 0) :
|
||||
name(p_name),
|
||||
type(p_type),
|
||||
input_port_count(p_input_port_count) {
|
||||
}
|
||||
};
|
||||
|
||||
Vector<AddOption> add_options;
|
||||
|
||||
void _add_node(int p_idx);
|
||||
void _update_options_menu(bool p_has_input_ports = false);
|
||||
|
||||
static AnimationNodeBlendTreeEditor *singleton;
|
||||
|
||||
void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which);
|
||||
void _node_renamed(const String &p_text, Ref<AnimationNode> p_node);
|
||||
void _node_renamed_focus_out(Ref<AnimationNode> p_node);
|
||||
void _node_rename_lineedit_changed(const String &p_text);
|
||||
void _node_changed(const StringName &p_node_name);
|
||||
|
||||
String current_node_rename_text;
|
||||
bool updating;
|
||||
|
||||
void _connection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index);
|
||||
void _disconnection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index);
|
||||
|
||||
void _scroll_changed(const Vector2 &p_scroll);
|
||||
void _node_selected(Object *p_node);
|
||||
void _open_in_editor(const String &p_which);
|
||||
void _anim_selected(int p_index, const Array &p_options, const String &p_node);
|
||||
void _delete_node_request(const String &p_which);
|
||||
void _delete_nodes_request(const TypedArray<StringName> &p_nodes);
|
||||
|
||||
bool _update_filters(const Ref<AnimationNode> &anode);
|
||||
void _inspect_filters(const String &p_which);
|
||||
void _filter_edited();
|
||||
void _filter_toggled();
|
||||
void _filter_fill_selection();
|
||||
void _filter_invert_selection();
|
||||
void _filter_clear_selection();
|
||||
void _filter_fill_selection_recursive(EditorUndoRedoManager *p_undo_redo, TreeItem *p_item, bool p_parent_filtered);
|
||||
void _filter_invert_selection_recursive(EditorUndoRedoManager *p_undo_redo, TreeItem *p_item);
|
||||
void _filter_clear_selection_recursive(EditorUndoRedoManager *p_undo_redo, TreeItem *p_item);
|
||||
Ref<AnimationNode> _filter_edit;
|
||||
|
||||
void _popup(bool p_has_input_ports, const Vector2 &p_node_position);
|
||||
void _popup_request(const Vector2 &p_position);
|
||||
void _connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position);
|
||||
void _connection_from_empty(const String &p_to, int p_to_slot, const Vector2 &p_release_position);
|
||||
void _popup_hide();
|
||||
|
||||
void _property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing);
|
||||
|
||||
void _update_editor_settings();
|
||||
|
||||
EditorFileDialog *open_file = nullptr;
|
||||
Ref<AnimationNode> file_loaded;
|
||||
void _file_opened(const String &p_file);
|
||||
|
||||
enum {
|
||||
MENU_LOAD_FILE = 1000,
|
||||
MENU_PASTE = 1001,
|
||||
MENU_LOAD_FILE_CONFIRM = 1002
|
||||
};
|
||||
|
||||
Ref<EditorInspectorPluginAnimationNodeAnimation> animation_node_inspector_plugin;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static AnimationNodeBlendTreeEditor *get_singleton() { return singleton; }
|
||||
|
||||
void add_custom_type(const String &p_name, const Ref<Script> &p_script);
|
||||
void remove_custom_type(const Ref<Script> &p_script);
|
||||
|
||||
virtual Size2 get_minimum_size() const override;
|
||||
|
||||
virtual bool can_edit(const Ref<AnimationNode> &p_node) override;
|
||||
virtual void edit(const Ref<AnimationNode> &p_node) override;
|
||||
|
||||
void update_graph();
|
||||
|
||||
AnimationNodeBlendTreeEditor();
|
||||
};
|
||||
|
||||
// EditorPluginAnimationNodeAnimation
|
||||
|
||||
class EditorInspectorPluginAnimationNodeAnimation : public EditorInspectorPlugin {
|
||||
GDCLASS(EditorInspectorPluginAnimationNodeAnimation, EditorInspectorPlugin);
|
||||
|
||||
public:
|
||||
virtual bool can_handle(Object *p_object) override;
|
||||
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) override;
|
||||
};
|
||||
|
||||
class AnimationNodeAnimationEditorDialog : public ConfirmationDialog {
|
||||
GDCLASS(AnimationNodeAnimationEditorDialog, ConfirmationDialog);
|
||||
|
||||
friend class AnimationNodeAnimationEditor;
|
||||
|
||||
OptionButton *select_start = nullptr;
|
||||
OptionButton *select_end = nullptr;
|
||||
|
||||
public:
|
||||
AnimationNodeAnimationEditorDialog();
|
||||
};
|
||||
|
||||
class AnimationNodeAnimationEditor : public VBoxContainer {
|
||||
GDCLASS(AnimationNodeAnimationEditor, VBoxContainer);
|
||||
|
||||
Ref<AnimationNodeAnimation> animation_node_animation;
|
||||
Button *button = nullptr;
|
||||
AnimationNodeAnimationEditorDialog *dialog = nullptr;
|
||||
void _open_set_custom_timeline_from_marker_dialog();
|
||||
void _validate_markers(int p_id);
|
||||
void _confirm_set_custom_timeline_from_marker_dialog();
|
||||
|
||||
public:
|
||||
AnimationNodeAnimationEditor(Ref<AnimationNodeAnimation> p_animation_node_animation);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,129 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* animation_library_editor.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/config_file.h"
|
||||
#include "core/templates/vector.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/animation/animation_mixer.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
class AnimationMixer;
|
||||
class EditorFileDialog;
|
||||
|
||||
class AnimationLibraryEditor : public AcceptDialog {
|
||||
GDCLASS(AnimationLibraryEditor, AcceptDialog)
|
||||
|
||||
enum {
|
||||
LIB_BUTTON_ADD,
|
||||
LIB_BUTTON_LOAD,
|
||||
LIB_BUTTON_PASTE,
|
||||
LIB_BUTTON_FILE,
|
||||
LIB_BUTTON_DELETE,
|
||||
};
|
||||
enum {
|
||||
ANIM_BUTTON_COPY,
|
||||
ANIM_BUTTON_FILE,
|
||||
ANIM_BUTTON_DELETE,
|
||||
};
|
||||
|
||||
enum FileMenuAction {
|
||||
FILE_MENU_SAVE_LIBRARY,
|
||||
FILE_MENU_SAVE_AS_LIBRARY,
|
||||
FILE_MENU_MAKE_LIBRARY_UNIQUE,
|
||||
FILE_MENU_EDIT_LIBRARY,
|
||||
|
||||
FILE_MENU_SAVE_ANIMATION,
|
||||
FILE_MENU_SAVE_AS_ANIMATION,
|
||||
FILE_MENU_MAKE_ANIMATION_UNIQUE,
|
||||
FILE_MENU_EDIT_ANIMATION,
|
||||
};
|
||||
|
||||
enum FileDialogAction {
|
||||
FILE_DIALOG_ACTION_OPEN_LIBRARY,
|
||||
FILE_DIALOG_ACTION_SAVE_LIBRARY,
|
||||
FILE_DIALOG_ACTION_OPEN_ANIMATION,
|
||||
FILE_DIALOG_ACTION_SAVE_ANIMATION,
|
||||
};
|
||||
|
||||
FileDialogAction file_dialog_action = FILE_DIALOG_ACTION_OPEN_ANIMATION;
|
||||
|
||||
StringName file_dialog_animation;
|
||||
StringName file_dialog_library;
|
||||
|
||||
Button *new_library_button = nullptr;
|
||||
Button *load_library_button = nullptr;
|
||||
|
||||
AcceptDialog *error_dialog = nullptr;
|
||||
bool adding_animation = false;
|
||||
StringName adding_animation_to_library;
|
||||
EditorFileDialog *file_dialog = nullptr;
|
||||
ConfirmationDialog *add_library_dialog = nullptr;
|
||||
LineEdit *add_library_name = nullptr;
|
||||
Label *add_library_validate = nullptr;
|
||||
PopupMenu *file_popup = nullptr;
|
||||
|
||||
Tree *tree = nullptr;
|
||||
|
||||
AnimationMixer *mixer = nullptr;
|
||||
|
||||
void _add_library();
|
||||
void _add_library_validate(const String &p_name);
|
||||
void _add_library_confirm();
|
||||
void _load_library();
|
||||
void _load_file(const String &p_path);
|
||||
void _load_files(const PackedStringArray &p_paths);
|
||||
|
||||
void _save_mixer_lib_folding(TreeItem *p_item);
|
||||
Vector<uint64_t> _load_mixer_libs_folding();
|
||||
void _load_config_libs_folding(Vector<uint64_t> &p_lib_ids, ConfigFile *p_config, String p_section);
|
||||
String _get_mixer_signature() const;
|
||||
|
||||
void _item_renamed();
|
||||
void _button_pressed(TreeItem *p_item, int p_column, int p_id, MouseButton p_button);
|
||||
|
||||
void _file_popup_selected(int p_id);
|
||||
|
||||
bool updating = false;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
void _update_editor(Object *p_mixer);
|
||||
virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_animation_mixer(Object *p_mixer);
|
||||
void show_dialog();
|
||||
void update_tree();
|
||||
AnimationLibraryEditor();
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,368 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* animation_player_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/animation_track_editor.h"
|
||||
#include "editor/plugins/animation_library_editor.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/slider.h"
|
||||
#include "scene/gui/spin_box.h"
|
||||
#include "scene/gui/texture_button.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
class AnimationPlayerEditorPlugin;
|
||||
class ImageTexture;
|
||||
|
||||
class AnimationPlayerEditor : public VBoxContainer {
|
||||
GDCLASS(AnimationPlayerEditor, VBoxContainer);
|
||||
|
||||
friend AnimationPlayerEditorPlugin;
|
||||
|
||||
AnimationPlayerEditorPlugin *plugin = nullptr;
|
||||
AnimationMixer *original_node = nullptr; // For pinned mark in SceneTree.
|
||||
AnimationPlayer *player = nullptr; // For AnimationPlayerEditor, could be dummy.
|
||||
ObjectID cached_root_node_id;
|
||||
bool is_dummy = false;
|
||||
|
||||
enum {
|
||||
TOOL_NEW_ANIM,
|
||||
TOOL_ANIM_LIBRARY,
|
||||
TOOL_DUPLICATE_ANIM,
|
||||
TOOL_RENAME_ANIM,
|
||||
TOOL_EDIT_TRANSITIONS,
|
||||
TOOL_REMOVE_ANIM,
|
||||
TOOL_EDIT_RESOURCE
|
||||
};
|
||||
|
||||
enum {
|
||||
ONION_SKINNING_ENABLE,
|
||||
ONION_SKINNING_PAST,
|
||||
ONION_SKINNING_FUTURE,
|
||||
ONION_SKINNING_1_STEP,
|
||||
ONION_SKINNING_2_STEPS,
|
||||
ONION_SKINNING_3_STEPS,
|
||||
ONION_SKINNING_LAST_STEPS_OPTION = ONION_SKINNING_3_STEPS,
|
||||
ONION_SKINNING_DIFFERENCES_ONLY,
|
||||
ONION_SKINNING_FORCE_WHITE_MODULATE,
|
||||
ONION_SKINNING_INCLUDE_GIZMOS,
|
||||
};
|
||||
|
||||
enum {
|
||||
ANIM_OPEN,
|
||||
ANIM_SAVE,
|
||||
ANIM_SAVE_AS
|
||||
};
|
||||
|
||||
enum {
|
||||
RESOURCE_LOAD,
|
||||
RESOURCE_SAVE
|
||||
};
|
||||
|
||||
OptionButton *animation = nullptr;
|
||||
Button *stop = nullptr;
|
||||
Button *play = nullptr;
|
||||
Button *play_from = nullptr;
|
||||
Button *play_bw = nullptr;
|
||||
Button *play_bw_from = nullptr;
|
||||
Button *autoplay = nullptr;
|
||||
|
||||
MenuButton *tool_anim = nullptr;
|
||||
Button *onion_toggle = nullptr;
|
||||
MenuButton *onion_skinning = nullptr;
|
||||
Button *pin = nullptr;
|
||||
SpinBox *frame = nullptr;
|
||||
LineEdit *scale = nullptr;
|
||||
LineEdit *name = nullptr;
|
||||
OptionButton *library = nullptr;
|
||||
Label *name_title = nullptr;
|
||||
|
||||
Ref<Texture2D> stop_icon;
|
||||
Ref<Texture2D> pause_icon;
|
||||
Ref<Texture2D> autoplay_icon;
|
||||
Ref<Texture2D> reset_icon;
|
||||
Ref<ImageTexture> autoplay_reset_icon;
|
||||
|
||||
bool finishing = false;
|
||||
bool last_active = false;
|
||||
float timeline_position = 0;
|
||||
|
||||
EditorFileDialog *file = nullptr;
|
||||
ConfirmationDialog *delete_dialog = nullptr;
|
||||
|
||||
AnimationLibraryEditor *library_editor = nullptr;
|
||||
|
||||
struct BlendEditor {
|
||||
AcceptDialog *dialog = nullptr;
|
||||
Tree *tree = nullptr;
|
||||
OptionButton *next = nullptr;
|
||||
|
||||
} blend_editor;
|
||||
|
||||
ConfirmationDialog *name_dialog = nullptr;
|
||||
AcceptDialog *error_dialog = nullptr;
|
||||
int name_dialog_op = TOOL_NEW_ANIM;
|
||||
|
||||
bool updating = false;
|
||||
bool updating_blends = false;
|
||||
|
||||
AnimationTrackEditor *track_editor = nullptr;
|
||||
static AnimationPlayerEditor *singleton;
|
||||
|
||||
// Onion skinning.
|
||||
struct {
|
||||
// Settings.
|
||||
bool enabled = false;
|
||||
bool past = true;
|
||||
bool future = false;
|
||||
uint32_t steps = 1;
|
||||
bool differences_only = false;
|
||||
bool force_white_modulate = false;
|
||||
bool include_gizmos = false;
|
||||
|
||||
uint32_t get_capture_count() const {
|
||||
// 'Differences only' needs a capture of the present.
|
||||
return (past && future ? 2 * steps : steps) + (differences_only ? 1 : 0);
|
||||
}
|
||||
|
||||
// Rendering.
|
||||
int64_t last_frame = 0;
|
||||
int can_overlay = 0;
|
||||
Size2 capture_size;
|
||||
LocalVector<RID> captures;
|
||||
LocalVector<bool> captures_valid;
|
||||
struct {
|
||||
RID canvas;
|
||||
RID canvas_item;
|
||||
Ref<ShaderMaterial> material;
|
||||
Ref<Shader> shader;
|
||||
} capture;
|
||||
|
||||
// Cross-call state.
|
||||
struct {
|
||||
double anim_player_position = 0.0;
|
||||
Ref<AnimatedValuesBackup> anim_values_backup;
|
||||
Rect2 screen_rect;
|
||||
Dictionary canvas_edit_state;
|
||||
Dictionary spatial_edit_state;
|
||||
} temp;
|
||||
} onion;
|
||||
|
||||
void _select_anim_by_name(const String &p_anim);
|
||||
float _get_editor_step() const;
|
||||
void _go_to_nearest_keyframe(bool p_backward);
|
||||
void _play_pressed();
|
||||
void _play_from_pressed();
|
||||
void _play_bw_pressed();
|
||||
void _play_bw_from_pressed();
|
||||
void _autoplay_pressed();
|
||||
void _stop_pressed();
|
||||
void _animation_selected(int p_which);
|
||||
void _animation_new();
|
||||
void _animation_rename();
|
||||
void _animation_name_edited();
|
||||
|
||||
void _animation_remove();
|
||||
void _animation_remove_confirmed();
|
||||
void _animation_edit();
|
||||
void _animation_duplicate();
|
||||
Ref<Animation> _animation_clone(const Ref<Animation> p_anim);
|
||||
void _animation_resource_edit();
|
||||
void _scale_changed(const String &p_scale);
|
||||
void _seek_value_changed(float p_value, bool p_timeline_only = false);
|
||||
void _blend_editor_next_changed(const int p_idx);
|
||||
|
||||
void _edit_animation_blend();
|
||||
void _update_animation_blend();
|
||||
|
||||
void _list_changed();
|
||||
void _animation_finished(const String &p_name);
|
||||
void _current_animation_changed(const String &p_name);
|
||||
void _update_animation();
|
||||
void _update_player();
|
||||
void _set_controls_disabled(bool p_disabled);
|
||||
void _update_animation_list_icons();
|
||||
void _update_name_dialog_library_dropdown();
|
||||
void _blend_edited();
|
||||
|
||||
void _animation_player_changed(Object *p_pl);
|
||||
void _animation_libraries_updated();
|
||||
|
||||
void _animation_key_editor_seek(float p_pos, bool p_timeline_only = false, bool p_update_position_only = false);
|
||||
void _animation_key_editor_anim_len_changed(float p_len);
|
||||
void _animation_update_key_frame();
|
||||
|
||||
virtual void shortcut_input(const Ref<InputEvent> &p_ev) override;
|
||||
void _animation_tool_menu(int p_option);
|
||||
void _onion_skinning_menu(int p_option);
|
||||
|
||||
void _editor_visibility_changed();
|
||||
bool _are_onion_layers_valid();
|
||||
void _allocate_onion_layers();
|
||||
void _free_onion_layers();
|
||||
void _prepare_onion_layers_1();
|
||||
void _prepare_onion_layers_2_prolog();
|
||||
void _prepare_onion_layers_2_step_prepare(int p_step_offset, uint32_t p_capture_idx);
|
||||
void _prepare_onion_layers_2_step_capture(int p_step_offset, uint32_t p_capture_idx);
|
||||
void _prepare_onion_layers_2_epilog();
|
||||
void _start_onion_skinning();
|
||||
void _stop_onion_skinning();
|
||||
|
||||
bool _validate_tracks(const Ref<Animation> p_anim);
|
||||
|
||||
void _pin_pressed();
|
||||
String _get_current() const;
|
||||
|
||||
void _ensure_dummy_player();
|
||||
|
||||
~AnimationPlayerEditor();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
void _node_removed(Node *p_node);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
AnimationMixer *get_editing_node() const;
|
||||
AnimationPlayer *get_player() const;
|
||||
AnimationMixer *fetch_mixer_for_library() const;
|
||||
Node *get_cached_root_node() const;
|
||||
|
||||
static AnimationPlayerEditor *get_singleton() { return singleton; }
|
||||
|
||||
bool is_pinned() const { return pin->is_pressed(); }
|
||||
void unpin() {
|
||||
pin->set_pressed(false);
|
||||
_pin_pressed();
|
||||
}
|
||||
AnimationTrackEditor *get_track_editor() { return track_editor; }
|
||||
Dictionary get_state() const;
|
||||
void set_state(const Dictionary &p_state);
|
||||
void clear();
|
||||
|
||||
void ensure_visibility();
|
||||
|
||||
void edit(AnimationMixer *p_node, AnimationPlayer *p_player, bool p_is_dummy);
|
||||
void forward_force_draw_over_viewport(Control *p_overlay);
|
||||
|
||||
AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plugin);
|
||||
};
|
||||
|
||||
class AnimationPlayerEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(AnimationPlayerEditorPlugin, EditorPlugin);
|
||||
|
||||
friend AnimationPlayerEditor;
|
||||
|
||||
AnimationPlayerEditor *anim_editor = nullptr;
|
||||
AnimationPlayer *player = nullptr;
|
||||
AnimationPlayer *dummy_player = nullptr;
|
||||
ObjectID last_mixer;
|
||||
|
||||
void _update_dummy_player(AnimationMixer *p_mixer);
|
||||
void _clear_dummy_player();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
void _property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance);
|
||||
void _transform_key_request(Object *sp, const String &p_sub, const Transform3D &p_key);
|
||||
void _update_keying();
|
||||
|
||||
public:
|
||||
virtual Dictionary get_state() const override { return anim_editor->get_state(); }
|
||||
virtual void set_state(const Dictionary &p_state) override { anim_editor->set_state(p_state); }
|
||||
virtual void clear() override { anim_editor->clear(); }
|
||||
|
||||
virtual String get_plugin_name() const override { return "Anim"; }
|
||||
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;
|
||||
|
||||
virtual void forward_canvas_force_draw_over_viewport(Control *p_overlay) override { anim_editor->forward_force_draw_over_viewport(p_overlay); }
|
||||
virtual void forward_3d_force_draw_over_viewport(Control *p_overlay) override { anim_editor->forward_force_draw_over_viewport(p_overlay); }
|
||||
|
||||
AnimationPlayerEditorPlugin();
|
||||
~AnimationPlayerEditorPlugin();
|
||||
};
|
||||
|
||||
// AnimationTrackKeyEditEditorPlugin
|
||||
|
||||
class EditorInspectorPluginAnimationTrackKeyEdit : public EditorInspectorPlugin {
|
||||
GDCLASS(EditorInspectorPluginAnimationTrackKeyEdit, EditorInspectorPlugin);
|
||||
|
||||
AnimationTrackKeyEditEditor *atk_editor = nullptr;
|
||||
|
||||
public:
|
||||
virtual bool can_handle(Object *p_object) override;
|
||||
virtual void parse_begin(Object *p_object) override;
|
||||
};
|
||||
|
||||
class AnimationTrackKeyEditEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(AnimationTrackKeyEditEditorPlugin, EditorPlugin);
|
||||
|
||||
EditorInspectorPluginAnimationTrackKeyEdit *atk_plugin = nullptr;
|
||||
|
||||
public:
|
||||
bool has_main_screen() const override { return false; }
|
||||
virtual bool handles(Object *p_object) const override;
|
||||
|
||||
virtual String get_plugin_name() const override { return "AnimationTrackKeyEdit"; }
|
||||
|
||||
AnimationTrackKeyEditEditorPlugin();
|
||||
};
|
||||
|
||||
// AnimationMarkerKeyEditEditorPlugin
|
||||
|
||||
class EditorInspectorPluginAnimationMarkerKeyEdit : public EditorInspectorPlugin {
|
||||
GDCLASS(EditorInspectorPluginAnimationMarkerKeyEdit, EditorInspectorPlugin);
|
||||
|
||||
AnimationMarkerKeyEditEditor *amk_editor = nullptr;
|
||||
|
||||
public:
|
||||
virtual bool can_handle(Object *p_object) override;
|
||||
virtual void parse_begin(Object *p_object) override;
|
||||
};
|
||||
|
||||
class AnimationMarkerKeyEditEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(AnimationMarkerKeyEditEditorPlugin, EditorPlugin);
|
||||
|
||||
EditorInspectorPluginAnimationMarkerKeyEdit *amk_plugin = nullptr;
|
||||
|
||||
public:
|
||||
bool has_main_screen() const override { return false; }
|
||||
virtual bool handles(Object *p_object) const override;
|
||||
|
||||
virtual String get_plugin_name() const override { return "AnimationMarkerKeyEdit"; }
|
||||
|
||||
AnimationMarkerKeyEditEditorPlugin();
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,322 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* animation_state_machine_editor.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/animation_tree_editor_plugin.h"
|
||||
#include "scene/animation/animation_node_state_machine.h"
|
||||
#include "scene/gui/graph_edit.h"
|
||||
#include "scene/gui/popup.h"
|
||||
|
||||
class ConfirmationDialog;
|
||||
class EditorFileDialog;
|
||||
class LineEdit;
|
||||
class OptionButton;
|
||||
class PanelContainer;
|
||||
|
||||
class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin {
|
||||
GDCLASS(AnimationNodeStateMachineEditor, AnimationTreeNodeEditorPlugin);
|
||||
|
||||
Ref<AnimationNodeStateMachine> state_machine;
|
||||
|
||||
bool read_only = false;
|
||||
|
||||
Button *tool_select = nullptr;
|
||||
Button *tool_create = nullptr;
|
||||
Button *tool_connect = nullptr;
|
||||
Popup *name_edit_popup = nullptr;
|
||||
LineEdit *name_edit = nullptr;
|
||||
|
||||
HBoxContainer *selection_tools_hb = nullptr;
|
||||
Button *tool_erase = nullptr;
|
||||
|
||||
HBoxContainer *transition_tools_hb = nullptr;
|
||||
OptionButton *switch_mode = nullptr;
|
||||
Button *auto_advance = nullptr;
|
||||
|
||||
OptionButton *play_mode = nullptr;
|
||||
|
||||
PanelContainer *panel = nullptr;
|
||||
|
||||
StringName selected_node;
|
||||
HashSet<StringName> selected_nodes;
|
||||
|
||||
HScrollBar *h_scroll = nullptr;
|
||||
VScrollBar *v_scroll = nullptr;
|
||||
|
||||
Control *state_machine_draw = nullptr;
|
||||
Control *state_machine_play_pos = nullptr;
|
||||
|
||||
PanelContainer *error_panel = nullptr;
|
||||
Label *error_label = nullptr;
|
||||
|
||||
struct ThemeCache {
|
||||
Ref<StyleBox> panel_style;
|
||||
Ref<StyleBox> error_panel_style;
|
||||
Color error_color;
|
||||
|
||||
Ref<Texture2D> tool_icon_select;
|
||||
Ref<Texture2D> tool_icon_create;
|
||||
Ref<Texture2D> tool_icon_connect;
|
||||
Ref<Texture2D> tool_icon_erase;
|
||||
|
||||
Ref<Texture2D> transition_icon_immediate;
|
||||
Ref<Texture2D> transition_icon_sync;
|
||||
Ref<Texture2D> transition_icon_end;
|
||||
|
||||
Ref<Texture2D> play_icon_start;
|
||||
Ref<Texture2D> play_icon_travel;
|
||||
Ref<Texture2D> play_icon_auto;
|
||||
|
||||
Ref<Texture2D> animation_icon;
|
||||
|
||||
Ref<StyleBox> node_frame;
|
||||
Ref<StyleBox> node_frame_selected;
|
||||
Ref<StyleBox> node_frame_playing;
|
||||
Ref<StyleBox> node_frame_start;
|
||||
Ref<StyleBox> node_frame_end;
|
||||
|
||||
Ref<Font> node_title_font;
|
||||
int node_title_font_size = 0;
|
||||
Color node_title_font_color;
|
||||
|
||||
Ref<Texture2D> play_node;
|
||||
Ref<Texture2D> edit_node;
|
||||
|
||||
Color transition_color;
|
||||
Color transition_disabled_color;
|
||||
Color transition_icon_color;
|
||||
Color transition_icon_disabled_color;
|
||||
Color highlight_color;
|
||||
Color highlight_disabled_color;
|
||||
Color focus_color;
|
||||
Color guideline_color;
|
||||
|
||||
Ref<Texture2D> transition_icons[6]{};
|
||||
|
||||
Color playback_color;
|
||||
Color playback_background_color;
|
||||
} theme_cache;
|
||||
|
||||
bool updating = false;
|
||||
|
||||
static AnimationNodeStateMachineEditor *singleton;
|
||||
|
||||
void _state_machine_gui_input(const Ref<InputEvent> &p_event);
|
||||
void _connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, float p_fade_ratio, bool p_auto_advance, bool p_is_across_group, float p_opacity = 1.0);
|
||||
|
||||
void _state_machine_draw();
|
||||
|
||||
void _state_machine_pos_draw_individual(const String &p_name, float p_ratio);
|
||||
void _state_machine_pos_draw_all();
|
||||
|
||||
void _update_graph();
|
||||
|
||||
PopupMenu *menu = nullptr;
|
||||
PopupMenu *connect_menu = nullptr;
|
||||
PopupMenu *state_machine_menu = nullptr;
|
||||
PopupMenu *end_menu = nullptr;
|
||||
PopupMenu *animations_menu = nullptr;
|
||||
Vector<String> animations_to_add;
|
||||
Vector<String> nodes_to_connect;
|
||||
|
||||
Vector2 add_node_pos;
|
||||
|
||||
bool box_selecting = false;
|
||||
Point2 box_selecting_from;
|
||||
Point2 box_selecting_to;
|
||||
Rect2 box_selecting_rect;
|
||||
HashSet<StringName> previous_selected;
|
||||
|
||||
bool dragging_selected_attempt = false;
|
||||
bool dragging_selected = false;
|
||||
Vector2 drag_from;
|
||||
Vector2 drag_ofs;
|
||||
StringName snap_x;
|
||||
StringName snap_y;
|
||||
|
||||
bool connecting = false;
|
||||
bool connection_follows_cursor = false;
|
||||
StringName connecting_from;
|
||||
Vector2 connecting_to;
|
||||
StringName connecting_to_node;
|
||||
|
||||
void _add_menu_type(int p_index);
|
||||
void _add_animation_type(int p_index);
|
||||
void _connect_to(int p_index);
|
||||
|
||||
struct NodeRect {
|
||||
StringName node_name;
|
||||
Rect2 node;
|
||||
Rect2 play;
|
||||
Rect2 name;
|
||||
Rect2 edit;
|
||||
bool can_edit;
|
||||
};
|
||||
|
||||
Vector<NodeRect> node_rects;
|
||||
|
||||
struct TransitionLine {
|
||||
StringName from_node;
|
||||
StringName to_node;
|
||||
Vector2 from;
|
||||
Vector2 to;
|
||||
AnimationNodeStateMachineTransition::SwitchMode mode;
|
||||
StringName advance_condition_name;
|
||||
bool advance_condition_state = false;
|
||||
bool disabled = false;
|
||||
bool auto_advance = false;
|
||||
float width = 0;
|
||||
bool selected;
|
||||
bool travel;
|
||||
float fade_ratio;
|
||||
bool hidden;
|
||||
int transition_index;
|
||||
bool is_across_group = false;
|
||||
};
|
||||
|
||||
Vector<TransitionLine> transition_lines;
|
||||
|
||||
struct NodeUR {
|
||||
StringName name;
|
||||
Ref<AnimationNode> node;
|
||||
Vector2 position;
|
||||
};
|
||||
|
||||
struct TransitionUR {
|
||||
StringName new_from;
|
||||
StringName new_to;
|
||||
StringName old_from;
|
||||
StringName old_to;
|
||||
Ref<AnimationNodeStateMachineTransition> transition;
|
||||
};
|
||||
|
||||
StringName selected_transition_from;
|
||||
StringName selected_transition_to;
|
||||
int selected_transition_index = -1;
|
||||
void _add_transition(const bool p_nested_action = false);
|
||||
|
||||
enum HoveredNodeArea {
|
||||
HOVER_NODE_NONE = -1,
|
||||
HOVER_NODE_PLAY = 0,
|
||||
HOVER_NODE_EDIT = 1,
|
||||
};
|
||||
|
||||
StringName hovered_node_name;
|
||||
HoveredNodeArea hovered_node_area = HOVER_NODE_NONE;
|
||||
|
||||
String prev_name;
|
||||
void _name_edited(const String &p_text);
|
||||
void _name_edited_focus_out();
|
||||
void _open_editor(const String &p_name);
|
||||
void _scroll_changed(double);
|
||||
|
||||
String _get_root_playback_path(String &r_node_directory);
|
||||
|
||||
void _clip_src_line_to_rect(Vector2 &r_from, const Vector2 &p_to, const Rect2 &p_rect);
|
||||
void _clip_dst_line_to_rect(const Vector2 &p_from, Vector2 &r_to, const Rect2 &p_rect);
|
||||
|
||||
void _erase_selected(const bool p_nested_action = false);
|
||||
void _update_mode();
|
||||
void _open_menu(const Vector2 &p_position);
|
||||
bool _create_submenu(PopupMenu *p_menu, Ref<AnimationNodeStateMachine> p_nodesm, const StringName &p_name, const StringName &p_path);
|
||||
void _stop_connecting();
|
||||
|
||||
bool last_active = false;
|
||||
StringName last_fading_from_node;
|
||||
StringName last_current_node;
|
||||
Vector<StringName> last_travel_path;
|
||||
|
||||
float fade_from_last_play_pos = 0.0f;
|
||||
float fade_from_current_play_pos = 0.0f;
|
||||
float fade_from_length = 0.0f;
|
||||
|
||||
float last_play_pos = 0.0f;
|
||||
float current_play_pos = 0.0f;
|
||||
float current_length = 0.0f;
|
||||
|
||||
float last_fading_time = 0.0f;
|
||||
float last_fading_pos = 0.0f;
|
||||
float fading_time = 0.0f;
|
||||
float fading_pos = 0.0f;
|
||||
|
||||
float error_time = 0.0f;
|
||||
String error_text;
|
||||
|
||||
EditorFileDialog *open_file = nullptr;
|
||||
Ref<AnimationNode> file_loaded;
|
||||
void _file_opened(const String &p_file);
|
||||
|
||||
enum {
|
||||
MENU_LOAD_FILE = 1000,
|
||||
MENU_PASTE = 1001,
|
||||
MENU_LOAD_FILE_CONFIRM = 1002
|
||||
};
|
||||
|
||||
HashSet<StringName> connected_nodes;
|
||||
void _update_connected_nodes(const StringName &p_node);
|
||||
|
||||
Ref<StyleBox> _adjust_stylebox_opacity(Ref<StyleBox> p_style, float p_opacity);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static AnimationNodeStateMachineEditor *get_singleton() { return singleton; }
|
||||
|
||||
virtual bool can_edit(const Ref<AnimationNode> &p_node) override;
|
||||
virtual void edit(const Ref<AnimationNode> &p_node) override;
|
||||
|
||||
virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override;
|
||||
virtual String get_tooltip(const Point2 &p_pos) const override;
|
||||
|
||||
AnimationNodeStateMachineEditor();
|
||||
};
|
||||
|
||||
class EditorAnimationMultiTransitionEdit : public RefCounted {
|
||||
GDCLASS(EditorAnimationMultiTransitionEdit, RefCounted);
|
||||
|
||||
struct Transition {
|
||||
StringName from;
|
||||
StringName to;
|
||||
Ref<AnimationNodeStateMachineTransition> transition;
|
||||
};
|
||||
|
||||
Vector<Transition> transitions;
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_property);
|
||||
bool _get(const StringName &p_name, Variant &r_property) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
public:
|
||||
void add_transition(const StringName &p_from, const StringName &p_to, Ref<AnimationNodeStateMachineTransition> p_transition);
|
||||
};
|
||||
|
|
@ -1,310 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* animation_tree_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 "animation_tree_editor_plugin.h"
|
||||
|
||||
#include "animation_blend_space_1d_editor.h"
|
||||
#include "animation_blend_space_2d_editor.h"
|
||||
#include "animation_blend_tree_editor_plugin.h"
|
||||
#include "animation_state_machine_editor.h"
|
||||
#include "editor/editor_command_palette.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/gui/editor_bottom_panel.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/animation/animation_blend_tree.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/margin_container.h"
|
||||
#include "scene/gui/scroll_container.h"
|
||||
#include "scene/gui/separator.h"
|
||||
|
||||
void AnimationTreeEditor::edit(AnimationTree *p_tree) {
|
||||
if (p_tree && !p_tree->is_connected("animation_list_changed", callable_mp(this, &AnimationTreeEditor::_animation_list_changed))) {
|
||||
p_tree->connect("animation_list_changed", callable_mp(this, &AnimationTreeEditor::_animation_list_changed), CONNECT_DEFERRED);
|
||||
}
|
||||
|
||||
if (tree == p_tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tree && tree->is_connected("animation_list_changed", callable_mp(this, &AnimationTreeEditor::_animation_list_changed))) {
|
||||
tree->disconnect("animation_list_changed", callable_mp(this, &AnimationTreeEditor::_animation_list_changed));
|
||||
}
|
||||
|
||||
tree = p_tree;
|
||||
|
||||
Vector<String> path;
|
||||
if (tree) {
|
||||
edit_path(path);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationTreeEditor::_node_removed(Node *p_node) {
|
||||
if (p_node == tree) {
|
||||
tree = nullptr;
|
||||
_clear_editors();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationTreeEditor::_path_button_pressed(int p_path) {
|
||||
edited_path.clear();
|
||||
for (int i = 0; i <= p_path; i++) {
|
||||
edited_path.push_back(button_path[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationTreeEditor::_animation_list_changed() {
|
||||
AnimationNodeBlendTreeEditor *bte = AnimationNodeBlendTreeEditor::get_singleton();
|
||||
if (bte) {
|
||||
bte->update_graph();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationTreeEditor::_update_path() {
|
||||
while (path_hb->get_child_count() > 1) {
|
||||
memdelete(path_hb->get_child(1));
|
||||
}
|
||||
|
||||
Ref<ButtonGroup> group;
|
||||
group.instantiate();
|
||||
|
||||
Button *b = memnew(Button);
|
||||
b->set_text(TTR("Root"));
|
||||
b->set_toggle_mode(true);
|
||||
b->set_button_group(group);
|
||||
b->set_pressed(true);
|
||||
b->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
b->connect(SceneStringName(pressed), callable_mp(this, &AnimationTreeEditor::_path_button_pressed).bind(-1));
|
||||
path_hb->add_child(b);
|
||||
for (int i = 0; i < button_path.size(); i++) {
|
||||
b = memnew(Button);
|
||||
b->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
b->set_text(button_path[i]);
|
||||
b->set_toggle_mode(true);
|
||||
b->set_button_group(group);
|
||||
path_hb->add_child(b);
|
||||
b->set_pressed(true);
|
||||
b->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
b->connect(SceneStringName(pressed), callable_mp(this, &AnimationTreeEditor::_path_button_pressed).bind(i));
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationTreeEditor::edit_path(const Vector<String> &p_path) {
|
||||
button_path.clear();
|
||||
|
||||
Ref<AnimationNode> node = tree->get_root_animation_node();
|
||||
|
||||
if (node.is_valid()) {
|
||||
current_root = node->get_instance_id();
|
||||
|
||||
for (int i = 0; i < p_path.size(); i++) {
|
||||
Ref<AnimationNode> child = node->get_child_by_name(p_path[i]);
|
||||
ERR_BREAK(child.is_null());
|
||||
node = child;
|
||||
button_path.push_back(p_path[i]);
|
||||
}
|
||||
|
||||
edited_path = button_path;
|
||||
|
||||
for (int i = 0; i < editors.size(); i++) {
|
||||
if (editors[i]->can_edit(node)) {
|
||||
editors[i]->edit(node);
|
||||
editors[i]->show();
|
||||
} else {
|
||||
editors[i]->edit(Ref<AnimationNode>());
|
||||
editors[i]->hide();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current_root = ObjectID();
|
||||
edited_path = button_path;
|
||||
for (int i = 0; i < editors.size(); i++) {
|
||||
editors[i]->edit(Ref<AnimationNode>());
|
||||
editors[i]->hide();
|
||||
}
|
||||
}
|
||||
|
||||
_update_path();
|
||||
}
|
||||
|
||||
void AnimationTreeEditor::_clear_editors() {
|
||||
button_path.clear();
|
||||
current_root = ObjectID();
|
||||
edited_path = button_path;
|
||||
for (int i = 0; i < editors.size(); i++) {
|
||||
editors[i]->edit(Ref<AnimationNode>());
|
||||
editors[i]->hide();
|
||||
}
|
||||
_update_path();
|
||||
}
|
||||
|
||||
Vector<String> AnimationTreeEditor::get_edited_path() const {
|
||||
return button_path;
|
||||
}
|
||||
|
||||
void AnimationTreeEditor::enter_editor(const String &p_path) {
|
||||
Vector<String> path = edited_path;
|
||||
path.push_back(p_path);
|
||||
edit_path(path);
|
||||
}
|
||||
|
||||
void AnimationTreeEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
get_tree()->connect("node_removed", callable_mp(this, &AnimationTreeEditor::_node_removed));
|
||||
} break;
|
||||
case NOTIFICATION_PROCESS: {
|
||||
ObjectID root;
|
||||
if (tree && tree->get_root_animation_node().is_valid()) {
|
||||
root = tree->get_root_animation_node()->get_instance_id();
|
||||
}
|
||||
|
||||
if (root != current_root) {
|
||||
edit_path(Vector<String>());
|
||||
}
|
||||
|
||||
if (button_path.size() != edited_path.size()) {
|
||||
edit_path(edited_path);
|
||||
}
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
get_tree()->disconnect("node_removed", callable_mp(this, &AnimationTreeEditor::_node_removed));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
AnimationTreeEditor *AnimationTreeEditor::singleton = nullptr;
|
||||
|
||||
void AnimationTreeEditor::add_plugin(AnimationTreeNodeEditorPlugin *p_editor) {
|
||||
ERR_FAIL_COND(p_editor->get_parent());
|
||||
editor_base->add_child(p_editor);
|
||||
editors.push_back(p_editor);
|
||||
p_editor->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
p_editor->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
p_editor->hide();
|
||||
}
|
||||
|
||||
void AnimationTreeEditor::remove_plugin(AnimationTreeNodeEditorPlugin *p_editor) {
|
||||
ERR_FAIL_COND(p_editor->get_parent() != editor_base);
|
||||
editor_base->remove_child(p_editor);
|
||||
editors.erase(p_editor);
|
||||
}
|
||||
|
||||
String AnimationTreeEditor::get_base_path() {
|
||||
String path = Animation::PARAMETERS_BASE_PATH;
|
||||
for (int i = 0; i < edited_path.size(); i++) {
|
||||
path += edited_path[i] + "/";
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
bool AnimationTreeEditor::can_edit(const Ref<AnimationNode> &p_node) const {
|
||||
for (int i = 0; i < editors.size(); i++) {
|
||||
if (editors[i]->can_edit(p_node)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector<String> AnimationTreeEditor::get_animation_list() {
|
||||
// This can be called off the main thread due to resource preview generation. Quit early in that case.
|
||||
if (!singleton->tree || !Thread::is_main_thread() || !singleton->is_visible()) {
|
||||
// When tree is empty, singleton not in the main thread.
|
||||
return Vector<String>();
|
||||
}
|
||||
|
||||
AnimationTree *tree = singleton->tree;
|
||||
if (!tree) {
|
||||
return Vector<String>();
|
||||
}
|
||||
|
||||
List<StringName> anims;
|
||||
tree->get_animation_list(&anims);
|
||||
Vector<String> ret;
|
||||
for (const StringName &E : anims) {
|
||||
ret.push_back(E);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
AnimationTreeEditor::AnimationTreeEditor() {
|
||||
AnimationNodeAnimation::get_editable_animation_list = get_animation_list;
|
||||
path_edit = memnew(ScrollContainer);
|
||||
add_child(path_edit);
|
||||
path_edit->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
|
||||
path_hb = memnew(HBoxContainer);
|
||||
path_edit->add_child(path_hb);
|
||||
path_hb->add_child(memnew(Label(TTR("Path:"))));
|
||||
|
||||
add_child(memnew(HSeparator));
|
||||
|
||||
singleton = this;
|
||||
editor_base = memnew(MarginContainer);
|
||||
editor_base->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
add_child(editor_base);
|
||||
|
||||
add_plugin(memnew(AnimationNodeBlendTreeEditor));
|
||||
add_plugin(memnew(AnimationNodeBlendSpace1DEditor));
|
||||
add_plugin(memnew(AnimationNodeBlendSpace2DEditor));
|
||||
add_plugin(memnew(AnimationNodeStateMachineEditor));
|
||||
}
|
||||
|
||||
void AnimationTreeEditorPlugin::edit(Object *p_object) {
|
||||
anim_tree_editor->edit(Object::cast_to<AnimationTree>(p_object));
|
||||
}
|
||||
|
||||
bool AnimationTreeEditorPlugin::handles(Object *p_object) const {
|
||||
return p_object->is_class("AnimationTree");
|
||||
}
|
||||
|
||||
void AnimationTreeEditorPlugin::make_visible(bool p_visible) {
|
||||
if (p_visible) {
|
||||
//editor->hide_animation_player_editors();
|
||||
//editor->animation_panel_make_visible(true);
|
||||
button->show();
|
||||
EditorNode::get_bottom_panel()->make_item_visible(anim_tree_editor);
|
||||
anim_tree_editor->set_process(true);
|
||||
} else {
|
||||
if (anim_tree_editor->is_visible_in_tree()) {
|
||||
EditorNode::get_bottom_panel()->hide_bottom_panel();
|
||||
}
|
||||
button->hide();
|
||||
anim_tree_editor->set_process(false);
|
||||
}
|
||||
}
|
||||
|
||||
AnimationTreeEditorPlugin::AnimationTreeEditorPlugin() {
|
||||
anim_tree_editor = memnew(AnimationTreeEditor);
|
||||
anim_tree_editor->set_custom_minimum_size(Size2(0, 300) * EDSCALE);
|
||||
|
||||
button = EditorNode::get_bottom_panel()->add_item(TTRC("AnimationTree"), anim_tree_editor, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_animation_tree_bottom_panel", TTRC("Toggle AnimationTree Bottom Panel")));
|
||||
button->hide();
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* animation_tree_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/animation/animation_tree.h"
|
||||
#include "scene/gui/graph_edit.h"
|
||||
|
||||
class Button;
|
||||
class EditorFileDialog;
|
||||
class ScrollContainer;
|
||||
|
||||
class AnimationTreeNodeEditorPlugin : public VBoxContainer {
|
||||
GDCLASS(AnimationTreeNodeEditorPlugin, VBoxContainer);
|
||||
|
||||
public:
|
||||
virtual bool can_edit(const Ref<AnimationNode> &p_node) = 0;
|
||||
virtual void edit(const Ref<AnimationNode> &p_node) = 0;
|
||||
};
|
||||
|
||||
class AnimationTreeEditor : public VBoxContainer {
|
||||
GDCLASS(AnimationTreeEditor, VBoxContainer);
|
||||
|
||||
ScrollContainer *path_edit = nullptr;
|
||||
HBoxContainer *path_hb = nullptr;
|
||||
|
||||
AnimationTree *tree = nullptr;
|
||||
MarginContainer *editor_base = nullptr;
|
||||
|
||||
Vector<String> button_path;
|
||||
Vector<String> edited_path;
|
||||
Vector<AnimationTreeNodeEditorPlugin *> editors;
|
||||
|
||||
void _update_path();
|
||||
void _clear_editors();
|
||||
ObjectID current_root;
|
||||
|
||||
void _path_button_pressed(int p_path);
|
||||
void _animation_list_changed();
|
||||
|
||||
static Vector<String> get_animation_list();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
void _node_removed(Node *p_node);
|
||||
|
||||
static AnimationTreeEditor *singleton;
|
||||
|
||||
public:
|
||||
AnimationTree *get_animation_tree() { return tree; }
|
||||
void add_plugin(AnimationTreeNodeEditorPlugin *p_editor);
|
||||
void remove_plugin(AnimationTreeNodeEditorPlugin *p_editor);
|
||||
|
||||
String get_base_path();
|
||||
|
||||
bool can_edit(const Ref<AnimationNode> &p_node) const;
|
||||
|
||||
void edit_path(const Vector<String> &p_path);
|
||||
Vector<String> get_edited_path() const;
|
||||
|
||||
void enter_editor(const String &p_path = "");
|
||||
static AnimationTreeEditor *get_singleton() { return singleton; }
|
||||
void edit(AnimationTree *p_tree);
|
||||
AnimationTreeEditor();
|
||||
};
|
||||
|
||||
class AnimationTreeEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(AnimationTreeEditorPlugin, EditorPlugin);
|
||||
|
||||
AnimationTreeEditor *anim_tree_editor = nullptr;
|
||||
Button *button = nullptr;
|
||||
|
||||
public:
|
||||
virtual String get_plugin_name() const override { return "AnimationTree"; }
|
||||
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;
|
||||
|
||||
AnimationTreeEditorPlugin();
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,351 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* asset_library_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/editor_asset_installer.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/grid_container.h"
|
||||
#include "scene/gui/line_edit.h"
|
||||
#include "scene/gui/link_button.h"
|
||||
#include "scene/gui/margin_container.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
#include "scene/gui/panel_container.h"
|
||||
#include "scene/gui/progress_bar.h"
|
||||
#include "scene/gui/rich_text_label.h"
|
||||
#include "scene/gui/scroll_container.h"
|
||||
#include "scene/gui/texture_button.h"
|
||||
#include "scene/gui/texture_rect.h"
|
||||
#include "scene/main/http_request.h"
|
||||
|
||||
class EditorFileDialog;
|
||||
class MenuButton;
|
||||
|
||||
class EditorAssetLibraryItem : public PanelContainer {
|
||||
GDCLASS(EditorAssetLibraryItem, PanelContainer);
|
||||
|
||||
TextureButton *icon = nullptr;
|
||||
LinkButton *title = nullptr;
|
||||
LinkButton *category = nullptr;
|
||||
LinkButton *author = nullptr;
|
||||
Label *price = nullptr;
|
||||
|
||||
String title_text;
|
||||
int asset_id = 0;
|
||||
int category_id = 0;
|
||||
int author_id = 0;
|
||||
|
||||
void _asset_clicked();
|
||||
void _category_clicked();
|
||||
void _author_clicked();
|
||||
|
||||
void set_image(int p_type, int p_index, const Ref<Texture2D> &p_image);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void configure(const String &p_title, int p_asset_id, const String &p_category, int p_category_id, const String &p_author, int p_author_id, const String &p_cost);
|
||||
|
||||
void clamp_width(int p_max_width);
|
||||
|
||||
EditorAssetLibraryItem(bool p_clickable = false);
|
||||
};
|
||||
|
||||
class EditorAssetLibraryItemDescription : public ConfirmationDialog {
|
||||
GDCLASS(EditorAssetLibraryItemDescription, ConfirmationDialog);
|
||||
|
||||
EditorAssetLibraryItem *item = nullptr;
|
||||
RichTextLabel *description = nullptr;
|
||||
VBoxContainer *previews_vbox = nullptr;
|
||||
ScrollContainer *previews = nullptr;
|
||||
HBoxContainer *preview_hb = nullptr;
|
||||
PanelContainer *previews_bg = nullptr;
|
||||
|
||||
struct Preview {
|
||||
int id = 0;
|
||||
bool is_video = false;
|
||||
String video_link;
|
||||
Button *button = nullptr;
|
||||
Ref<Texture2D> image;
|
||||
};
|
||||
|
||||
Vector<Preview> preview_images;
|
||||
TextureRect *preview = nullptr;
|
||||
|
||||
void set_image(int p_type, int p_index, const Ref<Texture2D> &p_image);
|
||||
|
||||
int asset_id = 0;
|
||||
String download_url;
|
||||
String title;
|
||||
String sha256;
|
||||
Ref<Texture2D> icon;
|
||||
|
||||
void _link_click(const String &p_url);
|
||||
void _preview_click(int p_id);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void configure(const String &p_title, int p_asset_id, const String &p_category, int p_category_id, const String &p_author, int p_author_id, const String &p_cost, int p_version, const String &p_version_string, const String &p_description, const String &p_download_url, const String &p_browse_url, const String &p_sha256_hash);
|
||||
void add_preview(int p_id, bool p_video, const String &p_url);
|
||||
|
||||
String get_title() { return title; }
|
||||
Ref<Texture2D> get_preview_icon() { return icon; }
|
||||
String get_download_url() { return download_url; }
|
||||
int get_asset_id() { return asset_id; }
|
||||
String get_sha256() { return sha256; }
|
||||
EditorAssetLibraryItemDescription();
|
||||
};
|
||||
|
||||
class EditorAssetLibraryItemDownload : public MarginContainer {
|
||||
GDCLASS(EditorAssetLibraryItemDownload, MarginContainer);
|
||||
|
||||
PanelContainer *panel = nullptr;
|
||||
TextureRect *icon = nullptr;
|
||||
Label *title = nullptr;
|
||||
ProgressBar *progress = nullptr;
|
||||
Button *install_button = nullptr;
|
||||
Button *retry_button = nullptr;
|
||||
TextureButton *dismiss_button = nullptr;
|
||||
|
||||
AcceptDialog *download_error = nullptr;
|
||||
HTTPRequest *download = nullptr;
|
||||
String host;
|
||||
String sha256;
|
||||
Label *status = nullptr;
|
||||
|
||||
int prev_status;
|
||||
|
||||
int asset_id = 0;
|
||||
|
||||
bool external_install;
|
||||
|
||||
EditorAssetInstaller *asset_installer = nullptr;
|
||||
|
||||
void _close();
|
||||
void _make_request();
|
||||
void _http_download_completed(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_external_install(bool p_enable) { external_install = p_enable; }
|
||||
int get_asset_id() { return asset_id; }
|
||||
void configure(const String &p_title, int p_asset_id, const Ref<Texture2D> &p_preview, const String &p_download_url, const String &p_sha256_hash);
|
||||
|
||||
bool can_install() const;
|
||||
void install();
|
||||
|
||||
EditorAssetLibraryItemDownload();
|
||||
};
|
||||
|
||||
class EditorAssetLibrary : public PanelContainer {
|
||||
GDCLASS(EditorAssetLibrary, PanelContainer);
|
||||
|
||||
String host;
|
||||
|
||||
EditorFileDialog *asset_open = nullptr;
|
||||
EditorAssetInstaller *asset_installer = nullptr;
|
||||
|
||||
void _asset_open();
|
||||
void _asset_file_selected(const String &p_file);
|
||||
void _update_repository_options();
|
||||
|
||||
PanelContainer *library_scroll_bg = nullptr;
|
||||
ScrollContainer *library_scroll = nullptr;
|
||||
VBoxContainer *library_vb = nullptr;
|
||||
VBoxContainer *library_message_box = nullptr;
|
||||
Label *library_message = nullptr;
|
||||
Button *library_message_button = nullptr;
|
||||
Callable library_message_action;
|
||||
|
||||
void _set_library_message(const String &p_message);
|
||||
void _set_library_message_with_action(const String &p_message, const String &p_action_text, const Callable &p_action);
|
||||
|
||||
LineEdit *filter = nullptr;
|
||||
Timer *filter_debounce_timer = nullptr;
|
||||
OptionButton *categories = nullptr;
|
||||
OptionButton *repository = nullptr;
|
||||
OptionButton *sort = nullptr;
|
||||
HBoxContainer *error_hb = nullptr;
|
||||
TextureRect *error_tr = nullptr;
|
||||
Label *error_label = nullptr;
|
||||
MenuButton *support = nullptr;
|
||||
|
||||
HBoxContainer *contents = nullptr;
|
||||
|
||||
HBoxContainer *asset_top_page = nullptr;
|
||||
GridContainer *asset_items = nullptr;
|
||||
HBoxContainer *asset_bottom_page = nullptr;
|
||||
|
||||
HTTPRequest *request = nullptr;
|
||||
|
||||
bool templates_only = false;
|
||||
bool initial_loading = true;
|
||||
bool loading_blocked = false;
|
||||
|
||||
void _force_online_mode();
|
||||
|
||||
enum Support {
|
||||
SUPPORT_FEATURED,
|
||||
SUPPORT_COMMUNITY,
|
||||
SUPPORT_TESTING,
|
||||
SUPPORT_MAX
|
||||
};
|
||||
|
||||
enum SortOrder {
|
||||
SORT_UPDATED,
|
||||
SORT_UPDATED_REVERSE,
|
||||
SORT_NAME,
|
||||
SORT_NAME_REVERSE,
|
||||
SORT_COST,
|
||||
SORT_COST_REVERSE,
|
||||
SORT_MAX
|
||||
};
|
||||
|
||||
static const char *sort_key[SORT_MAX];
|
||||
static const char *sort_text[SORT_MAX];
|
||||
static const char *support_key[SUPPORT_MAX];
|
||||
static const char *support_text[SUPPORT_MAX];
|
||||
|
||||
///MainListing
|
||||
|
||||
enum ImageType {
|
||||
IMAGE_QUEUE_ICON,
|
||||
IMAGE_QUEUE_THUMBNAIL,
|
||||
IMAGE_QUEUE_SCREENSHOT,
|
||||
|
||||
};
|
||||
|
||||
struct ImageQueue {
|
||||
bool active = false;
|
||||
int queue_id = 0;
|
||||
ImageType image_type = ImageType::IMAGE_QUEUE_ICON;
|
||||
int image_index = 0;
|
||||
String image_url;
|
||||
HTTPRequest *request = nullptr;
|
||||
ObjectID target;
|
||||
int asset_id = -1;
|
||||
};
|
||||
|
||||
int last_queue_id;
|
||||
HashMap<int, ImageQueue> image_queue;
|
||||
|
||||
void _image_update(bool p_use_cache, bool p_final, const PackedByteArray &p_data, int p_queue_id);
|
||||
void _image_request_completed(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data, int p_queue_id);
|
||||
void _request_image(ObjectID p_for, int p_asset_id, String p_image_url, ImageType p_type, int p_image_index);
|
||||
void _update_image_queue();
|
||||
|
||||
HBoxContainer *_make_pages(int p_page, int p_page_count, int p_page_len, int p_total_items, int p_current_items);
|
||||
|
||||
//
|
||||
EditorAssetLibraryItemDescription *description = nullptr;
|
||||
//
|
||||
|
||||
enum RequestType {
|
||||
REQUESTING_NONE,
|
||||
REQUESTING_CONFIG,
|
||||
REQUESTING_SEARCH,
|
||||
REQUESTING_ASSET,
|
||||
};
|
||||
|
||||
RequestType requesting;
|
||||
Dictionary category_map;
|
||||
|
||||
ScrollContainer *downloads_scroll = nullptr;
|
||||
HBoxContainer *downloads_hb = nullptr;
|
||||
|
||||
void _install_asset();
|
||||
|
||||
void _select_author(const String &p_author);
|
||||
void _select_category(int p_id);
|
||||
void _select_asset(int p_id);
|
||||
|
||||
void _manage_plugins();
|
||||
|
||||
void _search(int p_page = 0);
|
||||
void _rerun_search(int p_ignore);
|
||||
void _search_text_changed(const String &p_text = "");
|
||||
void _search_text_submitted(const String &p_text = "");
|
||||
void _api_request(const String &p_request, RequestType p_request_type, const String &p_arguments = "");
|
||||
void _http_request_completed(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data);
|
||||
void _filter_debounce_timer_timeout();
|
||||
void _request_current_config();
|
||||
EditorAssetLibraryItemDownload *_get_asset_in_progress(int p_asset_id) const;
|
||||
|
||||
void _repository_changed(int p_repository_id);
|
||||
void _support_toggled(int p_support);
|
||||
|
||||
void _install_external_asset(String p_zip_path, String p_title);
|
||||
|
||||
int asset_items_column_width = 0;
|
||||
|
||||
void _update_asset_items_columns();
|
||||
|
||||
friend class EditorAssetLibraryItemDescription;
|
||||
friend class EditorAssetLibraryItem;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
public:
|
||||
void disable_community_support();
|
||||
|
||||
EditorAssetLibrary(bool p_templates_only = false);
|
||||
};
|
||||
|
||||
class AssetLibraryEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(AssetLibraryEditorPlugin, EditorPlugin);
|
||||
|
||||
EditorAssetLibrary *addon_library = nullptr;
|
||||
|
||||
public:
|
||||
static bool is_available();
|
||||
|
||||
virtual String get_plugin_name() const override { return TTRC("AssetLib"); }
|
||||
bool has_main_screen() const override { return true; }
|
||||
virtual void edit(Object *p_object) override {}
|
||||
virtual bool handles(Object *p_object) const override { return false; }
|
||||
virtual void make_visible(bool p_visible) override;
|
||||
//virtual bool get_remove_list(List<Node*> *p_list) { return canvas_item_editor->get_remove_list(p_list); }
|
||||
//virtual Dictionary get_state() const;
|
||||
//virtual void set_state(const Dictionary& p_state);
|
||||
|
||||
AssetLibraryEditorPlugin();
|
||||
};
|
||||
|
|
@ -1,288 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* audio_stream_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 "audio_stream_editor_plugin.h"
|
||||
|
||||
#include "editor/audio_stream_preview.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/resources/audio_stream_wav.h"
|
||||
|
||||
// AudioStreamEditor
|
||||
|
||||
void AudioStreamEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
AudioStreamPreviewGenerator::get_singleton()->connect(SNAME("preview_updated"), callable_mp(this, &AudioStreamEditor::_preview_changed));
|
||||
} break;
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
Ref<Font> font = get_theme_font(SNAME("status_source"), EditorStringName(EditorFonts));
|
||||
|
||||
_current_label->add_theme_font_override(SceneStringName(font), font);
|
||||
_duration_label->add_theme_font_override(SceneStringName(font), font);
|
||||
|
||||
_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
_stop_button->set_button_icon(get_editor_theme_icon(SNAME("Stop")));
|
||||
_preview->set_color(get_theme_color(SNAME("dark_color_2"), EditorStringName(Editor)));
|
||||
|
||||
set_color(get_theme_color(SNAME("dark_color_1"), EditorStringName(Editor)));
|
||||
|
||||
_indicator->queue_redraw();
|
||||
_preview->queue_redraw();
|
||||
} break;
|
||||
case NOTIFICATION_PROCESS: {
|
||||
_current = _player->get_playback_position();
|
||||
_indicator->queue_redraw();
|
||||
} break;
|
||||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
if (!is_visible_in_tree()) {
|
||||
_stop();
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamEditor::_draw_preview() {
|
||||
Size2 size = get_size();
|
||||
int width = size.width;
|
||||
if (width <= 0) {
|
||||
return; // No points to draw.
|
||||
}
|
||||
|
||||
Rect2 rect = _preview->get_rect();
|
||||
|
||||
Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);
|
||||
float preview_len = preview->get_length();
|
||||
|
||||
Vector<Vector2> points;
|
||||
points.resize(width * 2);
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
float ofs = i * preview_len / size.width;
|
||||
float ofs_n = (i + 1) * preview_len / size.width;
|
||||
float max = preview->get_max(ofs, ofs_n) * 0.5 + 0.5;
|
||||
float min = preview->get_min(ofs, ofs_n) * 0.5 + 0.5;
|
||||
|
||||
int idx = i;
|
||||
points.write[idx * 2 + 0] = Vector2(i + 1, rect.position.y + min * rect.size.y);
|
||||
points.write[idx * 2 + 1] = Vector2(i + 1, rect.position.y + max * rect.size.y);
|
||||
}
|
||||
|
||||
Vector<Color> colors = { get_theme_color(SNAME("contrast_color_2"), EditorStringName(Editor)) };
|
||||
|
||||
RS::get_singleton()->canvas_item_add_multiline(_preview->get_canvas_item(), points, colors);
|
||||
}
|
||||
|
||||
void AudioStreamEditor::_preview_changed(ObjectID p_which) {
|
||||
if (stream.is_valid() && stream->get_instance_id() == p_which) {
|
||||
_preview->queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamEditor::_stream_changed() {
|
||||
if (!is_visible()) {
|
||||
return;
|
||||
}
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
void AudioStreamEditor::_play() {
|
||||
if (_player->is_playing()) {
|
||||
_pausing = true;
|
||||
_player->stop();
|
||||
_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
set_process(false);
|
||||
} else {
|
||||
_pausing = false;
|
||||
_player->play(_current);
|
||||
_play_button->set_button_icon(get_editor_theme_icon(SNAME("Pause")));
|
||||
set_process(true);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamEditor::_stop() {
|
||||
_player->stop();
|
||||
_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
_current = 0;
|
||||
_indicator->queue_redraw();
|
||||
set_process(false);
|
||||
}
|
||||
|
||||
void AudioStreamEditor::_on_finished() {
|
||||
_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
if (!_pausing) {
|
||||
_current = 0;
|
||||
_indicator->queue_redraw();
|
||||
} else {
|
||||
_pausing = false;
|
||||
}
|
||||
set_process(false);
|
||||
}
|
||||
|
||||
void AudioStreamEditor::_draw_indicator() {
|
||||
if (stream.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Rect2 rect = _preview->get_rect();
|
||||
float len = stream->get_length();
|
||||
float ofs_x = _current / len * rect.size.width;
|
||||
const Color col = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
|
||||
Ref<Texture2D> icon = get_editor_theme_icon(SNAME("TimelineIndicator"));
|
||||
_indicator->draw_line(Point2(ofs_x, 0), Point2(ofs_x, rect.size.height), col, Math::round(2 * EDSCALE));
|
||||
_indicator->draw_texture(
|
||||
icon,
|
||||
Point2(ofs_x - icon->get_width() * 0.5, 0),
|
||||
col);
|
||||
|
||||
_current_label->set_text(String::num(_current, 2).pad_decimals(2) + " /");
|
||||
}
|
||||
|
||||
void AudioStreamEditor::_on_input_indicator(Ref<InputEvent> p_event) {
|
||||
const Ref<InputEventMouseButton> mb = p_event;
|
||||
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
|
||||
if (mb->is_pressed()) {
|
||||
_seek_to(mb->get_position().x);
|
||||
}
|
||||
_dragging = mb->is_pressed();
|
||||
}
|
||||
|
||||
const Ref<InputEventMouseMotion> mm = p_event;
|
||||
if (mm.is_valid()) {
|
||||
if (_dragging) {
|
||||
_seek_to(mm->get_position().x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamEditor::_seek_to(real_t p_x) {
|
||||
_current = p_x / _preview->get_rect().size.x * stream->get_length();
|
||||
_current = CLAMP(_current, 0, stream->get_length());
|
||||
_player->seek(_current);
|
||||
_indicator->queue_redraw();
|
||||
}
|
||||
|
||||
void AudioStreamEditor::set_stream(const Ref<AudioStream> &p_stream) {
|
||||
if (stream.is_valid()) {
|
||||
stream->disconnect_changed(callable_mp(this, &AudioStreamEditor::_stream_changed));
|
||||
}
|
||||
|
||||
stream = p_stream;
|
||||
if (stream.is_null()) {
|
||||
hide();
|
||||
return;
|
||||
}
|
||||
stream->connect_changed(callable_mp(this, &AudioStreamEditor::_stream_changed));
|
||||
|
||||
_player->set_stream(stream);
|
||||
_current = 0;
|
||||
|
||||
String text = String::num(stream->get_length(), 2).pad_decimals(2) + "s";
|
||||
_duration_label->set_text(text);
|
||||
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
AudioStreamEditor::AudioStreamEditor() {
|
||||
set_custom_minimum_size(Size2(1, 100) * EDSCALE);
|
||||
|
||||
_player = memnew(AudioStreamPlayer);
|
||||
_player->connect(SceneStringName(finished), callable_mp(this, &AudioStreamEditor::_on_finished));
|
||||
add_child(_player);
|
||||
|
||||
VBoxContainer *vbox = memnew(VBoxContainer);
|
||||
vbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
|
||||
add_child(vbox);
|
||||
|
||||
_preview = memnew(ColorRect);
|
||||
_preview->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
_preview->connect(SceneStringName(draw), callable_mp(this, &AudioStreamEditor::_draw_preview));
|
||||
vbox->add_child(_preview);
|
||||
|
||||
_indicator = memnew(Control);
|
||||
_indicator->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
|
||||
_indicator->connect(SceneStringName(draw), callable_mp(this, &AudioStreamEditor::_draw_indicator));
|
||||
_indicator->connect(SceneStringName(gui_input), callable_mp(this, &AudioStreamEditor::_on_input_indicator));
|
||||
_preview->add_child(_indicator);
|
||||
|
||||
HBoxContainer *hbox = memnew(HBoxContainer);
|
||||
hbox->add_theme_constant_override("separation", 0);
|
||||
vbox->add_child(hbox);
|
||||
|
||||
_play_button = memnew(Button);
|
||||
hbox->add_child(_play_button);
|
||||
_play_button->set_flat(true);
|
||||
_play_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
|
||||
_play_button->connect(SceneStringName(pressed), callable_mp(this, &AudioStreamEditor::_play));
|
||||
_play_button->set_shortcut(ED_SHORTCUT("audio_stream_editor/audio_preview_play_pause", TTRC("Audio Preview Play/Pause"), Key::SPACE));
|
||||
_play_button->set_accessibility_name(TTRC("Play"));
|
||||
|
||||
_stop_button = memnew(Button);
|
||||
hbox->add_child(_stop_button);
|
||||
_stop_button->set_flat(true);
|
||||
_stop_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
|
||||
_stop_button->connect(SceneStringName(pressed), callable_mp(this, &AudioStreamEditor::_stop));
|
||||
_stop_button->set_accessibility_name(TTRC("Stop"));
|
||||
|
||||
_current_label = memnew(Label);
|
||||
_current_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
|
||||
_current_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
_current_label->set_modulate(Color(1, 1, 1, 0.5));
|
||||
hbox->add_child(_current_label);
|
||||
|
||||
_duration_label = memnew(Label);
|
||||
hbox->add_child(_duration_label);
|
||||
}
|
||||
|
||||
// EditorInspectorPluginAudioStream
|
||||
|
||||
bool EditorInspectorPluginAudioStream::can_handle(Object *p_object) {
|
||||
return Object::cast_to<AudioStreamWAV>(p_object) != nullptr;
|
||||
}
|
||||
|
||||
void EditorInspectorPluginAudioStream::parse_begin(Object *p_object) {
|
||||
AudioStream *stream = Object::cast_to<AudioStream>(p_object);
|
||||
|
||||
editor = memnew(AudioStreamEditor);
|
||||
editor->set_stream(Ref<AudioStream>(stream));
|
||||
|
||||
add_custom_control(editor);
|
||||
}
|
||||
|
||||
// AudioStreamEditorPlugin
|
||||
|
||||
AudioStreamEditorPlugin::AudioStreamEditorPlugin() {
|
||||
Ref<EditorInspectorPluginAudioStream> plugin;
|
||||
plugin.instantiate();
|
||||
add_inspector_plugin(plugin);
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* audio_stream_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/editor_inspector.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/audio/audio_stream_player.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/color_rect.h"
|
||||
#include "scene/gui/label.h"
|
||||
|
||||
class AudioStreamEditor : public ColorRect {
|
||||
GDCLASS(AudioStreamEditor, ColorRect);
|
||||
|
||||
Ref<AudioStream> stream;
|
||||
|
||||
AudioStreamPlayer *_player = nullptr;
|
||||
ColorRect *_preview = nullptr;
|
||||
Control *_indicator = nullptr;
|
||||
Label *_current_label = nullptr;
|
||||
Label *_duration_label = nullptr;
|
||||
|
||||
Button *_play_button = nullptr;
|
||||
Button *_stop_button = nullptr;
|
||||
|
||||
float _current = 0;
|
||||
bool _dragging = false;
|
||||
bool _pausing = false;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
void _preview_changed(ObjectID p_which);
|
||||
void _play();
|
||||
void _stop();
|
||||
void _on_finished();
|
||||
void _draw_preview();
|
||||
void _draw_indicator();
|
||||
void _on_input_indicator(Ref<InputEvent> p_event);
|
||||
void _seek_to(real_t p_x);
|
||||
void _stream_changed();
|
||||
|
||||
public:
|
||||
void set_stream(const Ref<AudioStream> &p_stream);
|
||||
|
||||
AudioStreamEditor();
|
||||
};
|
||||
|
||||
class EditorInspectorPluginAudioStream : public EditorInspectorPlugin {
|
||||
GDCLASS(EditorInspectorPluginAudioStream, EditorInspectorPlugin);
|
||||
AudioStreamEditor *editor = nullptr;
|
||||
|
||||
public:
|
||||
virtual bool can_handle(Object *p_object) override;
|
||||
virtual void parse_begin(Object *p_object) override;
|
||||
};
|
||||
|
||||
class AudioStreamEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(AudioStreamEditorPlugin, EditorPlugin);
|
||||
|
||||
public:
|
||||
AudioStreamEditorPlugin();
|
||||
};
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* audio_stream_randomizer_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 "audio_stream_randomizer_editor_plugin.h"
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "servers/audio/audio_stream.h"
|
||||
|
||||
void AudioStreamRandomizerEditorPlugin::edit(Object *p_object) {
|
||||
}
|
||||
|
||||
bool AudioStreamRandomizerEditorPlugin::handles(Object *p_object) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void AudioStreamRandomizerEditorPlugin::make_visible(bool p_visible) {
|
||||
}
|
||||
|
||||
void AudioStreamRandomizerEditorPlugin::_move_stream_array_element(Object *p_undo_redo, Object *p_edited, const String &p_array_prefix, int p_from_index, int p_to_pos) {
|
||||
EditorUndoRedoManager *undo_redo_man = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
|
||||
ERR_FAIL_NULL(undo_redo_man);
|
||||
|
||||
AudioStreamRandomizer *randomizer = Object::cast_to<AudioStreamRandomizer>(p_edited);
|
||||
if (!randomizer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute the array indices to save.
|
||||
int begin = 0;
|
||||
int end;
|
||||
if (p_array_prefix == "stream_") {
|
||||
end = randomizer->get_streams_count();
|
||||
} else {
|
||||
ERR_FAIL_MSG("Invalid array prefix for AudioStreamRandomizer.");
|
||||
}
|
||||
if (p_from_index < 0) {
|
||||
// Adding new.
|
||||
if (p_to_pos >= 0) {
|
||||
begin = p_to_pos;
|
||||
} else {
|
||||
end = 0; // Nothing to save when adding at the end.
|
||||
}
|
||||
} else if (p_to_pos < 0) {
|
||||
// Removing.
|
||||
begin = p_from_index;
|
||||
} else {
|
||||
// Moving.
|
||||
begin = MIN(p_from_index, p_to_pos);
|
||||
end = MIN(MAX(p_from_index, p_to_pos) + 1, end);
|
||||
}
|
||||
|
||||
#define ADD_UNDO(obj, property) undo_redo_man->add_undo_property(obj, property, obj->get(property));
|
||||
// Save layers' properties.
|
||||
if (p_from_index < 0) {
|
||||
undo_redo_man->add_undo_method(randomizer, "remove_stream", p_to_pos < 0 ? randomizer->get_streams_count() : p_to_pos);
|
||||
} else if (p_to_pos < 0) {
|
||||
undo_redo_man->add_undo_method(randomizer, "add_stream", p_from_index, Ref<AudioStream>());
|
||||
}
|
||||
|
||||
List<PropertyInfo> properties;
|
||||
randomizer->get_property_list(&properties);
|
||||
for (PropertyInfo pi : properties) {
|
||||
if (pi.name.begins_with(p_array_prefix)) {
|
||||
String str = pi.name.trim_prefix(p_array_prefix);
|
||||
int to_char_index = 0;
|
||||
while (to_char_index < str.length()) {
|
||||
if (str[to_char_index] < '0' || str[to_char_index] > '9') {
|
||||
break;
|
||||
}
|
||||
to_char_index++;
|
||||
}
|
||||
if (to_char_index > 0) {
|
||||
int array_index = str.left(to_char_index).to_int();
|
||||
if (array_index >= begin && array_index < end) {
|
||||
ADD_UNDO(randomizer, pi.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#undef ADD_UNDO
|
||||
|
||||
if (p_from_index < 0) {
|
||||
undo_redo_man->add_do_method(randomizer, "add_stream", p_to_pos, Ref<AudioStream>());
|
||||
} else if (p_to_pos < 0) {
|
||||
undo_redo_man->add_do_method(randomizer, "remove_stream", p_from_index);
|
||||
} else {
|
||||
undo_redo_man->add_do_method(randomizer, "move_stream", p_from_index, p_to_pos);
|
||||
}
|
||||
}
|
||||
|
||||
AudioStreamRandomizerEditorPlugin::AudioStreamRandomizerEditorPlugin() {
|
||||
EditorNode::get_editor_data().add_move_array_element_function(SNAME("AudioStreamRandomizer"), callable_mp(this, &AudioStreamRandomizerEditorPlugin::_move_stream_array_element));
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* audio_stream_randomizer_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
|
||||
class AudioStreamRandomizerEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(AudioStreamRandomizerEditorPlugin, EditorPlugin);
|
||||
|
||||
private:
|
||||
void _move_stream_array_element(Object *p_undo_redo, Object *p_edited, const String &p_array_prefix, int p_from_index, int p_to_pos);
|
||||
|
||||
public:
|
||||
virtual String get_plugin_name() const override { return "AudioStreamRandomizer"; }
|
||||
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;
|
||||
|
||||
AudioStreamRandomizerEditorPlugin();
|
||||
};
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* bit_map_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 "bit_map_editor_plugin.h"
|
||||
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/aspect_ratio_container.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/margin_container.h"
|
||||
#include "scene/gui/texture_rect.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
|
||||
void BitMapEditor::setup(const Ref<BitMap> &p_bitmap) {
|
||||
Ref<ImageTexture> bitmap_texture = ImageTexture::create_from_image(p_bitmap->convert_to_image());
|
||||
texture_rect->set_texture(bitmap_texture);
|
||||
if (bitmap_texture.is_valid()) {
|
||||
centering_container->set_custom_minimum_size(Size2(0, 250) * EDSCALE);
|
||||
centering_container->set_ratio(bitmap_texture->get_size().aspect());
|
||||
outline_overlay->connect(SceneStringName(draw), callable_mp(this, &BitMapEditor::_draw_outline));
|
||||
}
|
||||
size_label->set_text(vformat(U"%s×%s", p_bitmap->get_size().width, p_bitmap->get_size().height));
|
||||
}
|
||||
|
||||
void BitMapEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
cached_outline_color = get_theme_color(SNAME("extra_border_color_1"), EditorStringName(Editor));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void BitMapEditor::_draw_outline() {
|
||||
const float outline_width = Math::round(EDSCALE);
|
||||
const Rect2 outline_rect = Rect2(Vector2(), texture_rect->get_size()).grow(outline_width * 0.5);
|
||||
outline_overlay->draw_rect(outline_rect, cached_outline_color, false, outline_width);
|
||||
}
|
||||
|
||||
BitMapEditor::BitMapEditor() {
|
||||
MarginContainer *margin_container = memnew(MarginContainer);
|
||||
const float outline_width = Math::round(EDSCALE);
|
||||
margin_container->add_theme_constant_override("margin_right", outline_width);
|
||||
margin_container->add_theme_constant_override("margin_top", outline_width);
|
||||
margin_container->add_theme_constant_override("margin_left", outline_width);
|
||||
margin_container->add_theme_constant_override("margin_bottom", outline_width);
|
||||
add_child(margin_container);
|
||||
|
||||
centering_container = memnew(AspectRatioContainer);
|
||||
margin_container->add_child(centering_container);
|
||||
|
||||
texture_rect = memnew(TextureRect);
|
||||
texture_rect->set_texture_filter(TEXTURE_FILTER_NEAREST);
|
||||
texture_rect->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
|
||||
centering_container->add_child(texture_rect);
|
||||
|
||||
outline_overlay = memnew(Control);
|
||||
centering_container->add_child(outline_overlay);
|
||||
|
||||
size_label = memnew(Label);
|
||||
size_label->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
size_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
|
||||
add_child(size_label);
|
||||
|
||||
// Reduce extra padding on top and bottom of size label.
|
||||
Ref<StyleBoxEmpty> stylebox;
|
||||
stylebox.instantiate();
|
||||
stylebox->set_content_margin(SIDE_RIGHT, 4 * EDSCALE);
|
||||
size_label->add_theme_style_override(CoreStringName(normal), stylebox);
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
|
||||
bool EditorInspectorPluginBitMap::can_handle(Object *p_object) {
|
||||
return Object::cast_to<BitMap>(p_object) != nullptr;
|
||||
}
|
||||
|
||||
void EditorInspectorPluginBitMap::parse_begin(Object *p_object) {
|
||||
BitMap *bitmap = Object::cast_to<BitMap>(p_object);
|
||||
if (!bitmap) {
|
||||
return;
|
||||
}
|
||||
Ref<BitMap> bm(bitmap);
|
||||
|
||||
BitMapEditor *editor = memnew(BitMapEditor);
|
||||
editor->setup(bm);
|
||||
add_custom_control(editor);
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
|
||||
BitMapEditorPlugin::BitMapEditorPlugin() {
|
||||
Ref<EditorInspectorPluginBitMap> plugin;
|
||||
plugin.instantiate();
|
||||
add_inspector_plugin(plugin);
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* bit_map_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/editor_inspector.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/resources/bit_map.h"
|
||||
|
||||
class AspectRatioContainer;
|
||||
class TextureRect;
|
||||
|
||||
class BitMapEditor : public VBoxContainer {
|
||||
GDCLASS(BitMapEditor, VBoxContainer);
|
||||
|
||||
private:
|
||||
AspectRatioContainer *centering_container = nullptr;
|
||||
Control *outline_overlay = nullptr;
|
||||
TextureRect *texture_rect = nullptr;
|
||||
Label *size_label = nullptr;
|
||||
|
||||
Color cached_outline_color;
|
||||
|
||||
void _draw_outline();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void setup(const Ref<BitMap> &p_bitmap);
|
||||
|
||||
BitMapEditor();
|
||||
};
|
||||
|
||||
class EditorInspectorPluginBitMap : public EditorInspectorPlugin {
|
||||
GDCLASS(EditorInspectorPluginBitMap, EditorInspectorPlugin);
|
||||
|
||||
public:
|
||||
virtual bool can_handle(Object *p_object) override;
|
||||
virtual void parse_begin(Object *p_object) override;
|
||||
};
|
||||
|
||||
class BitMapEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(BitMapEditorPlugin, EditorPlugin);
|
||||
|
||||
public:
|
||||
BitMapEditorPlugin();
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,223 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* bone_map_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_properties.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
|
||||
#include "scene/3d/skeleton_3d.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/color_rect.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/resources/bone_map.h"
|
||||
#include "scene/resources/texture.h"
|
||||
|
||||
class AspectRatioContainer;
|
||||
|
||||
class BoneMapperButton : public TextureButton {
|
||||
GDCLASS(BoneMapperButton, TextureButton);
|
||||
|
||||
public:
|
||||
enum BoneMapState {
|
||||
BONE_MAP_STATE_UNSET,
|
||||
BONE_MAP_STATE_SET,
|
||||
BONE_MAP_STATE_MISSING,
|
||||
BONE_MAP_STATE_ERROR
|
||||
};
|
||||
|
||||
private:
|
||||
StringName profile_bone_name;
|
||||
bool selected = false;
|
||||
bool require = false;
|
||||
|
||||
TextureRect *circle = nullptr;
|
||||
|
||||
void fetch_textures();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
StringName get_profile_bone_name() const;
|
||||
void set_state(BoneMapState p_state);
|
||||
|
||||
bool is_require() const;
|
||||
|
||||
BoneMapperButton(const StringName &p_profile_bone_name, bool p_require, bool p_selected);
|
||||
};
|
||||
|
||||
class BoneMapperItem : public VBoxContainer {
|
||||
GDCLASS(BoneMapperItem, VBoxContainer);
|
||||
|
||||
int button_id = -1;
|
||||
StringName profile_bone_name;
|
||||
|
||||
Ref<BoneMap> bone_map;
|
||||
|
||||
EditorPropertyText *skeleton_bone_selector = nullptr;
|
||||
Button *picker_button = nullptr;
|
||||
|
||||
void _update_property();
|
||||
void _open_picker();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
virtual void _value_changed(const String &p_property, const Variant &p_value, const String &p_name, bool p_changing);
|
||||
virtual void create_editor();
|
||||
|
||||
public:
|
||||
void assign_button_id(int p_button_id);
|
||||
|
||||
BoneMapperItem(Ref<BoneMap> &p_bone_map, const StringName &p_profile_bone_name = StringName());
|
||||
};
|
||||
|
||||
class BonePicker : public AcceptDialog {
|
||||
GDCLASS(BonePicker, AcceptDialog);
|
||||
|
||||
Skeleton3D *skeleton = nullptr;
|
||||
Tree *bones = nullptr;
|
||||
|
||||
public:
|
||||
void popup_bones_tree(const Size2i &p_minsize = Size2i());
|
||||
bool has_selected_bone();
|
||||
StringName get_selected_bone();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
void _confirm();
|
||||
|
||||
private:
|
||||
void create_editors();
|
||||
void create_bones_tree(Skeleton3D *p_skeleton);
|
||||
|
||||
public:
|
||||
BonePicker(Skeleton3D *p_skeleton);
|
||||
};
|
||||
|
||||
class BoneMapper : public VBoxContainer {
|
||||
GDCLASS(BoneMapper, VBoxContainer);
|
||||
|
||||
Skeleton3D *skeleton = nullptr;
|
||||
Ref<BoneMap> bone_map;
|
||||
|
||||
EditorPropertyResource *profile_selector = nullptr;
|
||||
|
||||
Vector<BoneMapperItem *> bone_mapper_items;
|
||||
|
||||
Button *clear_mapping_button = nullptr;
|
||||
|
||||
VBoxContainer *mapper_item_vbox = nullptr;
|
||||
|
||||
int current_group_idx = 0;
|
||||
int current_bone_idx = -1;
|
||||
|
||||
AspectRatioContainer *bone_mapper_field = nullptr;
|
||||
EditorPropertyEnum *profile_group_selector = nullptr;
|
||||
ColorRect *profile_bg = nullptr;
|
||||
TextureRect *profile_texture = nullptr;
|
||||
Vector<BoneMapperButton *> bone_mapper_buttons;
|
||||
|
||||
void create_editor();
|
||||
void recreate_editor();
|
||||
void clear_items();
|
||||
void recreate_items();
|
||||
void update_group_idx();
|
||||
void _update_state();
|
||||
|
||||
/* Bone picker */
|
||||
BonePicker *picker = nullptr;
|
||||
StringName picker_key_name;
|
||||
void _pick_bone(const StringName &p_bone_name);
|
||||
void _apply_picker_selection();
|
||||
void _clear_mapping_current_group();
|
||||
|
||||
/* For auto mapping */
|
||||
enum BoneSegregation {
|
||||
BONE_SEGREGATION_NONE,
|
||||
BONE_SEGREGATION_LEFT,
|
||||
BONE_SEGREGATION_RIGHT
|
||||
};
|
||||
bool is_match_with_bone_name(const String &p_bone_name, const String &p_word);
|
||||
int search_bone_by_name(Skeleton3D *p_skeleton, const Vector<String> &p_picklist, BoneSegregation p_segregation = BONE_SEGREGATION_NONE, int p_parent = -1, int p_child = -1, int p_children_count = -1);
|
||||
BoneSegregation guess_bone_segregation(const String &p_bone_name);
|
||||
void auto_mapping_process(Ref<BoneMap> &p_bone_map);
|
||||
void _run_auto_mapping();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
virtual void _value_changed(const String &p_property, const Variant &p_value, const String &p_name, bool p_changing);
|
||||
virtual void _profile_changed(const String &p_property, const Variant &p_value, const String &p_name, bool p_changing);
|
||||
|
||||
public:
|
||||
void set_current_group_idx(int p_group_idx);
|
||||
int get_current_group_idx() const;
|
||||
void set_current_bone_idx(int p_bone_idx);
|
||||
int get_current_bone_idx() const;
|
||||
|
||||
BoneMapper(Skeleton3D *p_skeleton, Ref<BoneMap> &p_bone_map);
|
||||
};
|
||||
|
||||
class BoneMapEditor : public VBoxContainer {
|
||||
GDCLASS(BoneMapEditor, VBoxContainer);
|
||||
|
||||
Skeleton3D *skeleton = nullptr;
|
||||
Ref<BoneMap> bone_map;
|
||||
BoneMapper *bone_mapper = nullptr;
|
||||
|
||||
void fetch_objects();
|
||||
void create_editors();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
BoneMapEditor(Ref<BoneMap> &p_bone_map);
|
||||
};
|
||||
|
||||
class EditorInspectorPluginBoneMap : public EditorInspectorPlugin {
|
||||
GDCLASS(EditorInspectorPluginBoneMap, EditorInspectorPlugin);
|
||||
BoneMapEditor *editor = nullptr;
|
||||
|
||||
public:
|
||||
virtual bool can_handle(Object *p_object) override;
|
||||
virtual void parse_begin(Object *p_object) override;
|
||||
};
|
||||
|
||||
class BoneMapEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(BoneMapEditorPlugin, EditorPlugin);
|
||||
|
||||
public:
|
||||
virtual String get_plugin_name() const override { return "BoneMap"; }
|
||||
BoneMapEditorPlugin();
|
||||
};
|
||||
|
|
@ -1,333 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* camera_2d_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 "camera_2d_editor_plugin.h"
|
||||
|
||||
#include "canvas_item_editor_plugin.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/2d/camera_2d.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/menu_button.h"
|
||||
|
||||
void Camera2DEditor::edit(Camera2D *p_camera) {
|
||||
if (p_camera == selected_camera) {
|
||||
return;
|
||||
}
|
||||
const Callable update_overlays = callable_mp(plugin, &EditorPlugin::update_overlays);
|
||||
|
||||
if (selected_camera) {
|
||||
selected_camera->disconnect(SceneStringName(draw), update_overlays);
|
||||
if (drag_type != Drag::NONE) {
|
||||
selected_camera->set_limit_rect(drag_revert);
|
||||
}
|
||||
drag_type = Drag::NONE;
|
||||
hover_type = Drag::NONE;
|
||||
CanvasItemEditor::get_singleton()->set_cursor_shape_override(CURSOR_ARROW);
|
||||
}
|
||||
selected_camera = p_camera;
|
||||
|
||||
if (selected_camera) {
|
||||
selected_camera->connect(SceneStringName(draw), update_overlays);
|
||||
}
|
||||
plugin->update_overlays();
|
||||
}
|
||||
|
||||
bool Camera2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
|
||||
if (!selected_camera || !selected_camera->is_limit_enabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Ref<InputEventMouseButton> mb = p_event;
|
||||
if (mb.is_valid()) {
|
||||
if (mb->get_button_index() == MouseButton::LEFT) {
|
||||
if (mb->is_pressed()) {
|
||||
if (hover_type != Drag::NONE) {
|
||||
Vector2 pos = CanvasItemEditor::get_singleton()->get_canvas_transform().affine_inverse().xform(mb->get_position());
|
||||
const Rect2 limit_rect = selected_camera->get_limit_rect();
|
||||
|
||||
drag_type = hover_type;
|
||||
drag_revert = selected_camera->get_limit_rect();
|
||||
center_drag_point = pos - limit_rect.position;
|
||||
return true;
|
||||
}
|
||||
} else if (drag_type != Drag::NONE) {
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
ur->create_action(TTR("Edit Camera2D Limits"));
|
||||
ur->add_do_method(selected_camera, "_set_limit_rect", selected_camera->get_limit_rect());
|
||||
ur->add_do_method(this, "_update_overlays_if_needed", selected_camera);
|
||||
ur->add_undo_method(selected_camera, "_set_limit_rect", drag_revert);
|
||||
ur->add_undo_method(this, "_update_overlays_if_needed", selected_camera);
|
||||
ur->commit_action(false);
|
||||
|
||||
drag_type = Drag::NONE;
|
||||
return true;
|
||||
}
|
||||
} else if (drag_type != Drag::NONE && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
|
||||
selected_camera->set_limit_rect(drag_revert);
|
||||
drag_type = Drag::NONE;
|
||||
plugin->update_overlays();
|
||||
_update_hover(mb->get_position());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
if (mm.is_valid()) {
|
||||
Vector2 pos = mm->get_position();
|
||||
if (drag_type == Drag::NONE) {
|
||||
_update_hover(pos);
|
||||
return false;
|
||||
}
|
||||
|
||||
pos = CanvasItemEditor::get_singleton()->get_canvas_transform().affine_inverse().xform(pos);
|
||||
pos = CanvasItemEditor::get_singleton()->snap_point(pos);
|
||||
|
||||
switch (drag_type) {
|
||||
case Drag::LEFT: {
|
||||
selected_camera->set_limit(SIDE_LEFT, MIN(selected_camera->get_limit(SIDE_RIGHT), pos.x));
|
||||
plugin->update_overlays();
|
||||
} break;
|
||||
|
||||
case Drag::RIGHT: {
|
||||
selected_camera->set_limit(SIDE_RIGHT, MAX(selected_camera->get_limit(SIDE_LEFT), pos.x));
|
||||
plugin->update_overlays();
|
||||
} break;
|
||||
|
||||
case Drag::TOP: {
|
||||
selected_camera->set_limit(SIDE_TOP, MIN(selected_camera->get_limit(SIDE_BOTTOM), pos.y));
|
||||
plugin->update_overlays();
|
||||
} break;
|
||||
|
||||
case Drag::BOTTOM: {
|
||||
selected_camera->set_limit(SIDE_BOTTOM, MAX(selected_camera->get_limit(SIDE_TOP), pos.y));
|
||||
plugin->update_overlays();
|
||||
} break;
|
||||
|
||||
case Drag::TOP_LEFT: {
|
||||
selected_camera->set_limit(SIDE_LEFT, MIN(selected_camera->get_limit(SIDE_RIGHT), pos.x));
|
||||
selected_camera->set_limit(SIDE_TOP, MIN(selected_camera->get_limit(SIDE_BOTTOM), pos.y));
|
||||
plugin->update_overlays();
|
||||
} break;
|
||||
|
||||
case Drag::TOP_RIGHT: {
|
||||
selected_camera->set_limit(SIDE_RIGHT, MAX(selected_camera->get_limit(SIDE_LEFT), pos.x));
|
||||
selected_camera->set_limit(SIDE_TOP, MIN(selected_camera->get_limit(SIDE_BOTTOM), pos.y));
|
||||
plugin->update_overlays();
|
||||
} break;
|
||||
|
||||
case Drag::BOTTOM_LEFT: {
|
||||
selected_camera->set_limit(SIDE_LEFT, MIN(selected_camera->get_limit(SIDE_RIGHT), pos.x));
|
||||
selected_camera->set_limit(SIDE_BOTTOM, MAX(selected_camera->get_limit(SIDE_TOP), pos.y));
|
||||
plugin->update_overlays();
|
||||
} break;
|
||||
|
||||
case Drag::BOTTOM_RIGHT: {
|
||||
selected_camera->set_limit(SIDE_RIGHT, MAX(selected_camera->get_limit(SIDE_LEFT), pos.x));
|
||||
selected_camera->set_limit(SIDE_BOTTOM, MAX(selected_camera->get_limit(SIDE_TOP), pos.y));
|
||||
plugin->update_overlays();
|
||||
} break;
|
||||
|
||||
case Drag::CENTER: {
|
||||
Rect2 target_rect = selected_camera->get_limit_rect();
|
||||
target_rect.position = pos - center_drag_point;
|
||||
selected_camera->set_limit_rect(target_rect);
|
||||
plugin->update_overlays();
|
||||
} break;
|
||||
|
||||
case Drag::NONE: {
|
||||
} break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Camera2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
|
||||
if (!selected_camera || !selected_camera->is_limit_enabled()) {
|
||||
return;
|
||||
}
|
||||
Rect2 limit_rect = selected_camera->get_limit_rect();
|
||||
limit_rect = CanvasItemEditor::get_singleton()->get_canvas_transform().xform(limit_rect);
|
||||
p_overlay->draw_rect(limit_rect, Color(1, 1, 0.25, 0.63), false, 3);
|
||||
}
|
||||
|
||||
void Camera2DEditor::_menu_option(int p_option) {
|
||||
switch (p_option) {
|
||||
case MENU_SNAP_LIMITS_TO_VIEWPORT: {
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
ur->create_action(TTR("Snap Camera2D Limits to the Viewport"), UndoRedo::MERGE_DISABLE, selected_camera);
|
||||
ur->add_do_method(this, "_snap_limits_to_viewport", selected_camera);
|
||||
ur->add_undo_method(selected_camera, "_set_limit_rect", selected_camera->get_limit_rect());
|
||||
ur->add_undo_method(this, "_update_overlays_if_needed", selected_camera);
|
||||
ur->commit_action();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void Camera2DEditor::_snap_limits_to_viewport(Camera2D *p_camera) {
|
||||
p_camera->set_limit(SIDE_LEFT, 0);
|
||||
p_camera->set_limit(SIDE_TOP, 0);
|
||||
p_camera->set_limit(SIDE_RIGHT, GLOBAL_GET("display/window/size/viewport_width"));
|
||||
p_camera->set_limit(SIDE_BOTTOM, GLOBAL_GET("display/window/size/viewport_height"));
|
||||
_update_overlays_if_needed(p_camera);
|
||||
}
|
||||
|
||||
void Camera2DEditor::_update_overlays_if_needed(Camera2D *p_camera) {
|
||||
if (p_camera == selected_camera) {
|
||||
plugin->update_overlays();
|
||||
}
|
||||
}
|
||||
|
||||
void Camera2DEditor::_update_hover(const Vector2 &p_mouse_pos) {
|
||||
if (CanvasItemEditor::get_singleton()->get_current_tool() != CanvasItemEditor::TOOL_SELECT) {
|
||||
hover_type = Drag::NONE;
|
||||
CanvasItemEditor::get_singleton()->set_cursor_shape_override();
|
||||
return;
|
||||
}
|
||||
|
||||
const Rect2 limit_rect = CanvasItemEditor::get_singleton()->get_canvas_transform().xform(selected_camera->get_limit_rect());
|
||||
const float drag_tolerance = 8.0;
|
||||
const Vector2 tolerance_vector = Vector2(1, 1) * drag_tolerance;
|
||||
|
||||
hover_type = Drag::NONE;
|
||||
if (Rect2(limit_rect.position - tolerance_vector, tolerance_vector * 2).has_point(p_mouse_pos)) {
|
||||
hover_type = Drag::TOP_LEFT;
|
||||
} else if (Rect2(Vector2(limit_rect.get_end().x, limit_rect.position.y) - tolerance_vector, tolerance_vector * 2).has_point(p_mouse_pos)) {
|
||||
hover_type = Drag::TOP_RIGHT;
|
||||
} else if (Rect2(Vector2(limit_rect.position.x, limit_rect.get_end().y) - tolerance_vector, tolerance_vector * 2).has_point(p_mouse_pos)) {
|
||||
hover_type = Drag::BOTTOM_LEFT;
|
||||
} else if (Rect2(limit_rect.get_end() - tolerance_vector, tolerance_vector * 2).has_point(p_mouse_pos)) {
|
||||
hover_type = Drag::BOTTOM_RIGHT;
|
||||
} else if (p_mouse_pos.y > limit_rect.position.y && p_mouse_pos.y < limit_rect.get_end().y) {
|
||||
if (Math::abs(p_mouse_pos.x - limit_rect.position.x) < drag_tolerance) {
|
||||
hover_type = Drag::LEFT;
|
||||
} else if (Math::abs(p_mouse_pos.x - limit_rect.get_end().x) < drag_tolerance) {
|
||||
hover_type = Drag::RIGHT;
|
||||
}
|
||||
} else if (p_mouse_pos.x > limit_rect.position.x && p_mouse_pos.x < limit_rect.get_end().x) {
|
||||
if (Math::abs(p_mouse_pos.y - limit_rect.position.y) < drag_tolerance) {
|
||||
hover_type = Drag::TOP;
|
||||
} else if (Math::abs(p_mouse_pos.y - limit_rect.get_end().y) < drag_tolerance) {
|
||||
hover_type = Drag::BOTTOM;
|
||||
}
|
||||
}
|
||||
|
||||
if (hover_type == Drag::NONE && limit_rect.has_point(p_mouse_pos)) {
|
||||
const Rect2 editor_rect = Rect2(Vector2(), CanvasItemEditor::get_singleton()->get_viewport_control()->get_size());
|
||||
const Rect2 transformed_rect = selected_camera->get_viewport()->get_canvas_transform().xform_inv(limit_rect);
|
||||
|
||||
// Only allow center drag if any limit edge is visible on screen.
|
||||
bool edge_visible = false;
|
||||
edge_visible = edge_visible || (transformed_rect.get_end().y > editor_rect.position.y && transformed_rect.position.y < editor_rect.get_end().y && transformed_rect.position.x > editor_rect.position.x && transformed_rect.position.x < editor_rect.get_end().x);
|
||||
edge_visible = edge_visible || (transformed_rect.get_end().y > editor_rect.position.y && transformed_rect.position.y < editor_rect.get_end().y && transformed_rect.get_end().x > editor_rect.position.x && transformed_rect.get_end().x < editor_rect.get_end().x);
|
||||
edge_visible = edge_visible || (transformed_rect.get_end().x > editor_rect.position.x && transformed_rect.position.x < editor_rect.get_end().x && transformed_rect.position.y > editor_rect.position.y && transformed_rect.position.y < editor_rect.get_end().y);
|
||||
edge_visible = edge_visible || (transformed_rect.get_end().x > editor_rect.position.x && transformed_rect.position.x < editor_rect.get_end().x && transformed_rect.get_end().y > editor_rect.position.y && transformed_rect.get_end().y < editor_rect.get_end().y);
|
||||
|
||||
if (edge_visible) {
|
||||
hover_type = Drag::CENTER;
|
||||
}
|
||||
}
|
||||
|
||||
switch (hover_type) {
|
||||
case Drag::NONE: {
|
||||
CanvasItemEditor::get_singleton()->set_cursor_shape_override();
|
||||
} break;
|
||||
case Drag::LEFT:
|
||||
case Drag::RIGHT: {
|
||||
CanvasItemEditor::get_singleton()->set_cursor_shape_override(CURSOR_HSIZE);
|
||||
} break;
|
||||
case Drag::TOP:
|
||||
case Drag::BOTTOM: {
|
||||
CanvasItemEditor::get_singleton()->set_cursor_shape_override(CURSOR_VSIZE);
|
||||
} break;
|
||||
case Drag::TOP_LEFT:
|
||||
case Drag::BOTTOM_RIGHT: {
|
||||
CanvasItemEditor::get_singleton()->set_cursor_shape_override(CURSOR_FDIAGSIZE);
|
||||
} break;
|
||||
case Drag::TOP_RIGHT:
|
||||
case Drag::BOTTOM_LEFT: {
|
||||
CanvasItemEditor::get_singleton()->set_cursor_shape_override(CURSOR_BDIAGSIZE);
|
||||
} break;
|
||||
case Drag::CENTER: {
|
||||
CanvasItemEditor::get_singleton()->set_cursor_shape_override(CURSOR_MOVE);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void Camera2DEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
options->set_button_icon(get_editor_theme_icon(SNAME("Camera2D")));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void Camera2DEditor::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_snap_limits_to_viewport", "camera"), &Camera2DEditor::_snap_limits_to_viewport);
|
||||
ClassDB::bind_method(D_METHOD("_update_overlays_if_needed", "camera"), &Camera2DEditor::_update_overlays_if_needed);
|
||||
}
|
||||
|
||||
Camera2DEditor::Camera2DEditor(EditorPlugin *p_plugin) {
|
||||
plugin = p_plugin;
|
||||
|
||||
options = memnew(MenuButton);
|
||||
options->set_text(TTRC("Camera2D"));
|
||||
options->get_popup()->add_item(TTRC("Snap the Limits to the Viewport"), MENU_SNAP_LIMITS_TO_VIEWPORT);
|
||||
options->set_switch_on_hover(true);
|
||||
options->hide();
|
||||
CanvasItemEditor::get_singleton()->add_control_to_menu_panel(options);
|
||||
options->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &Camera2DEditor::_menu_option));
|
||||
}
|
||||
|
||||
void Camera2DEditorPlugin::edit(Object *p_object) {
|
||||
camera_2d_editor->edit(Object::cast_to<Camera2D>(p_object));
|
||||
}
|
||||
|
||||
bool Camera2DEditorPlugin::handles(Object *p_object) const {
|
||||
return p_object->is_class("Camera2D");
|
||||
}
|
||||
|
||||
void Camera2DEditorPlugin::make_visible(bool p_visible) {
|
||||
if (p_visible) {
|
||||
camera_2d_editor->options->show();
|
||||
} else {
|
||||
camera_2d_editor->options->hide();
|
||||
}
|
||||
}
|
||||
|
||||
Camera2DEditorPlugin::Camera2DEditorPlugin() {
|
||||
camera_2d_editor = memnew(Camera2DEditor(this));
|
||||
EditorNode::get_singleton()->get_gui_base()->add_child(camera_2d_editor);
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* camera_2d_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
|
||||
class Camera2D;
|
||||
class Label;
|
||||
class MenuButton;
|
||||
|
||||
class Camera2DEditor : public Control {
|
||||
GDCLASS(Camera2DEditor, Control);
|
||||
|
||||
EditorPlugin *plugin = nullptr;
|
||||
|
||||
enum Menu {
|
||||
MENU_SNAP_LIMITS_TO_VIEWPORT,
|
||||
};
|
||||
|
||||
enum class Drag {
|
||||
NONE,
|
||||
LEFT,
|
||||
TOP,
|
||||
RIGHT,
|
||||
BOTTOM,
|
||||
TOP_LEFT,
|
||||
TOP_RIGHT,
|
||||
BOTTOM_LEFT,
|
||||
BOTTOM_RIGHT,
|
||||
CENTER,
|
||||
};
|
||||
Drag drag_type = Drag::NONE;
|
||||
Drag hover_type = Drag::NONE;
|
||||
|
||||
Rect2 drag_revert;
|
||||
Vector2 center_drag_point;
|
||||
|
||||
Camera2D *selected_camera = nullptr;
|
||||
|
||||
friend class Camera2DEditorPlugin;
|
||||
MenuButton *options = nullptr;
|
||||
|
||||
void _menu_option(int p_option);
|
||||
void _snap_limits_to_viewport(Camera2D *p_camera);
|
||||
void _update_overlays_if_needed(Camera2D *p_camera);
|
||||
void _update_hover(const Vector2 &p_mouse_pos);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void edit(Camera2D *p_camera);
|
||||
|
||||
bool forward_canvas_gui_input(const Ref<InputEvent> &p_event);
|
||||
void forward_canvas_draw_over_viewport(Control *p_overlay);
|
||||
|
||||
Camera2DEditor(EditorPlugin *p_plugin);
|
||||
};
|
||||
|
||||
class Camera2DEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(Camera2DEditorPlugin, EditorPlugin);
|
||||
|
||||
Camera2DEditor *camera_2d_editor = nullptr;
|
||||
|
||||
public:
|
||||
virtual void edit(Object *p_object) override;
|
||||
virtual bool handles(Object *p_object) const override;
|
||||
virtual void make_visible(bool p_visible) override;
|
||||
|
||||
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override { return camera_2d_editor->forward_canvas_gui_input(p_event); }
|
||||
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { camera_2d_editor->forward_canvas_draw_over_viewport(p_overlay); }
|
||||
|
||||
Camera2DEditorPlugin();
|
||||
};
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* camera_3d_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 "camera_3d_editor_plugin.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "node_3d_editor_plugin.h"
|
||||
#include "scene/gui/texture_rect.h"
|
||||
#include "scene/main/viewport.h"
|
||||
|
||||
void Camera3DEditor::_node_removed(Node *p_node) {
|
||||
if (p_node == node) {
|
||||
node = nullptr;
|
||||
Node3DEditor::get_singleton()->set_custom_camera(nullptr);
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
void Camera3DEditor::_pressed() {
|
||||
Node *sn = (node && preview->is_pressed()) ? node : nullptr;
|
||||
Node3DEditor::get_singleton()->set_custom_camera(sn);
|
||||
}
|
||||
|
||||
void Camera3DEditor::edit(Node *p_camera) {
|
||||
node = p_camera;
|
||||
|
||||
if (!node) {
|
||||
preview->set_pressed(false);
|
||||
Node3DEditor::get_singleton()->set_custom_camera(nullptr);
|
||||
} else {
|
||||
if (preview->is_pressed()) {
|
||||
Node3DEditor::get_singleton()->set_custom_camera(p_camera);
|
||||
} else {
|
||||
Node3DEditor::get_singleton()->set_custom_camera(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Camera3DEditor::Camera3DEditor() {
|
||||
preview = memnew(Button);
|
||||
add_child(preview);
|
||||
|
||||
preview->set_text(TTR("Preview"));
|
||||
preview->set_toggle_mode(true);
|
||||
preview->set_anchor(SIDE_LEFT, Control::ANCHOR_END);
|
||||
preview->set_anchor(SIDE_RIGHT, Control::ANCHOR_END);
|
||||
preview->set_offset(SIDE_LEFT, -60);
|
||||
preview->set_offset(SIDE_RIGHT, 0);
|
||||
preview->set_offset(SIDE_TOP, 0);
|
||||
preview->set_offset(SIDE_BOTTOM, 10);
|
||||
preview->connect(SceneStringName(pressed), callable_mp(this, &Camera3DEditor::_pressed));
|
||||
}
|
||||
|
||||
void Camera3DPreview::_update_sub_viewport_size() {
|
||||
sub_viewport->set_size(Node3DEditor::get_camera_viewport_size(camera));
|
||||
}
|
||||
|
||||
Camera3DPreview::Camera3DPreview(Camera3D *p_camera) :
|
||||
TexturePreview(nullptr, false), camera(p_camera), sub_viewport(memnew(SubViewport)) {
|
||||
RenderingServer::get_singleton()->viewport_attach_camera(sub_viewport->get_viewport_rid(), camera->get_camera());
|
||||
add_child(sub_viewport);
|
||||
|
||||
TextureRect *display = get_texture_display();
|
||||
display->set_texture(sub_viewport->get_texture());
|
||||
sub_viewport->connect("size_changed", callable_mp((CanvasItem *)display, &CanvasItem::queue_redraw));
|
||||
sub_viewport->get_texture()->connect_changed(callable_mp((TexturePreview *)this, &Camera3DPreview::_update_texture_display_ratio));
|
||||
|
||||
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &Camera3DPreview::_update_sub_viewport_size));
|
||||
_update_sub_viewport_size();
|
||||
}
|
||||
|
||||
bool EditorInspectorPluginCamera3DPreview::can_handle(Object *p_object) {
|
||||
return Object::cast_to<Camera3D>(p_object) != nullptr;
|
||||
}
|
||||
|
||||
void EditorInspectorPluginCamera3DPreview::parse_begin(Object *p_object) {
|
||||
Camera3D *camera = Object::cast_to<Camera3D>(p_object);
|
||||
Camera3DPreview *preview = memnew(Camera3DPreview(camera));
|
||||
add_custom_control(preview);
|
||||
}
|
||||
|
||||
void Camera3DEditorPlugin::edit(Object *p_object) {
|
||||
Node3DEditor::get_singleton()->set_can_preview(Object::cast_to<Camera3D>(p_object));
|
||||
}
|
||||
|
||||
bool Camera3DEditorPlugin::handles(Object *p_object) const {
|
||||
return p_object->is_class("Camera3D");
|
||||
}
|
||||
|
||||
void Camera3DEditorPlugin::make_visible(bool p_visible) {
|
||||
if (!p_visible) {
|
||||
Node3DEditor::get_singleton()->set_can_preview(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
Camera3DEditorPlugin::Camera3DEditorPlugin() {
|
||||
Ref<EditorInspectorPluginCamera3DPreview> plugin;
|
||||
plugin.instantiate();
|
||||
add_inspector_plugin(plugin);
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* camera_3d_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "editor/plugins/texture_editor_plugin.h"
|
||||
|
||||
class Camera3D;
|
||||
class SubViewport;
|
||||
|
||||
class Camera3DEditor : public Control {
|
||||
GDCLASS(Camera3DEditor, Control);
|
||||
|
||||
Panel *panel = nullptr;
|
||||
Button *preview = nullptr;
|
||||
Node *node = nullptr;
|
||||
|
||||
void _pressed();
|
||||
|
||||
protected:
|
||||
void _node_removed(Node *p_node);
|
||||
|
||||
public:
|
||||
void edit(Node *p_camera);
|
||||
Camera3DEditor();
|
||||
};
|
||||
|
||||
class Camera3DPreview : public TexturePreview {
|
||||
GDCLASS(Camera3DPreview, TexturePreview);
|
||||
|
||||
Camera3D *camera = nullptr;
|
||||
SubViewport *sub_viewport = nullptr;
|
||||
|
||||
void _update_sub_viewport_size();
|
||||
|
||||
public:
|
||||
Camera3DPreview(Camera3D *p_camera);
|
||||
};
|
||||
|
||||
class EditorInspectorPluginCamera3DPreview : public EditorInspectorPluginTexture {
|
||||
GDCLASS(EditorInspectorPluginCamera3DPreview, EditorInspectorPluginTexture);
|
||||
|
||||
public:
|
||||
virtual bool can_handle(Object *p_object) override;
|
||||
virtual void parse_begin(Object *p_object) override;
|
||||
};
|
||||
|
||||
class Camera3DEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(Camera3DEditorPlugin, EditorPlugin);
|
||||
|
||||
public:
|
||||
virtual String get_plugin_name() const override { return "Camera3D"; }
|
||||
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;
|
||||
|
||||
Camera3DEditorPlugin();
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,667 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* canvas_item_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
|
||||
class AcceptDialog;
|
||||
class Button;
|
||||
class ButtonGroup;
|
||||
class CanvasItemEditorViewport;
|
||||
class ConfirmationDialog;
|
||||
class EditorData;
|
||||
class EditorSelection;
|
||||
class EditorZoomWidget;
|
||||
class HScrollBar;
|
||||
class HSplitContainer;
|
||||
class MenuButton;
|
||||
class PanelContainer;
|
||||
class StyleBoxTexture;
|
||||
class ViewPanner;
|
||||
class VScrollBar;
|
||||
class VSeparator;
|
||||
class VSplitContainer;
|
||||
|
||||
class CanvasItemEditorSelectedItem : public Object {
|
||||
GDCLASS(CanvasItemEditorSelectedItem, Object);
|
||||
|
||||
public:
|
||||
Transform2D prev_xform;
|
||||
Rect2 prev_rect;
|
||||
Vector2 prev_pivot;
|
||||
real_t prev_anchors[4] = { (real_t)0.0 };
|
||||
|
||||
Transform2D pre_drag_xform;
|
||||
Rect2 pre_drag_rect;
|
||||
|
||||
List<real_t> pre_drag_bones_length;
|
||||
List<Dictionary> pre_drag_bones_undo_state;
|
||||
|
||||
Dictionary undo_state;
|
||||
};
|
||||
|
||||
class CanvasItemEditor : public VBoxContainer {
|
||||
GDCLASS(CanvasItemEditor, VBoxContainer);
|
||||
|
||||
public:
|
||||
enum Tool {
|
||||
TOOL_SELECT,
|
||||
TOOL_LIST_SELECT,
|
||||
TOOL_MOVE,
|
||||
TOOL_SCALE,
|
||||
TOOL_ROTATE,
|
||||
TOOL_EDIT_PIVOT,
|
||||
TOOL_PAN,
|
||||
TOOL_RULER,
|
||||
TOOL_MAX
|
||||
};
|
||||
|
||||
enum AddNodeOption {
|
||||
ADD_NODE,
|
||||
ADD_INSTANCE,
|
||||
ADD_PASTE,
|
||||
ADD_MOVE,
|
||||
};
|
||||
|
||||
private:
|
||||
enum SnapTarget {
|
||||
SNAP_TARGET_NONE = 0,
|
||||
SNAP_TARGET_PARENT,
|
||||
SNAP_TARGET_SELF_ANCHORS,
|
||||
SNAP_TARGET_SELF,
|
||||
SNAP_TARGET_OTHER_NODE,
|
||||
SNAP_TARGET_GUIDE,
|
||||
SNAP_TARGET_GRID,
|
||||
SNAP_TARGET_PIXEL
|
||||
};
|
||||
|
||||
enum MenuOption {
|
||||
SNAP_USE,
|
||||
SNAP_USE_NODE_PARENT,
|
||||
SNAP_USE_NODE_ANCHORS,
|
||||
SNAP_USE_NODE_SIDES,
|
||||
SNAP_USE_NODE_CENTER,
|
||||
SNAP_USE_OTHER_NODES,
|
||||
SNAP_USE_GRID,
|
||||
SNAP_USE_GUIDES,
|
||||
SNAP_USE_ROTATION,
|
||||
SNAP_USE_SCALE,
|
||||
SNAP_RELATIVE,
|
||||
SNAP_CONFIGURE,
|
||||
SNAP_USE_PIXEL,
|
||||
SHOW_HELPERS,
|
||||
SHOW_RULERS,
|
||||
SHOW_GUIDES,
|
||||
SHOW_ORIGIN,
|
||||
SHOW_VIEWPORT,
|
||||
SHOW_POSITION_GIZMOS,
|
||||
SHOW_LOCK_GIZMOS,
|
||||
SHOW_GROUP_GIZMOS,
|
||||
SHOW_TRANSFORMATION_GIZMOS,
|
||||
LOCK_SELECTED,
|
||||
UNLOCK_SELECTED,
|
||||
GROUP_SELECTED,
|
||||
UNGROUP_SELECTED,
|
||||
ANIM_INSERT_KEY,
|
||||
ANIM_INSERT_KEY_EXISTING,
|
||||
ANIM_INSERT_POS,
|
||||
ANIM_INSERT_ROT,
|
||||
ANIM_INSERT_SCALE,
|
||||
ANIM_COPY_POSE,
|
||||
ANIM_PASTE_POSE,
|
||||
ANIM_CLEAR_POSE,
|
||||
CLEAR_GUIDES,
|
||||
VIEW_CENTER_TO_SELECTION,
|
||||
VIEW_FRAME_TO_SELECTION,
|
||||
PREVIEW_CANVAS_SCALE,
|
||||
SKELETON_MAKE_BONES,
|
||||
SKELETON_SHOW_BONES
|
||||
};
|
||||
|
||||
enum DragType {
|
||||
DRAG_NONE,
|
||||
DRAG_BOX_SELECTION,
|
||||
DRAG_LEFT,
|
||||
DRAG_TOP_LEFT,
|
||||
DRAG_TOP,
|
||||
DRAG_TOP_RIGHT,
|
||||
DRAG_RIGHT,
|
||||
DRAG_BOTTOM_RIGHT,
|
||||
DRAG_BOTTOM,
|
||||
DRAG_BOTTOM_LEFT,
|
||||
DRAG_ANCHOR_TOP_LEFT,
|
||||
DRAG_ANCHOR_TOP_RIGHT,
|
||||
DRAG_ANCHOR_BOTTOM_RIGHT,
|
||||
DRAG_ANCHOR_BOTTOM_LEFT,
|
||||
DRAG_ANCHOR_ALL,
|
||||
DRAG_QUEUED,
|
||||
DRAG_MOVE,
|
||||
DRAG_MOVE_X,
|
||||
DRAG_MOVE_Y,
|
||||
DRAG_SCALE_X,
|
||||
DRAG_SCALE_Y,
|
||||
DRAG_SCALE_BOTH,
|
||||
DRAG_ROTATE,
|
||||
DRAG_PIVOT,
|
||||
DRAG_TEMP_PIVOT,
|
||||
DRAG_V_GUIDE,
|
||||
DRAG_H_GUIDE,
|
||||
DRAG_DOUBLE_GUIDE,
|
||||
DRAG_KEY_MOVE
|
||||
};
|
||||
|
||||
enum GridVisibility {
|
||||
GRID_VISIBILITY_SHOW,
|
||||
GRID_VISIBILITY_SHOW_WHEN_SNAPPING,
|
||||
GRID_VISIBILITY_HIDE,
|
||||
};
|
||||
|
||||
const String locked_transform_warning = TTRC("All selected CanvasItems are either invisible or locked in some way and can't be transformed.");
|
||||
|
||||
bool selection_menu_additive_selection = false;
|
||||
|
||||
Tool tool = TOOL_SELECT;
|
||||
Control *viewport = nullptr;
|
||||
Control *viewport_scrollable = nullptr;
|
||||
|
||||
HScrollBar *h_scroll = nullptr;
|
||||
VScrollBar *v_scroll = nullptr;
|
||||
|
||||
// Used for secondary menu items which are displayed depending on the currently selected node
|
||||
// (such as MeshInstance's "Mesh" menu).
|
||||
PanelContainer *context_toolbar_panel = nullptr;
|
||||
HBoxContainer *context_toolbar_hbox = nullptr;
|
||||
HashMap<Control *, VSeparator *> context_toolbar_separators;
|
||||
|
||||
void _update_context_toolbar();
|
||||
|
||||
Transform2D transform;
|
||||
GridVisibility grid_visibility = GRID_VISIBILITY_SHOW_WHEN_SNAPPING;
|
||||
bool show_rulers = true;
|
||||
bool show_guides = true;
|
||||
bool show_origin = true;
|
||||
bool show_viewport = true;
|
||||
bool show_helpers = false;
|
||||
bool show_position_gizmos = true;
|
||||
bool show_lock_gizmos = true;
|
||||
bool show_group_gizmos = true;
|
||||
bool show_transformation_gizmos = true;
|
||||
|
||||
real_t zoom = 1.0;
|
||||
Point2 view_offset;
|
||||
Point2 previous_update_view_offset;
|
||||
|
||||
bool selected_from_canvas = false;
|
||||
|
||||
// Defaults are defined in clear().
|
||||
Point2 grid_offset;
|
||||
Point2 grid_step;
|
||||
Vector2i primary_grid_step;
|
||||
int grid_step_multiplier = 0;
|
||||
|
||||
real_t snap_rotation_step = 0.0;
|
||||
real_t snap_rotation_offset = 0.0;
|
||||
real_t snap_scale_step = 0.0;
|
||||
bool smart_snap_active = false;
|
||||
bool grid_snap_active = false;
|
||||
|
||||
bool snap_node_parent = true;
|
||||
bool snap_node_anchors = true;
|
||||
bool snap_node_sides = true;
|
||||
bool snap_node_center = true;
|
||||
bool snap_other_nodes = true;
|
||||
bool snap_guides = true;
|
||||
bool snap_rotation = false;
|
||||
bool snap_scale = false;
|
||||
bool snap_relative = false;
|
||||
// Enable pixel snapping even if pixel snap rendering is disabled in the Project Settings.
|
||||
// This results in crisper visuals by preventing 2D nodes from being placed at subpixel coordinates.
|
||||
bool snap_pixel = true;
|
||||
|
||||
bool key_pos = true;
|
||||
bool key_rot = true;
|
||||
bool key_scale = false;
|
||||
|
||||
bool pan_pressed = false;
|
||||
Vector2 temp_pivot = Vector2(Math::INF, Math::INF);
|
||||
|
||||
bool ruler_tool_active = false;
|
||||
Point2 ruler_tool_origin;
|
||||
real_t ruler_width_scaled = 16.0;
|
||||
int ruler_font_size = 8;
|
||||
Point2 node_create_position;
|
||||
|
||||
MenuOption last_option;
|
||||
|
||||
struct _SelectResult {
|
||||
CanvasItem *item = nullptr;
|
||||
real_t z_index = 0;
|
||||
bool has_z = true;
|
||||
_FORCE_INLINE_ bool operator<(const _SelectResult &p_rr) const {
|
||||
return has_z && p_rr.has_z ? p_rr.z_index < z_index : p_rr.has_z;
|
||||
}
|
||||
};
|
||||
Vector<_SelectResult> selection_results;
|
||||
Vector<_SelectResult> selection_results_menu;
|
||||
|
||||
struct _HoverResult {
|
||||
Point2 position;
|
||||
Ref<Texture2D> icon;
|
||||
String name;
|
||||
};
|
||||
Vector<_HoverResult> hovering_results;
|
||||
|
||||
struct BoneList {
|
||||
Transform2D xform;
|
||||
real_t length = 0;
|
||||
uint64_t last_pass = 0;
|
||||
};
|
||||
|
||||
uint64_t bone_last_frame = 0;
|
||||
|
||||
struct BoneKey {
|
||||
ObjectID from;
|
||||
ObjectID to;
|
||||
_FORCE_INLINE_ bool operator<(const BoneKey &p_key) const {
|
||||
if (from == p_key.from) {
|
||||
return to < p_key.to;
|
||||
} else {
|
||||
return from < p_key.from;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
HashMap<BoneKey, BoneList> bone_list;
|
||||
MenuButton *skeleton_menu = nullptr;
|
||||
|
||||
struct PoseClipboard {
|
||||
Vector2 pos;
|
||||
Vector2 scale;
|
||||
real_t rot = 0;
|
||||
ObjectID id;
|
||||
};
|
||||
List<PoseClipboard> pose_clipboard;
|
||||
|
||||
Button *select_button = nullptr;
|
||||
|
||||
Button *move_button = nullptr;
|
||||
Button *scale_button = nullptr;
|
||||
Button *rotate_button = nullptr;
|
||||
|
||||
Button *list_select_button = nullptr;
|
||||
Button *pivot_button = nullptr;
|
||||
Button *pan_button = nullptr;
|
||||
|
||||
Button *ruler_button = nullptr;
|
||||
|
||||
Button *smart_snap_button = nullptr;
|
||||
Button *grid_snap_button = nullptr;
|
||||
MenuButton *snap_config_menu = nullptr;
|
||||
PopupMenu *smartsnap_config_popup = nullptr;
|
||||
|
||||
Button *lock_button = nullptr;
|
||||
Button *unlock_button = nullptr;
|
||||
|
||||
Button *group_button = nullptr;
|
||||
Button *ungroup_button = nullptr;
|
||||
|
||||
MenuButton *view_menu = nullptr;
|
||||
PopupMenu *grid_menu = nullptr;
|
||||
PopupMenu *theme_menu = nullptr;
|
||||
PopupMenu *gizmos_menu = nullptr;
|
||||
HBoxContainer *animation_hb = nullptr;
|
||||
MenuButton *animation_menu = nullptr;
|
||||
|
||||
Button *key_loc_button = nullptr;
|
||||
Button *key_rot_button = nullptr;
|
||||
Button *key_scale_button = nullptr;
|
||||
Button *key_insert_button = nullptr;
|
||||
Button *key_auto_insert_button = nullptr;
|
||||
|
||||
PopupMenu *selection_menu = nullptr;
|
||||
PopupMenu *add_node_menu = nullptr;
|
||||
|
||||
Control *top_ruler = nullptr;
|
||||
Control *left_ruler = nullptr;
|
||||
|
||||
Point2 drag_start_origin;
|
||||
DragType drag_type = DRAG_NONE;
|
||||
Point2 drag_from;
|
||||
Point2 drag_to;
|
||||
Point2 drag_rotation_center;
|
||||
List<CanvasItem *> drag_selection;
|
||||
int dragged_guide_index = -1;
|
||||
Point2 dragged_guide_pos;
|
||||
bool is_hovering_h_guide = false;
|
||||
bool is_hovering_v_guide = false;
|
||||
|
||||
bool updating_value_dialog = false;
|
||||
Transform2D original_transform;
|
||||
|
||||
Point2 box_selecting_to;
|
||||
CursorShape cursor_shape_override = CURSOR_ARROW;
|
||||
|
||||
Ref<StyleBoxTexture> select_sb;
|
||||
Ref<Texture2D> select_handle;
|
||||
Ref<Texture2D> anchor_handle;
|
||||
|
||||
Ref<Shortcut> drag_pivot_shortcut;
|
||||
Ref<Shortcut> set_pivot_shortcut;
|
||||
Ref<Shortcut> multiply_grid_step_shortcut;
|
||||
Ref<Shortcut> divide_grid_step_shortcut;
|
||||
|
||||
Ref<ViewPanner> panner;
|
||||
void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event);
|
||||
void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event);
|
||||
|
||||
bool _is_node_locked(const Node *p_node) const;
|
||||
bool _is_node_movable(const Node *p_node, bool p_popup_warning = false);
|
||||
void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
|
||||
void _get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, bool p_allow_locked = false);
|
||||
|
||||
void _find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
|
||||
bool _select_click_on_item(CanvasItem *item, Point2 p_click_pos, bool p_append);
|
||||
|
||||
ConfirmationDialog *snap_dialog = nullptr;
|
||||
|
||||
CanvasItem *ref_item = nullptr;
|
||||
|
||||
void _save_canvas_item_state(const List<CanvasItem *> &p_canvas_items, bool save_bones = false);
|
||||
void _restore_canvas_item_state(const List<CanvasItem *> &p_canvas_items, bool restore_bones = false);
|
||||
void _commit_canvas_item_state(const List<CanvasItem *> &p_canvas_items, const String &action_name, bool commit_bones = false);
|
||||
|
||||
Vector2 _anchor_to_position(const Control *p_control, Vector2 anchor);
|
||||
Vector2 _position_to_anchor(const Control *p_control, Vector2 position);
|
||||
|
||||
void _prepare_view_menu();
|
||||
void _popup_callback(int p_op);
|
||||
bool updating_scroll = false;
|
||||
void _update_scroll(real_t);
|
||||
void _update_scrollbars();
|
||||
void _snap_changed();
|
||||
void _selection_result_pressed(int);
|
||||
void _selection_menu_hide();
|
||||
void _add_node_pressed(int p_result);
|
||||
void _adjust_new_node_position(Node *p_node);
|
||||
void _reset_create_position();
|
||||
void _update_editor_settings();
|
||||
bool _is_grid_visible() const;
|
||||
void _prepare_grid_menu();
|
||||
void _on_grid_menu_id_pressed(int p_id);
|
||||
|
||||
public:
|
||||
enum ThemePreviewMode {
|
||||
THEME_PREVIEW_PROJECT,
|
||||
THEME_PREVIEW_EDITOR,
|
||||
THEME_PREVIEW_DEFAULT,
|
||||
|
||||
THEME_PREVIEW_MAX // The number of options for enumerating.
|
||||
};
|
||||
|
||||
private:
|
||||
ThemePreviewMode theme_preview = THEME_PREVIEW_PROJECT;
|
||||
void _switch_theme_preview(int p_mode);
|
||||
|
||||
List<CanvasItem *> _get_edited_canvas_items(bool p_retrieve_locked = false, bool p_remove_canvas_item_if_parent_in_selection = true, bool *r_has_locked_items = nullptr) const;
|
||||
Rect2 _get_encompassing_rect_from_list(const List<CanvasItem *> &p_list);
|
||||
void _expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D(), bool include_locked_nodes = true);
|
||||
Rect2 _get_encompassing_rect(const Node *p_node);
|
||||
|
||||
Object *_get_editor_data(Object *p_what);
|
||||
|
||||
void _insert_animation_keys(bool p_location, bool p_rotation, bool p_scale, bool p_on_existing);
|
||||
|
||||
void _keying_changed();
|
||||
|
||||
virtual void shortcut_input(const Ref<InputEvent> &p_ev) override;
|
||||
|
||||
void _draw_text_at_position(Point2 p_position, const String &p_string, Side p_side);
|
||||
void _draw_margin_at_position(int p_value, Point2 p_position, Side p_side);
|
||||
void _draw_percentage_at_position(real_t p_value, Point2 p_position, Side p_side);
|
||||
void _draw_straight_line(Point2 p_from, Point2 p_to, Color p_color);
|
||||
|
||||
void _draw_smart_snapping();
|
||||
void _draw_rulers();
|
||||
void _draw_guides();
|
||||
void _draw_focus();
|
||||
void _draw_grid();
|
||||
void _draw_ruler_tool();
|
||||
void _draw_control_anchors(Control *control);
|
||||
void _draw_control_helpers(Control *control);
|
||||
void _draw_selection();
|
||||
void _draw_axis();
|
||||
void _draw_invisible_nodes_positions(Node *p_node, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
|
||||
void _draw_locks_and_groups(Node *p_node, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
|
||||
void _draw_hover();
|
||||
void _draw_message();
|
||||
|
||||
void _draw_viewport();
|
||||
|
||||
bool _gui_input_anchors(const Ref<InputEvent> &p_event);
|
||||
bool _gui_input_move(const Ref<InputEvent> &p_event);
|
||||
bool _gui_input_open_scene_on_double_click(const Ref<InputEvent> &p_event);
|
||||
bool _gui_input_scale(const Ref<InputEvent> &p_event);
|
||||
bool _gui_input_pivot(const Ref<InputEvent> &p_event);
|
||||
bool _gui_input_resize(const Ref<InputEvent> &p_event);
|
||||
bool _gui_input_rotate(const Ref<InputEvent> &p_event);
|
||||
bool _gui_input_select(const Ref<InputEvent> &p_event);
|
||||
bool _gui_input_ruler_tool(const Ref<InputEvent> &p_event);
|
||||
bool _gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bool p_already_accepted);
|
||||
bool _gui_input_rulers_and_guides(const Ref<InputEvent> &p_event);
|
||||
bool _gui_input_hover(const Ref<InputEvent> &p_event);
|
||||
|
||||
void _gui_input_viewport(const Ref<InputEvent> &p_event);
|
||||
void _update_cursor();
|
||||
void _update_lock_and_group_button();
|
||||
|
||||
void _selection_changed();
|
||||
void _focus_selection(int p_op);
|
||||
void _reset_drag();
|
||||
|
||||
void _project_settings_changed();
|
||||
|
||||
SnapTarget snap_target[2];
|
||||
Transform2D snap_transform;
|
||||
void _snap_if_closer_float(
|
||||
const real_t p_value,
|
||||
real_t &r_current_snap, SnapTarget &r_current_snap_target,
|
||||
const real_t p_target_value, const SnapTarget p_snap_target,
|
||||
const real_t p_radius = 10.0);
|
||||
void _snap_if_closer_point(
|
||||
Point2 p_value,
|
||||
Point2 &r_current_snap, SnapTarget (&r_current_snap_target)[2],
|
||||
Point2 p_target_value, const SnapTarget p_snap_target,
|
||||
const real_t rotation = 0.0,
|
||||
const real_t p_radius = 10.0);
|
||||
void _snap_other_nodes(
|
||||
const Point2 p_value,
|
||||
const Transform2D p_transform_to_snap,
|
||||
Point2 &r_current_snap, SnapTarget (&r_current_snap_target)[2],
|
||||
const SnapTarget p_snap_target, List<const CanvasItem *> p_exceptions,
|
||||
const Node *p_current);
|
||||
|
||||
VBoxContainer *controls_vb = nullptr;
|
||||
Button *button_center_view = nullptr;
|
||||
EditorZoomWidget *zoom_widget = nullptr;
|
||||
void _update_zoom(real_t p_zoom);
|
||||
void _shortcut_zoom_set(real_t p_zoom);
|
||||
void _zoom_on_position(real_t p_zoom, Point2 p_position = Point2());
|
||||
void _button_toggle_smart_snap(bool p_status);
|
||||
void _button_toggle_grid_snap(bool p_status);
|
||||
void _button_tool_select(int p_index);
|
||||
|
||||
HSplitContainer *left_panel_split = nullptr;
|
||||
HSplitContainer *right_panel_split = nullptr;
|
||||
VSplitContainer *bottom_split = nullptr;
|
||||
|
||||
void _set_owner_for_node_and_children(Node *p_node, Node *p_owner);
|
||||
|
||||
friend class CanvasItemEditorPlugin;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
static CanvasItemEditor *singleton;
|
||||
|
||||
public:
|
||||
enum SnapMode {
|
||||
SNAP_GRID = 1 << 0,
|
||||
SNAP_GUIDES = 1 << 1,
|
||||
SNAP_PIXEL = 1 << 2,
|
||||
SNAP_NODE_PARENT = 1 << 3,
|
||||
SNAP_NODE_ANCHORS = 1 << 4,
|
||||
SNAP_NODE_SIDES = 1 << 5,
|
||||
SNAP_NODE_CENTER = 1 << 6,
|
||||
SNAP_OTHER_NODES = 1 << 7,
|
||||
|
||||
SNAP_DEFAULT = SNAP_GRID | SNAP_GUIDES | SNAP_PIXEL,
|
||||
};
|
||||
|
||||
String message;
|
||||
|
||||
Point2 snap_point(Point2 p_target, unsigned int p_modes = SNAP_DEFAULT, unsigned int p_forced_modes = 0, const CanvasItem *p_self_canvas_item = nullptr, const List<CanvasItem *> &p_other_nodes_exceptions = List<CanvasItem *>());
|
||||
real_t snap_angle(real_t p_target, real_t p_start = 0) const;
|
||||
|
||||
Transform2D get_canvas_transform() const { return transform; }
|
||||
|
||||
static CanvasItemEditor *get_singleton() { return singleton; }
|
||||
Dictionary get_state() const;
|
||||
void set_state(const Dictionary &p_state);
|
||||
void clear();
|
||||
|
||||
void add_control_to_menu_panel(Control *p_control);
|
||||
void remove_control_from_menu_panel(Control *p_control);
|
||||
|
||||
void add_control_to_left_panel(Control *p_control);
|
||||
void remove_control_from_left_panel(Control *p_control);
|
||||
|
||||
void add_control_to_right_panel(Control *p_control);
|
||||
void remove_control_from_right_panel(Control *p_control);
|
||||
|
||||
VSplitContainer *get_bottom_split();
|
||||
|
||||
Control *get_viewport_control() { return viewport; }
|
||||
|
||||
Control *get_controls_container() { return controls_vb; }
|
||||
|
||||
void update_viewport();
|
||||
|
||||
Tool get_current_tool() { return tool; }
|
||||
void set_current_tool(Tool p_tool);
|
||||
|
||||
void edit(CanvasItem *p_canvas_item);
|
||||
|
||||
void focus_selection();
|
||||
void center_at(const Point2 &p_pos);
|
||||
|
||||
void set_cursor_shape_override(CursorShape p_shape = CURSOR_ARROW);
|
||||
virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override;
|
||||
|
||||
ThemePreviewMode get_theme_preview() const { return theme_preview; }
|
||||
|
||||
EditorSelection *editor_selection = nullptr;
|
||||
|
||||
CanvasItemEditor();
|
||||
};
|
||||
|
||||
class CanvasItemEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(CanvasItemEditorPlugin, EditorPlugin);
|
||||
|
||||
CanvasItemEditor *canvas_item_editor = nullptr;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
virtual String get_plugin_name() const override { return TTRC("2D"); }
|
||||
bool has_main_screen() const override { return true; }
|
||||
virtual void edit(Object *p_object) override;
|
||||
virtual bool handles(Object *p_object) const override;
|
||||
virtual void make_visible(bool p_visible) override;
|
||||
virtual Dictionary get_state() const override;
|
||||
virtual void set_state(const Dictionary &p_state) override;
|
||||
virtual void clear() override;
|
||||
|
||||
CanvasItemEditor *get_canvas_item_editor() { return canvas_item_editor; }
|
||||
|
||||
CanvasItemEditorPlugin();
|
||||
};
|
||||
|
||||
class CanvasItemEditorViewport : public Control {
|
||||
GDCLASS(CanvasItemEditorViewport, Control);
|
||||
|
||||
// The type of node that will be created when dropping texture into the viewport.
|
||||
String default_texture_node_type;
|
||||
// Node types that are available to select from when dropping texture into viewport.
|
||||
Vector<String> texture_node_types;
|
||||
|
||||
Vector<String> selected_files;
|
||||
Node *target_node = nullptr;
|
||||
Point2 drop_pos;
|
||||
|
||||
CanvasItemEditor *canvas_item_editor = nullptr;
|
||||
Control *preview_node = nullptr;
|
||||
AcceptDialog *accept = nullptr;
|
||||
AcceptDialog *texture_node_type_selector = nullptr;
|
||||
Label *label = nullptr;
|
||||
Label *label_desc = nullptr;
|
||||
Ref<ButtonGroup> button_group;
|
||||
|
||||
void _on_mouse_exit();
|
||||
void _on_select_texture_node_type(Object *selected);
|
||||
void _on_change_type_confirmed();
|
||||
void _on_change_type_closed();
|
||||
|
||||
void _create_preview(const Vector<String> &files) const;
|
||||
void _remove_preview();
|
||||
|
||||
bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) const;
|
||||
bool _is_any_texture_selected() const;
|
||||
void _create_texture_node(Node *p_parent, Node *p_child, const String &p_path, const Point2 &p_point);
|
||||
void _create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point);
|
||||
bool _create_instance(Node *p_parent, const String &p_path, const Point2 &p_point);
|
||||
void _perform_drop_data();
|
||||
void _show_texture_node_type_selector();
|
||||
void _update_theme();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override;
|
||||
virtual void drop_data(const Point2 &p_point, const Variant &p_data) override;
|
||||
|
||||
CanvasItemEditorViewport(CanvasItemEditor *p_canvas_item_editor);
|
||||
~CanvasItemEditorViewport();
|
||||
};
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* cast_2d_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 "cast_2d_editor_plugin.h"
|
||||
|
||||
#include "canvas_item_editor_plugin.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "scene/2d/physics/ray_cast_2d.h"
|
||||
#include "scene/2d/physics/shape_cast_2d.h"
|
||||
#include "scene/main/viewport.h"
|
||||
|
||||
void Cast2DEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
get_tree()->connect("node_removed", callable_mp(this, &Cast2DEditor::_node_removed));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
get_tree()->disconnect("node_removed", callable_mp(this, &Cast2DEditor::_node_removed));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void Cast2DEditor::_node_removed(Node *p_node) {
|
||||
if (p_node == node) {
|
||||
node = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool Cast2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
|
||||
if (!node || !node->is_visible_in_tree()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Viewport *vp = node->get_viewport();
|
||||
if (vp && !vp->is_visible_subviewport()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_screen_transform();
|
||||
|
||||
Ref<InputEventMouseButton> mb = p_event;
|
||||
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
|
||||
Vector2 target_position = node->get("target_position");
|
||||
|
||||
Vector2 gpoint = mb->get_position();
|
||||
|
||||
if (mb->is_pressed()) {
|
||||
if (xform.xform(target_position).distance_to(gpoint) < 8) {
|
||||
pressed = true;
|
||||
original_target_position = target_position;
|
||||
original_mouse_pos = gpoint;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
pressed = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
} else if (pressed) {
|
||||
if (original_mouse_pos != gpoint) {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Set Target Position"));
|
||||
undo_redo->add_do_property(node, "target_position", target_position);
|
||||
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->add_undo_property(node, "target_position", original_target_position);
|
||||
undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
pressed = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
if (mm.is_valid() && pressed) {
|
||||
Vector2 point = canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mm->get_position()));
|
||||
point = node->get_screen_transform().affine_inverse().xform(point);
|
||||
|
||||
node->set("target_position", point);
|
||||
canvas_item_editor->update_viewport();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Cast2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
|
||||
if (!node || !node->is_visible_in_tree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Viewport *vp = node->get_viewport();
|
||||
if (vp && !vp->is_visible_subviewport()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Transform2D gt = canvas_item_editor->get_canvas_transform() * node->get_screen_transform();
|
||||
|
||||
const Ref<Texture2D> handle = get_editor_theme_icon(SNAME("EditorHandle"));
|
||||
p_overlay->draw_texture(handle, gt.xform((Vector2)node->get("target_position")) - handle->get_size() / 2);
|
||||
}
|
||||
|
||||
void Cast2DEditor::edit(Node2D *p_node) {
|
||||
if (!canvas_item_editor) {
|
||||
canvas_item_editor = CanvasItemEditor::get_singleton();
|
||||
}
|
||||
|
||||
if (Object::cast_to<RayCast2D>(p_node) || Object::cast_to<ShapeCast2D>(p_node)) {
|
||||
node = p_node;
|
||||
} else {
|
||||
node = nullptr;
|
||||
}
|
||||
|
||||
canvas_item_editor->update_viewport();
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
|
||||
void Cast2DEditorPlugin::edit(Object *p_object) {
|
||||
cast_2d_editor->edit(Object::cast_to<Node2D>(p_object));
|
||||
}
|
||||
|
||||
bool Cast2DEditorPlugin::handles(Object *p_object) const {
|
||||
return Object::cast_to<RayCast2D>(p_object) != nullptr || Object::cast_to<ShapeCast2D>(p_object) != nullptr;
|
||||
}
|
||||
|
||||
void Cast2DEditorPlugin::make_visible(bool p_visible) {
|
||||
if (!p_visible) {
|
||||
edit(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
Cast2DEditorPlugin::Cast2DEditorPlugin() {
|
||||
cast_2d_editor = memnew(Cast2DEditor);
|
||||
EditorNode::get_singleton()->get_gui_base()->add_child(cast_2d_editor);
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* cast_2d_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/2d/node_2d.h"
|
||||
|
||||
class CanvasItemEditor;
|
||||
|
||||
class Cast2DEditor : public Control {
|
||||
GDCLASS(Cast2DEditor, Control);
|
||||
|
||||
CanvasItemEditor *canvas_item_editor = nullptr;
|
||||
Node2D *node = nullptr;
|
||||
|
||||
bool pressed = false;
|
||||
Point2 original_target_position;
|
||||
Vector2 original_mouse_pos;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
void _node_removed(Node *p_node);
|
||||
|
||||
public:
|
||||
bool forward_canvas_gui_input(const Ref<InputEvent> &p_event);
|
||||
void forward_canvas_draw_over_viewport(Control *p_overlay);
|
||||
void edit(Node2D *p_node);
|
||||
};
|
||||
|
||||
class Cast2DEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(Cast2DEditorPlugin, EditorPlugin);
|
||||
|
||||
Cast2DEditor *cast_2d_editor = nullptr;
|
||||
|
||||
public:
|
||||
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override { return cast_2d_editor->forward_canvas_gui_input(p_event); }
|
||||
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { cast_2d_editor->forward_canvas_draw_over_viewport(p_overlay); }
|
||||
|
||||
virtual String get_plugin_name() const override { return "Cast2D"; }
|
||||
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 visible) override;
|
||||
|
||||
Cast2DEditorPlugin();
|
||||
};
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* collision_polygon_2d_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 "collision_polygon_2d_editor_plugin.h"
|
||||
|
||||
Node2D *CollisionPolygon2DEditor::_get_node() const {
|
||||
return node;
|
||||
}
|
||||
|
||||
void CollisionPolygon2DEditor::_set_node(Node *p_polygon) {
|
||||
node = Object::cast_to<CollisionPolygon2D>(p_polygon);
|
||||
}
|
||||
|
||||
CollisionPolygon2DEditor::CollisionPolygon2DEditor() {
|
||||
set_edit_origin_and_center(true);
|
||||
}
|
||||
|
||||
CollisionPolygon2DEditorPlugin::CollisionPolygon2DEditorPlugin() :
|
||||
AbstractPolygon2DEditorPlugin(memnew(CollisionPolygon2DEditor), "CollisionPolygon2D") {
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* collision_polygon_2d_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/abstract_polygon_2d_editor.h"
|
||||
#include "scene/2d/physics/collision_polygon_2d.h"
|
||||
|
||||
class CollisionPolygon2DEditor : public AbstractPolygon2DEditor {
|
||||
GDCLASS(CollisionPolygon2DEditor, AbstractPolygon2DEditor);
|
||||
|
||||
CollisionPolygon2D *node = nullptr;
|
||||
|
||||
protected:
|
||||
virtual Node2D *_get_node() const override;
|
||||
virtual void _set_node(Node *p_polygon) override;
|
||||
|
||||
public:
|
||||
CollisionPolygon2DEditor();
|
||||
};
|
||||
|
||||
class CollisionPolygon2DEditorPlugin : public AbstractPolygon2DEditorPlugin {
|
||||
GDCLASS(CollisionPolygon2DEditorPlugin, AbstractPolygon2DEditorPlugin);
|
||||
|
||||
public:
|
||||
CollisionPolygon2DEditorPlugin();
|
||||
};
|
||||
|
|
@ -1,667 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* collision_shape_2d_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 "collision_shape_2d_editor_plugin.h"
|
||||
|
||||
#include "canvas_item_editor_plugin.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "scene/main/viewport.h"
|
||||
#include "scene/resources/2d/capsule_shape_2d.h"
|
||||
#include "scene/resources/2d/circle_shape_2d.h"
|
||||
#include "scene/resources/2d/concave_polygon_shape_2d.h"
|
||||
#include "scene/resources/2d/convex_polygon_shape_2d.h"
|
||||
#include "scene/resources/2d/rectangle_shape_2d.h"
|
||||
#include "scene/resources/2d/segment_shape_2d.h"
|
||||
#include "scene/resources/2d/separation_ray_shape_2d.h"
|
||||
#include "scene/resources/2d/world_boundary_shape_2d.h"
|
||||
|
||||
CollisionShape2DEditor::CollisionShape2DEditor() {
|
||||
grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
|
||||
}
|
||||
|
||||
void CollisionShape2DEditor::_node_removed(Node *p_node) {
|
||||
if (p_node == node) {
|
||||
node = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Variant CollisionShape2DEditor::get_handle_value(int idx) const {
|
||||
switch (shape_type) {
|
||||
case CAPSULE_SHAPE: {
|
||||
Ref<CapsuleShape2D> capsule = node->get_shape();
|
||||
return Vector2(capsule->get_radius(), capsule->get_height());
|
||||
|
||||
} break;
|
||||
|
||||
case CIRCLE_SHAPE: {
|
||||
Ref<CircleShape2D> circle = node->get_shape();
|
||||
|
||||
if (idx == 0) {
|
||||
return circle->get_radius();
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case CONCAVE_POLYGON_SHAPE: {
|
||||
Ref<ConcavePolygonShape2D> shape = node->get_shape();
|
||||
const Vector<Vector2> &segments = shape->get_segments();
|
||||
return segments[idx];
|
||||
|
||||
} break;
|
||||
|
||||
case CONVEX_POLYGON_SHAPE: {
|
||||
Ref<ConvexPolygonShape2D> shape = node->get_shape();
|
||||
const Vector<Vector2> &points = shape->get_points();
|
||||
return points[idx];
|
||||
|
||||
} break;
|
||||
|
||||
case WORLD_BOUNDARY_SHAPE: {
|
||||
Ref<WorldBoundaryShape2D> world_boundary = node->get_shape();
|
||||
|
||||
if (idx == 0) {
|
||||
return world_boundary->get_distance();
|
||||
} else {
|
||||
return world_boundary->get_normal();
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case SEPARATION_RAY_SHAPE: {
|
||||
Ref<SeparationRayShape2D> ray = node->get_shape();
|
||||
|
||||
if (idx == 0) {
|
||||
return ray->get_length();
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case RECTANGLE_SHAPE: {
|
||||
Ref<RectangleShape2D> rect = node->get_shape();
|
||||
|
||||
if (idx < 8) {
|
||||
return rect->get_size().abs();
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case SEGMENT_SHAPE: {
|
||||
Ref<SegmentShape2D> seg = node->get_shape();
|
||||
|
||||
if (idx == 0) {
|
||||
return seg->get_a();
|
||||
} else if (idx == 1) {
|
||||
return seg->get_b();
|
||||
}
|
||||
|
||||
} break;
|
||||
}
|
||||
|
||||
return Variant();
|
||||
}
|
||||
|
||||
void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
|
||||
switch (shape_type) {
|
||||
case CAPSULE_SHAPE: {
|
||||
if (idx < 2) {
|
||||
Ref<CapsuleShape2D> capsule = node->get_shape();
|
||||
|
||||
real_t parameter = Math::abs(p_point[idx]);
|
||||
|
||||
if (idx == 0) {
|
||||
capsule->set_radius(parameter);
|
||||
} else if (idx == 1) {
|
||||
capsule->set_height(parameter * 2);
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case CIRCLE_SHAPE: {
|
||||
Ref<CircleShape2D> circle = node->get_shape();
|
||||
circle->set_radius(p_point.length());
|
||||
} break;
|
||||
|
||||
case CONCAVE_POLYGON_SHAPE: {
|
||||
Ref<ConcavePolygonShape2D> concave_shape = node->get_shape();
|
||||
|
||||
Vector<Vector2> segments = concave_shape->get_segments();
|
||||
|
||||
ERR_FAIL_INDEX(idx, segments.size());
|
||||
segments.write[idx] = p_point;
|
||||
|
||||
concave_shape->set_segments(segments);
|
||||
|
||||
} break;
|
||||
|
||||
case CONVEX_POLYGON_SHAPE: {
|
||||
Ref<ConvexPolygonShape2D> convex_shape = node->get_shape();
|
||||
|
||||
Vector<Vector2> points = convex_shape->get_points();
|
||||
|
||||
ERR_FAIL_INDEX(idx, points.size());
|
||||
points.write[idx] = p_point;
|
||||
|
||||
convex_shape->set_points(points);
|
||||
|
||||
} break;
|
||||
|
||||
case WORLD_BOUNDARY_SHAPE: {
|
||||
if (idx < 2) {
|
||||
Ref<WorldBoundaryShape2D> world_boundary = node->get_shape();
|
||||
|
||||
if (idx == 0) {
|
||||
Vector2 normal = world_boundary->get_normal();
|
||||
world_boundary->set_distance(p_point.dot(normal) / normal.length_squared());
|
||||
} else {
|
||||
real_t dir = world_boundary->get_distance() < 0 ? -1 : 1;
|
||||
world_boundary->set_normal(p_point.normalized() * dir);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case SEPARATION_RAY_SHAPE: {
|
||||
Ref<SeparationRayShape2D> ray = node->get_shape();
|
||||
|
||||
ray->set_length(Math::abs(p_point.y));
|
||||
} break;
|
||||
|
||||
case RECTANGLE_SHAPE: {
|
||||
if (idx < 8) {
|
||||
Ref<RectangleShape2D> rect = node->get_shape();
|
||||
Vector2 size = (Point2)original;
|
||||
|
||||
if (RECT_HANDLES[idx].x != 0) {
|
||||
size.x = p_point.x * RECT_HANDLES[idx].x * 2;
|
||||
}
|
||||
if (RECT_HANDLES[idx].y != 0) {
|
||||
size.y = p_point.y * RECT_HANDLES[idx].y * 2;
|
||||
}
|
||||
|
||||
if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
|
||||
rect->set_size(size.abs());
|
||||
node->set_global_position(original_transform.get_origin());
|
||||
} else {
|
||||
rect->set_size(((Point2)original + (size - (Point2)original) * 0.5).abs());
|
||||
Point2 pos = original_transform.affine_inverse().xform(original_transform.get_origin());
|
||||
pos += (size - (Point2)original) * 0.5 * RECT_HANDLES[idx] * 0.5;
|
||||
node->set_global_position(original_transform.xform(pos));
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case SEGMENT_SHAPE: {
|
||||
if (edit_handle < 2) {
|
||||
Ref<SegmentShape2D> seg = node->get_shape();
|
||||
|
||||
if (idx == 0) {
|
||||
seg->set_a(p_point);
|
||||
} else if (idx == 1) {
|
||||
seg->set_b(p_point);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Set Handle"));
|
||||
|
||||
switch (shape_type) {
|
||||
case CAPSULE_SHAPE: {
|
||||
Ref<CapsuleShape2D> capsule = node->get_shape();
|
||||
|
||||
Vector2 values = p_org;
|
||||
|
||||
if (idx == 0) {
|
||||
undo_redo->add_do_method(capsule.ptr(), "set_radius", capsule->get_radius());
|
||||
} else if (idx == 1) {
|
||||
undo_redo->add_do_method(capsule.ptr(), "set_height", capsule->get_height());
|
||||
}
|
||||
undo_redo->add_undo_method(capsule.ptr(), "set_radius", values[0]);
|
||||
undo_redo->add_undo_method(capsule.ptr(), "set_height", values[1]);
|
||||
|
||||
} break;
|
||||
|
||||
case CIRCLE_SHAPE: {
|
||||
Ref<CircleShape2D> circle = node->get_shape();
|
||||
|
||||
undo_redo->add_do_method(circle.ptr(), "set_radius", circle->get_radius());
|
||||
undo_redo->add_undo_method(circle.ptr(), "set_radius", p_org);
|
||||
|
||||
} break;
|
||||
|
||||
case CONCAVE_POLYGON_SHAPE: {
|
||||
Ref<ConcavePolygonShape2D> concave_shape = node->get_shape();
|
||||
|
||||
Vector2 values = p_org;
|
||||
|
||||
Vector<Vector2> undo_segments = concave_shape->get_segments();
|
||||
|
||||
ERR_FAIL_INDEX(idx, undo_segments.size());
|
||||
undo_segments.write[idx] = values;
|
||||
|
||||
undo_redo->add_do_method(concave_shape.ptr(), "set_segments", concave_shape->get_segments());
|
||||
undo_redo->add_undo_method(concave_shape.ptr(), "set_segments", undo_segments);
|
||||
|
||||
} break;
|
||||
|
||||
case CONVEX_POLYGON_SHAPE: {
|
||||
Ref<ConvexPolygonShape2D> convex_shape = node->get_shape();
|
||||
|
||||
Vector2 values = p_org;
|
||||
|
||||
Vector<Vector2> undo_points = convex_shape->get_points();
|
||||
|
||||
ERR_FAIL_INDEX(idx, undo_points.size());
|
||||
undo_points.write[idx] = values;
|
||||
|
||||
undo_redo->add_do_method(convex_shape.ptr(), "set_points", convex_shape->get_points());
|
||||
undo_redo->add_undo_method(convex_shape.ptr(), "set_points", undo_points);
|
||||
|
||||
} break;
|
||||
|
||||
case WORLD_BOUNDARY_SHAPE: {
|
||||
Ref<WorldBoundaryShape2D> world_boundary = node->get_shape();
|
||||
|
||||
if (idx == 0) {
|
||||
undo_redo->add_do_method(world_boundary.ptr(), "set_distance", world_boundary->get_distance());
|
||||
undo_redo->add_undo_method(world_boundary.ptr(), "set_distance", p_org);
|
||||
} else {
|
||||
undo_redo->add_do_method(world_boundary.ptr(), "set_normal", world_boundary->get_normal());
|
||||
undo_redo->add_undo_method(world_boundary.ptr(), "set_normal", p_org);
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case SEPARATION_RAY_SHAPE: {
|
||||
Ref<SeparationRayShape2D> ray = node->get_shape();
|
||||
|
||||
undo_redo->add_do_method(ray.ptr(), "set_length", ray->get_length());
|
||||
undo_redo->add_undo_method(ray.ptr(), "set_length", p_org);
|
||||
|
||||
} break;
|
||||
|
||||
case RECTANGLE_SHAPE: {
|
||||
Ref<RectangleShape2D> rect = node->get_shape();
|
||||
|
||||
undo_redo->add_do_method(rect.ptr(), "set_size", rect->get_size());
|
||||
undo_redo->add_do_method(node, "set_global_transform", node->get_global_transform());
|
||||
undo_redo->add_undo_method(rect.ptr(), "set_size", p_org);
|
||||
undo_redo->add_undo_method(node, "set_global_transform", original_transform);
|
||||
|
||||
} break;
|
||||
|
||||
case SEGMENT_SHAPE: {
|
||||
Ref<SegmentShape2D> seg = node->get_shape();
|
||||
if (idx == 0) {
|
||||
undo_redo->add_do_method(seg.ptr(), "set_a", seg->get_a());
|
||||
undo_redo->add_undo_method(seg.ptr(), "set_a", p_org);
|
||||
} else if (idx == 1) {
|
||||
undo_redo->add_do_method(seg.ptr(), "set_b", seg->get_b());
|
||||
undo_redo->add_undo_method(seg.ptr(), "set_b", p_org);
|
||||
}
|
||||
|
||||
} break;
|
||||
}
|
||||
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
bool CollisionShape2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!node->is_visible_in_tree()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Viewport *vp = node->get_viewport();
|
||||
if (vp && !vp->is_visible_subviewport()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (shape_type == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Ref<InputEventMouseButton> mb = p_event;
|
||||
Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_screen_transform();
|
||||
|
||||
if (mb.is_valid()) {
|
||||
Vector2 gpoint = mb->get_position();
|
||||
|
||||
if (mb->get_button_index() == MouseButton::LEFT) {
|
||||
if (mb->is_pressed()) {
|
||||
for (int i = 0; i < handles.size(); i++) {
|
||||
if (xform.xform(handles[i]).distance_to(gpoint) < grab_threshold) {
|
||||
edit_handle = i;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (edit_handle == -1) {
|
||||
pressed = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
original_mouse_pos = gpoint;
|
||||
original_point = handles[edit_handle];
|
||||
original = get_handle_value(edit_handle);
|
||||
original_transform = node->get_global_transform();
|
||||
last_point = original;
|
||||
pressed = true;
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
if (pressed) {
|
||||
if (original_mouse_pos != gpoint) {
|
||||
commit_handle(edit_handle, original);
|
||||
}
|
||||
|
||||
edit_handle = -1;
|
||||
pressed = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
|
||||
if (mm.is_valid()) {
|
||||
if (edit_handle == -1 || !pressed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector2 cpoint = canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mm->get_position()));
|
||||
cpoint = node->get_viewport()->get_popup_base_transform().affine_inverse().xform(cpoint);
|
||||
cpoint = original_transform.affine_inverse().xform(cpoint);
|
||||
last_point = cpoint;
|
||||
|
||||
set_handle(edit_handle, cpoint);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Ref<InputEventKey> k = p_event;
|
||||
|
||||
if (k.is_valid()) {
|
||||
if (edit_handle == -1 || !pressed || k->is_echo()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (shape_type == RECTANGLE_SHAPE && k->get_keycode() == Key::ALT) {
|
||||
set_handle(edit_handle, last_point); // Update handle when Alt key is toggled.
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CollisionShape2DEditor::_shape_changed() {
|
||||
canvas_item_editor->update_viewport();
|
||||
|
||||
if (current_shape.is_valid()) {
|
||||
current_shape->disconnect_changed(callable_mp(canvas_item_editor, &CanvasItemEditor::update_viewport));
|
||||
current_shape = Ref<Shape2D>();
|
||||
shape_type = -1;
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
current_shape = node->get_shape();
|
||||
|
||||
if (current_shape.is_valid()) {
|
||||
current_shape->connect_changed(callable_mp(canvas_item_editor, &CanvasItemEditor::update_viewport));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Object::cast_to<CapsuleShape2D>(*current_shape)) {
|
||||
shape_type = CAPSULE_SHAPE;
|
||||
} else if (Object::cast_to<CircleShape2D>(*current_shape)) {
|
||||
shape_type = CIRCLE_SHAPE;
|
||||
} else if (Object::cast_to<ConcavePolygonShape2D>(*current_shape)) {
|
||||
shape_type = CONCAVE_POLYGON_SHAPE;
|
||||
} else if (Object::cast_to<ConvexPolygonShape2D>(*current_shape)) {
|
||||
shape_type = CONVEX_POLYGON_SHAPE;
|
||||
} else if (Object::cast_to<WorldBoundaryShape2D>(*current_shape)) {
|
||||
shape_type = WORLD_BOUNDARY_SHAPE;
|
||||
} else if (Object::cast_to<SeparationRayShape2D>(*current_shape)) {
|
||||
shape_type = SEPARATION_RAY_SHAPE;
|
||||
} else if (Object::cast_to<RectangleShape2D>(*current_shape)) {
|
||||
shape_type = RECTANGLE_SHAPE;
|
||||
} else if (Object::cast_to<SegmentShape2D>(*current_shape)) {
|
||||
shape_type = SEGMENT_SHAPE;
|
||||
}
|
||||
}
|
||||
|
||||
void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!node->is_visible_in_tree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Viewport *vp = node->get_viewport();
|
||||
if (vp && !vp->is_visible_subviewport()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (shape_type == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Transform2D gt = canvas_item_editor->get_canvas_transform() * node->get_screen_transform();
|
||||
|
||||
Ref<Texture2D> h = get_editor_theme_icon(SNAME("EditorHandle"));
|
||||
Vector2 size = h->get_size() * 0.5;
|
||||
|
||||
handles.clear();
|
||||
|
||||
switch (shape_type) {
|
||||
case CAPSULE_SHAPE: {
|
||||
Ref<CapsuleShape2D> shape = current_shape;
|
||||
|
||||
handles.resize(2);
|
||||
float radius = shape->get_radius();
|
||||
float height = shape->get_height() / 2;
|
||||
|
||||
handles.write[0] = Point2(radius, 0);
|
||||
handles.write[1] = Point2(0, height);
|
||||
|
||||
p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
|
||||
p_overlay->draw_texture(h, gt.xform(handles[1]) - size);
|
||||
|
||||
} break;
|
||||
|
||||
case CIRCLE_SHAPE: {
|
||||
Ref<CircleShape2D> shape = current_shape;
|
||||
|
||||
handles.resize(1);
|
||||
handles.write[0] = Point2(shape->get_radius(), 0);
|
||||
|
||||
p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
|
||||
|
||||
} break;
|
||||
|
||||
case CONCAVE_POLYGON_SHAPE: {
|
||||
Ref<ConcavePolygonShape2D> shape = current_shape;
|
||||
|
||||
const Vector<Vector2> &segments = shape->get_segments();
|
||||
|
||||
handles.resize(segments.size());
|
||||
for (int i = 0; i < handles.size(); i++) {
|
||||
handles.write[i] = segments[i];
|
||||
p_overlay->draw_texture(h, gt.xform(handles[i]) - size);
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case CONVEX_POLYGON_SHAPE: {
|
||||
Ref<ConvexPolygonShape2D> shape = current_shape;
|
||||
|
||||
const Vector<Vector2> &points = shape->get_points();
|
||||
|
||||
handles.resize(points.size());
|
||||
for (int i = 0; i < handles.size(); i++) {
|
||||
handles.write[i] = points[i];
|
||||
p_overlay->draw_texture(h, gt.xform(handles[i]) - size);
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case WORLD_BOUNDARY_SHAPE: {
|
||||
Ref<WorldBoundaryShape2D> shape = current_shape;
|
||||
|
||||
handles.resize(2);
|
||||
handles.write[0] = shape->get_normal() * shape->get_distance();
|
||||
handles.write[1] = shape->get_normal() * (shape->get_distance() + 30.0);
|
||||
|
||||
p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
|
||||
p_overlay->draw_texture(h, gt.xform(handles[1]) - size);
|
||||
|
||||
} break;
|
||||
|
||||
case SEPARATION_RAY_SHAPE: {
|
||||
Ref<SeparationRayShape2D> shape = current_shape;
|
||||
|
||||
handles.resize(1);
|
||||
handles.write[0] = Point2(0, shape->get_length());
|
||||
|
||||
p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
|
||||
|
||||
} break;
|
||||
|
||||
case RECTANGLE_SHAPE: {
|
||||
Ref<RectangleShape2D> shape = current_shape;
|
||||
|
||||
handles.resize(8);
|
||||
Vector2 ext = shape->get_size() / 2;
|
||||
for (int i = 0; i < handles.size(); i++) {
|
||||
handles.write[i] = RECT_HANDLES[i] * ext;
|
||||
p_overlay->draw_texture(h, gt.xform(handles[i]) - size);
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case SEGMENT_SHAPE: {
|
||||
Ref<SegmentShape2D> shape = current_shape;
|
||||
|
||||
handles.resize(2);
|
||||
handles.write[0] = shape->get_a();
|
||||
handles.write[1] = shape->get_b();
|
||||
|
||||
p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
|
||||
p_overlay->draw_texture(h, gt.xform(handles[1]) - size);
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void CollisionShape2DEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
get_tree()->connect("node_removed", callable_mp(this, &CollisionShape2DEditor::_node_removed));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
get_tree()->disconnect("node_removed", callable_mp(this, &CollisionShape2DEditor::_node_removed));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_PROCESS: {
|
||||
if (node && node->get_shape() != current_shape) {
|
||||
_shape_changed();
|
||||
}
|
||||
} break;
|
||||
|
||||
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
|
||||
if (EditorSettings::get_singleton()->check_changed_settings_in_group("editors/polygon_editor")) {
|
||||
grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void CollisionShape2DEditor::edit(Node *p_node) {
|
||||
if (!canvas_item_editor) {
|
||||
canvas_item_editor = CanvasItemEditor::get_singleton();
|
||||
}
|
||||
|
||||
if (p_node) {
|
||||
node = Object::cast_to<CollisionShape2D>(p_node);
|
||||
set_process(true);
|
||||
} else {
|
||||
if (pressed) {
|
||||
set_handle(edit_handle, original_point);
|
||||
pressed = false;
|
||||
}
|
||||
edit_handle = -1;
|
||||
node = nullptr;
|
||||
set_process(false);
|
||||
}
|
||||
_shape_changed();
|
||||
}
|
||||
|
||||
void CollisionShape2DEditorPlugin::edit(Object *p_obj) {
|
||||
collision_shape_2d_editor->edit(Object::cast_to<Node>(p_obj));
|
||||
}
|
||||
|
||||
bool CollisionShape2DEditorPlugin::handles(Object *p_obj) const {
|
||||
return p_obj->is_class("CollisionShape2D");
|
||||
}
|
||||
|
||||
void CollisionShape2DEditorPlugin::make_visible(bool visible) {
|
||||
if (!visible) {
|
||||
edit(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
CollisionShape2DEditorPlugin::CollisionShape2DEditorPlugin() {
|
||||
collision_shape_2d_editor = memnew(CollisionShape2DEditor);
|
||||
EditorNode::get_singleton()->get_gui_base()->add_child(collision_shape_2d_editor);
|
||||
}
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* collision_shape_2d_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/2d/physics/collision_shape_2d.h"
|
||||
|
||||
class CanvasItemEditor;
|
||||
|
||||
class CollisionShape2DEditor : public Control {
|
||||
GDCLASS(CollisionShape2DEditor, Control);
|
||||
|
||||
enum ShapeType {
|
||||
CAPSULE_SHAPE,
|
||||
CIRCLE_SHAPE,
|
||||
CONCAVE_POLYGON_SHAPE,
|
||||
CONVEX_POLYGON_SHAPE,
|
||||
WORLD_BOUNDARY_SHAPE,
|
||||
SEPARATION_RAY_SHAPE,
|
||||
RECTANGLE_SHAPE,
|
||||
SEGMENT_SHAPE
|
||||
};
|
||||
|
||||
const Point2 RECT_HANDLES[8] = {
|
||||
Point2(1, 0),
|
||||
Point2(1, 1),
|
||||
Point2(0, 1),
|
||||
Point2(-1, 1),
|
||||
Point2(-1, 0),
|
||||
Point2(-1, -1),
|
||||
Point2(0, -1),
|
||||
Point2(1, -1),
|
||||
};
|
||||
|
||||
CanvasItemEditor *canvas_item_editor = nullptr;
|
||||
CollisionShape2D *node = nullptr;
|
||||
|
||||
Vector<Point2> handles;
|
||||
|
||||
int shape_type = -1;
|
||||
int edit_handle = -1;
|
||||
bool pressed = false;
|
||||
real_t grab_threshold = 8;
|
||||
Variant original;
|
||||
Transform2D original_transform;
|
||||
Vector2 original_point;
|
||||
Point2 last_point;
|
||||
Vector2 original_mouse_pos;
|
||||
|
||||
Ref<Shape2D> current_shape;
|
||||
|
||||
Variant get_handle_value(int idx) const;
|
||||
void set_handle(int idx, Point2 &p_point);
|
||||
void commit_handle(int idx, Variant &p_org);
|
||||
|
||||
void _shape_changed();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
void _node_removed(Node *p_node);
|
||||
|
||||
public:
|
||||
bool forward_canvas_gui_input(const Ref<InputEvent> &p_event);
|
||||
void forward_canvas_draw_over_viewport(Control *p_overlay);
|
||||
void edit(Node *p_node);
|
||||
|
||||
CollisionShape2DEditor();
|
||||
};
|
||||
|
||||
class CollisionShape2DEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(CollisionShape2DEditorPlugin, EditorPlugin);
|
||||
|
||||
CollisionShape2DEditor *collision_shape_2d_editor = nullptr;
|
||||
|
||||
public:
|
||||
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override { return collision_shape_2d_editor->forward_canvas_gui_input(p_event); }
|
||||
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { collision_shape_2d_editor->forward_canvas_draw_over_viewport(p_overlay); }
|
||||
|
||||
virtual String get_plugin_name() const override { return "CollisionShape2D"; }
|
||||
bool has_main_screen() const override { return false; }
|
||||
virtual void edit(Object *p_obj) override;
|
||||
virtual bool handles(Object *p_obj) const override;
|
||||
virtual void make_visible(bool visible) override;
|
||||
|
||||
CollisionShape2DEditorPlugin();
|
||||
};
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* color_channel_selector.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 "color_channel_selector.h"
|
||||
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/panel_container.h"
|
||||
#include "scene/resources/style_box_flat.h"
|
||||
|
||||
ColorChannelSelector::ColorChannelSelector() {
|
||||
toggle_button = memnew(Button);
|
||||
toggle_button->set_flat(true);
|
||||
toggle_button->set_toggle_mode(true);
|
||||
toggle_button->connect(SceneStringName(toggled), callable_mp(this, &ColorChannelSelector::on_toggled));
|
||||
toggle_button->set_tooltip_text(TTRC("Toggle color channel preview selection."));
|
||||
toggle_button->set_v_size_flags(Control::SIZE_SHRINK_BEGIN);
|
||||
toggle_button->set_theme_type_variation("PreviewLightButton");
|
||||
add_child(toggle_button);
|
||||
|
||||
panel = memnew(PanelContainer);
|
||||
panel->hide();
|
||||
|
||||
HBoxContainer *container = memnew(HBoxContainer);
|
||||
container->add_theme_constant_override("separation", 0);
|
||||
|
||||
create_button(0, "R", container);
|
||||
create_button(1, "G", container);
|
||||
create_button(2, "B", container);
|
||||
create_button(3, "A", container);
|
||||
|
||||
// Use a bit of transparency to be less distracting.
|
||||
set_modulate(Color(1, 1, 1, 0.7));
|
||||
|
||||
panel->add_child(container);
|
||||
|
||||
add_child(panel);
|
||||
}
|
||||
|
||||
void ColorChannelSelector::_notification(int p_what) {
|
||||
if (p_what == NOTIFICATION_THEME_CHANGED) {
|
||||
// PanelContainer's background is invisible in the editor. We need a background.
|
||||
// And we need this in turn because buttons don't look good without background (for example, hover is transparent).
|
||||
Ref<StyleBox> bg_style = get_theme_stylebox(SceneStringName(panel), "TabContainer");
|
||||
ERR_FAIL_COND(bg_style.is_null());
|
||||
bg_style = bg_style->duplicate();
|
||||
// The default content margin makes the widget become a bit too large. It should be like mini-toolbar.
|
||||
bg_style->set_content_margin(SIDE_LEFT, 1.0f * EDSCALE);
|
||||
bg_style->set_content_margin(SIDE_RIGHT, 1.0f * EDSCALE);
|
||||
bg_style->set_content_margin(SIDE_TOP, 1.0f * EDSCALE);
|
||||
bg_style->set_content_margin(SIDE_BOTTOM, 1.0f * EDSCALE);
|
||||
panel->add_theme_style_override(SceneStringName(panel), bg_style);
|
||||
|
||||
Ref<Texture2D> icon = get_editor_theme_icon(SNAME("TexturePreviewChannels"));
|
||||
toggle_button->set_button_icon(icon);
|
||||
}
|
||||
}
|
||||
|
||||
void ColorChannelSelector::set_available_channels_mask(uint32_t p_mask) {
|
||||
for (unsigned int i = 0; i < CHANNEL_COUNT; ++i) {
|
||||
const bool available = (p_mask & (1u << i)) != 0;
|
||||
Button *button = channel_buttons[i];
|
||||
button->set_visible(available);
|
||||
}
|
||||
}
|
||||
|
||||
void ColorChannelSelector::on_channel_button_toggled(bool p_unused_pressed) {
|
||||
emit_signal("selected_channels_changed");
|
||||
}
|
||||
|
||||
uint32_t ColorChannelSelector::get_selected_channels_mask() const {
|
||||
uint32_t mask = 0;
|
||||
for (unsigned int i = 0; i < CHANNEL_COUNT; ++i) {
|
||||
Button *button = channel_buttons[i];
|
||||
if (button->is_visible() && channel_buttons[i]->is_pressed()) {
|
||||
mask |= (1 << i);
|
||||
}
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
// Helper
|
||||
Vector4 ColorChannelSelector::get_selected_channel_factors() const {
|
||||
Vector4 channel_factors;
|
||||
const uint32_t mask = get_selected_channels_mask();
|
||||
for (unsigned int i = 0; i < CHANNEL_COUNT; ++i) {
|
||||
if ((mask & (1 << i)) != 0) {
|
||||
channel_factors[i] = 1;
|
||||
}
|
||||
}
|
||||
return channel_factors;
|
||||
}
|
||||
|
||||
void ColorChannelSelector::create_button(unsigned int p_channel_index, const String &p_text, Control *p_parent) {
|
||||
ERR_FAIL_COND(p_channel_index >= CHANNEL_COUNT);
|
||||
ERR_FAIL_COND(channel_buttons[p_channel_index] != nullptr);
|
||||
Button *button = memnew(Button);
|
||||
button->set_text(p_text);
|
||||
button->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
button->set_toggle_mode(true);
|
||||
button->set_pressed(true);
|
||||
|
||||
// Don't show focus, it stands out too much and remains visible which can be confusing.
|
||||
button->add_theme_style_override("focus", memnew(StyleBoxEmpty));
|
||||
|
||||
// Make it look similar to toolbar buttons.
|
||||
button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
|
||||
button->connect(SceneStringName(toggled), callable_mp(this, &ColorChannelSelector::on_channel_button_toggled));
|
||||
p_parent->add_child(button);
|
||||
channel_buttons[p_channel_index] = button;
|
||||
}
|
||||
|
||||
void ColorChannelSelector::on_toggled(bool p_pressed) {
|
||||
panel->set_visible(p_pressed);
|
||||
}
|
||||
|
||||
void ColorChannelSelector::_bind_methods() {
|
||||
ADD_SIGNAL(MethodInfo("selected_channels_changed"));
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* color_channel_selector.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/box_container.h"
|
||||
|
||||
class PanelContainer;
|
||||
class Button;
|
||||
|
||||
class ColorChannelSelector : public HBoxContainer {
|
||||
GDCLASS(ColorChannelSelector, HBoxContainer);
|
||||
|
||||
static const unsigned int CHANNEL_COUNT = 4;
|
||||
|
||||
public:
|
||||
ColorChannelSelector();
|
||||
|
||||
void set_available_channels_mask(uint32_t p_mask);
|
||||
uint32_t get_selected_channels_mask() const;
|
||||
Vector4 get_selected_channel_factors() const;
|
||||
|
||||
private:
|
||||
void _notification(int p_what);
|
||||
|
||||
void on_channel_button_toggled(bool p_unused_pressed);
|
||||
void create_button(unsigned int p_channel_index, const String &p_text, Control *p_parent);
|
||||
void on_toggled(bool p_pressed);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
Button *channel_buttons[CHANNEL_COUNT] = {};
|
||||
PanelContainer *panel = nullptr;
|
||||
Button *toggle_button = nullptr;
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,257 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* control_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/editor_inspector.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/margin_container.h"
|
||||
|
||||
class CheckBox;
|
||||
class CheckButton;
|
||||
class EditorSelection;
|
||||
class GridContainer;
|
||||
class Label;
|
||||
class OptionButton;
|
||||
class PanelContainer;
|
||||
class PopupPanel;
|
||||
class Separator;
|
||||
class TextureRect;
|
||||
|
||||
// Inspector controls.
|
||||
class ControlPositioningWarning : public MarginContainer {
|
||||
GDCLASS(ControlPositioningWarning, MarginContainer);
|
||||
|
||||
Control *control_node = nullptr;
|
||||
|
||||
PanelContainer *bg_panel = nullptr;
|
||||
GridContainer *grid = nullptr;
|
||||
TextureRect *title_icon = nullptr;
|
||||
TextureRect *hint_icon = nullptr;
|
||||
Label *title_label = nullptr;
|
||||
Label *hint_label = nullptr;
|
||||
Control *hint_filler_left = nullptr;
|
||||
Control *hint_filler_right = nullptr;
|
||||
|
||||
void _update_warning();
|
||||
void _update_toggler();
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
protected:
|
||||
void _notification(int p_notification);
|
||||
|
||||
public:
|
||||
void set_control(Control *p_node);
|
||||
|
||||
ControlPositioningWarning();
|
||||
};
|
||||
|
||||
class EditorPropertyAnchorsPreset : public EditorProperty {
|
||||
GDCLASS(EditorPropertyAnchorsPreset, EditorProperty);
|
||||
OptionButton *options = nullptr;
|
||||
|
||||
void _option_selected(int p_which);
|
||||
|
||||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void setup(const Vector<String> &p_options);
|
||||
virtual void update_property() override;
|
||||
EditorPropertyAnchorsPreset();
|
||||
};
|
||||
|
||||
class EditorPropertySizeFlags : public EditorProperty {
|
||||
GDCLASS(EditorPropertySizeFlags, EditorProperty);
|
||||
|
||||
enum FlagPreset {
|
||||
SIZE_FLAGS_PRESET_FILL,
|
||||
SIZE_FLAGS_PRESET_SHRINK_BEGIN,
|
||||
SIZE_FLAGS_PRESET_SHRINK_CENTER,
|
||||
SIZE_FLAGS_PRESET_SHRINK_END,
|
||||
SIZE_FLAGS_PRESET_CUSTOM,
|
||||
};
|
||||
|
||||
OptionButton *flag_presets = nullptr;
|
||||
CheckBox *flag_expand = nullptr;
|
||||
VBoxContainer *flag_options = nullptr;
|
||||
Vector<CheckBox *> flag_checks;
|
||||
|
||||
bool vertical = false;
|
||||
|
||||
bool keep_selected_preset = false;
|
||||
|
||||
void _preset_selected(int p_which);
|
||||
void _expand_toggled();
|
||||
void _flag_toggled();
|
||||
|
||||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
|
||||
public:
|
||||
void setup(const Vector<String> &p_options, bool p_vertical);
|
||||
virtual void update_property() override;
|
||||
EditorPropertySizeFlags();
|
||||
};
|
||||
|
||||
class EditorInspectorPluginControl : public EditorInspectorPlugin {
|
||||
GDCLASS(EditorInspectorPluginControl, EditorInspectorPlugin);
|
||||
|
||||
bool inside_control_category = false;
|
||||
|
||||
public:
|
||||
virtual bool can_handle(Object *p_object) override;
|
||||
virtual void parse_category(Object *p_object, const String &p_category) override;
|
||||
virtual void parse_group(Object *p_object, const String &p_group) override;
|
||||
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
|
||||
};
|
||||
|
||||
// Toolbar controls.
|
||||
class ControlEditorPopupButton : public Button {
|
||||
GDCLASS(ControlEditorPopupButton, Button);
|
||||
|
||||
Ref<Texture2D> arrow_icon;
|
||||
|
||||
PopupPanel *popup_panel = nullptr;
|
||||
VBoxContainer *popup_vbox = nullptr;
|
||||
|
||||
void _popup_visibility_changed(bool p_visible);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
virtual Size2 get_minimum_size() const override;
|
||||
virtual void toggled(bool p_pressed) override;
|
||||
|
||||
VBoxContainer *get_popup_hbox() const { return popup_vbox; }
|
||||
|
||||
ControlEditorPopupButton();
|
||||
};
|
||||
|
||||
class ControlEditorPresetPicker : public MarginContainer {
|
||||
GDCLASS(ControlEditorPresetPicker, MarginContainer);
|
||||
|
||||
virtual void _preset_button_pressed(const int p_preset) {}
|
||||
|
||||
protected:
|
||||
static constexpr int grid_separation = 0;
|
||||
HashMap<int, Button *> preset_buttons;
|
||||
|
||||
void _add_row_button(HBoxContainer *p_row, const int p_preset, const String &p_name);
|
||||
void _add_separator(BoxContainer *p_box, Separator *p_separator);
|
||||
};
|
||||
|
||||
class AnchorPresetPicker : public ControlEditorPresetPicker {
|
||||
GDCLASS(AnchorPresetPicker, ControlEditorPresetPicker);
|
||||
|
||||
virtual void _preset_button_pressed(const int p_preset) override;
|
||||
|
||||
protected:
|
||||
void _notification(int p_notification);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
AnchorPresetPicker();
|
||||
};
|
||||
|
||||
class SizeFlagPresetPicker : public ControlEditorPresetPicker {
|
||||
GDCLASS(SizeFlagPresetPicker, ControlEditorPresetPicker);
|
||||
|
||||
CheckButton *expand_button = nullptr;
|
||||
|
||||
bool vertical = false;
|
||||
|
||||
virtual void _preset_button_pressed(const int p_preset) override;
|
||||
void _expand_button_pressed();
|
||||
|
||||
protected:
|
||||
void _notification(int p_notification);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_allowed_flags(Vector<SizeFlags> &p_flags);
|
||||
void set_expand_flag(bool p_expand);
|
||||
|
||||
SizeFlagPresetPicker(bool p_vertical);
|
||||
};
|
||||
|
||||
class ControlEditorToolbar : public HBoxContainer {
|
||||
GDCLASS(ControlEditorToolbar, HBoxContainer);
|
||||
|
||||
EditorSelection *editor_selection = nullptr;
|
||||
|
||||
ControlEditorPopupButton *anchors_button = nullptr;
|
||||
ControlEditorPopupButton *containers_button = nullptr;
|
||||
Button *anchor_mode_button = nullptr;
|
||||
|
||||
SizeFlagPresetPicker *container_h_picker = nullptr;
|
||||
SizeFlagPresetPicker *container_v_picker = nullptr;
|
||||
|
||||
bool anchors_mode = false;
|
||||
|
||||
void _anchors_preset_selected(int p_preset);
|
||||
void _anchors_to_current_ratio();
|
||||
void _anchor_mode_toggled(bool p_status);
|
||||
void _container_flags_selected(int p_flags, bool p_vertical);
|
||||
void _expand_flag_toggled(bool p_expand, bool p_vertical);
|
||||
|
||||
Vector2 _position_to_anchor(const Control *p_control, Vector2 position);
|
||||
bool _is_node_locked(const Node *p_node);
|
||||
List<Control *> _get_edited_controls();
|
||||
void _selection_changed();
|
||||
|
||||
protected:
|
||||
void _notification(int p_notification);
|
||||
|
||||
static ControlEditorToolbar *singleton;
|
||||
|
||||
public:
|
||||
bool is_anchors_mode_enabled() { return anchors_mode; }
|
||||
|
||||
static ControlEditorToolbar *get_singleton() { return singleton; }
|
||||
|
||||
ControlEditorToolbar();
|
||||
};
|
||||
|
||||
// Editor plugin.
|
||||
class ControlEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(ControlEditorPlugin, EditorPlugin);
|
||||
|
||||
ControlEditorToolbar *toolbar = nullptr;
|
||||
|
||||
public:
|
||||
virtual String get_plugin_name() const override { return "Control"; }
|
||||
|
||||
ControlEditorPlugin();
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,199 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* curve_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/editor_inspector.h"
|
||||
#include "editor/editor_resource_preview.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/resources/curve.h"
|
||||
|
||||
class EditorSpinSlider;
|
||||
class MenuButton;
|
||||
class PopupMenu;
|
||||
|
||||
class CurveEdit : public Control {
|
||||
GDCLASS(CurveEdit, Control);
|
||||
|
||||
public:
|
||||
CurveEdit();
|
||||
|
||||
void set_snap_enabled(bool p_enabled);
|
||||
void set_snap_count(int p_snap_count);
|
||||
void use_preset(int p_preset_id);
|
||||
|
||||
void set_curve(Ref<Curve> p_curve);
|
||||
Ref<Curve> get_curve();
|
||||
|
||||
Size2 get_minimum_size() const override;
|
||||
|
||||
enum PresetID {
|
||||
PRESET_CONSTANT = 0,
|
||||
PRESET_LINEAR,
|
||||
PRESET_EASE_IN,
|
||||
PRESET_EASE_OUT,
|
||||
PRESET_SMOOTHSTEP,
|
||||
PRESET_COUNT
|
||||
};
|
||||
|
||||
enum TangentIndex {
|
||||
TANGENT_NONE = -1,
|
||||
TANGENT_LEFT = 0,
|
||||
TANGENT_RIGHT = 1
|
||||
};
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
void _curve_changed();
|
||||
|
||||
int get_point_at(const Vector2 &p_pos) const;
|
||||
TangentIndex get_tangent_at(const Vector2 &p_pos) const;
|
||||
|
||||
float get_offset_without_collision(int p_current_index, float p_offset, bool p_prioritize_right = true);
|
||||
|
||||
void add_point(const Vector2 &p_pos);
|
||||
void remove_point(int p_index);
|
||||
void set_point_position(int p_index, const Vector2 &p_pos);
|
||||
|
||||
void set_point_tangents(int p_index, float p_left, float p_right);
|
||||
void set_point_left_tangent(int p_index, float p_tangent);
|
||||
void set_point_right_tangent(int p_index, float p_tangent);
|
||||
void toggle_linear(int p_index, TangentIndex p_tangent = TANGENT_NONE);
|
||||
|
||||
void update_view_transform();
|
||||
|
||||
void plot_curve_accurate(float p_step, const Color &p_line_color, const Color &p_edge_line_color);
|
||||
|
||||
void set_selected_index(int p_index);
|
||||
|
||||
Vector2 get_tangent_view_pos(int p_index, TangentIndex p_tangent) const;
|
||||
Vector2 get_view_pos(const Vector2 &p_world_pos) const;
|
||||
Vector2 get_world_pos(const Vector2 &p_view_pos) const;
|
||||
|
||||
void _redraw();
|
||||
|
||||
private:
|
||||
const float ASPECT_RATIO = 6.f / 13.f;
|
||||
const float LINE_WIDTH = 0.5f;
|
||||
const int STEP_SIZE = 2; // Number of pixels between plot points.
|
||||
|
||||
Transform2D _world_to_view;
|
||||
|
||||
Ref<Curve> curve;
|
||||
PopupMenu *_presets_menu = nullptr;
|
||||
|
||||
int selected_index = -1;
|
||||
int hovered_index = -1;
|
||||
TangentIndex selected_tangent_index = TANGENT_NONE;
|
||||
TangentIndex hovered_tangent_index = TANGENT_NONE;
|
||||
|
||||
// Make sure to use the scaled values below.
|
||||
const int BASE_POINT_RADIUS = 4;
|
||||
const int BASE_HOVER_RADIUS = 10;
|
||||
const int BASE_TANGENT_RADIUS = 3;
|
||||
const int BASE_TANGENT_HOVER_RADIUS = 8;
|
||||
const int BASE_TANGENT_LENGTH = 36;
|
||||
|
||||
int point_radius = BASE_POINT_RADIUS;
|
||||
int hover_radius = BASE_HOVER_RADIUS;
|
||||
int tangent_radius = BASE_TANGENT_RADIUS;
|
||||
int tangent_hover_radius = BASE_TANGENT_HOVER_RADIUS;
|
||||
int tangent_length = BASE_TANGENT_LENGTH;
|
||||
|
||||
enum GrabMode {
|
||||
GRAB_NONE,
|
||||
GRAB_ADD,
|
||||
GRAB_MOVE
|
||||
};
|
||||
GrabMode grabbing = GRAB_NONE;
|
||||
Vector2 initial_grab_pos;
|
||||
int initial_grab_index = -1;
|
||||
float initial_grab_left_tangent = 0;
|
||||
float initial_grab_right_tangent = 0;
|
||||
|
||||
bool snap_enabled = false;
|
||||
int snap_count = 10;
|
||||
};
|
||||
|
||||
// CurveEdit + toolbar
|
||||
class CurveEditor : public VBoxContainer {
|
||||
GDCLASS(CurveEditor, VBoxContainer);
|
||||
|
||||
// Make sure to use the scaled values below.
|
||||
const int BASE_SPACING = 4;
|
||||
int spacing = BASE_SPACING;
|
||||
|
||||
Button *snap_button = nullptr;
|
||||
EditorSpinSlider *snap_count_edit = nullptr;
|
||||
MenuButton *presets_button = nullptr;
|
||||
CurveEdit *curve_editor_rect = nullptr;
|
||||
|
||||
void _set_snap_enabled(bool p_enabled);
|
||||
void _set_snap_count(int p_snap_count);
|
||||
void _on_preset_item_selected(int p_preset_id);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
static const int DEFAULT_SNAP;
|
||||
void set_curve(const Ref<Curve> &p_curve);
|
||||
|
||||
CurveEditor();
|
||||
};
|
||||
|
||||
class EditorInspectorPluginCurve : public EditorInspectorPlugin {
|
||||
GDCLASS(EditorInspectorPluginCurve, EditorInspectorPlugin);
|
||||
|
||||
public:
|
||||
virtual bool can_handle(Object *p_object) override;
|
||||
virtual void parse_begin(Object *p_object) override;
|
||||
};
|
||||
|
||||
class CurveEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(CurveEditorPlugin, EditorPlugin);
|
||||
|
||||
public:
|
||||
CurveEditorPlugin();
|
||||
|
||||
virtual String get_plugin_name() const override { return "Curve"; }
|
||||
};
|
||||
|
||||
class CurvePreviewGenerator : public EditorResourcePreviewGenerator {
|
||||
GDCLASS(CurvePreviewGenerator, EditorResourcePreviewGenerator);
|
||||
|
||||
public:
|
||||
virtual bool handles(const String &p_type) const override;
|
||||
virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
|
||||
};
|
||||
|
|
@ -1,271 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* debugger_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 "debugger_editor_plugin.h"
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#include "editor/debugger/editor_debugger_server.h"
|
||||
#include "editor/debugger/editor_file_server.h"
|
||||
#include "editor/editor_command_palette.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/gui/editor_bottom_panel.h"
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "editor/run_instances_dialog.h"
|
||||
#include "scene/gui/popup_menu.h"
|
||||
|
||||
DebuggerEditorPlugin::DebuggerEditorPlugin(PopupMenu *p_debug_menu) {
|
||||
EditorDebuggerServer::initialize();
|
||||
|
||||
ED_SHORTCUT("debugger/step_into", TTRC("Step Into"), Key::F11);
|
||||
ED_SHORTCUT("debugger/step_over", TTRC("Step Over"), Key::F10);
|
||||
ED_SHORTCUT("debugger/break", TTRC("Break"));
|
||||
ED_SHORTCUT("debugger/continue", TTRC("Continue"), Key::F12);
|
||||
ED_SHORTCUT("debugger/debug_with_external_editor", TTRC("Debug with External Editor"));
|
||||
|
||||
// File Server for deploy with remote filesystem.
|
||||
file_server = memnew(EditorFileServer);
|
||||
|
||||
EditorDebuggerNode *debugger = memnew(EditorDebuggerNode);
|
||||
Button *db = EditorNode::get_bottom_panel()->add_item(TTRC("Debugger"), debugger, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_debugger_bottom_panel", TTRC("Toggle Debugger Bottom Panel"), KeyModifierMask::ALT | Key::D));
|
||||
debugger->set_tool_button(db);
|
||||
|
||||
// Main editor debug menu.
|
||||
debug_menu = p_debug_menu;
|
||||
debug_menu->set_hide_on_checkable_item_selection(false);
|
||||
debug_menu->add_check_shortcut(ED_SHORTCUT("editor/deploy_with_remote_debug", TTRC("Deploy with Remote Debug")), RUN_DEPLOY_REMOTE_DEBUG);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTRC("When this option is enabled, using one-click deploy will make the executable attempt to connect to this computer's IP so the running project can be debugged.\nThis option is intended to be used for remote debugging (typically with a mobile device).\nYou don't need to enable it to use the GDScript debugger locally."));
|
||||
debug_menu->add_check_shortcut(ED_SHORTCUT("editor/small_deploy_with_network_fs", TTRC("Small Deploy with Network Filesystem")), RUN_FILE_SERVER);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTRC("When this option is enabled, using one-click deploy for Android will only export an executable without the project data.\nThe filesystem will be provided from the project by the editor over the network.\nOn Android, deploying will use the USB cable for faster performance. This option speeds up testing for projects with large assets."));
|
||||
debug_menu->add_separator();
|
||||
debug_menu->add_check_shortcut(ED_SHORTCUT("editor/visible_collision_shapes", TTRC("Visible Collision Shapes")), RUN_DEBUG_COLLISIONS);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTRC("When this option is enabled, collision shapes and raycast nodes (for 2D and 3D) will be visible in the running project."));
|
||||
debug_menu->add_check_shortcut(ED_SHORTCUT("editor/visible_paths", TTRC("Visible Paths")), RUN_DEBUG_PATHS);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTRC("When this option is enabled, curve resources used by path nodes will be visible in the running project."));
|
||||
debug_menu->add_check_shortcut(ED_SHORTCUT("editor/visible_navigation", TTRC("Visible Navigation")), RUN_DEBUG_NAVIGATION);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTRC("When this option is enabled, navigation meshes, and polygons will be visible in the running project."));
|
||||
debug_menu->add_check_shortcut(ED_SHORTCUT("editor/visible_avoidance", TTRC("Visible Avoidance")), RUN_DEBUG_AVOIDANCE);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTRC("When this option is enabled, avoidance object shapes, radiuses, and velocities will be visible in the running project."));
|
||||
debug_menu->add_separator();
|
||||
debug_menu->add_check_shortcut(ED_SHORTCUT("editor/visible_canvas_redraw", TTRC("Debug CanvasItem Redraws")), RUN_DEBUG_CANVAS_REDRAW);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTRC("When this option is enabled, redraw requests of 2D objects will become visible (as a short flash) in the running project.\nThis is useful to troubleshoot low processor mode."));
|
||||
debug_menu->add_separator();
|
||||
debug_menu->add_check_shortcut(ED_SHORTCUT("editor/sync_scene_changes", TTRC("Synchronize Scene Changes")), RUN_LIVE_DEBUG);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTRC("When this option is enabled, any changes made to the scene in the editor will be replicated in the running project.\nWhen used remotely on a device, this is more efficient when the network filesystem option is enabled."));
|
||||
debug_menu->add_check_shortcut(ED_SHORTCUT("editor/sync_script_changes", TTRC("Synchronize Script Changes")), RUN_RELOAD_SCRIPTS);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTRC("When this option is enabled, any script that is saved will be reloaded in the running project.\nWhen used remotely on a device, this is more efficient when the network filesystem option is enabled."));
|
||||
debug_menu->add_check_shortcut(ED_SHORTCUT("editor/keep_server_open", TTRC("Keep Debug Server Open")), SERVER_KEEP_OPEN);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTRC("When this option is enabled, the editor debug server will stay open and listen for new sessions started outside of the editor itself."));
|
||||
|
||||
// Multi-instance, start/stop.
|
||||
debug_menu->add_separator();
|
||||
debug_menu->add_item(TTRC("Customize Run Instances..."), RUN_MULTIPLE_INSTANCES);
|
||||
debug_menu->connect(SceneStringName(id_pressed), callable_mp(this, &DebuggerEditorPlugin::_menu_option));
|
||||
|
||||
run_instances_dialog = memnew(RunInstancesDialog);
|
||||
EditorNode::get_singleton()->get_gui_base()->add_child(run_instances_dialog);
|
||||
}
|
||||
|
||||
DebuggerEditorPlugin::~DebuggerEditorPlugin() {
|
||||
EditorDebuggerServer::deinitialize();
|
||||
memdelete(file_server);
|
||||
}
|
||||
|
||||
void DebuggerEditorPlugin::_menu_option(int p_option) {
|
||||
switch (p_option) {
|
||||
case RUN_FILE_SERVER: {
|
||||
bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_FILE_SERVER));
|
||||
|
||||
if (ischecked) {
|
||||
file_server->stop();
|
||||
set_process(false);
|
||||
} else {
|
||||
file_server->start();
|
||||
set_process(true);
|
||||
}
|
||||
|
||||
debug_menu->set_item_checked(debug_menu->get_item_index(RUN_FILE_SERVER), !ischecked);
|
||||
if (!initializing) {
|
||||
EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_file_server", !ischecked);
|
||||
}
|
||||
|
||||
} break;
|
||||
case RUN_LIVE_DEBUG: {
|
||||
bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_LIVE_DEBUG));
|
||||
|
||||
debug_menu->set_item_checked(debug_menu->get_item_index(RUN_LIVE_DEBUG), !ischecked);
|
||||
EditorDebuggerNode::get_singleton()->set_live_debugging(!ischecked);
|
||||
if (!initializing) {
|
||||
EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_live_debug", !ischecked);
|
||||
}
|
||||
|
||||
} break;
|
||||
case RUN_DEPLOY_REMOTE_DEBUG: {
|
||||
bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEPLOY_REMOTE_DEBUG));
|
||||
debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEPLOY_REMOTE_DEBUG), !ischecked);
|
||||
if (!initializing) {
|
||||
EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_deploy_remote_debug", !ischecked);
|
||||
}
|
||||
|
||||
} break;
|
||||
case RUN_DEBUG_COLLISIONS: {
|
||||
bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEBUG_COLLISIONS));
|
||||
debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEBUG_COLLISIONS), !ischecked);
|
||||
if (!initializing) {
|
||||
EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_collisions", !ischecked);
|
||||
}
|
||||
|
||||
} break;
|
||||
case RUN_DEBUG_PATHS: {
|
||||
bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEBUG_PATHS));
|
||||
debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEBUG_PATHS), !ischecked);
|
||||
if (!initializing) {
|
||||
EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_paths", !ischecked);
|
||||
}
|
||||
|
||||
} break;
|
||||
case RUN_DEBUG_NAVIGATION: {
|
||||
bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEBUG_NAVIGATION));
|
||||
debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEBUG_NAVIGATION), !ischecked);
|
||||
if (!initializing) {
|
||||
EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_navigation", !ischecked);
|
||||
}
|
||||
|
||||
} break;
|
||||
case RUN_DEBUG_AVOIDANCE: {
|
||||
bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEBUG_AVOIDANCE));
|
||||
debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEBUG_AVOIDANCE), !ischecked);
|
||||
if (!initializing) {
|
||||
EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_avoidance", !ischecked);
|
||||
}
|
||||
|
||||
} break;
|
||||
case RUN_DEBUG_CANVAS_REDRAW: {
|
||||
bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEBUG_CANVAS_REDRAW));
|
||||
debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEBUG_CANVAS_REDRAW), !ischecked);
|
||||
if (!initializing) {
|
||||
EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_canvas_redraw", !ischecked);
|
||||
}
|
||||
|
||||
} break;
|
||||
case RUN_RELOAD_SCRIPTS: {
|
||||
bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_RELOAD_SCRIPTS));
|
||||
debug_menu->set_item_checked(debug_menu->get_item_index(RUN_RELOAD_SCRIPTS), !ischecked);
|
||||
|
||||
ScriptEditor::get_singleton()->set_live_auto_reload_running_scripts(!ischecked);
|
||||
if (!initializing) {
|
||||
EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_reload_scripts", !ischecked);
|
||||
}
|
||||
|
||||
} break;
|
||||
case SERVER_KEEP_OPEN: {
|
||||
bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(SERVER_KEEP_OPEN));
|
||||
debug_menu->set_item_checked(debug_menu->get_item_index(SERVER_KEEP_OPEN), !ischecked);
|
||||
|
||||
EditorDebuggerNode::get_singleton()->set_keep_open(!ischecked);
|
||||
if (!initializing) {
|
||||
EditorSettings::get_singleton()->set_project_metadata("debug_options", "server_keep_open", !ischecked);
|
||||
}
|
||||
|
||||
} break;
|
||||
case RUN_MULTIPLE_INSTANCES: {
|
||||
run_instances_dialog->popup_dialog();
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void DebuggerEditorPlugin::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
_update_debug_options();
|
||||
initializing = false;
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_PROCESS: {
|
||||
file_server->poll();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void DebuggerEditorPlugin::_update_debug_options() {
|
||||
bool check_deploy_remote = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_deploy_remote_debug", true);
|
||||
bool check_file_server = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_file_server", false);
|
||||
bool check_debug_collisions = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_collisions", false);
|
||||
bool check_debug_paths = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_paths", false);
|
||||
bool check_debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false);
|
||||
bool check_debug_avoidance = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_avoidance", false);
|
||||
bool check_debug_canvas_redraw = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_canvas_redraw", false);
|
||||
bool check_live_debug = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_live_debug", true);
|
||||
bool check_reload_scripts = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_reload_scripts", true);
|
||||
bool check_server_keep_open = EditorSettings::get_singleton()->get_project_metadata("debug_options", "server_keep_open", false);
|
||||
|
||||
if (check_deploy_remote) {
|
||||
_menu_option(RUN_DEPLOY_REMOTE_DEBUG);
|
||||
}
|
||||
if (check_file_server) {
|
||||
_menu_option(RUN_FILE_SERVER);
|
||||
}
|
||||
if (check_debug_collisions) {
|
||||
_menu_option(RUN_DEBUG_COLLISIONS);
|
||||
}
|
||||
if (check_debug_paths) {
|
||||
_menu_option(RUN_DEBUG_PATHS);
|
||||
}
|
||||
if (check_debug_navigation) {
|
||||
_menu_option(RUN_DEBUG_NAVIGATION);
|
||||
}
|
||||
if (check_debug_avoidance) {
|
||||
_menu_option(RUN_DEBUG_AVOIDANCE);
|
||||
}
|
||||
if (check_debug_canvas_redraw) {
|
||||
_menu_option(RUN_DEBUG_CANVAS_REDRAW);
|
||||
}
|
||||
if (check_live_debug) {
|
||||
_menu_option(RUN_LIVE_DEBUG);
|
||||
}
|
||||
if (check_reload_scripts) {
|
||||
_menu_option(RUN_RELOAD_SCRIPTS);
|
||||
}
|
||||
if (check_server_keep_open) {
|
||||
_menu_option(SERVER_KEEP_OPEN);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* debugger_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
|
||||
class EditorFileServer;
|
||||
class MenuButton;
|
||||
class PopupMenu;
|
||||
class RunInstancesDialog;
|
||||
|
||||
class DebuggerEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(DebuggerEditorPlugin, EditorPlugin);
|
||||
|
||||
private:
|
||||
PopupMenu *debug_menu = nullptr;
|
||||
EditorFileServer *file_server = nullptr;
|
||||
RunInstancesDialog *run_instances_dialog = nullptr;
|
||||
|
||||
enum MenuOptions {
|
||||
RUN_FILE_SERVER,
|
||||
RUN_LIVE_DEBUG,
|
||||
RUN_DEBUG_COLLISIONS,
|
||||
RUN_DEBUG_PATHS,
|
||||
RUN_DEBUG_NAVIGATION,
|
||||
RUN_DEBUG_AVOIDANCE,
|
||||
RUN_DEBUG_CANVAS_REDRAW,
|
||||
RUN_DEPLOY_REMOTE_DEBUG,
|
||||
RUN_RELOAD_SCRIPTS,
|
||||
SERVER_KEEP_OPEN,
|
||||
RUN_MULTIPLE_INSTANCES,
|
||||
};
|
||||
|
||||
bool initializing = true;
|
||||
|
||||
void _update_debug_options();
|
||||
void _notification(int p_what);
|
||||
void _menu_option(int p_option);
|
||||
|
||||
public:
|
||||
virtual String get_plugin_name() const override { return "Debugger"; }
|
||||
bool has_main_screen() const override { return false; }
|
||||
|
||||
DebuggerEditorPlugin(PopupMenu *p_menu);
|
||||
~DebuggerEditorPlugin();
|
||||
};
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* dedicated_server_export_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 "dedicated_server_export_plugin.h"
|
||||
|
||||
EditorExportPreset::FileExportMode DedicatedServerExportPlugin::_get_export_mode_for_path(const String &p_path) {
|
||||
Ref<EditorExportPreset> preset = get_export_preset();
|
||||
ERR_FAIL_COND_V(preset.is_null(), EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED);
|
||||
|
||||
EditorExportPreset::FileExportMode mode = preset->get_file_export_mode(p_path);
|
||||
if (mode != EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) {
|
||||
return mode;
|
||||
}
|
||||
|
||||
String path = p_path;
|
||||
if (path.begins_with("res://")) {
|
||||
path = path.substr(6);
|
||||
}
|
||||
|
||||
Vector<String> parts = path.split("/");
|
||||
|
||||
while (parts.size() > 0) {
|
||||
parts.resize(parts.size() - 1);
|
||||
|
||||
String test_path = "res://";
|
||||
if (parts.size() > 0) {
|
||||
test_path += String("/").join(parts) + "/";
|
||||
}
|
||||
|
||||
mode = preset->get_file_export_mode(test_path);
|
||||
if (mode != EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
PackedStringArray DedicatedServerExportPlugin::_get_export_features(const Ref<EditorExportPlatform> &p_platform, bool p_debug) const {
|
||||
PackedStringArray ret;
|
||||
|
||||
Ref<EditorExportPreset> preset = get_export_preset();
|
||||
ERR_FAIL_COND_V(preset.is_null(), ret);
|
||||
|
||||
if (preset->is_dedicated_server()) {
|
||||
ret.append("dedicated_server");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t DedicatedServerExportPlugin::_get_customization_configuration_hash() const {
|
||||
Ref<EditorExportPreset> preset = get_export_preset();
|
||||
ERR_FAIL_COND_V(preset.is_null(), 0);
|
||||
|
||||
if (preset->get_export_filter() != EditorExportPreset::EXPORT_CUSTOMIZED) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return preset->get_customized_files().hash();
|
||||
}
|
||||
|
||||
bool DedicatedServerExportPlugin::_begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) {
|
||||
Ref<EditorExportPreset> preset = get_export_preset();
|
||||
ERR_FAIL_COND_V(preset.is_null(), false);
|
||||
|
||||
current_export_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED;
|
||||
|
||||
return preset->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED;
|
||||
}
|
||||
|
||||
bool DedicatedServerExportPlugin::_begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) {
|
||||
Ref<EditorExportPreset> preset = get_export_preset();
|
||||
ERR_FAIL_COND_V(preset.is_null(), false);
|
||||
|
||||
current_export_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED;
|
||||
|
||||
return preset->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED;
|
||||
}
|
||||
|
||||
Node *DedicatedServerExportPlugin::_customize_scene(Node *p_root, const String &p_path) {
|
||||
// Simply set the export mode based on the scene path. All the real
|
||||
// customization happens in _customize_resource().
|
||||
current_export_mode = _get_export_mode_for_path(p_path);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ref<Resource> DedicatedServerExportPlugin::_customize_resource(const Ref<Resource> &p_resource, const String &p_path) {
|
||||
// If the resource has a path, we use that to get our export mode. But if it
|
||||
// doesn't, we assume that this resource is embedded in the last resource with
|
||||
// a path.
|
||||
if (p_path != "") {
|
||||
current_export_mode = _get_export_mode_for_path(p_path);
|
||||
}
|
||||
|
||||
if (p_resource.is_valid() && current_export_mode == EditorExportPreset::MODE_FILE_STRIP && p_resource->has_method("create_placeholder")) {
|
||||
Callable::CallError err;
|
||||
Ref<Resource> result = p_resource->callp("create_placeholder", nullptr, 0, err);
|
||||
if (err.error == Callable::CallError::CALL_OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return Ref<Resource>();
|
||||
}
|
||||
|
||||
void DedicatedServerExportPlugin::_end_customize_scenes() {
|
||||
current_export_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED;
|
||||
}
|
||||
|
||||
void DedicatedServerExportPlugin::_end_customize_resources() {
|
||||
current_export_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED;
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* dedicated_server_export_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/export/editor_export_plugin.h"
|
||||
|
||||
class DedicatedServerExportPlugin : public EditorExportPlugin {
|
||||
private:
|
||||
EditorExportPreset::FileExportMode current_export_mode;
|
||||
|
||||
EditorExportPreset::FileExportMode _get_export_mode_for_path(const String &p_path);
|
||||
|
||||
protected:
|
||||
String get_name() const override { return "DedicatedServer"; }
|
||||
|
||||
PackedStringArray _get_export_features(const Ref<EditorExportPlatform> &p_platform, bool p_debug) const override;
|
||||
uint64_t _get_customization_configuration_hash() const override;
|
||||
|
||||
bool _begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) override;
|
||||
bool _begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) override;
|
||||
|
||||
Node *_customize_scene(Node *p_root, const String &p_path) override;
|
||||
Ref<Resource> _customize_resource(const Ref<Resource> &p_resource, const String &p_path) override;
|
||||
|
||||
void _end_customize_scenes() override;
|
||||
void _end_customize_resources() override;
|
||||
};
|
||||
|
|
@ -1,212 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* editor_context_menu_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 "editor_context_menu_plugin.h"
|
||||
|
||||
#include "core/input/shortcut.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "scene/gui/popup_menu.h"
|
||||
#include "scene/resources/texture.h"
|
||||
|
||||
void EditorContextMenuPlugin::get_options(const Vector<String> &p_paths) {
|
||||
GDVIRTUAL_CALL(_popup_menu, p_paths);
|
||||
}
|
||||
|
||||
void EditorContextMenuPlugin::add_menu_shortcut(const Ref<Shortcut> &p_shortcut, const Callable &p_callable) {
|
||||
context_menu_shortcuts.insert(p_shortcut, p_callable);
|
||||
}
|
||||
|
||||
void EditorContextMenuPlugin::add_context_menu_item(const String &p_name, const Callable &p_callable, const Ref<Texture2D> &p_texture) {
|
||||
ERR_FAIL_COND_MSG(context_menu_items.has(p_name), "Context menu item already registered.");
|
||||
ERR_FAIL_COND_MSG(context_menu_items.size() == MAX_ITEMS, "Maximum number of context menu items reached.");
|
||||
|
||||
ContextMenuItem item;
|
||||
item.item_name = p_name;
|
||||
item.callable = p_callable;
|
||||
item.icon = p_texture;
|
||||
context_menu_items.insert(p_name, item);
|
||||
}
|
||||
|
||||
void EditorContextMenuPlugin::add_context_menu_item_from_shortcut(const String &p_name, const Ref<Shortcut> &p_shortcut, const Ref<Texture2D> &p_texture) {
|
||||
Callable *callback = context_menu_shortcuts.getptr(p_shortcut);
|
||||
ERR_FAIL_NULL_MSG(callback, "Shortcut not registered. Use add_menu_shortcut() first.");
|
||||
|
||||
ContextMenuItem item;
|
||||
item.item_name = p_name;
|
||||
item.callable = *callback;
|
||||
item.icon = p_texture;
|
||||
item.shortcut = p_shortcut;
|
||||
context_menu_items.insert(p_name, item);
|
||||
}
|
||||
|
||||
void EditorContextMenuPlugin::add_context_submenu_item(const String &p_name, PopupMenu *p_menu, const Ref<Texture2D> &p_texture) {
|
||||
ERR_FAIL_NULL(p_menu);
|
||||
|
||||
ContextMenuItem item;
|
||||
item.item_name = p_name;
|
||||
item.icon = p_texture;
|
||||
item.submenu = p_menu;
|
||||
context_menu_items.insert(p_name, item);
|
||||
}
|
||||
|
||||
void EditorContextMenuPlugin::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("add_menu_shortcut", "shortcut", "callback"), &EditorContextMenuPlugin::add_menu_shortcut);
|
||||
ClassDB::bind_method(D_METHOD("add_context_menu_item", "name", "callback", "icon"), &EditorContextMenuPlugin::add_context_menu_item, DEFVAL(Ref<Texture2D>()));
|
||||
ClassDB::bind_method(D_METHOD("add_context_menu_item_from_shortcut", "name", "shortcut", "icon"), &EditorContextMenuPlugin::add_context_menu_item_from_shortcut, DEFVAL(Ref<Texture2D>()));
|
||||
ClassDB::bind_method(D_METHOD("add_context_submenu_item", "name", "menu", "icon"), &EditorContextMenuPlugin::add_context_submenu_item, DEFVAL(Ref<Texture2D>()));
|
||||
|
||||
GDVIRTUAL_BIND(_popup_menu, "paths");
|
||||
|
||||
BIND_ENUM_CONSTANT(CONTEXT_SLOT_SCENE_TREE);
|
||||
BIND_ENUM_CONSTANT(CONTEXT_SLOT_FILESYSTEM);
|
||||
BIND_ENUM_CONSTANT(CONTEXT_SLOT_SCRIPT_EDITOR);
|
||||
BIND_ENUM_CONSTANT(CONTEXT_SLOT_FILESYSTEM_CREATE);
|
||||
BIND_ENUM_CONSTANT(CONTEXT_SLOT_SCRIPT_EDITOR_CODE);
|
||||
BIND_ENUM_CONSTANT(CONTEXT_SLOT_SCENE_TABS);
|
||||
BIND_ENUM_CONSTANT(CONTEXT_SLOT_2D_EDITOR);
|
||||
}
|
||||
|
||||
void EditorContextMenuPluginManager::add_plugin(EditorContextMenuPlugin::ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin) {
|
||||
ERR_FAIL_COND(p_plugin.is_null());
|
||||
ERR_FAIL_COND(plugin_list.has(p_plugin));
|
||||
|
||||
p_plugin->slot = p_slot;
|
||||
plugin_list.push_back(p_plugin);
|
||||
}
|
||||
|
||||
void EditorContextMenuPluginManager::remove_plugin(const Ref<EditorContextMenuPlugin> &p_plugin) {
|
||||
ERR_FAIL_COND(p_plugin.is_null());
|
||||
ERR_FAIL_COND(!plugin_list.has(p_plugin));
|
||||
|
||||
plugin_list.erase(p_plugin);
|
||||
}
|
||||
|
||||
bool EditorContextMenuPluginManager::has_plugins_for_slot(ContextMenuSlot p_slot) {
|
||||
for (Ref<EditorContextMenuPlugin> &plugin : plugin_list) {
|
||||
if (plugin->slot == p_slot) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EditorContextMenuPluginManager::add_options_from_plugins(PopupMenu *p_popup, ContextMenuSlot p_slot, const Vector<String> &p_paths) {
|
||||
bool separator_added = false;
|
||||
const int icon_size = p_popup->get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));
|
||||
int id = EditorContextMenuPlugin::BASE_ID;
|
||||
|
||||
for (Ref<EditorContextMenuPlugin> &plugin : plugin_list) {
|
||||
if (plugin->slot != p_slot) {
|
||||
continue;
|
||||
}
|
||||
plugin->context_menu_items.clear();
|
||||
plugin->get_options(p_paths);
|
||||
|
||||
HashMap<String, EditorContextMenuPlugin::ContextMenuItem> &items = plugin->context_menu_items;
|
||||
if (items.size() > 0 && !separator_added) {
|
||||
separator_added = true;
|
||||
p_popup->add_separator();
|
||||
}
|
||||
|
||||
for (KeyValue<String, EditorContextMenuPlugin::ContextMenuItem> &E : items) {
|
||||
EditorContextMenuPlugin::ContextMenuItem &item = E.value;
|
||||
item.id = id;
|
||||
|
||||
if (item.submenu) {
|
||||
p_popup->add_submenu_node_item(item.item_name, item.submenu, id);
|
||||
} else {
|
||||
p_popup->add_item(item.item_name, id);
|
||||
}
|
||||
|
||||
if (item.icon.is_valid()) {
|
||||
p_popup->set_item_icon(-1, item.icon);
|
||||
p_popup->set_item_icon_max_width(-1, icon_size);
|
||||
}
|
||||
|
||||
if (item.shortcut.is_valid()) {
|
||||
p_popup->set_item_shortcut(-1, item.shortcut, true);
|
||||
}
|
||||
id++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Callable EditorContextMenuPluginManager::match_custom_shortcut(EditorContextMenuPlugin::ContextMenuSlot p_slot, const Ref<InputEvent> &p_event) {
|
||||
for (Ref<EditorContextMenuPlugin> &plugin : plugin_list) {
|
||||
if (plugin->slot != p_slot) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (KeyValue<Ref<Shortcut>, Callable> &E : plugin->context_menu_shortcuts) {
|
||||
if (E.key->matches_event(p_event)) {
|
||||
return E.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Callable();
|
||||
}
|
||||
|
||||
bool EditorContextMenuPluginManager::activate_custom_option(ContextMenuSlot p_slot, int p_option, const Variant &p_arg) {
|
||||
for (Ref<EditorContextMenuPlugin> &plugin : plugin_list) {
|
||||
if (plugin->slot != p_slot) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (KeyValue<String, EditorContextMenuPlugin::ContextMenuItem> &E : plugin->context_menu_items) {
|
||||
if (E.value.id == p_option) {
|
||||
invoke_callback(E.value.callable, p_arg);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EditorContextMenuPluginManager::invoke_callback(const Callable &p_callback, const Variant &p_arg) {
|
||||
const Variant *argptr = &p_arg;
|
||||
Callable::CallError ce;
|
||||
Variant result;
|
||||
p_callback.callp(&argptr, 1, result, ce);
|
||||
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_FAIL_MSG("Failed to execute context menu callback: " + Variant::get_callable_error_text(p_callback, &argptr, 1, ce) + ".");
|
||||
}
|
||||
}
|
||||
|
||||
void EditorContextMenuPluginManager::create() {
|
||||
ERR_FAIL_COND(singleton != nullptr);
|
||||
singleton = memnew(EditorContextMenuPluginManager);
|
||||
}
|
||||
|
||||
void EditorContextMenuPluginManager::cleanup() {
|
||||
ERR_FAIL_NULL(singleton);
|
||||
memdelete(singleton);
|
||||
singleton = nullptr;
|
||||
}
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* editor_context_menu_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/object/gdvirtual.gen.inc"
|
||||
#include "core/object/ref_counted.h"
|
||||
|
||||
class InputEvent;
|
||||
class PopupMenu;
|
||||
class Shortcut;
|
||||
class Texture2D;
|
||||
|
||||
class EditorContextMenuPlugin : public RefCounted {
|
||||
GDCLASS(EditorContextMenuPlugin, RefCounted);
|
||||
|
||||
friend class EditorContextMenuPluginManager;
|
||||
|
||||
static constexpr int MAX_ITEMS = 100;
|
||||
|
||||
public:
|
||||
enum ContextMenuSlot {
|
||||
CONTEXT_SLOT_SCENE_TREE,
|
||||
CONTEXT_SLOT_FILESYSTEM,
|
||||
CONTEXT_SLOT_SCRIPT_EDITOR,
|
||||
CONTEXT_SLOT_FILESYSTEM_CREATE,
|
||||
CONTEXT_SLOT_SCRIPT_EDITOR_CODE,
|
||||
CONTEXT_SLOT_SCENE_TABS,
|
||||
CONTEXT_SLOT_2D_EDITOR,
|
||||
};
|
||||
static constexpr int BASE_ID = 2000;
|
||||
|
||||
private:
|
||||
int slot = -1;
|
||||
|
||||
public:
|
||||
struct ContextMenuItem {
|
||||
int id = 0;
|
||||
String item_name;
|
||||
Callable callable;
|
||||
Ref<Texture2D> icon;
|
||||
Ref<Shortcut> shortcut;
|
||||
PopupMenu *submenu = nullptr;
|
||||
};
|
||||
HashMap<String, ContextMenuItem> context_menu_items;
|
||||
HashMap<Ref<Shortcut>, Callable> context_menu_shortcuts;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
GDVIRTUAL1(_popup_menu, Vector<String>);
|
||||
|
||||
public:
|
||||
virtual void get_options(const Vector<String> &p_paths);
|
||||
|
||||
void add_menu_shortcut(const Ref<Shortcut> &p_shortcut, const Callable &p_callable);
|
||||
void add_context_menu_item(const String &p_name, const Callable &p_callable, const Ref<Texture2D> &p_texture);
|
||||
void add_context_menu_item_from_shortcut(const String &p_name, const Ref<Shortcut> &p_shortcut, const Ref<Texture2D> &p_texture);
|
||||
void add_context_submenu_item(const String &p_name, PopupMenu *p_menu, const Ref<Texture2D> &p_texture);
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(EditorContextMenuPlugin::ContextMenuSlot);
|
||||
|
||||
class EditorContextMenuPluginManager : public Object {
|
||||
GDCLASS(EditorContextMenuPluginManager, Object);
|
||||
|
||||
using ContextMenuSlot = EditorContextMenuPlugin::ContextMenuSlot;
|
||||
static inline EditorContextMenuPluginManager *singleton = nullptr;
|
||||
|
||||
LocalVector<Ref<EditorContextMenuPlugin>> plugin_list;
|
||||
|
||||
public:
|
||||
static EditorContextMenuPluginManager *get_singleton() { return singleton; }
|
||||
|
||||
void add_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin);
|
||||
void remove_plugin(const Ref<EditorContextMenuPlugin> &p_plugin);
|
||||
|
||||
bool has_plugins_for_slot(ContextMenuSlot p_slot);
|
||||
void add_options_from_plugins(PopupMenu *p_popup, ContextMenuSlot p_slot, const Vector<String> &p_paths);
|
||||
Callable match_custom_shortcut(ContextMenuSlot p_slot, const Ref<InputEvent> &p_event);
|
||||
bool activate_custom_option(ContextMenuSlot p_slot, int p_option, const Variant &p_arg);
|
||||
|
||||
void invoke_callback(const Callable &p_callback, const Variant &p_arg);
|
||||
|
||||
static void create();
|
||||
static void cleanup();
|
||||
};
|
||||
|
|
@ -1,220 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* editor_debugger_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 "editor_debugger_plugin.h"
|
||||
|
||||
#include "editor/debugger/script_editor_debugger.h"
|
||||
|
||||
void EditorDebuggerSession::_breaked(bool p_really_did, bool p_can_debug, const String &p_message, bool p_has_stackdump) {
|
||||
if (p_really_did) {
|
||||
emit_signal(SNAME("breaked"), p_can_debug);
|
||||
} else {
|
||||
emit_signal(SNAME("continued"));
|
||||
}
|
||||
}
|
||||
|
||||
void EditorDebuggerSession::_started() {
|
||||
emit_signal(SNAME("started"));
|
||||
}
|
||||
|
||||
void EditorDebuggerSession::_stopped() {
|
||||
emit_signal(SNAME("stopped"));
|
||||
}
|
||||
|
||||
void EditorDebuggerSession::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("send_message", "message", "data"), &EditorDebuggerSession::send_message, DEFVAL(Array()));
|
||||
ClassDB::bind_method(D_METHOD("toggle_profiler", "profiler", "enable", "data"), &EditorDebuggerSession::toggle_profiler, DEFVAL(Array()));
|
||||
ClassDB::bind_method(D_METHOD("is_breaked"), &EditorDebuggerSession::is_breaked);
|
||||
ClassDB::bind_method(D_METHOD("is_debuggable"), &EditorDebuggerSession::is_debuggable);
|
||||
ClassDB::bind_method(D_METHOD("is_active"), &EditorDebuggerSession::is_active);
|
||||
ClassDB::bind_method(D_METHOD("add_session_tab", "control"), &EditorDebuggerSession::add_session_tab);
|
||||
ClassDB::bind_method(D_METHOD("remove_session_tab", "control"), &EditorDebuggerSession::remove_session_tab);
|
||||
ClassDB::bind_method(D_METHOD("set_breakpoint", "path", "line", "enabled"), &EditorDebuggerSession::set_breakpoint);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("started"));
|
||||
ADD_SIGNAL(MethodInfo("stopped"));
|
||||
ADD_SIGNAL(MethodInfo("breaked", PropertyInfo(Variant::BOOL, "can_debug")));
|
||||
ADD_SIGNAL(MethodInfo("continued"));
|
||||
}
|
||||
|
||||
void EditorDebuggerSession::add_session_tab(Control *p_tab) {
|
||||
ERR_FAIL_COND(!p_tab || !debugger);
|
||||
debugger->add_debugger_tab(p_tab);
|
||||
tabs.insert(p_tab);
|
||||
}
|
||||
|
||||
void EditorDebuggerSession::remove_session_tab(Control *p_tab) {
|
||||
ERR_FAIL_COND(!p_tab || !debugger);
|
||||
debugger->remove_debugger_tab(p_tab);
|
||||
tabs.erase(p_tab);
|
||||
}
|
||||
|
||||
void EditorDebuggerSession::send_message(const String &p_message, const Array &p_args) {
|
||||
ERR_FAIL_NULL_MSG(debugger, "Plugin is not attached to debugger.");
|
||||
debugger->send_message(p_message, p_args);
|
||||
}
|
||||
|
||||
void EditorDebuggerSession::toggle_profiler(const String &p_profiler, bool p_enable, const Array &p_data) {
|
||||
ERR_FAIL_NULL_MSG(debugger, "Plugin is not attached to debugger.");
|
||||
debugger->toggle_profiler(p_profiler, p_enable, p_data);
|
||||
}
|
||||
|
||||
bool EditorDebuggerSession::is_breaked() {
|
||||
ERR_FAIL_NULL_V_MSG(debugger, false, "Plugin is not attached to debugger.");
|
||||
return debugger->is_breaked();
|
||||
}
|
||||
|
||||
bool EditorDebuggerSession::is_debuggable() {
|
||||
ERR_FAIL_NULL_V_MSG(debugger, false, "Plugin is not attached to debugger.");
|
||||
return debugger->is_debuggable();
|
||||
}
|
||||
|
||||
bool EditorDebuggerSession::is_active() {
|
||||
ERR_FAIL_NULL_V_MSG(debugger, false, "Plugin is not attached to debugger.");
|
||||
return debugger->is_session_active();
|
||||
}
|
||||
|
||||
void EditorDebuggerSession::set_breakpoint(const String &p_path, int p_line, bool p_enabled) {
|
||||
ERR_FAIL_NULL_MSG(debugger, "Plugin is not attached to debugger.");
|
||||
debugger->set_breakpoint(p_path, p_line, p_enabled);
|
||||
}
|
||||
|
||||
void EditorDebuggerSession::detach_debugger() {
|
||||
if (!debugger) {
|
||||
return;
|
||||
}
|
||||
debugger->disconnect("started", callable_mp(this, &EditorDebuggerSession::_started));
|
||||
debugger->disconnect("stopped", callable_mp(this, &EditorDebuggerSession::_stopped));
|
||||
debugger->disconnect("breaked", callable_mp(this, &EditorDebuggerSession::_breaked));
|
||||
debugger->disconnect(SceneStringName(tree_exited), callable_mp(this, &EditorDebuggerSession::_debugger_gone_away));
|
||||
for (Control *tab : tabs) {
|
||||
debugger->remove_debugger_tab(tab);
|
||||
}
|
||||
tabs.clear();
|
||||
debugger = nullptr;
|
||||
}
|
||||
|
||||
void EditorDebuggerSession::_debugger_gone_away() {
|
||||
debugger = nullptr;
|
||||
tabs.clear();
|
||||
}
|
||||
|
||||
EditorDebuggerSession::EditorDebuggerSession(ScriptEditorDebugger *p_debugger) {
|
||||
ERR_FAIL_NULL(p_debugger);
|
||||
debugger = p_debugger;
|
||||
debugger->connect("started", callable_mp(this, &EditorDebuggerSession::_started));
|
||||
debugger->connect("stopped", callable_mp(this, &EditorDebuggerSession::_stopped));
|
||||
debugger->connect("breaked", callable_mp(this, &EditorDebuggerSession::_breaked));
|
||||
debugger->connect(SceneStringName(tree_exited), callable_mp(this, &EditorDebuggerSession::_debugger_gone_away), CONNECT_ONE_SHOT);
|
||||
}
|
||||
|
||||
EditorDebuggerSession::~EditorDebuggerSession() {
|
||||
detach_debugger();
|
||||
}
|
||||
|
||||
/// EditorDebuggerPlugin
|
||||
|
||||
EditorDebuggerPlugin::~EditorDebuggerPlugin() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void EditorDebuggerPlugin::clear() {
|
||||
for (Ref<EditorDebuggerSession> &session : sessions) {
|
||||
session->detach_debugger();
|
||||
}
|
||||
sessions.clear();
|
||||
}
|
||||
|
||||
void EditorDebuggerPlugin::create_session(ScriptEditorDebugger *p_debugger) {
|
||||
sessions.push_back(Ref<EditorDebuggerSession>(memnew(EditorDebuggerSession(p_debugger))));
|
||||
setup_session(sessions.size() - 1);
|
||||
}
|
||||
|
||||
void EditorDebuggerPlugin::setup_session(int p_idx) {
|
||||
GDVIRTUAL_CALL(_setup_session, p_idx);
|
||||
}
|
||||
|
||||
Ref<EditorDebuggerSession> EditorDebuggerPlugin::get_session(int p_idx) {
|
||||
ERR_FAIL_INDEX_V(p_idx, sessions.size(), nullptr);
|
||||
return sessions.get(p_idx);
|
||||
}
|
||||
|
||||
Array EditorDebuggerPlugin::get_sessions() {
|
||||
Array ret;
|
||||
for (const Ref<EditorDebuggerSession> &session : sessions) {
|
||||
ret.push_back(session);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool EditorDebuggerPlugin::has_capture(const String &p_message) const {
|
||||
bool ret = false;
|
||||
if (GDVIRTUAL_CALL(_has_capture, p_message, ret)) {
|
||||
return ret;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EditorDebuggerPlugin::capture(const String &p_message, const Array &p_data, int p_session_id) {
|
||||
bool ret = false;
|
||||
if (GDVIRTUAL_CALL(_capture, p_message, p_data, p_session_id, ret)) {
|
||||
return ret;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EditorDebuggerPlugin::goto_script_line(const Ref<Script> &p_script, int p_line) {
|
||||
GDVIRTUAL_CALL(_goto_script_line, p_script, p_line);
|
||||
}
|
||||
|
||||
void EditorDebuggerPlugin::breakpoints_cleared_in_tree() {
|
||||
GDVIRTUAL_CALL(_breakpoints_cleared_in_tree);
|
||||
}
|
||||
|
||||
void EditorDebuggerPlugin::breakpoint_set_in_tree(const Ref<Script> &p_script, int p_line, bool p_enabled) {
|
||||
GDVIRTUAL_CALL(_breakpoint_set_in_tree, p_script, p_line, p_enabled);
|
||||
}
|
||||
|
||||
void EditorDebuggerPlugin::_bind_methods() {
|
||||
GDVIRTUAL_BIND(_setup_session, "session_id");
|
||||
GDVIRTUAL_BIND(_has_capture, "capture");
|
||||
GDVIRTUAL_BIND(_capture, "message", "data", "session_id");
|
||||
GDVIRTUAL_BIND(_goto_script_line, "script", "line");
|
||||
GDVIRTUAL_BIND(_breakpoints_cleared_in_tree);
|
||||
GDVIRTUAL_BIND(_breakpoint_set_in_tree, "script", "line", "enabled");
|
||||
ClassDB::bind_method(D_METHOD("get_session", "id"), &EditorDebuggerPlugin::get_session);
|
||||
ClassDB::bind_method(D_METHOD("get_sessions"), &EditorDebuggerPlugin::get_sessions);
|
||||
}
|
||||
|
||||
EditorDebuggerPlugin::EditorDebuggerPlugin() {
|
||||
EditorDebuggerNode::get_singleton()->connect("goto_script_line", callable_mp(this, &EditorDebuggerPlugin::goto_script_line));
|
||||
EditorDebuggerNode::get_singleton()->connect("breakpoints_cleared_in_tree", callable_mp(this, &EditorDebuggerPlugin::breakpoints_cleared_in_tree));
|
||||
EditorDebuggerNode::get_singleton()->connect("breakpoint_set_in_tree", callable_mp(this, &EditorDebuggerPlugin::breakpoint_set_in_tree));
|
||||
}
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* editor_debugger_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
|
||||
class ScriptEditorDebugger;
|
||||
|
||||
class EditorDebuggerSession : public RefCounted {
|
||||
GDCLASS(EditorDebuggerSession, RefCounted);
|
||||
|
||||
private:
|
||||
HashSet<Control *> tabs;
|
||||
|
||||
ScriptEditorDebugger *debugger = nullptr;
|
||||
|
||||
void _breaked(bool p_really_did, bool p_can_debug, const String &p_message, bool p_has_stackdump);
|
||||
void _started();
|
||||
void _stopped();
|
||||
void _debugger_gone_away();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void detach_debugger();
|
||||
|
||||
void add_session_tab(Control *p_tab);
|
||||
void remove_session_tab(Control *p_tab);
|
||||
void send_message(const String &p_message, const Array &p_args = Array());
|
||||
void toggle_profiler(const String &p_profiler, bool p_enable, const Array &p_data = Array());
|
||||
bool is_breaked();
|
||||
bool is_debuggable();
|
||||
bool is_active();
|
||||
|
||||
void set_breakpoint(const String &p_path, int p_line, bool p_enabled);
|
||||
|
||||
EditorDebuggerSession(ScriptEditorDebugger *p_debugger);
|
||||
~EditorDebuggerSession();
|
||||
};
|
||||
|
||||
class EditorDebuggerPlugin : public RefCounted {
|
||||
GDCLASS(EditorDebuggerPlugin, RefCounted);
|
||||
|
||||
private:
|
||||
List<Ref<EditorDebuggerSession>> sessions;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void create_session(ScriptEditorDebugger *p_debugger);
|
||||
void clear();
|
||||
|
||||
virtual void setup_session(int p_idx);
|
||||
virtual bool capture(const String &p_message, const Array &p_data, int p_session);
|
||||
virtual bool has_capture(const String &p_capture) const;
|
||||
|
||||
Ref<EditorDebuggerSession> get_session(int p_session_id);
|
||||
Array get_sessions();
|
||||
|
||||
GDVIRTUAL3R(bool, _capture, const String &, const Array &, int);
|
||||
GDVIRTUAL1RC(bool, _has_capture, const String &);
|
||||
GDVIRTUAL1(_setup_session, int);
|
||||
|
||||
virtual void goto_script_line(const Ref<Script> &p_script, int p_line);
|
||||
virtual void breakpoints_cleared_in_tree();
|
||||
virtual void breakpoint_set_in_tree(const Ref<Script> &p_script, int p_line, bool p_enabled);
|
||||
|
||||
GDVIRTUAL2(_goto_script_line, const Ref<Script> &, int);
|
||||
GDVIRTUAL0(_breakpoints_cleared_in_tree);
|
||||
GDVIRTUAL3(_breakpoint_set_in_tree, const Ref<Script> &, int, bool);
|
||||
|
||||
EditorDebuggerPlugin();
|
||||
~EditorDebuggerPlugin();
|
||||
};
|
||||
|
|
@ -32,27 +32,27 @@
|
|||
#include "editor_plugin.compat.inc"
|
||||
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#include "editor/editor_dock_manager.h"
|
||||
#include "editor/editor_file_system.h"
|
||||
#include "editor/editor_inspector.h"
|
||||
#include "editor/debugger/editor_debugger_plugin.h"
|
||||
#include "editor/docks/editor_dock_manager.h"
|
||||
#include "editor/docks/inspector_dock.h"
|
||||
#include "editor/docks/scene_tree_dock.h"
|
||||
#include "editor/editor_interface.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_translation_parser.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/export/editor_export.h"
|
||||
#include "editor/export/editor_export_platform.h"
|
||||
#include "editor/file_system/editor_file_system.h"
|
||||
#include "editor/gui/editor_bottom_panel.h"
|
||||
#include "editor/gui/editor_title_bar.h"
|
||||
#include "editor/import/3d/resource_importer_scene.h"
|
||||
#include "editor/import/editor_import_plugin.h"
|
||||
#include "editor/inspector_dock.h"
|
||||
#include "editor/plugins/canvas_item_editor_plugin.h"
|
||||
#include "editor/plugins/editor_debugger_plugin.h"
|
||||
#include "editor/inspector/editor_inspector.h"
|
||||
#include "editor/plugins/editor_resource_conversion_plugin.h"
|
||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "editor/project_settings_editor.h"
|
||||
#include "editor/scene_tree_dock.h"
|
||||
#include "editor/scene/3d/node_3d_editor_plugin.h"
|
||||
#include "editor/scene/canvas_item_editor_plugin.h"
|
||||
#include "editor/script/script_editor_plugin.h"
|
||||
#include "editor/settings/project_settings_editor.h"
|
||||
#include "editor/translations/editor_translation_parser.h"
|
||||
#include "scene/3d/camera_3d.h"
|
||||
#include "scene/gui/popup_menu.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/io/config_file.h"
|
||||
#include "editor/plugins/editor_context_menu_plugin.h"
|
||||
#include "editor/inspector/editor_context_menu_plugin.h"
|
||||
#include "scene/3d/camera_3d.h"
|
||||
#include "scene/gui/control.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,933 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* editor_preview_plugins.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 "editor_preview_plugins.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/object/script_language.h"
|
||||
#include "editor/editor_paths.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/resources/atlas_texture.h"
|
||||
#include "scene/resources/bit_map.h"
|
||||
#include "scene/resources/font.h"
|
||||
#include "scene/resources/gradient_texture.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
#include "scene/resources/material.h"
|
||||
#include "scene/resources/mesh.h"
|
||||
#include "servers/audio/audio_stream.h"
|
||||
|
||||
void post_process_preview(Ref<Image> p_image) {
|
||||
if (p_image->get_format() != Image::FORMAT_RGBA8) {
|
||||
p_image->convert(Image::FORMAT_RGBA8);
|
||||
}
|
||||
|
||||
const int w = p_image->get_width();
|
||||
const int h = p_image->get_height();
|
||||
|
||||
const int r = MIN(w, h) / 32;
|
||||
const int r2 = r * r;
|
||||
Color transparent = Color(0, 0, 0, 0);
|
||||
|
||||
for (int i = 0; i < r; i++) {
|
||||
for (int j = 0; j < r; j++) {
|
||||
int dx = i - r;
|
||||
int dy = j - r;
|
||||
if (dx * dx + dy * dy > r2) {
|
||||
p_image->set_pixel(i, j, transparent);
|
||||
p_image->set_pixel(w - 1 - i, j, transparent);
|
||||
p_image->set_pixel(w - 1 - i, h - 1 - j, transparent);
|
||||
p_image->set_pixel(i, h - 1 - j, transparent);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorTexturePreviewPlugin::handles(const String &p_type) const {
|
||||
return ClassDB::is_parent_class(p_type, "Texture");
|
||||
}
|
||||
|
||||
bool EditorTexturePreviewPlugin::generate_small_preview_automatically() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
|
||||
Ref<Image> img;
|
||||
|
||||
Ref<AtlasTexture> tex_atlas = p_from;
|
||||
Ref<Texture3D> tex_3d = p_from;
|
||||
Ref<TextureLayered> tex_lyr = p_from;
|
||||
|
||||
if (tex_atlas.is_valid()) {
|
||||
Ref<Texture2D> tex = tex_atlas->get_atlas();
|
||||
if (tex.is_null()) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
||||
Ref<Image> atlas = tex->get_image();
|
||||
if (atlas.is_null()) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
||||
if (atlas->is_compressed()) {
|
||||
atlas = atlas->duplicate();
|
||||
if (atlas->decompress() != OK) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
}
|
||||
|
||||
if (!tex_atlas->get_region().has_area()) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
||||
img = atlas->get_region(tex_atlas->get_region());
|
||||
|
||||
} else if (tex_3d.is_valid()) {
|
||||
if (tex_3d->get_depth() == 0) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
||||
Vector<Ref<Image>> data = tex_3d->get_data();
|
||||
if (data.size() != tex_3d->get_depth()) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
||||
// Use the middle slice for the thumbnail.
|
||||
const int mid_depth = (tex_3d->get_depth() - 1) / 2;
|
||||
if (!data.is_empty() && data[mid_depth].is_valid()) {
|
||||
img = data[mid_depth]->duplicate();
|
||||
}
|
||||
|
||||
} else if (tex_lyr.is_valid()) {
|
||||
if (tex_lyr->get_layers() == 0) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
||||
// Use the middle slice for the thumbnail.
|
||||
const int mid_layer = (tex_lyr->get_layers() - 1) / 2;
|
||||
|
||||
Ref<Image> data = tex_lyr->get_layer_data(mid_layer);
|
||||
if (data.is_valid()) {
|
||||
img = data->duplicate();
|
||||
}
|
||||
|
||||
} else {
|
||||
Ref<Texture2D> tex = p_from;
|
||||
if (tex.is_valid()) {
|
||||
img = tex->get_image();
|
||||
if (img.is_valid()) {
|
||||
img = img->duplicate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (img.is_null() || img->is_empty()) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
||||
p_metadata["dimensions"] = img->get_size();
|
||||
|
||||
img->clear_mipmaps();
|
||||
|
||||
if (img->is_compressed()) {
|
||||
if (img->decompress() != OK) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
} else if (img->get_format() != Image::FORMAT_RGB8 && img->get_format() != Image::FORMAT_RGBA8) {
|
||||
img->convert(Image::FORMAT_RGBA8);
|
||||
}
|
||||
|
||||
Vector2 new_size = img->get_size();
|
||||
if (new_size.x > p_size.x) {
|
||||
new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x);
|
||||
}
|
||||
if (new_size.y > p_size.y) {
|
||||
new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y);
|
||||
}
|
||||
Vector2i new_size_i = Vector2i(new_size).maxi(1);
|
||||
img->resize(new_size_i.x, new_size_i.y, Image::INTERPOLATE_CUBIC);
|
||||
post_process_preview(img);
|
||||
|
||||
return ImageTexture::create_from_image(img);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool EditorImagePreviewPlugin::handles(const String &p_type) const {
|
||||
return p_type == "Image";
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorImagePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
|
||||
Ref<Image> img = p_from;
|
||||
|
||||
if (img.is_null() || img->is_empty()) {
|
||||
return Ref<Image>();
|
||||
}
|
||||
|
||||
img = img->duplicate();
|
||||
img->clear_mipmaps();
|
||||
|
||||
if (img->is_compressed()) {
|
||||
if (img->decompress() != OK) {
|
||||
return Ref<Image>();
|
||||
}
|
||||
} else if (img->get_format() != Image::FORMAT_RGB8 && img->get_format() != Image::FORMAT_RGBA8) {
|
||||
img->convert(Image::FORMAT_RGBA8);
|
||||
}
|
||||
|
||||
Vector2 new_size = img->get_size();
|
||||
if (new_size.x > p_size.x) {
|
||||
new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x);
|
||||
}
|
||||
if (new_size.y > p_size.y) {
|
||||
new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y);
|
||||
}
|
||||
img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC);
|
||||
post_process_preview(img);
|
||||
|
||||
return ImageTexture::create_from_image(img);
|
||||
}
|
||||
|
||||
bool EditorImagePreviewPlugin::generate_small_preview_automatically() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool EditorBitmapPreviewPlugin::handles(const String &p_type) const {
|
||||
return ClassDB::is_parent_class(p_type, "BitMap");
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorBitmapPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
|
||||
Ref<BitMap> bm = p_from;
|
||||
|
||||
if (bm->get_size() == Size2()) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
||||
Vector<uint8_t> data;
|
||||
|
||||
data.resize(bm->get_size().width * bm->get_size().height);
|
||||
|
||||
{
|
||||
uint8_t *w = data.ptrw();
|
||||
|
||||
for (int i = 0; i < bm->get_size().width; i++) {
|
||||
for (int j = 0; j < bm->get_size().height; j++) {
|
||||
if (bm->get_bit(i, j)) {
|
||||
w[j * (int)bm->get_size().width + i] = 255;
|
||||
} else {
|
||||
w[j * (int)bm->get_size().width + i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Image> img = Image::create_from_data(bm->get_size().width, bm->get_size().height, false, Image::FORMAT_L8, data);
|
||||
|
||||
if (img->is_compressed()) {
|
||||
if (img->decompress() != OK) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
} else if (img->get_format() != Image::FORMAT_RGB8 && img->get_format() != Image::FORMAT_RGBA8) {
|
||||
img->convert(Image::FORMAT_RGBA8);
|
||||
}
|
||||
|
||||
Vector2 new_size = img->get_size();
|
||||
if (new_size.x > p_size.x) {
|
||||
new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x);
|
||||
}
|
||||
if (new_size.y > p_size.y) {
|
||||
new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y);
|
||||
}
|
||||
img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC);
|
||||
post_process_preview(img);
|
||||
|
||||
return ImageTexture::create_from_image(img);
|
||||
}
|
||||
|
||||
bool EditorBitmapPreviewPlugin::generate_small_preview_automatically() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool EditorPackedScenePreviewPlugin::handles(const String &p_type) const {
|
||||
return ClassDB::is_parent_class(p_type, "PackedScene");
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorPackedScenePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
|
||||
return generate_from_path(p_from->get_path(), p_size, p_metadata);
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const {
|
||||
String temp_path = EditorPaths::get_singleton()->get_cache_dir();
|
||||
String cache_base = ProjectSettings::get_singleton()->globalize_path(p_path).md5_text();
|
||||
cache_base = temp_path.path_join("resthumb-" + cache_base);
|
||||
|
||||
//does not have it, try to load a cached thumbnail
|
||||
|
||||
String path = cache_base + ".png";
|
||||
|
||||
if (!FileAccess::exists(path)) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
||||
Ref<Image> img;
|
||||
img.instantiate();
|
||||
Error err = img->load(path);
|
||||
if (err == OK) {
|
||||
post_process_preview(img);
|
||||
return ImageTexture::create_from_image(img);
|
||||
|
||||
} else {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
void EditorMaterialPreviewPlugin::abort() {
|
||||
draw_requester.abort();
|
||||
}
|
||||
|
||||
bool EditorMaterialPreviewPlugin::handles(const String &p_type) const {
|
||||
return ClassDB::is_parent_class(p_type, "Material"); // Any material.
|
||||
}
|
||||
|
||||
bool EditorMaterialPreviewPlugin::generate_small_preview_automatically() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorMaterialPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
|
||||
Ref<Material> material = p_from;
|
||||
ERR_FAIL_COND_V(material.is_null(), Ref<Texture2D>());
|
||||
|
||||
if (material->get_shader_mode() == Shader::MODE_SPATIAL) {
|
||||
RS::get_singleton()->mesh_surface_set_material(sphere, 0, material->get_rid());
|
||||
|
||||
draw_requester.request_and_wait(viewport);
|
||||
|
||||
Ref<Image> img = RS::get_singleton()->texture_2d_get(viewport_texture);
|
||||
RS::get_singleton()->mesh_surface_set_material(sphere, 0, RID());
|
||||
|
||||
ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>());
|
||||
|
||||
img->convert(Image::FORMAT_RGBA8);
|
||||
int thumbnail_size = MAX(p_size.x, p_size.y);
|
||||
img->resize(thumbnail_size, thumbnail_size, Image::INTERPOLATE_CUBIC);
|
||||
post_process_preview(img);
|
||||
return ImageTexture::create_from_image(img);
|
||||
}
|
||||
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
||||
EditorMaterialPreviewPlugin::EditorMaterialPreviewPlugin() {
|
||||
scenario = RS::get_singleton()->scenario_create();
|
||||
|
||||
viewport = RS::get_singleton()->viewport_create();
|
||||
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_DISABLED);
|
||||
RS::get_singleton()->viewport_set_scenario(viewport, scenario);
|
||||
RS::get_singleton()->viewport_set_size(viewport, 128, 128);
|
||||
RS::get_singleton()->viewport_set_transparent_background(viewport, true);
|
||||
RS::get_singleton()->viewport_set_active(viewport, true);
|
||||
viewport_texture = RS::get_singleton()->viewport_get_texture(viewport);
|
||||
|
||||
camera = RS::get_singleton()->camera_create();
|
||||
RS::get_singleton()->viewport_attach_camera(viewport, camera);
|
||||
RS::get_singleton()->camera_set_transform(camera, Transform3D(Basis(), Vector3(0, 0, 3)));
|
||||
RS::get_singleton()->camera_set_perspective(camera, 45, 0.1, 10);
|
||||
|
||||
if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) {
|
||||
camera_attributes = RS::get_singleton()->camera_attributes_create();
|
||||
RS::get_singleton()->camera_attributes_set_exposure(camera_attributes, 1.0, 0.000032552); // Matches default CameraAttributesPhysical to work well with default DirectionalLight3Ds.
|
||||
RS::get_singleton()->camera_set_camera_attributes(camera, camera_attributes);
|
||||
}
|
||||
|
||||
light = RS::get_singleton()->directional_light_create();
|
||||
light_instance = RS::get_singleton()->instance_create2(light, scenario);
|
||||
RS::get_singleton()->instance_set_transform(light_instance, Transform3D().looking_at(Vector3(-1, -1, -1), Vector3(0, 1, 0)));
|
||||
|
||||
light2 = RS::get_singleton()->directional_light_create();
|
||||
RS::get_singleton()->light_set_color(light2, Color(0.7, 0.7, 0.7));
|
||||
//RS::get_singleton()->light_set_color(light2, Color(0.7, 0.7, 0.7));
|
||||
|
||||
light_instance2 = RS::get_singleton()->instance_create2(light2, scenario);
|
||||
|
||||
RS::get_singleton()->instance_set_transform(light_instance2, Transform3D().looking_at(Vector3(0, 1, 0), Vector3(0, 0, 1)));
|
||||
|
||||
sphere = RS::get_singleton()->mesh_create();
|
||||
sphere_instance = RS::get_singleton()->instance_create2(sphere, scenario);
|
||||
|
||||
int lats = 32;
|
||||
int lons = 32;
|
||||
const double lat_step = Math::TAU / lats;
|
||||
const double lon_step = Math::TAU / lons;
|
||||
real_t radius = 1.0;
|
||||
|
||||
Vector<Vector3> vertices;
|
||||
Vector<Vector3> normals;
|
||||
Vector<Vector2> uvs;
|
||||
Vector<real_t> tangents;
|
||||
Basis tt = Basis(Vector3(0, 1, 0), Math::PI * 0.5);
|
||||
|
||||
for (int i = 1; i <= lats; i++) {
|
||||
double lat0 = lat_step * (i - 1) - Math::TAU / 4;
|
||||
double z0 = Math::sin(lat0);
|
||||
double zr0 = Math::cos(lat0);
|
||||
|
||||
double lat1 = lat_step * i - Math::TAU / 4;
|
||||
double z1 = Math::sin(lat1);
|
||||
double zr1 = Math::cos(lat1);
|
||||
|
||||
for (int j = lons; j >= 1; j--) {
|
||||
double lng0 = lon_step * (j - 1);
|
||||
double x0 = Math::cos(lng0);
|
||||
double y0 = Math::sin(lng0);
|
||||
|
||||
double lng1 = lon_step * j;
|
||||
double x1 = Math::cos(lng1);
|
||||
double y1 = Math::sin(lng1);
|
||||
|
||||
Vector3 v[4] = {
|
||||
Vector3(x1 * zr0, z0, y1 * zr0),
|
||||
Vector3(x1 * zr1, z1, y1 * zr1),
|
||||
Vector3(x0 * zr1, z1, y0 * zr1),
|
||||
Vector3(x0 * zr0, z0, y0 * zr0)
|
||||
};
|
||||
|
||||
#define ADD_POINT(m_idx) \
|
||||
normals.push_back(v[m_idx]); \
|
||||
vertices.push_back(v[m_idx] * radius); \
|
||||
{ \
|
||||
Vector2 uv(Math::atan2(v[m_idx].x, v[m_idx].z), Math::atan2(-v[m_idx].y, v[m_idx].z)); \
|
||||
uv /= Math::PI; \
|
||||
uv *= 4.0; \
|
||||
uv = uv * 0.5 + Vector2(0.5, 0.5); \
|
||||
uvs.push_back(uv); \
|
||||
} \
|
||||
{ \
|
||||
Vector3 t = tt.xform(v[m_idx]); \
|
||||
tangents.push_back(t.x); \
|
||||
tangents.push_back(t.y); \
|
||||
tangents.push_back(t.z); \
|
||||
tangents.push_back(1.0); \
|
||||
}
|
||||
|
||||
ADD_POINT(0);
|
||||
ADD_POINT(1);
|
||||
ADD_POINT(2);
|
||||
|
||||
ADD_POINT(2);
|
||||
ADD_POINT(3);
|
||||
ADD_POINT(0);
|
||||
}
|
||||
}
|
||||
|
||||
Array arr;
|
||||
arr.resize(RS::ARRAY_MAX);
|
||||
arr[RS::ARRAY_VERTEX] = vertices;
|
||||
arr[RS::ARRAY_NORMAL] = normals;
|
||||
arr[RS::ARRAY_TANGENT] = tangents;
|
||||
arr[RS::ARRAY_TEX_UV] = uvs;
|
||||
RS::get_singleton()->mesh_add_surface_from_arrays(sphere, RS::PRIMITIVE_TRIANGLES, arr);
|
||||
}
|
||||
|
||||
EditorMaterialPreviewPlugin::~EditorMaterialPreviewPlugin() {
|
||||
ERR_FAIL_NULL(RenderingServer::get_singleton());
|
||||
RS::get_singleton()->free(sphere);
|
||||
RS::get_singleton()->free(sphere_instance);
|
||||
RS::get_singleton()->free(viewport);
|
||||
RS::get_singleton()->free(light);
|
||||
RS::get_singleton()->free(light_instance);
|
||||
RS::get_singleton()->free(light2);
|
||||
RS::get_singleton()->free(light_instance2);
|
||||
RS::get_singleton()->free(camera);
|
||||
RS::get_singleton()->free(camera_attributes);
|
||||
RS::get_singleton()->free(scenario);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool EditorScriptPreviewPlugin::handles(const String &p_type) const {
|
||||
return ClassDB::is_parent_class(p_type, "Script");
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorScriptPreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const {
|
||||
Error err;
|
||||
String code = FileAccess::get_file_as_string(p_path, &err);
|
||||
if (err != OK) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
||||
ScriptLanguage *lang = ScriptServer::get_language_for_extension(p_path.get_extension());
|
||||
return _generate_from_source_code(lang, code, p_size, p_metadata);
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
|
||||
Ref<Script> scr = p_from;
|
||||
if (scr.is_null()) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
||||
String code = scr->get_source_code().strip_edges();
|
||||
return _generate_from_source_code(scr->get_language(), code, p_size, p_metadata);
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorScriptPreviewPlugin::_generate_from_source_code(const ScriptLanguage *p_language, const String &p_source_code, const Size2 &p_size, Dictionary &p_metadata) const {
|
||||
if (p_source_code.is_empty()) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
||||
HashSet<String> control_flow_keywords;
|
||||
HashSet<String> keywords;
|
||||
|
||||
if (p_language) {
|
||||
for (const String &keyword : p_language->get_reserved_words()) {
|
||||
if (p_language->is_control_flow_keyword(keyword)) {
|
||||
control_flow_keywords.insert(keyword);
|
||||
} else {
|
||||
keywords.insert(keyword);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int line = 0;
|
||||
int col = 0;
|
||||
int thumbnail_size = MAX(p_size.x, p_size.y);
|
||||
Ref<Image> img = Image::create_empty(thumbnail_size, thumbnail_size, false, Image::FORMAT_RGBA8);
|
||||
|
||||
Color bg_color = EDITOR_GET("text_editor/theme/highlighting/background_color");
|
||||
Color keyword_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color");
|
||||
Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color");
|
||||
Color text_color = EDITOR_GET("text_editor/theme/highlighting/text_color");
|
||||
Color symbol_color = EDITOR_GET("text_editor/theme/highlighting/symbol_color");
|
||||
Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color");
|
||||
Color doc_comment_color = EDITOR_GET("text_editor/theme/highlighting/doc_comment_color");
|
||||
|
||||
if (bg_color.a == 0) {
|
||||
bg_color = Color(0, 0, 0, 0);
|
||||
}
|
||||
bg_color.a = MAX(bg_color.a, 0.2); // Ensure we have some background, regardless of the text editor setting.
|
||||
|
||||
img->fill(bg_color);
|
||||
|
||||
const int x0 = thumbnail_size / 8;
|
||||
const int y0 = thumbnail_size / 8;
|
||||
const int available_height = thumbnail_size - 2 * y0;
|
||||
col = x0;
|
||||
|
||||
bool prev_is_text = false;
|
||||
bool in_control_flow_keyword = false;
|
||||
bool in_keyword = false;
|
||||
bool in_comment = false;
|
||||
bool in_doc_comment = false;
|
||||
for (int i = 0; i < p_source_code.length(); i++) {
|
||||
char32_t c = p_source_code[i];
|
||||
if (c > 32) {
|
||||
if (col < thumbnail_size) {
|
||||
Color color = text_color;
|
||||
|
||||
if (c == '#') {
|
||||
if (i < p_source_code.length() - 1 && p_source_code[i + 1] == '#') {
|
||||
in_doc_comment = true;
|
||||
} else {
|
||||
in_comment = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_comment) {
|
||||
color = comment_color;
|
||||
} else if (in_doc_comment) {
|
||||
color = doc_comment_color;
|
||||
} else {
|
||||
if (is_symbol(c)) {
|
||||
// Make symbol a little visible.
|
||||
color = symbol_color;
|
||||
in_control_flow_keyword = false;
|
||||
in_keyword = false;
|
||||
} else if (!prev_is_text && is_ascii_identifier_char(c)) {
|
||||
int pos = i;
|
||||
|
||||
while (is_ascii_identifier_char(p_source_code[pos])) {
|
||||
pos++;
|
||||
}
|
||||
String word = p_source_code.substr(i, pos - i);
|
||||
if (control_flow_keywords.has(word)) {
|
||||
in_control_flow_keyword = true;
|
||||
} else if (keywords.has(word)) {
|
||||
in_keyword = true;
|
||||
}
|
||||
|
||||
} else if (!is_ascii_identifier_char(c)) {
|
||||
in_keyword = false;
|
||||
}
|
||||
|
||||
if (in_control_flow_keyword) {
|
||||
color = control_flow_keyword_color;
|
||||
} else if (in_keyword) {
|
||||
color = keyword_color;
|
||||
}
|
||||
}
|
||||
Color ul = color;
|
||||
ul.a *= 0.5;
|
||||
img->set_pixel(col, y0 + line * 2, bg_color.blend(ul));
|
||||
img->set_pixel(col, y0 + line * 2 + 1, color);
|
||||
|
||||
prev_is_text = is_ascii_identifier_char(c);
|
||||
}
|
||||
col++;
|
||||
} else {
|
||||
prev_is_text = false;
|
||||
in_control_flow_keyword = false;
|
||||
in_keyword = false;
|
||||
|
||||
if (c == '\n') {
|
||||
in_comment = false;
|
||||
in_doc_comment = false;
|
||||
|
||||
col = x0;
|
||||
line++;
|
||||
if (line >= available_height / 2) {
|
||||
break;
|
||||
}
|
||||
} else if (c == '\t') {
|
||||
col += 3;
|
||||
} else {
|
||||
col++;
|
||||
}
|
||||
}
|
||||
}
|
||||
post_process_preview(img);
|
||||
return ImageTexture::create_from_image(img);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
bool EditorAudioStreamPreviewPlugin::handles(const String &p_type) const {
|
||||
return ClassDB::is_parent_class(p_type, "AudioStream");
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorAudioStreamPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
|
||||
Ref<AudioStream> stream = p_from;
|
||||
ERR_FAIL_COND_V(stream.is_null(), Ref<Texture2D>());
|
||||
|
||||
Vector<uint8_t> img;
|
||||
|
||||
int w = p_size.x;
|
||||
int h = p_size.y;
|
||||
img.resize(w * h * 3);
|
||||
|
||||
uint8_t *imgdata = img.ptrw();
|
||||
uint8_t *imgw = imgdata;
|
||||
|
||||
Ref<AudioStreamPlayback> playback = stream->instantiate_playback();
|
||||
ERR_FAIL_COND_V(playback.is_null(), Ref<Texture2D>());
|
||||
|
||||
real_t len_s = stream->get_length();
|
||||
if (len_s == 0) {
|
||||
len_s = 60; //one minute audio if no length specified
|
||||
}
|
||||
int frame_length = AudioServer::get_singleton()->get_mix_rate() * len_s;
|
||||
|
||||
Vector<AudioFrame> frames;
|
||||
frames.resize(frame_length);
|
||||
|
||||
playback->start();
|
||||
playback->mix(frames.ptrw(), 1, frames.size());
|
||||
playback->stop();
|
||||
|
||||
for (int i = 0; i < w; i++) {
|
||||
real_t max = -1000;
|
||||
real_t min = 1000;
|
||||
int from = uint64_t(i) * frame_length / w;
|
||||
int to = (uint64_t(i) + 1) * frame_length / w;
|
||||
to = MIN(to, frame_length);
|
||||
from = MIN(from, frame_length - 1);
|
||||
if (to == from) {
|
||||
to = from + 1;
|
||||
}
|
||||
|
||||
for (int j = from; j < to; j++) {
|
||||
max = MAX(max, frames[j].left);
|
||||
max = MAX(max, frames[j].right);
|
||||
|
||||
min = MIN(min, frames[j].left);
|
||||
min = MIN(min, frames[j].right);
|
||||
}
|
||||
|
||||
int pfrom = CLAMP((min * 0.5 + 0.5) * h / 2, 0, h / 2) + h / 4;
|
||||
int pto = CLAMP((max * 0.5 + 0.5) * h / 2, 0, h / 2) + h / 4;
|
||||
|
||||
for (int j = 0; j < h; j++) {
|
||||
uint8_t *p = &imgw[(j * w + i) * 3];
|
||||
if (j < pfrom || j > pto) {
|
||||
p[0] = 100;
|
||||
p[1] = 100;
|
||||
p[2] = 100;
|
||||
} else {
|
||||
p[0] = 180;
|
||||
p[1] = 180;
|
||||
p[2] = 180;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p_metadata["length"] = stream->get_length();
|
||||
|
||||
//post_process_preview(img);
|
||||
|
||||
Ref<Image> image = Image::create_from_data(w, h, false, Image::FORMAT_RGB8, img);
|
||||
return ImageTexture::create_from_image(image);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EditorMeshPreviewPlugin::abort() {
|
||||
draw_requester.abort();
|
||||
}
|
||||
|
||||
bool EditorMeshPreviewPlugin::handles(const String &p_type) const {
|
||||
return ClassDB::is_parent_class(p_type, "Mesh"); // Any mesh.
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorMeshPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
|
||||
Ref<Mesh> mesh = p_from;
|
||||
ERR_FAIL_COND_V(mesh.is_null(), Ref<Texture2D>());
|
||||
|
||||
RS::get_singleton()->instance_set_base(mesh_instance, mesh->get_rid());
|
||||
|
||||
AABB aabb = mesh->get_aabb();
|
||||
Vector3 ofs = aabb.get_center();
|
||||
aabb.position -= ofs;
|
||||
Transform3D xform;
|
||||
xform.basis = Basis().rotated(Vector3(0, 1, 0), -Math::PI * 0.125);
|
||||
xform.basis = Basis().rotated(Vector3(1, 0, 0), Math::PI * 0.125) * xform.basis;
|
||||
AABB rot_aabb = xform.xform(aabb);
|
||||
real_t m = MAX(rot_aabb.size.x, rot_aabb.size.y) * 0.5;
|
||||
if (m == 0) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
m = 1.0 / m;
|
||||
m *= 0.5;
|
||||
xform.basis.scale(Vector3(m, m, m));
|
||||
xform.origin = -xform.basis.xform(ofs); //-ofs*m;
|
||||
xform.origin.z -= rot_aabb.size.z * 2;
|
||||
RS::get_singleton()->instance_set_transform(mesh_instance, xform);
|
||||
|
||||
draw_requester.request_and_wait(viewport);
|
||||
|
||||
Ref<Image> img = RS::get_singleton()->texture_2d_get(viewport_texture);
|
||||
ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>());
|
||||
|
||||
RS::get_singleton()->instance_set_base(mesh_instance, RID());
|
||||
|
||||
img->convert(Image::FORMAT_RGBA8);
|
||||
|
||||
Vector2 new_size = img->get_size();
|
||||
if (new_size.x > p_size.x) {
|
||||
new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x);
|
||||
}
|
||||
if (new_size.y > p_size.y) {
|
||||
new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y);
|
||||
}
|
||||
img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC);
|
||||
post_process_preview(img);
|
||||
|
||||
return ImageTexture::create_from_image(img);
|
||||
}
|
||||
|
||||
EditorMeshPreviewPlugin::EditorMeshPreviewPlugin() {
|
||||
scenario = RS::get_singleton()->scenario_create();
|
||||
|
||||
viewport = RS::get_singleton()->viewport_create();
|
||||
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_DISABLED);
|
||||
RS::get_singleton()->viewport_set_scenario(viewport, scenario);
|
||||
RS::get_singleton()->viewport_set_size(viewport, 128, 128);
|
||||
RS::get_singleton()->viewport_set_transparent_background(viewport, true);
|
||||
RS::get_singleton()->viewport_set_active(viewport, true);
|
||||
viewport_texture = RS::get_singleton()->viewport_get_texture(viewport);
|
||||
|
||||
camera = RS::get_singleton()->camera_create();
|
||||
RS::get_singleton()->viewport_attach_camera(viewport, camera);
|
||||
RS::get_singleton()->camera_set_transform(camera, Transform3D(Basis(), Vector3(0, 0, 3)));
|
||||
//RS::get_singleton()->camera_set_perspective(camera,45,0.1,10);
|
||||
RS::get_singleton()->camera_set_orthogonal(camera, 1.0, 0.01, 1000.0);
|
||||
|
||||
if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) {
|
||||
camera_attributes = RS::get_singleton()->camera_attributes_create();
|
||||
RS::get_singleton()->camera_attributes_set_exposure(camera_attributes, 1.0, 0.000032552); // Matches default CameraAttributesPhysical to work well with default DirectionalLight3Ds.
|
||||
RS::get_singleton()->camera_set_camera_attributes(camera, camera_attributes);
|
||||
}
|
||||
|
||||
light = RS::get_singleton()->directional_light_create();
|
||||
light_instance = RS::get_singleton()->instance_create2(light, scenario);
|
||||
RS::get_singleton()->instance_set_transform(light_instance, Transform3D().looking_at(Vector3(-1, -1, -1), Vector3(0, 1, 0)));
|
||||
|
||||
light2 = RS::get_singleton()->directional_light_create();
|
||||
RS::get_singleton()->light_set_color(light2, Color(0.7, 0.7, 0.7));
|
||||
//RS::get_singleton()->light_set_color(light2, RS::LIGHT_COLOR_SPECULAR, Color(0.0, 0.0, 0.0));
|
||||
light_instance2 = RS::get_singleton()->instance_create2(light2, scenario);
|
||||
|
||||
RS::get_singleton()->instance_set_transform(light_instance2, Transform3D().looking_at(Vector3(0, 1, 0), Vector3(0, 0, 1)));
|
||||
|
||||
//sphere = RS::get_singleton()->mesh_create();
|
||||
mesh_instance = RS::get_singleton()->instance_create();
|
||||
RS::get_singleton()->instance_set_scenario(mesh_instance, scenario);
|
||||
}
|
||||
|
||||
EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() {
|
||||
ERR_FAIL_NULL(RenderingServer::get_singleton());
|
||||
//RS::get_singleton()->free(sphere);
|
||||
RS::get_singleton()->free(mesh_instance);
|
||||
RS::get_singleton()->free(viewport);
|
||||
RS::get_singleton()->free(light);
|
||||
RS::get_singleton()->free(light_instance);
|
||||
RS::get_singleton()->free(light2);
|
||||
RS::get_singleton()->free(light_instance2);
|
||||
RS::get_singleton()->free(camera);
|
||||
RS::get_singleton()->free(camera_attributes);
|
||||
RS::get_singleton()->free(scenario);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EditorFontPreviewPlugin::abort() {
|
||||
draw_requester.abort();
|
||||
}
|
||||
|
||||
bool EditorFontPreviewPlugin::handles(const String &p_type) const {
|
||||
return ClassDB::is_parent_class(p_type, "Font");
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const {
|
||||
Ref<Font> sampled_font = ResourceLoader::load(p_path);
|
||||
ERR_FAIL_COND_V(sampled_font.is_null(), Ref<Texture2D>());
|
||||
|
||||
String sample;
|
||||
static const String sample_base = U"12漢字ԱբΑαАбΑαאבابܐܒހށआআਆઆଆஆఆಆആආกิກິༀကႠა한글ሀᎣᐁᚁᚠᜀᜠᝀᝠកᠠᤁᥐAb😀";
|
||||
for (int i = 0; i < sample_base.length(); i++) {
|
||||
if (sampled_font->has_char(sample_base[i])) {
|
||||
sample += sample_base[i];
|
||||
}
|
||||
}
|
||||
if (sample.is_empty()) {
|
||||
sample = sampled_font->get_supported_chars().substr(0, 6);
|
||||
}
|
||||
Vector2 size = sampled_font->get_string_size(sample, HORIZONTAL_ALIGNMENT_LEFT, -1, 50);
|
||||
|
||||
Vector2 pos;
|
||||
|
||||
pos.x = 64 - size.x / 2;
|
||||
pos.y = 80;
|
||||
|
||||
const Color c = GLOBAL_GET("rendering/environment/defaults/default_clear_color");
|
||||
const float fg = c.get_luminance() < 0.5 ? 1.0 : 0.0;
|
||||
sampled_font->draw_string(canvas_item, pos, sample, HORIZONTAL_ALIGNMENT_LEFT, -1.f, 50, Color(fg, fg, fg));
|
||||
|
||||
draw_requester.request_and_wait(viewport);
|
||||
|
||||
RS::get_singleton()->canvas_item_clear(canvas_item);
|
||||
|
||||
Ref<Image> img = RS::get_singleton()->texture_2d_get(viewport_texture);
|
||||
ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>());
|
||||
|
||||
img->convert(Image::FORMAT_RGBA8);
|
||||
|
||||
Vector2 new_size = img->get_size();
|
||||
if (new_size.x > p_size.x) {
|
||||
new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x);
|
||||
}
|
||||
if (new_size.y > p_size.y) {
|
||||
new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y);
|
||||
}
|
||||
img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC);
|
||||
post_process_preview(img);
|
||||
|
||||
return ImageTexture::create_from_image(img);
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorFontPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
|
||||
String path = p_from->get_path();
|
||||
if (!FileAccess::exists(path)) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
return generate_from_path(path, p_size, p_metadata);
|
||||
}
|
||||
|
||||
EditorFontPreviewPlugin::EditorFontPreviewPlugin() {
|
||||
viewport = RS::get_singleton()->viewport_create();
|
||||
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_DISABLED);
|
||||
RS::get_singleton()->viewport_set_size(viewport, 128, 128);
|
||||
RS::get_singleton()->viewport_set_active(viewport, true);
|
||||
viewport_texture = RS::get_singleton()->viewport_get_texture(viewport);
|
||||
|
||||
canvas = RS::get_singleton()->canvas_create();
|
||||
canvas_item = RS::get_singleton()->canvas_item_create();
|
||||
|
||||
RS::get_singleton()->viewport_attach_canvas(viewport, canvas);
|
||||
RS::get_singleton()->canvas_item_set_parent(canvas_item, canvas);
|
||||
}
|
||||
|
||||
EditorFontPreviewPlugin::~EditorFontPreviewPlugin() {
|
||||
ERR_FAIL_NULL(RenderingServer::get_singleton());
|
||||
RS::get_singleton()->free(canvas_item);
|
||||
RS::get_singleton()->free(canvas);
|
||||
RS::get_singleton()->free(viewport);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const real_t GRADIENT_PREVIEW_TEXTURE_SCALE_FACTOR = 4.0;
|
||||
|
||||
bool EditorGradientPreviewPlugin::handles(const String &p_type) const {
|
||||
return ClassDB::is_parent_class(p_type, "Gradient");
|
||||
}
|
||||
|
||||
bool EditorGradientPreviewPlugin::generate_small_preview_automatically() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorGradientPreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const {
|
||||
Ref<Gradient> gradient = p_from;
|
||||
if (gradient.is_valid()) {
|
||||
Ref<GradientTexture1D> ptex;
|
||||
ptex.instantiate();
|
||||
ptex->set_width(p_size.width * GRADIENT_PREVIEW_TEXTURE_SCALE_FACTOR * EDSCALE);
|
||||
ptex->set_gradient(gradient);
|
||||
return ImageTexture::create_from_image(ptex->get_image());
|
||||
}
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* editor_preview_plugins.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/editor_resource_preview.h"
|
||||
|
||||
class ScriptLanguage;
|
||||
|
||||
void post_process_preview(Ref<Image> p_image);
|
||||
|
||||
class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator {
|
||||
GDCLASS(EditorTexturePreviewPlugin, EditorResourcePreviewGenerator);
|
||||
|
||||
public:
|
||||
virtual bool handles(const String &p_type) const override;
|
||||
virtual bool generate_small_preview_automatically() const override;
|
||||
virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
|
||||
};
|
||||
|
||||
class EditorImagePreviewPlugin : public EditorResourcePreviewGenerator {
|
||||
GDCLASS(EditorImagePreviewPlugin, EditorResourcePreviewGenerator);
|
||||
|
||||
public:
|
||||
virtual bool handles(const String &p_type) const override;
|
||||
virtual bool generate_small_preview_automatically() const override;
|
||||
virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
|
||||
};
|
||||
|
||||
class EditorBitmapPreviewPlugin : public EditorResourcePreviewGenerator {
|
||||
GDCLASS(EditorBitmapPreviewPlugin, EditorResourcePreviewGenerator);
|
||||
|
||||
public:
|
||||
virtual bool handles(const String &p_type) const override;
|
||||
virtual bool generate_small_preview_automatically() const override;
|
||||
virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
|
||||
};
|
||||
|
||||
class EditorPackedScenePreviewPlugin : public EditorResourcePreviewGenerator {
|
||||
GDCLASS(EditorPackedScenePreviewPlugin, EditorResourcePreviewGenerator);
|
||||
|
||||
public:
|
||||
virtual bool handles(const String &p_type) const override;
|
||||
virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
|
||||
virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const override;
|
||||
};
|
||||
|
||||
class EditorMaterialPreviewPlugin : public EditorResourcePreviewGenerator {
|
||||
GDCLASS(EditorMaterialPreviewPlugin, EditorResourcePreviewGenerator);
|
||||
|
||||
RID scenario;
|
||||
RID sphere;
|
||||
RID sphere_instance;
|
||||
RID viewport;
|
||||
RID viewport_texture;
|
||||
RID light;
|
||||
RID light_instance;
|
||||
RID light2;
|
||||
RID light_instance2;
|
||||
RID camera;
|
||||
RID camera_attributes;
|
||||
mutable DrawRequester draw_requester;
|
||||
|
||||
public:
|
||||
virtual bool handles(const String &p_type) const override;
|
||||
virtual bool generate_small_preview_automatically() const override;
|
||||
virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
|
||||
virtual void abort() override;
|
||||
|
||||
EditorMaterialPreviewPlugin();
|
||||
~EditorMaterialPreviewPlugin();
|
||||
};
|
||||
|
||||
class EditorScriptPreviewPlugin : public EditorResourcePreviewGenerator {
|
||||
GDCLASS(EditorScriptPreviewPlugin, EditorResourcePreviewGenerator);
|
||||
|
||||
Ref<Texture2D> _generate_from_source_code(const ScriptLanguage *p_language, const String &p_source_code, const Size2 &p_size, Dictionary &p_metadata) const;
|
||||
|
||||
public:
|
||||
virtual bool handles(const String &p_type) const override;
|
||||
virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
|
||||
virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const override;
|
||||
};
|
||||
|
||||
class EditorAudioStreamPreviewPlugin : public EditorResourcePreviewGenerator {
|
||||
GDCLASS(EditorAudioStreamPreviewPlugin, EditorResourcePreviewGenerator);
|
||||
|
||||
public:
|
||||
virtual bool handles(const String &p_type) const override;
|
||||
virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
|
||||
};
|
||||
|
||||
class EditorMeshPreviewPlugin : public EditorResourcePreviewGenerator {
|
||||
GDCLASS(EditorMeshPreviewPlugin, EditorResourcePreviewGenerator);
|
||||
|
||||
RID scenario;
|
||||
RID mesh_instance;
|
||||
RID viewport;
|
||||
RID viewport_texture;
|
||||
RID light;
|
||||
RID light_instance;
|
||||
RID light2;
|
||||
RID light_instance2;
|
||||
RID camera;
|
||||
RID camera_attributes;
|
||||
mutable DrawRequester draw_requester;
|
||||
|
||||
public:
|
||||
virtual bool handles(const String &p_type) const override;
|
||||
virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
|
||||
virtual void abort() override;
|
||||
|
||||
EditorMeshPreviewPlugin();
|
||||
~EditorMeshPreviewPlugin();
|
||||
};
|
||||
|
||||
class EditorFontPreviewPlugin : public EditorResourcePreviewGenerator {
|
||||
GDCLASS(EditorFontPreviewPlugin, EditorResourcePreviewGenerator);
|
||||
|
||||
RID viewport;
|
||||
RID viewport_texture;
|
||||
RID canvas;
|
||||
RID canvas_item;
|
||||
mutable DrawRequester draw_requester;
|
||||
|
||||
public:
|
||||
virtual bool handles(const String &p_type) const override;
|
||||
virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
|
||||
virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const override;
|
||||
virtual void abort() override;
|
||||
|
||||
EditorFontPreviewPlugin();
|
||||
~EditorFontPreviewPlugin();
|
||||
};
|
||||
|
||||
class EditorGradientPreviewPlugin : public EditorResourcePreviewGenerator {
|
||||
GDCLASS(EditorGradientPreviewPlugin, EditorResourcePreviewGenerator);
|
||||
|
||||
public:
|
||||
virtual bool handles(const String &p_type) const override;
|
||||
virtual bool generate_small_preview_automatically() const override;
|
||||
virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const override;
|
||||
};
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* editor_resource_tooltip_plugins.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 "editor_resource_tooltip_plugins.h"
|
||||
|
||||
#include "editor/editor_file_system.h"
|
||||
#include "editor/editor_resource_preview.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/texture_rect.h"
|
||||
|
||||
void EditorResourceTooltipPlugin::_thumbnail_ready(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_udata) {
|
||||
ObjectID trid = p_udata;
|
||||
TextureRect *tr = ObjectDB::get_instance<TextureRect>(trid);
|
||||
|
||||
if (!tr) {
|
||||
return;
|
||||
}
|
||||
|
||||
tr->set_texture(p_preview);
|
||||
}
|
||||
|
||||
void EditorResourceTooltipPlugin::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_thumbnail_ready"), &EditorResourceTooltipPlugin::_thumbnail_ready);
|
||||
ClassDB::bind_method(D_METHOD("request_thumbnail", "path", "control"), &EditorResourceTooltipPlugin::request_thumbnail);
|
||||
|
||||
GDVIRTUAL_BIND(_handles, "type");
|
||||
GDVIRTUAL_BIND(_make_tooltip_for_path, "path", "metadata", "base");
|
||||
}
|
||||
|
||||
VBoxContainer *EditorResourceTooltipPlugin::make_default_tooltip(const String &p_resource_path) {
|
||||
VBoxContainer *vb = memnew(VBoxContainer);
|
||||
vb->add_theme_constant_override("separation", -4 * EDSCALE);
|
||||
{
|
||||
Label *label = memnew(Label(p_resource_path.get_file()));
|
||||
vb->add_child(label);
|
||||
}
|
||||
|
||||
ResourceUID::ID id = EditorFileSystem::get_singleton()->get_file_uid(p_resource_path);
|
||||
if (id != ResourceUID::INVALID_ID) {
|
||||
Label *label = memnew(Label(ResourceUID::get_singleton()->id_to_text(id)));
|
||||
vb->add_child(label);
|
||||
}
|
||||
|
||||
{
|
||||
Ref<FileAccess> f = FileAccess::open(p_resource_path, FileAccess::READ);
|
||||
Label *label = memnew(Label(vformat(TTR("Size: %s"), String::humanize_size(f->get_length()))));
|
||||
vb->add_child(label);
|
||||
}
|
||||
|
||||
if (ResourceLoader::exists(p_resource_path)) {
|
||||
String type = ResourceLoader::get_resource_type(p_resource_path);
|
||||
Label *label = memnew(Label(vformat(TTR("Type: %s"), type)));
|
||||
vb->add_child(label);
|
||||
}
|
||||
return vb;
|
||||
}
|
||||
|
||||
void EditorResourceTooltipPlugin::request_thumbnail(const String &p_path, TextureRect *p_for_control) const {
|
||||
ERR_FAIL_NULL(p_for_control);
|
||||
EditorResourcePreview::get_singleton()->queue_resource_preview(p_path, const_cast<EditorResourceTooltipPlugin *>(this), "_thumbnail_ready", p_for_control->get_instance_id());
|
||||
}
|
||||
|
||||
bool EditorResourceTooltipPlugin::handles(const String &p_resource_type) const {
|
||||
bool ret = false;
|
||||
GDVIRTUAL_CALL(_handles, p_resource_type, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Control *EditorResourceTooltipPlugin::make_tooltip_for_path(const String &p_resource_path, const Dictionary &p_metadata, Control *p_base) const {
|
||||
Control *ret = nullptr;
|
||||
GDVIRTUAL_CALL(_make_tooltip_for_path, p_resource_path, p_metadata, p_base, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// EditorTextureTooltipPlugin
|
||||
|
||||
bool EditorTextureTooltipPlugin::handles(const String &p_resource_type) const {
|
||||
return ClassDB::is_parent_class(p_resource_type, "Texture2D") || ClassDB::is_parent_class(p_resource_type, "Image");
|
||||
}
|
||||
|
||||
Control *EditorTextureTooltipPlugin::make_tooltip_for_path(const String &p_resource_path, const Dictionary &p_metadata, Control *p_base) const {
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
VBoxContainer *vb = Object::cast_to<VBoxContainer>(p_base);
|
||||
DEV_ASSERT(vb);
|
||||
vb->set_alignment(BoxContainer::ALIGNMENT_CENTER);
|
||||
|
||||
Vector2 dimensions = p_metadata.get("dimensions", Vector2());
|
||||
Label *label = memnew(Label(vformat(TTR(U"Dimensions: %d × %d"), dimensions.x, dimensions.y)));
|
||||
vb->add_child(label);
|
||||
|
||||
TextureRect *tr = memnew(TextureRect);
|
||||
tr->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
|
||||
hb->add_child(tr);
|
||||
request_thumbnail(p_resource_path, tr);
|
||||
|
||||
hb->add_child(vb);
|
||||
return hb;
|
||||
}
|
||||
|
||||
// EditorAudioStreamTooltipPlugin
|
||||
|
||||
bool EditorAudioStreamTooltipPlugin::handles(const String &p_resource_type) const {
|
||||
return ClassDB::is_parent_class(p_resource_type, "AudioStream");
|
||||
}
|
||||
|
||||
Control *EditorAudioStreamTooltipPlugin::make_tooltip_for_path(const String &p_resource_path, const Dictionary &p_metadata, Control *p_base) const {
|
||||
VBoxContainer *vb = Object::cast_to<VBoxContainer>(p_base);
|
||||
DEV_ASSERT(vb);
|
||||
|
||||
double length = p_metadata.get("length", 0.0);
|
||||
if (length >= 60.0) {
|
||||
vb->add_child(memnew(Label(vformat(TTR("Length: %0dm %0ds"), int(length / 60.0), int(std::fmod(length, 60))))));
|
||||
} else if (length >= 1.0) {
|
||||
vb->add_child(memnew(Label(vformat(TTR("Length: %0.1fs"), length))));
|
||||
} else {
|
||||
vb->add_child(memnew(Label(vformat(TTR("Length: %0.3fs"), length))));
|
||||
}
|
||||
|
||||
TextureRect *tr = memnew(TextureRect);
|
||||
vb->add_child(tr);
|
||||
request_thumbnail(p_resource_path, tr);
|
||||
|
||||
return vb;
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* editor_resource_tooltip_plugins.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/object/gdvirtual.gen.inc"
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "scene/gui/control.h"
|
||||
|
||||
class Texture2D;
|
||||
class TextureRect;
|
||||
class VBoxContainer;
|
||||
|
||||
class EditorResourceTooltipPlugin : public RefCounted {
|
||||
GDCLASS(EditorResourceTooltipPlugin, RefCounted);
|
||||
|
||||
void _thumbnail_ready(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_udata);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
GDVIRTUAL1RC(bool, _handles, String)
|
||||
GDVIRTUAL3RC(Control *, _make_tooltip_for_path, String, Dictionary, Control *)
|
||||
|
||||
public:
|
||||
static VBoxContainer *make_default_tooltip(const String &p_resource_path);
|
||||
void request_thumbnail(const String &p_path, TextureRect *p_for_control) const;
|
||||
|
||||
virtual bool handles(const String &p_resource_type) const;
|
||||
virtual Control *make_tooltip_for_path(const String &p_resource_path, const Dictionary &p_metadata, Control *p_base) const;
|
||||
};
|
||||
|
||||
class EditorTextureTooltipPlugin : public EditorResourceTooltipPlugin {
|
||||
GDCLASS(EditorTextureTooltipPlugin, EditorResourceTooltipPlugin);
|
||||
|
||||
public:
|
||||
virtual bool handles(const String &p_resource_type) const override;
|
||||
virtual Control *make_tooltip_for_path(const String &p_resource_path, const Dictionary &p_metadata, Control *p_base) const override;
|
||||
};
|
||||
|
||||
class EditorAudioStreamTooltipPlugin : public EditorResourceTooltipPlugin {
|
||||
GDCLASS(EditorAudioStreamTooltipPlugin, EditorResourceTooltipPlugin);
|
||||
|
||||
public:
|
||||
virtual bool handles(const String &p_resource_type) const override;
|
||||
virtual Control *make_tooltip_for_path(const String &p_resource_path, const Dictionary &p_metadata, Control *p_base) const override;
|
||||
};
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* editor_script_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 "editor_script_plugin.h"
|
||||
|
||||
#include "editor/editor_command_palette.h"
|
||||
#include "editor/editor_interface.h"
|
||||
#include "editor/editor_script.h"
|
||||
|
||||
Ref<EditorScript> create_instance(const StringName &p_name) {
|
||||
Ref<EditorScript> es;
|
||||
es.instantiate();
|
||||
Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_name), "Script", ResourceFormatLoader::CACHE_MODE_REUSE);
|
||||
if (scr.is_valid()) {
|
||||
es->set_script(scr);
|
||||
}
|
||||
return es;
|
||||
}
|
||||
|
||||
void EditorScriptPlugin::run_command(const StringName &p_name) {
|
||||
create_instance(p_name)->run();
|
||||
}
|
||||
|
||||
void EditorScriptPlugin::command_palette_about_to_popup() {
|
||||
for (const StringName &command : commands) {
|
||||
EditorInterface::get_singleton()->get_command_palette()->remove_command("editor_scripts/" + command);
|
||||
}
|
||||
commands.clear();
|
||||
ScriptServer::get_inheriters_list(SNAME("EditorScript"), &commands);
|
||||
for (const StringName &command : commands) {
|
||||
EditorInterface::get_singleton()->get_command_palette()->add_command(String(command).capitalize(), "editor_scripts/" + command, callable_mp(this, &EditorScriptPlugin::run_command), varray(command), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
EditorScriptPlugin::EditorScriptPlugin() {
|
||||
EditorInterface::get_singleton()->get_command_palette()->connect("about_to_popup", callable_mp(this, &EditorScriptPlugin::command_palette_about_to_popup));
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* editor_script_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
|
||||
class EditorScriptPlugin : public EditorPlugin {
|
||||
GDCLASS(EditorScriptPlugin, EditorPlugin);
|
||||
|
||||
private:
|
||||
List<StringName> commands;
|
||||
|
||||
void run_command(const StringName &p_name);
|
||||
void command_palette_about_to_popup();
|
||||
|
||||
public:
|
||||
EditorScriptPlugin();
|
||||
|
||||
virtual String get_plugin_name() const override { return "EditorScript"; }
|
||||
};
|
||||
|
|
@ -1,458 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* embedded_process.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 "embedded_process.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/resources/style_box_flat.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
void EmbeddedProcessBase::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &EmbeddedProcessBase::_project_settings_changed));
|
||||
} break;
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
window = get_window();
|
||||
transp_enabled = GLOBAL_GET("display/window/per_pixel_transparency/allowed");
|
||||
clear_color = GLOBAL_GET("rendering/environment/defaults/default_clear_color");
|
||||
} break;
|
||||
case NOTIFICATION_DRAW: {
|
||||
_draw();
|
||||
} break;
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
checkerboard = get_editor_theme_icon(SNAME("Checkerboard"));
|
||||
focus_style_box = get_theme_stylebox(SNAME("FocusViewport"), EditorStringName(EditorStyles));
|
||||
Ref<StyleBoxFlat> focus_style_box_flat = focus_style_box;
|
||||
if (focus_style_box_flat.is_valid()) {
|
||||
margin_top_left = Point2i(focus_style_box_flat->get_border_width(SIDE_LEFT), focus_style_box_flat->get_border_width(SIDE_TOP));
|
||||
margin_bottom_right = Point2i(focus_style_box_flat->get_border_width(SIDE_RIGHT), focus_style_box_flat->get_border_width(SIDE_BOTTOM));
|
||||
} else if (focus_style_box.is_valid()) {
|
||||
margin_top_left = Point2i(focus_style_box->get_margin(SIDE_LEFT), focus_style_box->get_margin(SIDE_TOP));
|
||||
margin_bottom_right = Point2i(focus_style_box->get_margin(SIDE_RIGHT), focus_style_box->get_margin(SIDE_BOTTOM));
|
||||
} else {
|
||||
margin_top_left = Point2i();
|
||||
margin_bottom_right = Point2i();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EmbeddedProcessBase::_project_settings_changed() {
|
||||
transp_enabled = GLOBAL_GET("display/window/per_pixel_transparency/allowed");
|
||||
clear_color = GLOBAL_GET("rendering/environment/defaults/default_clear_color");
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
void EmbeddedProcessBase::_bind_methods() {
|
||||
ADD_SIGNAL(MethodInfo("embedding_completed"));
|
||||
ADD_SIGNAL(MethodInfo("embedding_failed"));
|
||||
ADD_SIGNAL(MethodInfo("embedded_process_updated"));
|
||||
ADD_SIGNAL(MethodInfo("embedded_process_focused"));
|
||||
}
|
||||
|
||||
void EmbeddedProcessBase::_draw() {
|
||||
if (is_embedding_completed()) {
|
||||
Rect2 r = get_adjusted_embedded_window_rect(get_rect());
|
||||
#ifndef MACOS_ENABLED
|
||||
r.position -= get_window()->get_position();
|
||||
#endif
|
||||
if (transp_enabled) {
|
||||
draw_texture_rect(checkerboard, r, true);
|
||||
} else {
|
||||
draw_rect(r, clear_color, true);
|
||||
}
|
||||
}
|
||||
if (is_process_focused() && focus_style_box.is_valid()) {
|
||||
Size2 size = get_size();
|
||||
Rect2 r = Rect2(Point2(), size);
|
||||
focus_style_box->draw(get_canvas_item(), r);
|
||||
}
|
||||
}
|
||||
|
||||
void EmbeddedProcessBase::set_window_size(const Size2i &p_window_size) {
|
||||
if (window_size != p_window_size) {
|
||||
window_size = p_window_size;
|
||||
queue_update_embedded_process();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void EmbeddedProcessBase::set_keep_aspect(bool p_keep_aspect) {
|
||||
if (keep_aspect != p_keep_aspect) {
|
||||
keep_aspect = p_keep_aspect;
|
||||
queue_update_embedded_process();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
Rect2i EmbeddedProcessBase::get_screen_embedded_window_rect() const {
|
||||
return get_adjusted_embedded_window_rect(get_global_rect());
|
||||
}
|
||||
|
||||
int EmbeddedProcessBase::get_margin_size(Side p_side) const {
|
||||
ERR_FAIL_INDEX_V((int)p_side, 4, 0);
|
||||
|
||||
switch (p_side) {
|
||||
case SIDE_LEFT:
|
||||
return margin_top_left.x;
|
||||
case SIDE_RIGHT:
|
||||
return margin_bottom_right.x;
|
||||
case SIDE_TOP:
|
||||
return margin_top_left.y;
|
||||
case SIDE_BOTTOM:
|
||||
return margin_bottom_right.y;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Size2 EmbeddedProcessBase::get_margins_size() const {
|
||||
return margin_top_left + margin_bottom_right;
|
||||
}
|
||||
|
||||
EmbeddedProcessBase::EmbeddedProcessBase() {
|
||||
set_focus_mode(FOCUS_ALL);
|
||||
}
|
||||
|
||||
EmbeddedProcessBase::~EmbeddedProcessBase() {
|
||||
}
|
||||
|
||||
Rect2i EmbeddedProcess::get_adjusted_embedded_window_rect(const Rect2i &p_rect) const {
|
||||
Rect2i control_rect = Rect2i(p_rect.position + margin_top_left, (p_rect.size - get_margins_size()).maxi(1));
|
||||
if (window) {
|
||||
control_rect.position += window->get_position();
|
||||
}
|
||||
if (window_size != Size2i()) {
|
||||
Rect2i desired_rect;
|
||||
if (!keep_aspect && control_rect.size.x >= window_size.x && control_rect.size.y >= window_size.y) {
|
||||
// Fixed at the desired size.
|
||||
desired_rect.size = window_size;
|
||||
} else {
|
||||
float ratio = MIN((float)control_rect.size.x / window_size.x, (float)control_rect.size.y / window_size.y);
|
||||
desired_rect.size = Size2i(window_size.x * ratio, window_size.y * ratio).maxi(1);
|
||||
}
|
||||
desired_rect.position = Size2i(control_rect.position.x + ((control_rect.size.x - desired_rect.size.x) / 2), control_rect.position.y + ((control_rect.size.y - desired_rect.size.y) / 2));
|
||||
return desired_rect;
|
||||
} else {
|
||||
// Stretch, use all the control area.
|
||||
return control_rect;
|
||||
}
|
||||
}
|
||||
|
||||
bool EmbeddedProcess::is_embedding_in_progress() const {
|
||||
return !timer_embedding->is_stopped();
|
||||
}
|
||||
|
||||
bool EmbeddedProcess::is_embedding_completed() const {
|
||||
return embedding_completed;
|
||||
}
|
||||
|
||||
bool EmbeddedProcess::is_process_focused() const {
|
||||
return focused_process_id == current_process_id && has_focus();
|
||||
}
|
||||
|
||||
int EmbeddedProcess::get_embedded_pid() const {
|
||||
return current_process_id;
|
||||
}
|
||||
|
||||
void EmbeddedProcess::embed_process(OS::ProcessID p_pid) {
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_MSG(!DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_WINDOW_EMBEDDING), "Embedded process not supported by this display server.");
|
||||
|
||||
if (current_process_id != 0) {
|
||||
// Stop embedding the last process.
|
||||
OS::get_singleton()->kill(current_process_id);
|
||||
}
|
||||
|
||||
reset();
|
||||
|
||||
current_process_id = p_pid;
|
||||
start_embedding_time = OS::get_singleton()->get_ticks_msec();
|
||||
embedding_grab_focus = has_focus();
|
||||
timer_update_embedded_process->start();
|
||||
set_process(true);
|
||||
set_notify_transform(true);
|
||||
|
||||
// Attempt to embed the process, but if it has just started and the window is not ready yet,
|
||||
// we will retry in this case.
|
||||
_try_embed_process();
|
||||
}
|
||||
|
||||
void EmbeddedProcess::reset() {
|
||||
if (current_process_id != 0 && embedding_completed) {
|
||||
DisplayServer::get_singleton()->remove_embedded_process(current_process_id);
|
||||
}
|
||||
current_process_id = 0;
|
||||
embedding_completed = false;
|
||||
start_embedding_time = 0;
|
||||
embedding_grab_focus = false;
|
||||
timer_embedding->stop();
|
||||
timer_update_embedded_process->stop();
|
||||
set_process(false);
|
||||
set_notify_transform(false);
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
void EmbeddedProcess::request_close() {
|
||||
if (current_process_id != 0 && embedding_completed) {
|
||||
DisplayServer::get_singleton()->request_close_embedded_process(current_process_id);
|
||||
}
|
||||
}
|
||||
|
||||
void EmbeddedProcess::_try_embed_process() {
|
||||
bool is_visible = is_visible_in_tree();
|
||||
Error err = DisplayServer::get_singleton()->embed_process(window->get_window_id(), current_process_id, get_screen_embedded_window_rect(), is_visible, is_visible && application_has_focus && embedding_grab_focus);
|
||||
if (err == OK) {
|
||||
embedding_completed = true;
|
||||
queue_redraw();
|
||||
emit_signal(SNAME("embedding_completed"));
|
||||
} else if (err == ERR_DOES_NOT_EXIST) {
|
||||
if (OS::get_singleton()->get_ticks_msec() - start_embedding_time >= (uint64_t)embedding_timeout) {
|
||||
// Embedding process timed out.
|
||||
reset();
|
||||
emit_signal(SNAME("embedding_failed"));
|
||||
} else {
|
||||
// Tries another shot.
|
||||
timer_embedding->start();
|
||||
}
|
||||
} else {
|
||||
// Another unknown error.
|
||||
reset();
|
||||
emit_signal(SNAME("embedding_failed"));
|
||||
}
|
||||
}
|
||||
|
||||
bool EmbeddedProcess::_is_embedded_process_updatable() {
|
||||
return window && current_process_id != 0 && embedding_completed;
|
||||
}
|
||||
|
||||
void EmbeddedProcess::queue_update_embedded_process() {
|
||||
updated_embedded_process_queued = true;
|
||||
}
|
||||
|
||||
void EmbeddedProcess::_timer_update_embedded_process_timeout() {
|
||||
_check_focused_process_id();
|
||||
_check_mouse_over();
|
||||
|
||||
if (!updated_embedded_process_queued) {
|
||||
// We need to detect when the control globally changes location or size on the screen.
|
||||
// NOTIFICATION_RESIZED and NOTIFICATION_WM_POSITION_CHANGED are not enough to detect
|
||||
// resized parent to siblings controls that can affect global position.
|
||||
Rect2i new_global_rect = get_global_rect();
|
||||
if (last_global_rect != new_global_rect) {
|
||||
last_global_rect = new_global_rect;
|
||||
queue_update_embedded_process();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmbeddedProcess::_update_embedded_process() {
|
||||
if (!_is_embedded_process_updatable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool must_grab_focus = false;
|
||||
bool focus = has_focus();
|
||||
if (last_updated_embedded_process_focused != focus) {
|
||||
if (focus) {
|
||||
must_grab_focus = true;
|
||||
}
|
||||
last_updated_embedded_process_focused = focus;
|
||||
}
|
||||
|
||||
DisplayServer::get_singleton()->embed_process(window->get_window_id(), current_process_id, get_screen_embedded_window_rect(), is_visible_in_tree(), must_grab_focus);
|
||||
emit_signal(SNAME("embedded_process_updated"));
|
||||
}
|
||||
|
||||
void EmbeddedProcess::_timer_embedding_timeout() {
|
||||
_try_embed_process();
|
||||
}
|
||||
|
||||
void EmbeddedProcess::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_PROCESS: {
|
||||
if (updated_embedded_process_queued) {
|
||||
updated_embedded_process_queued = false;
|
||||
_update_embedded_process();
|
||||
}
|
||||
} break;
|
||||
case NOTIFICATION_RESIZED:
|
||||
case NOTIFICATION_VISIBILITY_CHANGED:
|
||||
case NOTIFICATION_WM_POSITION_CHANGED: {
|
||||
queue_update_embedded_process();
|
||||
} break;
|
||||
case NOTIFICATION_APPLICATION_FOCUS_IN: {
|
||||
application_has_focus = true;
|
||||
last_application_focus_time = OS::get_singleton()->get_ticks_msec();
|
||||
} break;
|
||||
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
|
||||
application_has_focus = false;
|
||||
} break;
|
||||
case NOTIFICATION_FOCUS_ENTER: {
|
||||
queue_update_embedded_process();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EmbeddedProcess::_check_mouse_over() {
|
||||
// This method checks if the mouse is over the embedded process while the current application is focused.
|
||||
// The goal is to give focus to the embedded process as soon as the mouse hovers over it,
|
||||
// allowing the user to interact with it immediately without needing to click first.
|
||||
if (!embedding_completed || !application_has_focus || !window || has_focus() || !is_visible_in_tree() || !window->has_focus() || Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT) || Input::get_singleton()->is_mouse_button_pressed(MouseButton::RIGHT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Before checking whether the mouse is truly inside the embedded process, ensure
|
||||
// the editor has enough time to re-render. When a breakpoint is hit in the script editor,
|
||||
// `_check_mouse_over` may be triggered before the editor hides the game workspace.
|
||||
// This prevents the embedded process from regaining focus immediately after the editor has taken it.
|
||||
if (OS::get_singleton()->get_ticks_msec() - last_application_focus_time < 500) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Input::is_mouse_button_pressed is not sufficient to detect the mouse button state
|
||||
// while the floating game window is being resized.
|
||||
BitField<MouseButtonMask> mouse_button_mask = DisplayServer::get_singleton()->mouse_get_button_state();
|
||||
if (!mouse_button_mask.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Not stealing focus from a textfield.
|
||||
if (get_viewport()->gui_get_focus_owner() && get_viewport()->gui_get_focus_owner()->is_text_field()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 mouse_position = DisplayServer::get_singleton()->mouse_get_position();
|
||||
Rect2i window_rect = get_screen_embedded_window_rect();
|
||||
if (!window_rect.has_point(mouse_position)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't grab the focus if mouse over another window.
|
||||
DisplayServer::WindowID window_id_over = DisplayServer::get_singleton()->get_window_at_screen_position(mouse_position);
|
||||
if (window_id_over > 0 && window_id_over != window->get_window_id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if there's an exclusive popup, an open menu, or a tooltip.
|
||||
// We don't want to grab focus to prevent the game window from coming to the front of the modal window
|
||||
// or the open menu from closing when the mouse cursor moves outside the menu and over the embedded game.
|
||||
Vector<DisplayServer::WindowID> wl = DisplayServer::get_singleton()->get_window_list();
|
||||
for (const DisplayServer::WindowID &window_id : wl) {
|
||||
Window *w = Window::get_from_id(window_id);
|
||||
if (w && (w->is_exclusive() || w->get_flag(Window::FLAG_POPUP))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Force "regrabbing" the game window focus.
|
||||
last_updated_embedded_process_focused = false;
|
||||
|
||||
grab_focus();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
void EmbeddedProcess::_check_focused_process_id() {
|
||||
OS::ProcessID process_id = DisplayServer::get_singleton()->get_focused_process_id();
|
||||
if (process_id != focused_process_id) {
|
||||
focused_process_id = process_id;
|
||||
if (focused_process_id == current_process_id) {
|
||||
// The embedded process got the focus.
|
||||
|
||||
// Refocus the current model when focusing the embedded process.
|
||||
Window *modal_window = _get_current_modal_window();
|
||||
if (!modal_window) {
|
||||
emit_signal(SNAME("embedded_process_focused"));
|
||||
if (has_focus()) {
|
||||
// Redraw to updated the focus style.
|
||||
queue_redraw();
|
||||
} else {
|
||||
grab_focus();
|
||||
}
|
||||
}
|
||||
} else if (has_focus()) {
|
||||
release_focus();
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the opened modal dialog is refocused when the focused process is the embedded process.
|
||||
if (!application_has_focus && focused_process_id == current_process_id) {
|
||||
Window *modal_window = _get_current_modal_window();
|
||||
if (modal_window) {
|
||||
if (modal_window->get_mode() == Window::MODE_MINIMIZED) {
|
||||
modal_window->set_mode(Window::MODE_WINDOWED);
|
||||
}
|
||||
callable_mp(modal_window, &Window::grab_focus).call_deferred();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Window *EmbeddedProcess::_get_current_modal_window() {
|
||||
Vector<DisplayServer::WindowID> wl = DisplayServer::get_singleton()->get_window_list();
|
||||
for (const DisplayServer::WindowID &window_id : wl) {
|
||||
Window *w = Window::get_from_id(window_id);
|
||||
if (!w) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (w->is_exclusive()) {
|
||||
return w;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EmbeddedProcess::EmbeddedProcess() :
|
||||
EmbeddedProcessBase() {
|
||||
timer_embedding = memnew(Timer);
|
||||
timer_embedding->set_wait_time(0.1);
|
||||
timer_embedding->set_one_shot(true);
|
||||
add_child(timer_embedding);
|
||||
timer_embedding->connect("timeout", callable_mp(this, &EmbeddedProcess::_timer_embedding_timeout));
|
||||
|
||||
timer_update_embedded_process = memnew(Timer);
|
||||
timer_update_embedded_process->set_wait_time(0.1);
|
||||
add_child(timer_update_embedded_process);
|
||||
timer_update_embedded_process->connect("timeout", callable_mp(this, &EmbeddedProcess::_timer_update_embedded_process_timeout));
|
||||
}
|
||||
|
||||
EmbeddedProcess::~EmbeddedProcess() {
|
||||
if (current_process_id != 0) {
|
||||
// Stop embedding the last process.
|
||||
OS::get_singleton()->kill(current_process_id);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* embedded_process.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
|
||||
class ScriptEditorDebugger;
|
||||
|
||||
class EmbeddedProcessBase : public Control {
|
||||
GDCLASS(EmbeddedProcessBase, Control);
|
||||
|
||||
void _draw();
|
||||
|
||||
protected:
|
||||
Ref<StyleBox> focus_style_box;
|
||||
Size2i window_size;
|
||||
bool keep_aspect = false;
|
||||
Point2i margin_top_left;
|
||||
Point2i margin_bottom_right;
|
||||
Window *window = nullptr;
|
||||
|
||||
bool transp_enabled = false;
|
||||
Color clear_color;
|
||||
Ref<Texture2D> checkerboard;
|
||||
|
||||
void _project_settings_changed();
|
||||
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
virtual void set_script_debugger(ScriptEditorDebugger *p_debugger) {}
|
||||
|
||||
virtual bool is_embedding_completed() const = 0;
|
||||
virtual bool is_embedding_in_progress() const = 0;
|
||||
virtual bool is_process_focused() const = 0;
|
||||
virtual void embed_process(OS::ProcessID p_pid) = 0;
|
||||
virtual int get_embedded_pid() const = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual void request_close() = 0;
|
||||
virtual void queue_update_embedded_process() = 0;
|
||||
|
||||
void set_window_size(const Size2i &p_window_size);
|
||||
void set_keep_aspect(bool p_keep_aspect);
|
||||
virtual Rect2i get_adjusted_embedded_window_rect(const Rect2i &p_rect) const = 0;
|
||||
Rect2i get_screen_embedded_window_rect() const;
|
||||
int get_margin_size(Side p_side) const;
|
||||
Size2 get_margins_size() const;
|
||||
|
||||
EmbeddedProcessBase();
|
||||
virtual ~EmbeddedProcessBase();
|
||||
};
|
||||
|
||||
class EmbeddedProcess : public EmbeddedProcessBase {
|
||||
GDCLASS(EmbeddedProcess, EmbeddedProcessBase);
|
||||
|
||||
bool application_has_focus = true;
|
||||
uint64_t last_application_focus_time = 0;
|
||||
OS::ProcessID focused_process_id = 0;
|
||||
OS::ProcessID current_process_id = 0;
|
||||
bool embedding_grab_focus = false;
|
||||
bool embedding_completed = false;
|
||||
uint64_t start_embedding_time = 0;
|
||||
bool updated_embedded_process_queued = false;
|
||||
bool last_updated_embedded_process_focused = false;
|
||||
|
||||
Timer *timer_embedding = nullptr;
|
||||
Timer *timer_update_embedded_process = nullptr;
|
||||
|
||||
const int embedding_timeout = 45000;
|
||||
|
||||
Rect2i last_global_rect;
|
||||
|
||||
void _try_embed_process();
|
||||
void _update_embedded_process();
|
||||
void _timer_embedding_timeout();
|
||||
void _timer_update_embedded_process_timeout();
|
||||
void _check_mouse_over();
|
||||
void _check_focused_process_id();
|
||||
bool _is_embedded_process_updatable();
|
||||
Window *_get_current_modal_window();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
bool is_embedding_in_progress() const override;
|
||||
bool is_embedding_completed() const override;
|
||||
bool is_process_focused() const override;
|
||||
void embed_process(OS::ProcessID p_pid) override;
|
||||
int get_embedded_pid() const override;
|
||||
void reset() override;
|
||||
void request_close() override;
|
||||
void queue_update_embedded_process() override;
|
||||
|
||||
Rect2i get_adjusted_embedded_window_rect(const Rect2i &p_rect) const override;
|
||||
|
||||
EmbeddedProcess();
|
||||
~EmbeddedProcess() override;
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,276 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* font_config_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/editor_properties.h"
|
||||
#include "editor/editor_properties_array_dict.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
class EditorPropertyFontMetaObject : public RefCounted {
|
||||
GDCLASS(EditorPropertyFontMetaObject, RefCounted);
|
||||
|
||||
Dictionary dict;
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
|
||||
public:
|
||||
void set_dict(const Dictionary &p_dict);
|
||||
Dictionary get_dict();
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
class EditorPropertyFontOTObject : public RefCounted {
|
||||
GDCLASS(EditorPropertyFontOTObject, RefCounted);
|
||||
|
||||
Dictionary dict;
|
||||
Dictionary defaults_dict;
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
bool _property_can_revert(const StringName &p_name) const;
|
||||
bool _property_get_revert(const StringName &p_name, Variant &r_property) const;
|
||||
|
||||
public:
|
||||
void set_dict(const Dictionary &p_dict);
|
||||
Dictionary get_dict();
|
||||
|
||||
void set_defaults(const Dictionary &p_dict);
|
||||
Dictionary get_defaults();
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
class EditorPropertyFontMetaOverride : public EditorProperty {
|
||||
GDCLASS(EditorPropertyFontMetaOverride, EditorProperty);
|
||||
|
||||
Ref<EditorPropertyFontMetaObject> object;
|
||||
|
||||
MarginContainer *container = nullptr;
|
||||
VBoxContainer *property_vbox = nullptr;
|
||||
|
||||
Button *button_add = nullptr;
|
||||
Button *edit = nullptr;
|
||||
PopupMenu *menu = nullptr;
|
||||
EditorLocaleDialog *locale_select = nullptr;
|
||||
|
||||
Vector<String> script_codes;
|
||||
|
||||
bool script_editor = false;
|
||||
bool updating = false;
|
||||
int page_length = 20;
|
||||
int page_index = 0;
|
||||
EditorPaginator *paginator = nullptr;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods() {}
|
||||
|
||||
void _edit_pressed();
|
||||
void _page_changed(int p_page);
|
||||
void _property_changed(const String &p_property, const Variant &p_value, const String &p_name = "", bool p_changing = false);
|
||||
void _remove(Object *p_button, const String &p_key);
|
||||
void _add_menu();
|
||||
void _add_script(int p_option);
|
||||
void _add_lang(const String &p_locale);
|
||||
void _object_id_selected(const StringName &p_property, ObjectID p_id);
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
||||
EditorPropertyFontMetaOverride(bool p_script);
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
class EditorPropertyOTVariation : public EditorProperty {
|
||||
GDCLASS(EditorPropertyOTVariation, EditorProperty);
|
||||
|
||||
Ref<EditorPropertyFontOTObject> object;
|
||||
|
||||
MarginContainer *container = nullptr;
|
||||
VBoxContainer *property_vbox = nullptr;
|
||||
|
||||
Button *edit = nullptr;
|
||||
|
||||
bool updating = false;
|
||||
int page_length = 20;
|
||||
int page_index = 0;
|
||||
EditorPaginator *paginator = nullptr;
|
||||
|
||||
protected:
|
||||
static void _bind_methods() {}
|
||||
|
||||
void _edit_pressed();
|
||||
void _page_changed(int p_page);
|
||||
void _property_changed(const String &p_property, const Variant &p_value, const String &p_name = "", bool p_changing = false);
|
||||
void _object_id_selected(const StringName &p_property, ObjectID p_id);
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
||||
EditorPropertyOTVariation();
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
class EditorPropertyOTFeatures : public EditorProperty {
|
||||
GDCLASS(EditorPropertyOTFeatures, EditorProperty);
|
||||
|
||||
enum FeatureGroups {
|
||||
FGRP_STYLISTIC_SET,
|
||||
FGRP_CHARACTER_VARIANT,
|
||||
FGRP_CAPITLS,
|
||||
FGRP_LIGATURES,
|
||||
FGRP_ALTERNATES,
|
||||
FGRP_EAL,
|
||||
FGRP_EAW,
|
||||
FGRP_NUMAL,
|
||||
FGRP_CUSTOM,
|
||||
FGRP_MAX,
|
||||
};
|
||||
|
||||
Ref<EditorPropertyFontOTObject> object;
|
||||
|
||||
MarginContainer *container = nullptr;
|
||||
VBoxContainer *property_vbox = nullptr;
|
||||
|
||||
Button *button_add = nullptr;
|
||||
Button *edit = nullptr;
|
||||
PopupMenu *menu = nullptr;
|
||||
PopupMenu *menu_sub[FGRP_MAX];
|
||||
String group_names[FGRP_MAX];
|
||||
|
||||
bool updating = false;
|
||||
int page_length = 20;
|
||||
int page_index = 0;
|
||||
EditorPaginator *paginator = nullptr;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods() {}
|
||||
|
||||
void _edit_pressed();
|
||||
void _page_changed(int p_page);
|
||||
void _property_changed(const String &p_property, const Variant &p_value, const String &p_name = "", bool p_changing = false);
|
||||
void _remove(Object *p_button, int p_key);
|
||||
void _add_menu();
|
||||
void _add_feature(int p_option);
|
||||
void _object_id_selected(const StringName &p_property, ObjectID p_id);
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
||||
EditorPropertyOTFeatures();
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
class EditorInspectorPluginFontVariation : public EditorInspectorPlugin {
|
||||
GDCLASS(EditorInspectorPluginFontVariation, EditorInspectorPlugin);
|
||||
|
||||
public:
|
||||
virtual bool can_handle(Object *p_object) override;
|
||||
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
class FontPreview : public Control {
|
||||
GDCLASS(FontPreview, Control);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
Ref<Font> prev_font;
|
||||
|
||||
void _preview_changed();
|
||||
|
||||
public:
|
||||
virtual Size2 get_minimum_size() const override;
|
||||
|
||||
void set_data(const Ref<Font> &p_f);
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
class EditorInspectorPluginFontPreview : public EditorInspectorPlugin {
|
||||
GDCLASS(EditorInspectorPluginFontPreview, EditorInspectorPlugin);
|
||||
|
||||
public:
|
||||
virtual bool can_handle(Object *p_object) override;
|
||||
virtual void parse_begin(Object *p_object) override;
|
||||
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
class EditorPropertyFontNamesArray : public EditorPropertyArray {
|
||||
GDCLASS(EditorPropertyFontNamesArray, EditorPropertyArray);
|
||||
|
||||
PopupMenu *menu = nullptr;
|
||||
|
||||
protected:
|
||||
virtual void _add_element() override;
|
||||
|
||||
void _add_font(int p_option);
|
||||
static void _bind_methods() {}
|
||||
|
||||
public:
|
||||
EditorPropertyFontNamesArray();
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
class EditorInspectorPluginSystemFont : public EditorInspectorPlugin {
|
||||
GDCLASS(EditorInspectorPluginSystemFont, EditorInspectorPlugin);
|
||||
|
||||
public:
|
||||
virtual bool can_handle(Object *p_object) override;
|
||||
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
class FontEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(FontEditorPlugin, EditorPlugin);
|
||||
|
||||
public:
|
||||
FontEditorPlugin();
|
||||
|
||||
virtual String get_plugin_name() const override { return "Font"; }
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,285 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* game_view_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#include "editor/editor_main_screen.h"
|
||||
#include "editor/plugins/editor_debugger_plugin.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/debugger/scene_debugger.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
|
||||
class EmbeddedProcessBase;
|
||||
class VSeparator;
|
||||
class WindowWrapper;
|
||||
class ScriptEditorDebugger;
|
||||
|
||||
class GameViewDebugger : public EditorDebuggerPlugin {
|
||||
GDCLASS(GameViewDebugger, EditorDebuggerPlugin);
|
||||
|
||||
private:
|
||||
Vector<Ref<EditorDebuggerSession>> sessions;
|
||||
|
||||
bool is_feature_enabled = true;
|
||||
int node_type = RuntimeNodeSelect::NODE_TYPE_NONE;
|
||||
bool selection_visible = true;
|
||||
int select_mode = RuntimeNodeSelect::SELECT_MODE_SINGLE;
|
||||
bool mute_audio = false;
|
||||
EditorDebuggerNode::CameraOverride camera_override_mode = EditorDebuggerNode::OVERRIDE_INGAME;
|
||||
|
||||
void _session_started(Ref<EditorDebuggerSession> p_session);
|
||||
void _session_stopped();
|
||||
|
||||
void _feature_profile_changed();
|
||||
|
||||
struct ScreenshotCB {
|
||||
Callable cb;
|
||||
Rect2i rect;
|
||||
};
|
||||
|
||||
int64_t scr_rq_id = 0;
|
||||
HashMap<uint64_t, ScreenshotCB> screenshot_callbacks;
|
||||
|
||||
bool _msg_get_screenshot(const Array &p_args);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual bool capture(const String &p_message, const Array &p_data, int p_session) override;
|
||||
virtual bool has_capture(const String &p_capture) const override;
|
||||
|
||||
bool add_screenshot_callback(const Callable &p_callaback, const Rect2i &p_rect);
|
||||
|
||||
void set_suspend(bool p_enabled);
|
||||
void next_frame();
|
||||
|
||||
void set_node_type(int p_type);
|
||||
void set_select_mode(int p_mode);
|
||||
|
||||
void set_selection_visible(bool p_visible);
|
||||
|
||||
void set_debug_mute_audio(bool p_enabled);
|
||||
|
||||
void set_camera_override(bool p_enabled);
|
||||
void set_camera_manipulate_mode(EditorDebuggerNode::CameraOverride p_mode);
|
||||
|
||||
void reset_camera_2d_position();
|
||||
void reset_camera_3d_position();
|
||||
|
||||
virtual void setup_session(int p_session_id) override;
|
||||
|
||||
GameViewDebugger();
|
||||
};
|
||||
|
||||
class GameView : public VBoxContainer {
|
||||
GDCLASS(GameView, VBoxContainer);
|
||||
|
||||
enum {
|
||||
CAMERA_RESET_2D,
|
||||
CAMERA_RESET_3D,
|
||||
CAMERA_MODE_INGAME,
|
||||
CAMERA_MODE_EDITORS,
|
||||
EMBED_RUN_GAME_EMBEDDED,
|
||||
EMBED_MAKE_FLOATING_ON_PLAY,
|
||||
};
|
||||
|
||||
enum EmbedSizeMode {
|
||||
SIZE_MODE_FIXED,
|
||||
SIZE_MODE_KEEP_ASPECT,
|
||||
SIZE_MODE_STRETCH,
|
||||
};
|
||||
|
||||
enum EmbedAvailability {
|
||||
EMBED_AVAILABLE,
|
||||
EMBED_NOT_AVAILABLE_FEATURE_NOT_SUPPORTED,
|
||||
EMBED_NOT_AVAILABLE_MINIMIZED,
|
||||
EMBED_NOT_AVAILABLE_MAXIMIZED,
|
||||
EMBED_NOT_AVAILABLE_FULLSCREEN,
|
||||
EMBED_NOT_AVAILABLE_SINGLE_WINDOW_MODE,
|
||||
EMBED_NOT_AVAILABLE_PROJECT_DISPLAY_DRIVER,
|
||||
};
|
||||
|
||||
inline static GameView *singleton = nullptr;
|
||||
|
||||
Ref<GameViewDebugger> debugger;
|
||||
WindowWrapper *window_wrapper = nullptr;
|
||||
|
||||
bool is_feature_enabled = true;
|
||||
int active_sessions = 0;
|
||||
int screen_index_before_start = -1;
|
||||
ScriptEditorDebugger *embedded_script_debugger = nullptr;
|
||||
|
||||
bool embed_on_play = true;
|
||||
bool make_floating_on_play = true;
|
||||
EmbedSizeMode embed_size_mode = SIZE_MODE_FIXED;
|
||||
bool paused = false;
|
||||
Size2 size_paused;
|
||||
|
||||
Rect2i floating_window_rect;
|
||||
int floating_window_screen = -1;
|
||||
|
||||
bool debug_mute_audio = false;
|
||||
|
||||
Button *suspend_button = nullptr;
|
||||
Button *next_frame_button = nullptr;
|
||||
|
||||
Button *node_type_button[RuntimeNodeSelect::NODE_TYPE_MAX];
|
||||
Button *select_mode_button[RuntimeNodeSelect::SELECT_MODE_MAX];
|
||||
|
||||
Button *hide_selection = nullptr;
|
||||
|
||||
Button *debug_mute_audio_button = nullptr;
|
||||
|
||||
Button *camera_override_button = nullptr;
|
||||
MenuButton *camera_override_menu = nullptr;
|
||||
|
||||
VSeparator *embedding_separator = nullptr;
|
||||
Button *fixed_size_button = nullptr;
|
||||
Button *keep_aspect_button = nullptr;
|
||||
Button *stretch_button = nullptr;
|
||||
MenuButton *embed_options_menu = nullptr;
|
||||
Label *game_size_label = nullptr;
|
||||
Panel *panel = nullptr;
|
||||
EmbeddedProcessBase *embedded_process = nullptr;
|
||||
Label *state_label = nullptr;
|
||||
|
||||
void _sessions_changed();
|
||||
|
||||
void _update_debugger_buttons();
|
||||
|
||||
void _handle_shortcut_requested(int p_embed_action);
|
||||
void _toggle_suspend_button();
|
||||
void _suspend_button_toggled(bool p_pressed);
|
||||
|
||||
void _node_type_pressed(int p_option);
|
||||
void _select_mode_pressed(int p_option);
|
||||
void _embed_options_menu_menu_id_pressed(int p_id);
|
||||
void _size_mode_button_pressed(int size_mode);
|
||||
|
||||
void _play_pressed();
|
||||
static void _instance_starting_static(int p_idx, List<String> &r_arguments);
|
||||
void _instance_starting(int p_idx, List<String> &r_arguments);
|
||||
static bool _instance_rq_screenshot_static(const Callable &p_callback);
|
||||
bool _instance_rq_screenshot(const Callable &p_callback);
|
||||
void _stop_pressed();
|
||||
void _embedding_completed();
|
||||
void _embedding_failed();
|
||||
void _embedded_process_updated();
|
||||
void _embedded_process_focused();
|
||||
void _editor_or_project_settings_changed();
|
||||
|
||||
EmbedAvailability _get_embed_available();
|
||||
void _update_ui();
|
||||
void _update_embed_menu_options();
|
||||
void _update_embed_window_size();
|
||||
void _update_arguments_for_instance(int p_idx, List<String> &r_arguments);
|
||||
void _show_update_window_wrapper();
|
||||
|
||||
void _hide_selection_toggled(bool p_pressed);
|
||||
|
||||
void _debug_mute_audio_button_pressed();
|
||||
|
||||
void _camera_override_button_toggled(bool p_pressed);
|
||||
void _camera_override_menu_id_pressed(int p_id);
|
||||
|
||||
void _window_close_request();
|
||||
void _update_floating_window_settings();
|
||||
void _attach_script_debugger();
|
||||
void _detach_script_debugger();
|
||||
void _remote_window_title_changed(String title);
|
||||
|
||||
void _debugger_breaked(bool p_breaked, bool p_can_debug);
|
||||
|
||||
void _feature_profile_changed();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void set_state(const Dictionary &p_state);
|
||||
Dictionary get_state() const;
|
||||
|
||||
void set_window_layout(Ref<ConfigFile> p_layout);
|
||||
void get_window_layout(Ref<ConfigFile> p_layout);
|
||||
|
||||
GameView(Ref<GameViewDebugger> p_debugger, EmbeddedProcessBase *p_embedded_process, WindowWrapper *p_wrapper);
|
||||
};
|
||||
|
||||
class GameViewPluginBase : public EditorPlugin {
|
||||
GDCLASS(GameViewPluginBase, EditorPlugin);
|
||||
|
||||
#ifndef ANDROID_ENABLED
|
||||
GameView *game_view = nullptr;
|
||||
WindowWrapper *window_wrapper = nullptr;
|
||||
#endif // ANDROID_ENABLED
|
||||
|
||||
Ref<GameViewDebugger> debugger;
|
||||
|
||||
String last_editor;
|
||||
|
||||
#ifndef ANDROID_ENABLED
|
||||
void _window_visibility_changed(bool p_visible);
|
||||
#endif // ANDROID_ENABLED
|
||||
void _save_last_editor(const String &p_editor);
|
||||
void _focus_another_editor();
|
||||
bool _is_window_wrapper_enabled() const;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
#ifndef ANDROID_ENABLED
|
||||
void setup(Ref<GameViewDebugger> p_debugger, EmbeddedProcessBase *p_embedded_process);
|
||||
#endif
|
||||
|
||||
public:
|
||||
virtual String get_plugin_name() const override { return TTRC("Game"); }
|
||||
bool has_main_screen() const override { return true; }
|
||||
virtual void edit(Object *p_object) override {}
|
||||
virtual bool handles(Object *p_object) const override { return false; }
|
||||
virtual void selected_notify() override;
|
||||
|
||||
Ref<GameViewDebugger> get_debugger() const { return debugger; }
|
||||
|
||||
#ifndef ANDROID_ENABLED
|
||||
virtual void make_visible(bool p_visible) override;
|
||||
|
||||
virtual void set_window_layout(Ref<ConfigFile> p_layout) override;
|
||||
virtual void get_window_layout(Ref<ConfigFile> p_layout) override;
|
||||
#endif // ANDROID_ENABLED
|
||||
GameViewPluginBase();
|
||||
};
|
||||
|
||||
class GameViewPlugin : public GameViewPluginBase {
|
||||
GDCLASS(GameViewPlugin, GameViewPluginBase);
|
||||
|
||||
public:
|
||||
GameViewPlugin();
|
||||
};
|
||||
|
|
@ -1,169 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* gdextension_export_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/extension/gdextension_library_loader.h"
|
||||
#include "editor/export/editor_export.h"
|
||||
|
||||
class GDExtensionExportPlugin : public EditorExportPlugin {
|
||||
protected:
|
||||
virtual void _export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features);
|
||||
virtual String get_name() const { return "GDExtension"; }
|
||||
};
|
||||
|
||||
void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) {
|
||||
if (p_type != "GDExtension") {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<ConfigFile> config;
|
||||
config.instantiate();
|
||||
|
||||
Error err = config->load(p_path);
|
||||
ERR_FAIL_COND_MSG(err, "Failed to load GDExtension file: " + p_path);
|
||||
|
||||
// Check whether this GDExtension should be exported.
|
||||
bool android_aar_plugin = config->get_value("configuration", "android_aar_plugin", false);
|
||||
if (android_aar_plugin && p_features.has("android")) {
|
||||
// The gdextension configuration and Android .so files will be provided by the Android aar
|
||||
// plugin it's part of, so we abort here.
|
||||
skip();
|
||||
return;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_MSG(!config->has_section_key("configuration", "entry_symbol"), "Failed to export GDExtension file, missing entry symbol: " + p_path);
|
||||
|
||||
String entry_symbol = config->get_value("configuration", "entry_symbol");
|
||||
|
||||
HashSet<String> all_archs;
|
||||
all_archs.insert("x86_32");
|
||||
all_archs.insert("x86_64");
|
||||
all_archs.insert("arm32");
|
||||
all_archs.insert("arm64");
|
||||
all_archs.insert("rv64");
|
||||
all_archs.insert("ppc64");
|
||||
all_archs.insert("wasm32");
|
||||
all_archs.insert("loongarch64");
|
||||
all_archs.insert("universal");
|
||||
|
||||
HashSet<String> archs;
|
||||
HashSet<String> features_wo_arch;
|
||||
Vector<String> features_vector;
|
||||
for (const String &tag : p_features) {
|
||||
if (all_archs.has(tag)) {
|
||||
archs.insert(tag);
|
||||
} else {
|
||||
features_wo_arch.insert(tag);
|
||||
}
|
||||
features_vector.append(tag);
|
||||
}
|
||||
|
||||
if (archs.is_empty()) {
|
||||
archs.insert("unknown_arch"); // Not archs specified, still try to match.
|
||||
}
|
||||
|
||||
HashSet<String> libs_added;
|
||||
struct FoundLibInfo {
|
||||
int count = 0;
|
||||
Vector<String> libs;
|
||||
};
|
||||
HashMap<String, FoundLibInfo> libs_found;
|
||||
for (const String &arch_tag : archs) {
|
||||
if (arch_tag != "universal") {
|
||||
libs_found[arch_tag] = FoundLibInfo();
|
||||
}
|
||||
}
|
||||
|
||||
for (const String &arch_tag : archs) {
|
||||
PackedStringArray tags;
|
||||
String library_path = GDExtensionLibraryLoader::find_extension_library(
|
||||
p_path, config, [features_wo_arch, arch_tag](const String &p_feature) { return features_wo_arch.has(p_feature) || (p_feature == arch_tag); }, &tags);
|
||||
|
||||
if (libs_added.has(library_path)) {
|
||||
continue; // Universal library, already added for another arch, do not duplicate.
|
||||
}
|
||||
if (!library_path.is_empty()) {
|
||||
libs_added.insert(library_path);
|
||||
add_shared_object(library_path, tags);
|
||||
|
||||
if (p_features.has("apple_embedded") && (library_path.ends_with(".a") || library_path.ends_with(".xcframework"))) {
|
||||
String additional_code = "extern void register_dynamic_symbol(char *name, void *address);\n"
|
||||
"extern void add_apple_embedded_platform_init_callback(void (*cb)());\n"
|
||||
"\n"
|
||||
"extern \"C\" void $ENTRY();\n"
|
||||
"void $ENTRY_init() {\n"
|
||||
" if (&$ENTRY) register_dynamic_symbol((char *)\"$ENTRY\", (void *)$ENTRY);\n"
|
||||
"}\n"
|
||||
"struct $ENTRY_struct {\n"
|
||||
" $ENTRY_struct() {\n"
|
||||
" add_apple_embedded_platform_init_callback($ENTRY_init);\n"
|
||||
" }\n"
|
||||
"};\n"
|
||||
"$ENTRY_struct $ENTRY_struct_instance;\n\n";
|
||||
additional_code = additional_code.replace("$ENTRY", entry_symbol);
|
||||
add_apple_embedded_platform_cpp_code(additional_code);
|
||||
|
||||
String linker_flags = "-Wl,-U,_" + entry_symbol;
|
||||
add_apple_embedded_platform_linker_flags(linker_flags);
|
||||
}
|
||||
|
||||
// Update found library info.
|
||||
if (arch_tag == "universal") {
|
||||
for (const String &sub_arch_tag : archs) {
|
||||
if (sub_arch_tag != "universal") {
|
||||
libs_found[sub_arch_tag].count++;
|
||||
libs_found[sub_arch_tag].libs.push_back(library_path);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
libs_found[arch_tag].count++;
|
||||
libs_found[arch_tag].libs.push_back(library_path);
|
||||
}
|
||||
}
|
||||
|
||||
Vector<SharedObject> dependencies_shared_objects = GDExtensionLibraryLoader::find_extension_dependencies(p_path, config, [p_features](String p_feature) { return p_features.has(p_feature); });
|
||||
for (const SharedObject &shared_object : dependencies_shared_objects) {
|
||||
_add_shared_object(shared_object);
|
||||
}
|
||||
}
|
||||
|
||||
for (const KeyValue<String, FoundLibInfo> &E : libs_found) {
|
||||
if (E.value.count == 0) {
|
||||
if (get_export_platform().is_valid()) {
|
||||
get_export_platform()->add_message(EditorExportPlatform::EXPORT_MESSAGE_WARNING, TTR("GDExtension"), vformat(TTR("No \"%s\" library found for GDExtension: \"%s\". Possible feature flags for your platform: %s"), E.key, p_path, String(", ").join(features_vector)));
|
||||
}
|
||||
} else if (E.value.count > 1) {
|
||||
if (get_export_platform().is_valid()) {
|
||||
get_export_platform()->add_message(EditorExportPlatform::EXPORT_MESSAGE_WARNING, TTR("GDExtension"), vformat(TTR("Multiple \"%s\" libraries found for GDExtension: \"%s\": \"%s\"."), E.key, p_path, String(", ").join(E.value.libs)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.editor_sources, "*.cpp")
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* audio_listener_3d_gizmo_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 "audio_listener_3d_gizmo_plugin.h"
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "scene/3d/audio_listener_3d.h"
|
||||
|
||||
AudioListener3DGizmoPlugin::AudioListener3DGizmoPlugin() {
|
||||
create_icon_material("audio_listener_3d_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoAudioListener3D"), EditorStringName(EditorIcons)));
|
||||
}
|
||||
|
||||
bool AudioListener3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
|
||||
return Object::cast_to<AudioListener3D>(p_spatial) != nullptr;
|
||||
}
|
||||
|
||||
String AudioListener3DGizmoPlugin::get_gizmo_name() const {
|
||||
return "AudioListener3D";
|
||||
}
|
||||
|
||||
int AudioListener3DGizmoPlugin::get_priority() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void AudioListener3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
const Ref<Material> icon = get_material("audio_listener_3d_icon", p_gizmo);
|
||||
p_gizmo->add_unscaled_billboard(icon, 0.05);
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* audio_listener_3d_gizmo_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class AudioListener3DGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(AudioListener3DGizmoPlugin, EditorNode3DGizmoPlugin);
|
||||
|
||||
public:
|
||||
bool has_gizmo(Node3D *p_spatial) override;
|
||||
String get_gizmo_name() const override;
|
||||
int get_priority() const override;
|
||||
|
||||
void redraw(EditorNode3DGizmo *p_gizmo) override;
|
||||
|
||||
AudioListener3DGizmoPlugin();
|
||||
};
|
||||
|
|
@ -1,314 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* audio_stream_player_3d_gizmo_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 "audio_stream_player_3d_gizmo_plugin.h"
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "scene/3d/audio_stream_player_3d.h"
|
||||
|
||||
AudioStreamPlayer3DGizmoPlugin::AudioStreamPlayer3DGizmoPlugin() {
|
||||
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/stream_player_3d");
|
||||
|
||||
create_icon_material("stream_player_3d_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Gizmo3DSamplePlayer"), EditorStringName(EditorIcons)));
|
||||
create_material("stream_player_3d_material_primary", gizmo_color);
|
||||
create_material("stream_player_3d_material_secondary", gizmo_color * Color(1, 1, 1, 0.35));
|
||||
// Enable vertex colors for the billboard material as the gizmo color depends on the
|
||||
// AudioStreamPlayer3D attenuation type and source (Unit Size or Max Distance).
|
||||
create_material("stream_player_3d_material_billboard", Color(1, 1, 1), true, false, true);
|
||||
create_handle_material("handles");
|
||||
}
|
||||
|
||||
bool AudioStreamPlayer3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
|
||||
return Object::cast_to<AudioStreamPlayer3D>(p_spatial) != nullptr;
|
||||
}
|
||||
|
||||
String AudioStreamPlayer3DGizmoPlugin::get_gizmo_name() const {
|
||||
return "AudioStreamPlayer3D";
|
||||
}
|
||||
|
||||
int AudioStreamPlayer3DGizmoPlugin::get_priority() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
String AudioStreamPlayer3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
|
||||
return "Emission Radius";
|
||||
}
|
||||
|
||||
Variant AudioStreamPlayer3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
|
||||
AudioStreamPlayer3D *player = Object::cast_to<AudioStreamPlayer3D>(p_gizmo->get_node_3d());
|
||||
return player->get_emission_angle();
|
||||
}
|
||||
|
||||
void AudioStreamPlayer3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
|
||||
AudioStreamPlayer3D *player = Object::cast_to<AudioStreamPlayer3D>(p_gizmo->get_node_3d());
|
||||
|
||||
Transform3D gt = player->get_global_transform();
|
||||
Transform3D gi = gt.affine_inverse();
|
||||
|
||||
Vector3 ray_from = p_camera->project_ray_origin(p_point);
|
||||
Vector3 ray_dir = p_camera->project_ray_normal(p_point);
|
||||
Vector3 ray_to = ray_from + ray_dir * 4096;
|
||||
|
||||
ray_from = gi.xform(ray_from);
|
||||
ray_to = gi.xform(ray_to);
|
||||
|
||||
float closest_dist = 1e20;
|
||||
float closest_angle = 1e20;
|
||||
|
||||
for (int i = 0; i < 180; i++) {
|
||||
float a = Math::deg_to_rad((float)i);
|
||||
float an = Math::deg_to_rad((float)(i + 1));
|
||||
|
||||
Vector3 from(Math::sin(a), 0, -Math::cos(a));
|
||||
Vector3 to(Math::sin(an), 0, -Math::cos(an));
|
||||
|
||||
Vector3 r1, r2;
|
||||
Geometry3D::get_closest_points_between_segments(from, to, ray_from, ray_to, r1, r2);
|
||||
float d = r1.distance_to(r2);
|
||||
if (d < closest_dist) {
|
||||
closest_dist = d;
|
||||
closest_angle = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (closest_angle < 91) {
|
||||
player->set_emission_angle(closest_angle);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamPlayer3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
|
||||
AudioStreamPlayer3D *player = Object::cast_to<AudioStreamPlayer3D>(p_gizmo->get_node_3d());
|
||||
|
||||
if (p_cancel) {
|
||||
player->set_emission_angle(p_restore);
|
||||
|
||||
} else {
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
ur->create_action(TTR("Change AudioStreamPlayer3D Emission Angle"));
|
||||
ur->add_do_method(player, "set_emission_angle", player->get_emission_angle());
|
||||
ur->add_undo_method(player, "set_emission_angle", p_restore);
|
||||
ur->commit_action();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamPlayer3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
p_gizmo->clear();
|
||||
|
||||
if (p_gizmo->is_selected()) {
|
||||
const AudioStreamPlayer3D *player = Object::cast_to<AudioStreamPlayer3D>(p_gizmo->get_node_3d());
|
||||
|
||||
if (player->get_attenuation_model() != AudioStreamPlayer3D::ATTENUATION_DISABLED || player->get_max_distance() > CMP_EPSILON) {
|
||||
// Draw a circle to represent sound volume attenuation.
|
||||
// Use only a billboard circle to represent radius.
|
||||
// This helps distinguish AudioStreamPlayer3D gizmos from OmniLight3D gizmos.
|
||||
const Ref<Material> lines_billboard_material = get_material("stream_player_3d_material_billboard", p_gizmo);
|
||||
|
||||
// Soft distance cap varies depending on attenuation model, as some will fade out more aggressively than others.
|
||||
// Multipliers were empirically determined through testing.
|
||||
float soft_multiplier;
|
||||
switch (player->get_attenuation_model()) {
|
||||
case AudioStreamPlayer3D::ATTENUATION_INVERSE_DISTANCE:
|
||||
soft_multiplier = 12.0;
|
||||
break;
|
||||
case AudioStreamPlayer3D::ATTENUATION_INVERSE_SQUARE_DISTANCE:
|
||||
soft_multiplier = 4.0;
|
||||
break;
|
||||
case AudioStreamPlayer3D::ATTENUATION_LOGARITHMIC:
|
||||
soft_multiplier = 3.25;
|
||||
break;
|
||||
default:
|
||||
// Ensures Max Distance's radius visualization is not capped by Unit Size
|
||||
// (when the attenuation mode is Disabled).
|
||||
soft_multiplier = 10000.0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Draw the distance at which the sound can be reasonably heard.
|
||||
// This can be either a hard distance cap with the Max Distance property (if set above 0.0),
|
||||
// or a soft distance cap with the Unit Size property (sound never reaches true zero).
|
||||
// When Max Distance is 0.0, `r` represents the distance above which the
|
||||
// sound can't be heard in *most* (but not all) scenarios.
|
||||
float radius = player->get_unit_size() * soft_multiplier;
|
||||
if (player->get_max_distance() > CMP_EPSILON) {
|
||||
radius = MIN(radius, player->get_max_distance());
|
||||
}
|
||||
|
||||
#define PUSH_QUARTER_XY(m_from_x, m_from_y, m_to_x, m_to_y, m_y) \
|
||||
points_ptrw[index++] = Vector3(m_from_x, -m_from_y - m_y, 0); \
|
||||
points_ptrw[index++] = Vector3(m_to_x, -m_to_y - m_y, 0); \
|
||||
points_ptrw[index++] = Vector3(m_from_x, m_from_y + m_y, 0); \
|
||||
points_ptrw[index++] = Vector3(m_to_x, m_to_y + m_y, 0); \
|
||||
points_ptrw[index++] = Vector3(-m_from_x, -m_from_y - m_y, 0); \
|
||||
points_ptrw[index++] = Vector3(-m_to_x, -m_to_y - m_y, 0); \
|
||||
points_ptrw[index++] = Vector3(-m_from_x, m_from_y + m_y, 0); \
|
||||
points_ptrw[index++] = Vector3(-m_to_x, m_to_y + m_y, 0);
|
||||
|
||||
// Number of points in an octant. So there will be 8 * points_in_octant points in total.
|
||||
// This corresponds to the smoothness of the circle.
|
||||
const uint32_t points_in_octant = 15;
|
||||
const real_t octant_angle = Math::PI / 4;
|
||||
const real_t inc = (Math::PI / (4 * points_in_octant));
|
||||
const real_t radius_squared = radius * radius;
|
||||
real_t r = 0;
|
||||
|
||||
Vector<Vector3> points_billboard;
|
||||
points_billboard.resize(8 * points_in_octant * 2);
|
||||
Vector3 *points_ptrw = points_billboard.ptrw();
|
||||
|
||||
uint32_t index = 0;
|
||||
float previous_x = radius;
|
||||
float previous_y = 0.f;
|
||||
|
||||
for (uint32_t i = 0; i < points_in_octant; i++) {
|
||||
r += inc;
|
||||
real_t x = Math::cos((i == points_in_octant - 1) ? octant_angle : r) * radius;
|
||||
real_t y = Math::sqrt(radius_squared - (x * x));
|
||||
|
||||
PUSH_QUARTER_XY(previous_x, previous_y, x, y, 0);
|
||||
PUSH_QUARTER_XY(previous_y, previous_x, y, x, 0);
|
||||
previous_x = x;
|
||||
previous_y = y;
|
||||
}
|
||||
|
||||
#undef PUSH_QUARTER_XY
|
||||
|
||||
Color color;
|
||||
switch (player->get_attenuation_model()) {
|
||||
// Pick cold colors for all attenuation models (except Disabled),
|
||||
// so that soft caps can be easily distinguished from hard caps
|
||||
// (which use warm colors).
|
||||
case AudioStreamPlayer3D::ATTENUATION_INVERSE_DISTANCE:
|
||||
color = Color(0.4, 0.8, 1);
|
||||
break;
|
||||
case AudioStreamPlayer3D::ATTENUATION_INVERSE_SQUARE_DISTANCE:
|
||||
color = Color(0.4, 0.5, 1);
|
||||
break;
|
||||
case AudioStreamPlayer3D::ATTENUATION_LOGARITHMIC:
|
||||
color = Color(0.4, 0.2, 1);
|
||||
break;
|
||||
default:
|
||||
// Disabled attenuation mode.
|
||||
// This is never reached when Max Distance is 0, but the
|
||||
// hue-inverted form of this color will be used if Max Distance is greater than 0.
|
||||
color = Color(1, 1, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (player->get_max_distance() > CMP_EPSILON) {
|
||||
// Sound is hard-capped by max distance. The attenuation model still matters,
|
||||
// so invert the hue of the color that was chosen above.
|
||||
color.set_h(color.get_h() + 0.5);
|
||||
}
|
||||
|
||||
p_gizmo->add_lines(points_billboard, lines_billboard_material, true, color);
|
||||
}
|
||||
|
||||
if (player->is_emission_angle_enabled()) {
|
||||
const float ha = Math::deg_to_rad(player->get_emission_angle());
|
||||
const float ofs = -Math::cos(ha);
|
||||
const float radius = Math::sin(ha);
|
||||
|
||||
const uint32_t points_in_octant = 7;
|
||||
const real_t octant_angle = Math::PI / 4;
|
||||
const real_t inc = (Math::PI / (4 * points_in_octant));
|
||||
const real_t radius_squared = radius * radius;
|
||||
real_t r = 0;
|
||||
|
||||
Vector<Vector3> points_primary;
|
||||
points_primary.resize(8 * points_in_octant * 2);
|
||||
Vector3 *points_ptrw = points_primary.ptrw();
|
||||
|
||||
uint32_t index = 0;
|
||||
float previous_x = radius;
|
||||
float previous_y = 0.f;
|
||||
#define PUSH_QUARTER(m_from_x, m_from_y, m_to_x, m_to_y, m_y) \
|
||||
points_ptrw[index++] = Vector3(m_from_x, -m_from_y, m_y); \
|
||||
points_ptrw[index++] = Vector3(m_to_x, -m_to_y, m_y); \
|
||||
points_ptrw[index++] = Vector3(m_from_x, m_from_y, m_y); \
|
||||
points_ptrw[index++] = Vector3(m_to_x, m_to_y, m_y); \
|
||||
points_ptrw[index++] = Vector3(-m_from_x, -m_from_y, m_y); \
|
||||
points_ptrw[index++] = Vector3(-m_to_x, -m_to_y, m_y); \
|
||||
points_ptrw[index++] = Vector3(-m_from_x, m_from_y, m_y); \
|
||||
points_ptrw[index++] = Vector3(-m_to_x, m_to_y, m_y);
|
||||
|
||||
for (uint32_t i = 0; i < points_in_octant; i++) {
|
||||
r += inc;
|
||||
real_t x = Math::cos((i == points_in_octant - 1) ? octant_angle : r) * radius;
|
||||
real_t y = Math::sqrt(radius_squared - (x * x));
|
||||
|
||||
PUSH_QUARTER(previous_x, previous_y, x, y, ofs);
|
||||
PUSH_QUARTER(previous_y, previous_x, y, x, ofs);
|
||||
|
||||
previous_x = x;
|
||||
previous_y = y;
|
||||
}
|
||||
#undef PUSH_QUARTER
|
||||
|
||||
const Ref<Material> material_primary = get_material("stream_player_3d_material_primary", p_gizmo);
|
||||
p_gizmo->add_lines(points_primary, material_primary);
|
||||
|
||||
Vector<Vector3> points_secondary;
|
||||
points_secondary.resize(16);
|
||||
Vector3 *points_second_ptrw = points_secondary.ptrw();
|
||||
uint32_t index2 = 0;
|
||||
// Lines to the circle.
|
||||
points_second_ptrw[index2++] = Vector3();
|
||||
points_second_ptrw[index2++] = Vector3(radius, 0, ofs);
|
||||
points_second_ptrw[index2++] = Vector3();
|
||||
points_second_ptrw[index2++] = Vector3(-radius, 0, ofs);
|
||||
points_second_ptrw[index2++] = Vector3();
|
||||
points_second_ptrw[index2++] = Vector3(0, radius, ofs);
|
||||
points_second_ptrw[index2++] = Vector3();
|
||||
points_second_ptrw[index2++] = Vector3(0, -radius, ofs);
|
||||
real_t octant_value = Math::cos(octant_angle) * radius;
|
||||
points_second_ptrw[index2++] = Vector3();
|
||||
points_second_ptrw[index2++] = Vector3(octant_value, octant_value, ofs);
|
||||
points_second_ptrw[index2++] = Vector3();
|
||||
points_second_ptrw[index2++] = Vector3(-octant_value, octant_value, ofs);
|
||||
points_second_ptrw[index2++] = Vector3();
|
||||
points_second_ptrw[index2++] = Vector3(-octant_value, -octant_value, ofs);
|
||||
points_second_ptrw[index2++] = Vector3();
|
||||
points_second_ptrw[index2++] = Vector3(octant_value, -octant_value, ofs);
|
||||
|
||||
const Ref<Material> material_secondary = get_material("stream_player_3d_material_secondary", p_gizmo);
|
||||
p_gizmo->add_lines(points_secondary, material_secondary);
|
||||
|
||||
Vector<Vector3> handles;
|
||||
handles.push_back(Vector3(Math::sin(ha), 0, -Math::cos(ha)));
|
||||
p_gizmo->add_handles(handles, get_material("handles"));
|
||||
}
|
||||
}
|
||||
|
||||
const Ref<Material> icon = get_material("stream_player_3d_icon", p_gizmo);
|
||||
p_gizmo->add_unscaled_billboard(icon, 0.05);
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* audio_stream_player_3d_gizmo_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class AudioStreamPlayer3DGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(AudioStreamPlayer3DGizmoPlugin, EditorNode3DGizmoPlugin);
|
||||
|
||||
public:
|
||||
bool has_gizmo(Node3D *p_spatial) override;
|
||||
String get_gizmo_name() const override;
|
||||
int get_priority() const override;
|
||||
|
||||
String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
|
||||
Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
|
||||
void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) override;
|
||||
void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false) override;
|
||||
void redraw(EditorNode3DGizmo *p_gizmo) override;
|
||||
|
||||
AudioStreamPlayer3DGizmoPlugin();
|
||||
};
|
||||
|
|
@ -1,292 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* camera_3d_gizmo_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 "camera_3d_gizmo_plugin.h"
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||
#include "scene/3d/camera_3d.h"
|
||||
|
||||
Camera3DGizmoPlugin::Camera3DGizmoPlugin() {
|
||||
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/camera");
|
||||
|
||||
create_material("camera_material", gizmo_color);
|
||||
create_icon_material("camera_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoCamera3D"), EditorStringName(EditorIcons)));
|
||||
create_handle_material("handles");
|
||||
}
|
||||
|
||||
bool Camera3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
|
||||
return Object::cast_to<Camera3D>(p_spatial) != nullptr;
|
||||
}
|
||||
|
||||
String Camera3DGizmoPlugin::get_gizmo_name() const {
|
||||
return "Camera3D";
|
||||
}
|
||||
|
||||
int Camera3DGizmoPlugin::get_priority() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
String Camera3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
|
||||
Camera3D *camera = Object::cast_to<Camera3D>(p_gizmo->get_node_3d());
|
||||
|
||||
if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) {
|
||||
return "FOV";
|
||||
} else {
|
||||
return "Size";
|
||||
}
|
||||
}
|
||||
|
||||
Variant Camera3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
|
||||
Camera3D *camera = Object::cast_to<Camera3D>(p_gizmo->get_node_3d());
|
||||
|
||||
if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) {
|
||||
return camera->get_fov();
|
||||
} else {
|
||||
return camera->get_size();
|
||||
}
|
||||
}
|
||||
|
||||
void Camera3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
|
||||
Camera3D *camera = Object::cast_to<Camera3D>(p_gizmo->get_node_3d());
|
||||
|
||||
Transform3D gt = camera->get_global_transform();
|
||||
Transform3D gi = gt.affine_inverse();
|
||||
|
||||
Vector3 ray_from = p_camera->project_ray_origin(p_point);
|
||||
Vector3 ray_dir = p_camera->project_ray_normal(p_point);
|
||||
|
||||
Vector3 s[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) };
|
||||
|
||||
if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) {
|
||||
Transform3D gt2 = camera->get_global_transform();
|
||||
float a = _find_closest_angle_to_half_pi_arc(s[0], s[1], 1.0, gt2);
|
||||
camera->set("fov", CLAMP(a * 2.0, 1, 179));
|
||||
} else {
|
||||
Camera3D::KeepAspect aspect = camera->get_keep_aspect_mode();
|
||||
Vector3 camera_far = aspect == Camera3D::KeepAspect::KEEP_WIDTH ? Vector3(4096, 0, -1) : Vector3(0, 4096, -1);
|
||||
|
||||
Vector3 ra, rb;
|
||||
Geometry3D::get_closest_points_between_segments(Vector3(0, 0, -1), camera_far, s[0], s[1], ra, rb);
|
||||
float d = aspect == Camera3D::KeepAspect::KEEP_WIDTH ? ra.x * 2 : ra.y * 2;
|
||||
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
|
||||
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
|
||||
}
|
||||
|
||||
d = CLAMP(d, 0.1, 16384);
|
||||
|
||||
camera->set("size", d);
|
||||
}
|
||||
}
|
||||
|
||||
void Camera3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
|
||||
Camera3D *camera = Object::cast_to<Camera3D>(p_gizmo->get_node_3d());
|
||||
|
||||
if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) {
|
||||
if (p_cancel) {
|
||||
camera->set("fov", p_restore);
|
||||
} else {
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
ur->create_action(TTR("Change Camera FOV"));
|
||||
ur->add_do_property(camera, "fov", camera->get_fov());
|
||||
ur->add_undo_property(camera, "fov", p_restore);
|
||||
ur->commit_action();
|
||||
}
|
||||
|
||||
} else {
|
||||
if (p_cancel) {
|
||||
camera->set("size", p_restore);
|
||||
} else {
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
ur->create_action(TTR("Change Camera Size"));
|
||||
ur->add_do_property(camera, "size", camera->get_size());
|
||||
ur->add_undo_property(camera, "size", p_restore);
|
||||
ur->commit_action();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
Camera3D *camera = Object::cast_to<Camera3D>(p_gizmo->get_node_3d());
|
||||
|
||||
p_gizmo->clear();
|
||||
|
||||
Vector<Vector3> lines;
|
||||
Vector<Vector3> handles;
|
||||
|
||||
Ref<Material> material = get_material("camera_material", p_gizmo);
|
||||
Ref<Material> icon = get_material("camera_icon", p_gizmo);
|
||||
|
||||
const Size2i viewport_size = Node3DEditor::get_camera_viewport_size(camera);
|
||||
const real_t viewport_aspect = viewport_size.x > 0 && viewport_size.y > 0 ? viewport_size.aspect() : 1.0;
|
||||
const Size2 size_factor = viewport_aspect > 1.0 ? Size2(1.0, 1.0 / viewport_aspect) : Size2(viewport_aspect, 1.0);
|
||||
|
||||
#define ADD_TRIANGLE(m_a, m_b, m_c) \
|
||||
{ \
|
||||
lines.push_back(m_a); \
|
||||
lines.push_back(m_b); \
|
||||
lines.push_back(m_b); \
|
||||
lines.push_back(m_c); \
|
||||
lines.push_back(m_c); \
|
||||
lines.push_back(m_a); \
|
||||
}
|
||||
|
||||
#define ADD_QUAD(m_a, m_b, m_c, m_d) \
|
||||
{ \
|
||||
lines.push_back(m_a); \
|
||||
lines.push_back(m_b); \
|
||||
lines.push_back(m_b); \
|
||||
lines.push_back(m_c); \
|
||||
lines.push_back(m_c); \
|
||||
lines.push_back(m_d); \
|
||||
lines.push_back(m_d); \
|
||||
lines.push_back(m_a); \
|
||||
}
|
||||
|
||||
switch (camera->get_projection()) {
|
||||
case Camera3D::PROJECTION_PERSPECTIVE: {
|
||||
// The real FOV is halved for accurate representation
|
||||
float fov = camera->get_fov() / 2.0;
|
||||
|
||||
const float hsize = Math::sin(Math::deg_to_rad(fov));
|
||||
const float depth = -Math::cos(Math::deg_to_rad(fov));
|
||||
|
||||
Vector3 side;
|
||||
if (camera->get_keep_aspect_mode() == Camera3D::KEEP_WIDTH) {
|
||||
side = Vector3(hsize * size_factor.x, 0, depth * size_factor.x);
|
||||
} else {
|
||||
side = Vector3(hsize * size_factor.x, 0, depth * size_factor.y);
|
||||
}
|
||||
Vector3 nside = Vector3(-side.x, side.y, side.z);
|
||||
Vector3 up = Vector3(0, hsize * size_factor.y, 0);
|
||||
|
||||
ADD_TRIANGLE(Vector3(), side + up, side - up);
|
||||
ADD_TRIANGLE(Vector3(), nside + up, nside - up);
|
||||
ADD_TRIANGLE(Vector3(), side + up, nside + up);
|
||||
ADD_TRIANGLE(Vector3(), side - up, nside - up);
|
||||
|
||||
handles.push_back(side);
|
||||
side.x = MIN(side.x, hsize * 0.25);
|
||||
nside.x = -side.x;
|
||||
Vector3 tup(0, up.y + hsize / 2, side.z);
|
||||
ADD_TRIANGLE(tup, side + up, nside + up);
|
||||
} break;
|
||||
|
||||
case Camera3D::PROJECTION_ORTHOGONAL: {
|
||||
Camera3D::KeepAspect aspect = camera->get_keep_aspect_mode();
|
||||
|
||||
float size = camera->get_size();
|
||||
float keep_size = size * 0.5;
|
||||
|
||||
Vector3 right, up;
|
||||
Vector3 back(0, 0, -1.0);
|
||||
|
||||
if (aspect == Camera3D::KeepAspect::KEEP_WIDTH) {
|
||||
right = Vector3(keep_size, 0, 0);
|
||||
up = Vector3(0, keep_size / viewport_aspect, 0);
|
||||
handles.push_back(right + back);
|
||||
} else {
|
||||
right = Vector3(keep_size * viewport_aspect, 0, 0);
|
||||
up = Vector3(0, keep_size, 0);
|
||||
handles.push_back(up + back);
|
||||
}
|
||||
|
||||
ADD_QUAD(-up - right, -up + right, up + right, up - right);
|
||||
ADD_QUAD(-up - right + back, -up + right + back, up + right + back, up - right + back);
|
||||
ADD_QUAD(up + right, up + right + back, up - right + back, up - right);
|
||||
ADD_QUAD(-up + right, -up + right + back, -up - right + back, -up - right);
|
||||
|
||||
right.x = MIN(right.x, keep_size * 0.25);
|
||||
Vector3 tup(0, up.y + keep_size / 2, back.z);
|
||||
ADD_TRIANGLE(tup, right + up + back, -right + up + back);
|
||||
} break;
|
||||
|
||||
case Camera3D::PROJECTION_FRUSTUM: {
|
||||
float hsize = camera->get_size() / 2.0;
|
||||
|
||||
Vector3 side = Vector3(hsize, 0, -camera->get_near()).normalized();
|
||||
side.x *= size_factor.x;
|
||||
Vector3 nside = Vector3(-side.x, side.y, side.z);
|
||||
Vector3 up = Vector3(0, hsize * size_factor.y, 0);
|
||||
Vector3 offset = Vector3(camera->get_frustum_offset().x, camera->get_frustum_offset().y, 0.0);
|
||||
|
||||
ADD_TRIANGLE(Vector3(), side + up + offset, side - up + offset);
|
||||
ADD_TRIANGLE(Vector3(), nside + up + offset, nside - up + offset);
|
||||
ADD_TRIANGLE(Vector3(), side + up + offset, nside + up + offset);
|
||||
ADD_TRIANGLE(Vector3(), side - up + offset, nside - up + offset);
|
||||
|
||||
side.x = MIN(side.x, hsize * 0.25);
|
||||
nside.x = -side.x;
|
||||
Vector3 tup(0, up.y + hsize / 2, side.z);
|
||||
ADD_TRIANGLE(tup + offset, side + up + offset, nside + up + offset);
|
||||
} break;
|
||||
}
|
||||
|
||||
#undef ADD_TRIANGLE
|
||||
#undef ADD_QUAD
|
||||
|
||||
p_gizmo->add_lines(lines, material);
|
||||
p_gizmo->add_unscaled_billboard(icon, 0.05);
|
||||
p_gizmo->add_collision_segments(lines);
|
||||
|
||||
if (!handles.is_empty()) {
|
||||
p_gizmo->add_handles(handles, get_material("handles"));
|
||||
}
|
||||
}
|
||||
|
||||
float Camera3DGizmoPlugin::_find_closest_angle_to_half_pi_arc(const Vector3 &p_from, const Vector3 &p_to, float p_arc_radius, const Transform3D &p_arc_xform) {
|
||||
//bleh, discrete is simpler
|
||||
static const int arc_test_points = 64;
|
||||
float min_d = 1e20;
|
||||
Vector3 min_p;
|
||||
|
||||
for (int i = 0; i < arc_test_points; i++) {
|
||||
float a = i * Math::PI * 0.5 / arc_test_points;
|
||||
float an = (i + 1) * Math::PI * 0.5 / arc_test_points;
|
||||
Vector3 p = Vector3(Math::cos(a), 0, -Math::sin(a)) * p_arc_radius;
|
||||
Vector3 n = Vector3(Math::cos(an), 0, -Math::sin(an)) * p_arc_radius;
|
||||
|
||||
Vector3 ra, rb;
|
||||
Geometry3D::get_closest_points_between_segments(p, n, p_from, p_to, ra, rb);
|
||||
|
||||
float d = ra.distance_to(rb);
|
||||
if (d < min_d) {
|
||||
min_d = d;
|
||||
min_p = ra;
|
||||
}
|
||||
}
|
||||
|
||||
//min_p = p_arc_xform.affine_inverse().xform(min_p);
|
||||
float a = (Math::PI * 0.5) - Vector2(min_p.x, -min_p.z).angle();
|
||||
return Math::rad_to_deg(a);
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* camera_3d_gizmo_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class Camera3DGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(Camera3DGizmoPlugin, EditorNode3DGizmoPlugin);
|
||||
|
||||
private:
|
||||
static float _find_closest_angle_to_half_pi_arc(const Vector3 &p_from, const Vector3 &p_to, float p_arc_radius, const Transform3D &p_arc_xform);
|
||||
|
||||
public:
|
||||
bool has_gizmo(Node3D *p_spatial) override;
|
||||
String get_gizmo_name() const override;
|
||||
int get_priority() const override;
|
||||
|
||||
String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
|
||||
Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
|
||||
void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) override;
|
||||
void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false) override;
|
||||
void redraw(EditorNode3DGizmo *p_gizmo) override;
|
||||
|
||||
Camera3DGizmoPlugin();
|
||||
};
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* collision_object_3d_gizmo_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 "collision_object_3d_gizmo_plugin.h"
|
||||
|
||||
#include "scene/3d/physics/collision_object_3d.h"
|
||||
#include "scene/3d/physics/collision_polygon_3d.h"
|
||||
#include "scene/3d/physics/collision_shape_3d.h"
|
||||
#include "scene/resources/surface_tool.h"
|
||||
|
||||
CollisionObject3DGizmoPlugin::CollisionObject3DGizmoPlugin() {
|
||||
const Color gizmo_color = SceneTree::get_singleton()->get_debug_collisions_color();
|
||||
create_material("shape_material", gizmo_color);
|
||||
const float gizmo_value = gizmo_color.get_v();
|
||||
const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65);
|
||||
create_material("shape_material_disabled", gizmo_color_disabled);
|
||||
}
|
||||
|
||||
bool CollisionObject3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
|
||||
return Object::cast_to<CollisionObject3D>(p_spatial) != nullptr;
|
||||
}
|
||||
|
||||
String CollisionObject3DGizmoPlugin::get_gizmo_name() const {
|
||||
return "CollisionObject3D";
|
||||
}
|
||||
|
||||
int CollisionObject3DGizmoPlugin::get_priority() const {
|
||||
return -2;
|
||||
}
|
||||
|
||||
void CollisionObject3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_gizmo->get_node_3d());
|
||||
|
||||
p_gizmo->clear();
|
||||
|
||||
List<uint32_t> owner_ids;
|
||||
co->get_shape_owners(&owner_ids);
|
||||
for (uint32_t &owner_id : owner_ids) {
|
||||
Transform3D xform = co->shape_owner_get_transform(owner_id);
|
||||
Object *owner = co->shape_owner_get_owner(owner_id);
|
||||
// Exclude CollisionShape3D and CollisionPolygon3D as they have their gizmo.
|
||||
if (!Object::cast_to<CollisionShape3D>(owner) && !Object::cast_to<CollisionPolygon3D>(owner)) {
|
||||
Ref<Material> material = get_material(!co->is_shape_owner_disabled(owner_id) ? "shape_material" : "shape_material_disabled", p_gizmo);
|
||||
for (int shape_id = 0; shape_id < co->shape_owner_get_shape_count(owner_id); shape_id++) {
|
||||
Ref<Shape3D> s = co->shape_owner_get_shape(owner_id, shape_id);
|
||||
if (s.is_null()) {
|
||||
continue;
|
||||
}
|
||||
SurfaceTool st;
|
||||
st.append_from(s->get_debug_mesh(), 0, xform);
|
||||
|
||||
p_gizmo->add_mesh(st.commit(), material);
|
||||
p_gizmo->add_collision_segments(s->get_debug_mesh_lines());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* collision_object_3d_gizmo_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class CollisionObject3DGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(CollisionObject3DGizmoPlugin, EditorNode3DGizmoPlugin);
|
||||
|
||||
public:
|
||||
bool has_gizmo(Node3D *p_spatial) override;
|
||||
String get_gizmo_name() const override;
|
||||
int get_priority() const override;
|
||||
void redraw(EditorNode3DGizmo *p_gizmo) override;
|
||||
|
||||
CollisionObject3DGizmoPlugin();
|
||||
};
|
||||
|
|
@ -1,237 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* collision_polygon_3d_gizmo_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 "collision_polygon_3d_gizmo_plugin.h"
|
||||
|
||||
#include "core/math/geometry_2d.h"
|
||||
#include "scene/3d/physics/collision_polygon_3d.h"
|
||||
|
||||
CollisionPolygon3DGizmoPlugin::CollisionPolygon3DGizmoPlugin() {
|
||||
create_collision_material("shape_material", 2.0);
|
||||
create_collision_material("shape_material_arraymesh", 0.0625);
|
||||
|
||||
create_collision_material("shape_material_disabled", 0.0625);
|
||||
create_collision_material("shape_material_arraymesh_disabled", 0.015625);
|
||||
}
|
||||
|
||||
void CollisionPolygon3DGizmoPlugin::create_collision_material(const String &p_name, float p_alpha) {
|
||||
Vector<Ref<StandardMaterial3D>> mats;
|
||||
|
||||
const Color collision_color(1.0, 1.0, 1.0, p_alpha);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
bool instantiated = i < 2;
|
||||
|
||||
Ref<StandardMaterial3D> material;
|
||||
material.instantiate();
|
||||
|
||||
Color color = collision_color;
|
||||
color.a *= instantiated ? 0.25 : 1.0;
|
||||
|
||||
material->set_albedo(color);
|
||||
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
|
||||
material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
|
||||
material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1);
|
||||
material->set_cull_mode(StandardMaterial3D::CULL_BACK);
|
||||
material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
|
||||
material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
|
||||
material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
|
||||
|
||||
mats.push_back(material);
|
||||
}
|
||||
|
||||
materials[p_name] = mats;
|
||||
}
|
||||
|
||||
bool CollisionPolygon3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
|
||||
return Object::cast_to<CollisionPolygon3D>(p_spatial) != nullptr;
|
||||
}
|
||||
|
||||
String CollisionPolygon3DGizmoPlugin::get_gizmo_name() const {
|
||||
return "CollisionPolygon3D";
|
||||
}
|
||||
|
||||
int CollisionPolygon3DGizmoPlugin::get_priority() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void CollisionPolygon3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
CollisionPolygon3D *polygon = Object::cast_to<CollisionPolygon3D>(p_gizmo->get_node_3d());
|
||||
|
||||
p_gizmo->clear();
|
||||
|
||||
const Ref<StandardMaterial3D> material =
|
||||
get_material(!polygon->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo);
|
||||
const Ref<StandardMaterial3D> material_arraymesh =
|
||||
get_material(!polygon->is_disabled() ? "shape_material_arraymesh" : "shape_material_arraymesh_disabled", p_gizmo);
|
||||
|
||||
const Color collision_color = polygon->is_disabled() ? Color(1.0, 1.0, 1.0, 0.75) : polygon->get_debug_color();
|
||||
|
||||
Vector<Vector2> points = polygon->get_polygon();
|
||||
float depth = polygon->get_depth() * 0.5;
|
||||
|
||||
Vector<Vector3> lines;
|
||||
const int points_size = points.size();
|
||||
|
||||
for (int i = 0; i < points_size; i++) {
|
||||
int n = (i + 1) % points_size;
|
||||
lines.push_back(Vector3(points[i].x, points[i].y, depth));
|
||||
lines.push_back(Vector3(points[n].x, points[n].y, depth));
|
||||
lines.push_back(Vector3(points[i].x, points[i].y, -depth));
|
||||
lines.push_back(Vector3(points[n].x, points[n].y, -depth));
|
||||
lines.push_back(Vector3(points[i].x, points[i].y, depth));
|
||||
lines.push_back(Vector3(points[i].x, points[i].y, -depth));
|
||||
}
|
||||
|
||||
if (polygon->get_debug_fill_enabled()) {
|
||||
Ref<ArrayMesh> array_mesh;
|
||||
array_mesh.instantiate();
|
||||
|
||||
Vector<Vector3> verts;
|
||||
Vector<Color> colors;
|
||||
Vector<int> indices;
|
||||
|
||||
// Determine orientation of the 2D polygon's vertices to determine
|
||||
// which direction to draw outer polygons.
|
||||
float signed_area = 0.0f;
|
||||
for (int i = 0; i < points_size; i++) {
|
||||
const int j = (i + 1) % points_size;
|
||||
signed_area += points[i].x * points[j].y - points[j].x * points[i].y;
|
||||
}
|
||||
|
||||
// Generate triangles for the sides of the extruded polygon.
|
||||
for (int i = 0; i < points_size; i++) {
|
||||
verts.push_back(Vector3(points[i].x, points[i].y, depth));
|
||||
verts.push_back(Vector3(points[i].x, points[i].y, -depth));
|
||||
|
||||
colors.push_back(collision_color);
|
||||
colors.push_back(collision_color);
|
||||
}
|
||||
|
||||
const int verts_size = verts.size();
|
||||
for (int i = 0; i < verts_size; i += 2) {
|
||||
const int j = (i + 1) % verts_size;
|
||||
const int k = (i + 2) % verts_size;
|
||||
const int l = (i + 3) % verts_size;
|
||||
|
||||
indices.push_back(i);
|
||||
if (signed_area < 0) {
|
||||
indices.push_back(j);
|
||||
indices.push_back(k);
|
||||
} else {
|
||||
indices.push_back(k);
|
||||
indices.push_back(j);
|
||||
}
|
||||
|
||||
indices.push_back(j);
|
||||
if (signed_area < 0) {
|
||||
indices.push_back(l);
|
||||
indices.push_back(k);
|
||||
} else {
|
||||
indices.push_back(k);
|
||||
indices.push_back(l);
|
||||
}
|
||||
}
|
||||
|
||||
Vector<Vector<Vector2>> decomp = Geometry2D::decompose_polygon_in_convex(polygon->get_polygon());
|
||||
|
||||
// Generate triangles for the bottom cap of the extruded polygon.
|
||||
for (int i = 0; i < decomp.size(); i++) {
|
||||
Vector<Vector3> cap_verts_bottom;
|
||||
Vector<Color> cap_colours_bottom;
|
||||
Vector<int> cap_indices_bottom;
|
||||
|
||||
const int index_offset = verts_size;
|
||||
|
||||
const Vector<Vector2> &convex = decomp[i];
|
||||
const int convex_size = convex.size();
|
||||
|
||||
for (int j = 0; j < convex_size; j++) {
|
||||
cap_verts_bottom.push_back(Vector3(convex[j].x, convex[j].y, -depth));
|
||||
cap_colours_bottom.push_back(collision_color);
|
||||
}
|
||||
|
||||
if (convex_size >= 3) {
|
||||
for (int j = 1; j < convex_size; j++) {
|
||||
const int k = (j + 1) % convex_size;
|
||||
|
||||
cap_indices_bottom.push_back(index_offset + 0);
|
||||
cap_indices_bottom.push_back(index_offset + j);
|
||||
cap_indices_bottom.push_back(index_offset + k);
|
||||
}
|
||||
}
|
||||
verts.append_array(cap_verts_bottom);
|
||||
colors.append_array(cap_colours_bottom);
|
||||
indices.append_array(cap_indices_bottom);
|
||||
}
|
||||
|
||||
// Generate triangles for the top cap of the extruded polygon.
|
||||
for (int i = 0; i < decomp.size(); i++) {
|
||||
Vector<Vector3> cap_verts_top;
|
||||
Vector<Color> cap_colours_top;
|
||||
Vector<int> cap_indices_top;
|
||||
|
||||
const int index_offset = verts_size;
|
||||
|
||||
const Vector<Vector2> &convex = decomp[i];
|
||||
const int convex_size = convex.size();
|
||||
|
||||
for (int j = 0; j < convex_size; j++) {
|
||||
cap_verts_top.push_back(Vector3(convex[j].x, convex[j].y, depth));
|
||||
cap_colours_top.push_back(collision_color);
|
||||
}
|
||||
|
||||
if (convex_size >= 3) {
|
||||
for (int j = 1; j < convex_size; j++) {
|
||||
const int k = (j + 1) % convex_size;
|
||||
|
||||
cap_indices_top.push_back(index_offset + k);
|
||||
cap_indices_top.push_back(index_offset + j);
|
||||
cap_indices_top.push_back(index_offset + 0);
|
||||
}
|
||||
}
|
||||
verts.append_array(cap_verts_top);
|
||||
colors.append_array(cap_colours_top);
|
||||
indices.append_array(cap_indices_top);
|
||||
}
|
||||
|
||||
Array a;
|
||||
a.resize(Mesh::ARRAY_MAX);
|
||||
a[RS::ARRAY_VERTEX] = verts;
|
||||
a[RS::ARRAY_COLOR] = colors;
|
||||
a[RS::ARRAY_INDEX] = indices;
|
||||
array_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a);
|
||||
|
||||
p_gizmo->add_mesh(array_mesh, material_arraymesh);
|
||||
}
|
||||
|
||||
p_gizmo->add_lines(lines, material, false, collision_color);
|
||||
p_gizmo->add_collision_segments(lines);
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* collision_polygon_3d_gizmo_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class CollisionPolygon3DGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(CollisionPolygon3DGizmoPlugin, EditorNode3DGizmoPlugin);
|
||||
|
||||
void create_collision_material(const String &p_name, float p_alpha);
|
||||
|
||||
public:
|
||||
bool has_gizmo(Node3D *p_spatial) override;
|
||||
String get_gizmo_name() const override;
|
||||
int get_priority() const override;
|
||||
void redraw(EditorNode3DGizmo *p_gizmo) override;
|
||||
CollisionPolygon3DGizmoPlugin();
|
||||
};
|
||||
|
|
@ -1,669 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* collision_shape_3d_gizmo_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 "collision_shape_3d_gizmo_plugin.h"
|
||||
|
||||
#include "core/math/convex_hull.h"
|
||||
#include "core/math/geometry_3d.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/plugins/gizmos/gizmo_3d_helper.h"
|
||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||
#include "scene/3d/physics/collision_shape_3d.h"
|
||||
#include "scene/resources/3d/box_shape_3d.h"
|
||||
#include "scene/resources/3d/capsule_shape_3d.h"
|
||||
#include "scene/resources/3d/concave_polygon_shape_3d.h"
|
||||
#include "scene/resources/3d/convex_polygon_shape_3d.h"
|
||||
#include "scene/resources/3d/cylinder_shape_3d.h"
|
||||
#include "scene/resources/3d/height_map_shape_3d.h"
|
||||
#include "scene/resources/3d/separation_ray_shape_3d.h"
|
||||
#include "scene/resources/3d/sphere_shape_3d.h"
|
||||
#include "scene/resources/3d/world_boundary_shape_3d.h"
|
||||
|
||||
CollisionShape3DGizmoPlugin::CollisionShape3DGizmoPlugin() {
|
||||
helper.instantiate();
|
||||
|
||||
create_collision_material("shape_material", 2.0);
|
||||
create_collision_material("shape_material_arraymesh", 0.0625);
|
||||
|
||||
create_collision_material("shape_material_disabled", 0.0625);
|
||||
create_collision_material("shape_material_arraymesh_disabled", 0.015625);
|
||||
|
||||
create_handle_material("handles");
|
||||
}
|
||||
|
||||
void CollisionShape3DGizmoPlugin::create_collision_material(const String &p_name, float p_alpha) {
|
||||
Vector<Ref<StandardMaterial3D>> mats;
|
||||
|
||||
const Color collision_color(1.0, 1.0, 1.0, p_alpha);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
bool instantiated = i < 2;
|
||||
|
||||
Ref<StandardMaterial3D> material = memnew(StandardMaterial3D);
|
||||
|
||||
Color color = collision_color;
|
||||
color.a *= instantiated ? 0.25 : 1.0;
|
||||
|
||||
material->set_albedo(color);
|
||||
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
|
||||
material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
|
||||
material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1);
|
||||
material->set_cull_mode(StandardMaterial3D::CULL_BACK);
|
||||
material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
|
||||
material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
|
||||
material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
|
||||
|
||||
mats.push_back(material);
|
||||
}
|
||||
|
||||
materials[p_name] = mats;
|
||||
}
|
||||
|
||||
bool CollisionShape3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
|
||||
return Object::cast_to<CollisionShape3D>(p_spatial) != nullptr;
|
||||
}
|
||||
|
||||
String CollisionShape3DGizmoPlugin::get_gizmo_name() const {
|
||||
return "CollisionShape3D";
|
||||
}
|
||||
|
||||
int CollisionShape3DGizmoPlugin::get_priority() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
String CollisionShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
|
||||
const CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d());
|
||||
|
||||
Ref<Shape3D> s = cs->get_shape();
|
||||
if (s.is_null()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (Object::cast_to<SphereShape3D>(*s)) {
|
||||
return "Radius";
|
||||
}
|
||||
|
||||
if (Object::cast_to<BoxShape3D>(*s)) {
|
||||
return helper->box_get_handle_name(p_id);
|
||||
}
|
||||
|
||||
if (Object::cast_to<CapsuleShape3D>(*s)) {
|
||||
return helper->capsule_get_handle_name(p_id);
|
||||
}
|
||||
|
||||
if (Object::cast_to<CylinderShape3D>(*s)) {
|
||||
return helper->cylinder_get_handle_name(p_id);
|
||||
}
|
||||
|
||||
if (Object::cast_to<SeparationRayShape3D>(*s)) {
|
||||
return "Length";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
Variant CollisionShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
|
||||
CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d());
|
||||
|
||||
Ref<Shape3D> s = cs->get_shape();
|
||||
if (s.is_null()) {
|
||||
return Variant();
|
||||
}
|
||||
|
||||
if (Object::cast_to<SphereShape3D>(*s)) {
|
||||
Ref<SphereShape3D> ss = s;
|
||||
return ss->get_radius();
|
||||
}
|
||||
|
||||
if (Object::cast_to<BoxShape3D>(*s)) {
|
||||
Ref<BoxShape3D> bs = s;
|
||||
return bs->get_size();
|
||||
}
|
||||
|
||||
if (Object::cast_to<CapsuleShape3D>(*s)) {
|
||||
Ref<CapsuleShape3D> cs2 = s;
|
||||
return Vector2(cs2->get_radius(), cs2->get_height());
|
||||
}
|
||||
|
||||
if (Object::cast_to<CylinderShape3D>(*s)) {
|
||||
Ref<CylinderShape3D> cs2 = s;
|
||||
return Vector2(cs2->get_radius(), cs2->get_height());
|
||||
}
|
||||
|
||||
if (Object::cast_to<SeparationRayShape3D>(*s)) {
|
||||
Ref<SeparationRayShape3D> cs2 = s;
|
||||
return cs2->get_length();
|
||||
}
|
||||
|
||||
return Variant();
|
||||
}
|
||||
|
||||
void CollisionShape3DGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) {
|
||||
helper->initialize_handle_action(get_handle_value(p_gizmo, p_id, p_secondary), p_gizmo->get_node_3d()->get_global_transform());
|
||||
}
|
||||
|
||||
void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
|
||||
CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d());
|
||||
|
||||
Ref<Shape3D> s = cs->get_shape();
|
||||
if (s.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 sg[2];
|
||||
helper->get_segment(p_camera, p_point, sg);
|
||||
|
||||
if (Object::cast_to<SphereShape3D>(*s)) {
|
||||
Ref<SphereShape3D> ss = s;
|
||||
Vector3 ra, rb;
|
||||
Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb);
|
||||
float d = ra.x;
|
||||
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
|
||||
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
|
||||
}
|
||||
|
||||
if (d < 0.001) {
|
||||
d = 0.001;
|
||||
}
|
||||
|
||||
ss->set_radius(d);
|
||||
}
|
||||
|
||||
if (Object::cast_to<SeparationRayShape3D>(*s)) {
|
||||
Ref<SeparationRayShape3D> rs = s;
|
||||
Vector3 ra, rb;
|
||||
Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(0, 0, 4096), sg[0], sg[1], ra, rb);
|
||||
float d = ra.z;
|
||||
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
|
||||
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
|
||||
}
|
||||
|
||||
if (d < 0.001) {
|
||||
d = 0.001;
|
||||
}
|
||||
|
||||
rs->set_length(d);
|
||||
}
|
||||
|
||||
if (Object::cast_to<BoxShape3D>(*s)) {
|
||||
Ref<BoxShape3D> bs = s;
|
||||
Vector3 size = bs->get_size();
|
||||
Vector3 position;
|
||||
helper->box_set_handle(sg, p_id, size, position);
|
||||
bs->set_size(size);
|
||||
cs->set_global_position(position);
|
||||
}
|
||||
|
||||
if (Object::cast_to<CapsuleShape3D>(*s)) {
|
||||
Ref<CapsuleShape3D> cs2 = s;
|
||||
|
||||
real_t height = cs2->get_height();
|
||||
real_t radius = cs2->get_radius();
|
||||
Vector3 position;
|
||||
helper->capsule_set_handle(sg, p_id, height, radius, position);
|
||||
cs2->set_height(height);
|
||||
cs2->set_radius(radius);
|
||||
cs->set_global_position(position);
|
||||
}
|
||||
|
||||
if (Object::cast_to<CylinderShape3D>(*s)) {
|
||||
Ref<CylinderShape3D> cs2 = s;
|
||||
|
||||
real_t height = cs2->get_height();
|
||||
real_t radius = cs2->get_radius();
|
||||
Vector3 position;
|
||||
helper->cylinder_set_handle(sg, p_id, height, radius, position);
|
||||
cs2->set_height(height);
|
||||
cs2->set_radius(radius);
|
||||
cs->set_global_position(position);
|
||||
}
|
||||
}
|
||||
|
||||
void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
|
||||
CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d());
|
||||
|
||||
Ref<Shape3D> s = cs->get_shape();
|
||||
if (s.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Object::cast_to<SphereShape3D>(*s)) {
|
||||
Ref<SphereShape3D> ss = s;
|
||||
if (p_cancel) {
|
||||
ss->set_radius(p_restore);
|
||||
return;
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
ur->create_action(TTR("Change Sphere Shape Radius"));
|
||||
ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
|
||||
ur->add_undo_method(ss.ptr(), "set_radius", p_restore);
|
||||
ur->commit_action();
|
||||
}
|
||||
|
||||
if (Object::cast_to<BoxShape3D>(*s)) {
|
||||
helper->box_commit_handle(TTR("Change Box Shape Size"), p_cancel, cs, s.ptr());
|
||||
}
|
||||
|
||||
if (Object::cast_to<CapsuleShape3D>(*s)) {
|
||||
Ref<CapsuleShape3D> ss = s;
|
||||
helper->cylinder_commit_handle(p_id, TTR("Change Capsule Shape Radius"), TTR("Change Capsule Shape Height"), p_cancel, cs, *ss, *ss);
|
||||
}
|
||||
|
||||
if (Object::cast_to<CylinderShape3D>(*s)) {
|
||||
Ref<CylinderShape3D> ss = s;
|
||||
helper->cylinder_commit_handle(p_id, TTR("Change Cylinder Shape Radius"), TTR("Change Cylinder Shape Height"), p_cancel, cs, *ss, *ss);
|
||||
}
|
||||
|
||||
if (Object::cast_to<SeparationRayShape3D>(*s)) {
|
||||
Ref<SeparationRayShape3D> ss = s;
|
||||
if (p_cancel) {
|
||||
ss->set_length(p_restore);
|
||||
return;
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
ur->create_action(TTR("Change Separation Ray Shape Length"));
|
||||
ur->add_do_method(ss.ptr(), "set_length", ss->get_length());
|
||||
ur->add_undo_method(ss.ptr(), "set_length", p_restore);
|
||||
ur->commit_action();
|
||||
}
|
||||
}
|
||||
|
||||
void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d());
|
||||
|
||||
p_gizmo->clear();
|
||||
|
||||
Ref<Shape3D> s = cs->get_shape();
|
||||
if (s.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Ref<StandardMaterial3D> material =
|
||||
get_material(!cs->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo);
|
||||
const Ref<StandardMaterial3D> material_arraymesh =
|
||||
get_material(!cs->is_disabled() ? "shape_material_arraymesh" : "shape_material_arraymesh_disabled", p_gizmo);
|
||||
const Ref<Material> handles_material = get_material("handles");
|
||||
|
||||
const Color collision_color = cs->is_disabled() ? Color(1.0, 1.0, 1.0, 0.75) : cs->get_debug_color();
|
||||
|
||||
if (cs->get_debug_fill_enabled()) {
|
||||
Ref<ArrayMesh> array_mesh = s->get_debug_arraymesh_faces(collision_color);
|
||||
if (array_mesh.is_valid() && array_mesh->get_surface_count() > 0) {
|
||||
p_gizmo->add_mesh(array_mesh, material_arraymesh);
|
||||
}
|
||||
}
|
||||
|
||||
if (Object::cast_to<SphereShape3D>(*s)) {
|
||||
Ref<SphereShape3D> sp = s;
|
||||
float radius = sp->get_radius();
|
||||
|
||||
#define PUSH_QUARTER(from_x, from_y, to_x, to_y, y) \
|
||||
points_ptrw[index++] = Vector3(from_x, y, from_y); \
|
||||
points_ptrw[index++] = Vector3(to_x, y, to_y); \
|
||||
points_ptrw[index++] = Vector3(from_x, y, -from_y); \
|
||||
points_ptrw[index++] = Vector3(to_x, y, -to_y); \
|
||||
points_ptrw[index++] = Vector3(-from_x, y, from_y); \
|
||||
points_ptrw[index++] = Vector3(-to_x, y, to_y); \
|
||||
points_ptrw[index++] = Vector3(-from_x, y, -from_y); \
|
||||
points_ptrw[index++] = Vector3(-to_x, y, -to_y);
|
||||
|
||||
#define PUSH_QUARTER_XY(from_x, from_y, to_x, to_y, y) \
|
||||
points_ptrw[index++] = Vector3(from_x, -from_y - y, 0); \
|
||||
points_ptrw[index++] = Vector3(to_x, -to_y - y, 0); \
|
||||
points_ptrw[index++] = Vector3(from_x, from_y + y, 0); \
|
||||
points_ptrw[index++] = Vector3(to_x, to_y + y, 0); \
|
||||
points_ptrw[index++] = Vector3(-from_x, -from_y - y, 0); \
|
||||
points_ptrw[index++] = Vector3(-to_x, -to_y - y, 0); \
|
||||
points_ptrw[index++] = Vector3(-from_x, from_y + y, 0); \
|
||||
points_ptrw[index++] = Vector3(-to_x, to_y + y, 0);
|
||||
|
||||
#define PUSH_QUARTER_YZ(from_x, from_y, to_x, to_y, y) \
|
||||
points_ptrw[index++] = Vector3(0, -from_y - y, from_x); \
|
||||
points_ptrw[index++] = Vector3(0, -to_y - y, to_x); \
|
||||
points_ptrw[index++] = Vector3(0, from_y + y, from_x); \
|
||||
points_ptrw[index++] = Vector3(0, to_y + y, to_x); \
|
||||
points_ptrw[index++] = Vector3(0, -from_y - y, -from_x); \
|
||||
points_ptrw[index++] = Vector3(0, -to_y - y, -to_x); \
|
||||
points_ptrw[index++] = Vector3(0, from_y + y, -from_x); \
|
||||
points_ptrw[index++] = Vector3(0, to_y + y, -to_x);
|
||||
|
||||
// Number of points in an octant. So there will be 8 * points_in_octant * 2 points in total for one circle.
|
||||
// This Corresponds to the smoothness of the circle.
|
||||
const uint32_t points_in_octant = 16;
|
||||
const real_t inc = (Math::PI / (4 * points_in_octant));
|
||||
const real_t radius_squared = radius * radius;
|
||||
real_t r = 0;
|
||||
|
||||
Vector<Vector3> points;
|
||||
uint32_t index = 0;
|
||||
// 3 full circles.
|
||||
points.resize(3 * 8 * points_in_octant * 2);
|
||||
Vector3 *points_ptrw = points.ptrw();
|
||||
|
||||
float previous_x = radius;
|
||||
float previous_y = 0.f;
|
||||
|
||||
for (uint32_t i = 0; i < points_in_octant; ++i) {
|
||||
r += inc;
|
||||
real_t x = Math::cos(r) * radius;
|
||||
real_t y = Math::sqrt(radius_squared - (x * x));
|
||||
|
||||
PUSH_QUARTER(previous_x, previous_y, x, y, 0);
|
||||
PUSH_QUARTER(previous_y, previous_x, y, x, 0);
|
||||
|
||||
PUSH_QUARTER_XY(previous_x, previous_y, x, y, 0);
|
||||
PUSH_QUARTER_XY(previous_y, previous_x, y, x, 0);
|
||||
|
||||
PUSH_QUARTER_YZ(previous_x, previous_y, x, y, 0);
|
||||
PUSH_QUARTER_YZ(previous_y, previous_x, y, x, 0)
|
||||
|
||||
previous_x = x;
|
||||
previous_y = y;
|
||||
}
|
||||
#undef PUSH_QUARTER
|
||||
#undef PUSH_QUARTER_XY
|
||||
#undef PUSH_QUARTER_YZ
|
||||
|
||||
p_gizmo->add_lines(points, material, false, collision_color);
|
||||
p_gizmo->add_collision_segments(points);
|
||||
Vector<Vector3> handles;
|
||||
handles.push_back(Vector3(r, 0, 0));
|
||||
p_gizmo->add_handles(handles, handles_material);
|
||||
}
|
||||
|
||||
if (Object::cast_to<BoxShape3D>(*s)) {
|
||||
Ref<BoxShape3D> bs = s;
|
||||
Vector<Vector3> lines;
|
||||
AABB aabb;
|
||||
aabb.position = -bs->get_size() / 2;
|
||||
aabb.size = bs->get_size();
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
Vector3 a, b;
|
||||
aabb.get_edge(i, a, b);
|
||||
lines.push_back(a);
|
||||
lines.push_back(b);
|
||||
}
|
||||
|
||||
const Vector<Vector3> handles = helper->box_get_handles(bs->get_size());
|
||||
|
||||
p_gizmo->add_lines(lines, material, false, collision_color);
|
||||
p_gizmo->add_collision_segments(lines);
|
||||
p_gizmo->add_handles(handles, handles_material);
|
||||
}
|
||||
|
||||
if (Object::cast_to<CapsuleShape3D>(*s)) {
|
||||
Ref<CapsuleShape3D> cs2 = s;
|
||||
float radius = cs2->get_radius();
|
||||
float height = cs2->get_height();
|
||||
|
||||
// Number of points in an octant. So there will be 8 * points_in_octant points in total.
|
||||
// This corresponds to the smoothness of the circle.
|
||||
const uint32_t points_in_octant = 16;
|
||||
const real_t octant_angle = Math::PI / 4;
|
||||
const real_t inc = (Math::PI / (4 * points_in_octant));
|
||||
const real_t radius_squared = radius * radius;
|
||||
real_t r = 0;
|
||||
|
||||
Vector<Vector3> points;
|
||||
// 4 vertical lines and 4 full circles.
|
||||
points.resize(4 * 2 + 4 * 8 * points_in_octant * 2);
|
||||
Vector3 *points_ptrw = points.ptrw();
|
||||
|
||||
uint32_t index = 0;
|
||||
float y_value = height * 0.5 - radius;
|
||||
|
||||
// Vertical Lines.
|
||||
points_ptrw[index++] = Vector3(0.f, y_value, radius);
|
||||
points_ptrw[index++] = Vector3(0.f, -y_value, radius);
|
||||
points_ptrw[index++] = Vector3(0.f, y_value, -radius);
|
||||
points_ptrw[index++] = Vector3(0.f, -y_value, -radius);
|
||||
points_ptrw[index++] = Vector3(radius, y_value, 0.f);
|
||||
points_ptrw[index++] = Vector3(radius, -y_value, 0.f);
|
||||
points_ptrw[index++] = Vector3(-radius, y_value, 0.f);
|
||||
points_ptrw[index++] = Vector3(-radius, -y_value, 0.f);
|
||||
|
||||
#define PUSH_QUARTER(from_x, from_y, to_x, to_y, y) \
|
||||
points_ptrw[index++] = Vector3(from_x, y, from_y); \
|
||||
points_ptrw[index++] = Vector3(to_x, y, to_y); \
|
||||
points_ptrw[index++] = Vector3(from_x, y, -from_y); \
|
||||
points_ptrw[index++] = Vector3(to_x, y, -to_y); \
|
||||
points_ptrw[index++] = Vector3(-from_x, y, from_y); \
|
||||
points_ptrw[index++] = Vector3(-to_x, y, to_y); \
|
||||
points_ptrw[index++] = Vector3(-from_x, y, -from_y); \
|
||||
points_ptrw[index++] = Vector3(-to_x, y, -to_y);
|
||||
|
||||
#define PUSH_QUARTER_XY(from_x, from_y, to_x, to_y, y) \
|
||||
points_ptrw[index++] = Vector3(from_x, -from_y - y, 0); \
|
||||
points_ptrw[index++] = Vector3(to_x, -to_y - y, 0); \
|
||||
points_ptrw[index++] = Vector3(from_x, from_y + y, 0); \
|
||||
points_ptrw[index++] = Vector3(to_x, to_y + y, 0); \
|
||||
points_ptrw[index++] = Vector3(-from_x, -from_y - y, 0); \
|
||||
points_ptrw[index++] = Vector3(-to_x, -to_y - y, 0); \
|
||||
points_ptrw[index++] = Vector3(-from_x, from_y + y, 0); \
|
||||
points_ptrw[index++] = Vector3(-to_x, to_y + y, 0);
|
||||
|
||||
#define PUSH_QUARTER_YZ(from_x, from_y, to_x, to_y, y) \
|
||||
points_ptrw[index++] = Vector3(0, -from_y - y, from_x); \
|
||||
points_ptrw[index++] = Vector3(0, -to_y - y, to_x); \
|
||||
points_ptrw[index++] = Vector3(0, from_y + y, from_x); \
|
||||
points_ptrw[index++] = Vector3(0, to_y + y, to_x); \
|
||||
points_ptrw[index++] = Vector3(0, -from_y - y, -from_x); \
|
||||
points_ptrw[index++] = Vector3(0, -to_y - y, -to_x); \
|
||||
points_ptrw[index++] = Vector3(0, from_y + y, -from_x); \
|
||||
points_ptrw[index++] = Vector3(0, to_y + y, -to_x);
|
||||
|
||||
float previous_x = radius;
|
||||
float previous_y = 0.f;
|
||||
|
||||
for (uint32_t i = 0; i < points_in_octant; ++i) {
|
||||
r += inc;
|
||||
real_t x = Math::cos((i == points_in_octant - 1) ? octant_angle : r) * radius;
|
||||
real_t y = Math::sqrt(radius_squared - (x * x));
|
||||
|
||||
// High circle ring.
|
||||
PUSH_QUARTER(previous_x, previous_y, x, y, y_value);
|
||||
PUSH_QUARTER(previous_y, previous_x, y, x, y_value);
|
||||
|
||||
// Low circle ring.
|
||||
PUSH_QUARTER(previous_x, previous_y, x, y, -y_value);
|
||||
PUSH_QUARTER(previous_y, previous_x, y, x, -y_value);
|
||||
|
||||
// Up and Low circle in X-Y plane.
|
||||
PUSH_QUARTER_XY(previous_x, previous_y, x, y, y_value);
|
||||
PUSH_QUARTER_XY(previous_y, previous_x, y, x, y_value);
|
||||
|
||||
// Up and Low circle in Y-Z plane.
|
||||
PUSH_QUARTER_YZ(previous_x, previous_y, x, y, y_value);
|
||||
PUSH_QUARTER_YZ(previous_y, previous_x, y, x, y_value)
|
||||
|
||||
previous_x = x;
|
||||
previous_y = y;
|
||||
}
|
||||
|
||||
#undef PUSH_QUARTER
|
||||
#undef PUSH_QUARTER_XY
|
||||
#undef PUSH_QUARTER_YZ
|
||||
|
||||
p_gizmo->add_lines(points, material, false, collision_color);
|
||||
p_gizmo->add_collision_segments(points);
|
||||
|
||||
Vector<Vector3> handles = helper->capsule_get_handles(cs2->get_height(), cs2->get_radius());
|
||||
p_gizmo->add_handles(handles, handles_material);
|
||||
}
|
||||
|
||||
if (Object::cast_to<CylinderShape3D>(*s)) {
|
||||
Ref<CylinderShape3D> cs2 = s;
|
||||
float radius = cs2->get_radius();
|
||||
float height = cs2->get_height();
|
||||
|
||||
#define PUSH_QUARTER(from_x, from_y, to_x, to_y, y) \
|
||||
points_ptrw[index++] = Vector3(from_x, y, from_y); \
|
||||
points_ptrw[index++] = Vector3(to_x, y, to_y); \
|
||||
points_ptrw[index++] = Vector3(from_x, y, -from_y); \
|
||||
points_ptrw[index++] = Vector3(to_x, y, -to_y); \
|
||||
points_ptrw[index++] = Vector3(-from_x, y, from_y); \
|
||||
points_ptrw[index++] = Vector3(-to_x, y, to_y); \
|
||||
points_ptrw[index++] = Vector3(-from_x, y, -from_y); \
|
||||
points_ptrw[index++] = Vector3(-to_x, y, -to_y);
|
||||
|
||||
// Number of points in an octant. So there will be 8 * points_in_octant * 2 points in total for one circle.
|
||||
// This corresponds to the smoothness of the circle.
|
||||
const uint32_t points_in_octant = 16;
|
||||
const real_t inc = (Math::PI / (4 * points_in_octant));
|
||||
const real_t radius_squared = radius * radius;
|
||||
real_t r = 0;
|
||||
|
||||
Vector<Vector3> points;
|
||||
uint32_t index = 0;
|
||||
// 4 vertical lines and 2 full circles.
|
||||
points.resize(4 * 2 + 2 * 8 * points_in_octant * 2);
|
||||
Vector3 *points_ptrw = points.ptrw();
|
||||
float y_value = height * 0.5;
|
||||
|
||||
// Vertical lines.
|
||||
points_ptrw[index++] = Vector3(0.f, y_value, radius);
|
||||
points_ptrw[index++] = Vector3(0.f, -y_value, radius);
|
||||
points_ptrw[index++] = Vector3(0.f, y_value, -radius);
|
||||
points_ptrw[index++] = Vector3(0.f, -y_value, -radius);
|
||||
points_ptrw[index++] = Vector3(radius, y_value, 0.f);
|
||||
points_ptrw[index++] = Vector3(radius, -y_value, 0.f);
|
||||
points_ptrw[index++] = Vector3(-radius, y_value, 0.f);
|
||||
points_ptrw[index++] = Vector3(-radius, -y_value, 0.f);
|
||||
|
||||
float previous_x = radius;
|
||||
float previous_y = 0.f;
|
||||
|
||||
for (uint32_t i = 0; i < points_in_octant; ++i) {
|
||||
r += inc;
|
||||
real_t x = Math::cos(r) * radius;
|
||||
real_t y = Math::sqrt(radius_squared - (x * x));
|
||||
|
||||
// High circle ring.
|
||||
PUSH_QUARTER(previous_x, previous_y, x, y, y_value);
|
||||
PUSH_QUARTER(previous_y, previous_x, y, x, y_value);
|
||||
|
||||
// Low circle ring.
|
||||
PUSH_QUARTER(previous_x, previous_y, x, y, -y_value);
|
||||
PUSH_QUARTER(previous_y, previous_x, y, x, -y_value);
|
||||
|
||||
previous_x = x;
|
||||
previous_y = y;
|
||||
}
|
||||
#undef PUSH_QUARTER
|
||||
|
||||
p_gizmo->add_lines(points, material, false, collision_color);
|
||||
p_gizmo->add_collision_segments(points);
|
||||
|
||||
Vector<Vector3> handles = helper->cylinder_get_handles(cs2->get_height(), cs2->get_radius());
|
||||
p_gizmo->add_handles(handles, handles_material);
|
||||
}
|
||||
|
||||
if (Object::cast_to<WorldBoundaryShape3D>(*s)) {
|
||||
Ref<WorldBoundaryShape3D> wbs = s;
|
||||
const Plane &p = wbs->get_plane();
|
||||
|
||||
Vector3 n1 = p.get_any_perpendicular_normal();
|
||||
Vector3 n2 = p.normal.cross(n1).normalized();
|
||||
|
||||
Vector3 pface[4] = {
|
||||
p.normal * p.d + n1 * 10.0 + n2 * 10.0,
|
||||
p.normal * p.d + n1 * 10.0 + n2 * -10.0,
|
||||
p.normal * p.d + n1 * -10.0 + n2 * -10.0,
|
||||
p.normal * p.d + n1 * -10.0 + n2 * 10.0,
|
||||
};
|
||||
|
||||
Vector<Vector3> points = {
|
||||
pface[0],
|
||||
pface[1],
|
||||
pface[1],
|
||||
pface[2],
|
||||
pface[2],
|
||||
pface[3],
|
||||
pface[3],
|
||||
pface[0],
|
||||
p.normal * p.d,
|
||||
p.normal * p.d + p.normal * 3
|
||||
};
|
||||
|
||||
p_gizmo->add_lines(points, material, false, collision_color);
|
||||
p_gizmo->add_collision_segments(points);
|
||||
}
|
||||
|
||||
if (Object::cast_to<ConvexPolygonShape3D>(*s)) {
|
||||
Vector<Vector3> points = Object::cast_to<ConvexPolygonShape3D>(*s)->get_points();
|
||||
|
||||
if (points.size() > 1) { // Need at least 2 points for a line.
|
||||
Vector<Vector3> varr = Variant(points);
|
||||
Geometry3D::MeshData md;
|
||||
Error err = ConvexHullComputer::convex_hull(varr, md);
|
||||
if (err == OK) {
|
||||
Vector<Vector3> lines;
|
||||
lines.resize(md.edges.size() * 2);
|
||||
for (uint32_t i = 0; i < md.edges.size(); i++) {
|
||||
lines.write[i * 2 + 0] = md.vertices[md.edges[i].vertex_a];
|
||||
lines.write[i * 2 + 1] = md.vertices[md.edges[i].vertex_b];
|
||||
}
|
||||
p_gizmo->add_lines(lines, material, false, collision_color);
|
||||
p_gizmo->add_collision_segments(lines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Object::cast_to<ConcavePolygonShape3D>(*s)) {
|
||||
Ref<ConcavePolygonShape3D> cs2 = s;
|
||||
Ref<ArrayMesh> mesh = cs2->get_debug_mesh();
|
||||
p_gizmo->add_lines(cs2->get_debug_mesh_lines(), material, false, collision_color);
|
||||
p_gizmo->add_collision_segments(cs2->get_debug_mesh_lines());
|
||||
}
|
||||
|
||||
if (Object::cast_to<SeparationRayShape3D>(*s)) {
|
||||
Ref<SeparationRayShape3D> rs = s;
|
||||
|
||||
Vector<Vector3> points = {
|
||||
Vector3(),
|
||||
Vector3(0, 0, rs->get_length())
|
||||
};
|
||||
p_gizmo->add_lines(points, material, false, collision_color);
|
||||
p_gizmo->add_collision_segments(points);
|
||||
Vector<Vector3> handles;
|
||||
handles.push_back(Vector3(0, 0, rs->get_length()));
|
||||
p_gizmo->add_handles(handles, handles_material);
|
||||
}
|
||||
|
||||
if (Object::cast_to<HeightMapShape3D>(*s)) {
|
||||
Ref<HeightMapShape3D> hms = s;
|
||||
|
||||
Vector<Vector3> lines = hms->get_debug_mesh_lines();
|
||||
p_gizmo->add_lines(lines, material, false, collision_color);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* collision_shape_3d_gizmo_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class Gizmo3DHelper;
|
||||
|
||||
class CollisionShape3DGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(CollisionShape3DGizmoPlugin, EditorNode3DGizmoPlugin);
|
||||
|
||||
void create_collision_material(const String &p_name, float p_alpha);
|
||||
|
||||
Ref<Gizmo3DHelper> helper;
|
||||
|
||||
public:
|
||||
bool has_gizmo(Node3D *p_spatial) override;
|
||||
String get_gizmo_name() const override;
|
||||
int get_priority() const override;
|
||||
void redraw(EditorNode3DGizmo *p_gizmo) override;
|
||||
|
||||
String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
|
||||
Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
|
||||
void begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) override;
|
||||
void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) override;
|
||||
void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false) override;
|
||||
|
||||
CollisionShape3DGizmoPlugin();
|
||||
};
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* cpu_particles_3d_gizmo_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 "cpu_particles_3d_gizmo_plugin.h"
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "scene/3d/cpu_particles_3d.h"
|
||||
|
||||
CPUParticles3DGizmoPlugin::CPUParticles3DGizmoPlugin() {
|
||||
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/particles");
|
||||
create_material("particles_material", gizmo_color);
|
||||
gizmo_color.a = MAX((gizmo_color.a - 0.2) * 0.02, 0.0);
|
||||
create_material("particles_solid_material", gizmo_color);
|
||||
create_icon_material("particles_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoCPUParticles3D"), EditorStringName(EditorIcons)));
|
||||
}
|
||||
|
||||
bool CPUParticles3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
|
||||
return Object::cast_to<CPUParticles3D>(p_spatial) != nullptr;
|
||||
}
|
||||
|
||||
String CPUParticles3DGizmoPlugin::get_gizmo_name() const {
|
||||
return "CPUParticles3D";
|
||||
}
|
||||
|
||||
int CPUParticles3DGizmoPlugin::get_priority() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool CPUParticles3DGizmoPlugin::is_selectable_when_hidden() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
CPUParticles3D *particles = Object::cast_to<CPUParticles3D>(p_gizmo->get_node_3d());
|
||||
|
||||
p_gizmo->clear();
|
||||
|
||||
Vector<Vector3> lines;
|
||||
AABB aabb = particles->get_visibility_aabb();
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
Vector3 a, b;
|
||||
aabb.get_edge(i, a, b);
|
||||
lines.push_back(a);
|
||||
lines.push_back(b);
|
||||
}
|
||||
|
||||
Vector<Vector3> handles;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Vector3 ax;
|
||||
ax[i] = aabb.position[i] + aabb.size[i];
|
||||
ax[(i + 1) % 3] = aabb.position[(i + 1) % 3] + aabb.size[(i + 1) % 3] * 0.5;
|
||||
ax[(i + 2) % 3] = aabb.position[(i + 2) % 3] + aabb.size[(i + 2) % 3] * 0.5;
|
||||
handles.push_back(ax);
|
||||
}
|
||||
|
||||
Vector3 center = aabb.get_center();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Vector3 ax;
|
||||
ax[i] = 1.0;
|
||||
handles.push_back(center + ax);
|
||||
lines.push_back(center);
|
||||
lines.push_back(center + ax);
|
||||
}
|
||||
|
||||
Ref<Material> material = get_material("particles_material", p_gizmo);
|
||||
|
||||
p_gizmo->add_lines(lines, material);
|
||||
|
||||
if (p_gizmo->is_selected()) {
|
||||
Ref<Material> solid_material = get_material("particles_solid_material", p_gizmo);
|
||||
p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_center());
|
||||
}
|
||||
|
||||
Ref<Material> icon = get_material("particles_icon", p_gizmo);
|
||||
p_gizmo->add_unscaled_billboard(icon, 0.05);
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* cpu_particles_3d_gizmo_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class CPUParticles3DGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(CPUParticles3DGizmoPlugin, EditorNode3DGizmoPlugin);
|
||||
|
||||
public:
|
||||
bool has_gizmo(Node3D *p_spatial) override;
|
||||
String get_gizmo_name() const override;
|
||||
int get_priority() const override;
|
||||
bool is_selectable_when_hidden() const override;
|
||||
void redraw(EditorNode3DGizmo *p_gizmo) override;
|
||||
CPUParticles3DGizmoPlugin();
|
||||
};
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* decal_gizmo_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 "decal_gizmo_plugin.h"
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/plugins/gizmos/gizmo_3d_helper.h"
|
||||
#include "scene/3d/decal.h"
|
||||
|
||||
DecalGizmoPlugin::DecalGizmoPlugin() {
|
||||
helper.instantiate();
|
||||
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/decal");
|
||||
|
||||
create_icon_material("decal_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoDecal"), EditorStringName(EditorIcons)));
|
||||
|
||||
create_material("decal_material", gizmo_color);
|
||||
|
||||
create_handle_material("handles");
|
||||
}
|
||||
|
||||
bool DecalGizmoPlugin::has_gizmo(Node3D *p_spatial) {
|
||||
return Object::cast_to<Decal>(p_spatial) != nullptr;
|
||||
}
|
||||
|
||||
String DecalGizmoPlugin::get_gizmo_name() const {
|
||||
return "Decal";
|
||||
}
|
||||
|
||||
int DecalGizmoPlugin::get_priority() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
String DecalGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
|
||||
return helper->box_get_handle_name(p_id);
|
||||
}
|
||||
|
||||
Variant DecalGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
|
||||
Decal *decal = Object::cast_to<Decal>(p_gizmo->get_node_3d());
|
||||
return decal->get_size();
|
||||
}
|
||||
|
||||
void DecalGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) {
|
||||
helper->initialize_handle_action(get_handle_value(p_gizmo, p_id, p_secondary), p_gizmo->get_node_3d()->get_global_transform());
|
||||
}
|
||||
|
||||
void DecalGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
|
||||
Decal *decal = Object::cast_to<Decal>(p_gizmo->get_node_3d());
|
||||
Vector3 size = decal->get_size();
|
||||
|
||||
Vector3 sg[2];
|
||||
helper->get_segment(p_camera, p_point, sg);
|
||||
|
||||
Vector3 position;
|
||||
helper->box_set_handle(sg, p_id, size, position);
|
||||
decal->set_size(size);
|
||||
decal->set_global_position(position);
|
||||
}
|
||||
|
||||
void DecalGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
|
||||
helper->box_commit_handle(TTR("Change Decal Size"), p_cancel, p_gizmo->get_node_3d());
|
||||
}
|
||||
|
||||
void DecalGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
Decal *decal = Object::cast_to<Decal>(p_gizmo->get_node_3d());
|
||||
|
||||
p_gizmo->clear();
|
||||
|
||||
Vector<Vector3> lines;
|
||||
Vector3 size = decal->get_size();
|
||||
|
||||
AABB aabb;
|
||||
aabb.position = -size / 2;
|
||||
aabb.size = size;
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
Vector3 a, b;
|
||||
aabb.get_edge(i, a, b);
|
||||
if (a.y == b.y) {
|
||||
lines.push_back(a);
|
||||
lines.push_back(b);
|
||||
} else {
|
||||
Vector3 ah = a.lerp(b, 0.2);
|
||||
lines.push_back(a);
|
||||
lines.push_back(ah);
|
||||
Vector3 bh = b.lerp(a, 0.2);
|
||||
lines.push_back(b);
|
||||
lines.push_back(bh);
|
||||
}
|
||||
}
|
||||
|
||||
float half_size_y = size.y / 2;
|
||||
lines.push_back(Vector3(0, half_size_y, 0));
|
||||
lines.push_back(Vector3(0, half_size_y * 1.2, 0));
|
||||
|
||||
Vector<Vector3> handles = helper->box_get_handles(decal->get_size());
|
||||
Ref<Material> material = get_material("decal_material", p_gizmo);
|
||||
const Ref<Material> icon = get_material("decal_icon", p_gizmo);
|
||||
|
||||
p_gizmo->add_lines(lines, material);
|
||||
p_gizmo->add_unscaled_billboard(icon, 0.05);
|
||||
p_gizmo->add_handles(handles, get_material("handles"));
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* decal_gizmo_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class Gizmo3DHelper;
|
||||
|
||||
class DecalGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(DecalGizmoPlugin, EditorNode3DGizmoPlugin);
|
||||
|
||||
Ref<Gizmo3DHelper> helper;
|
||||
|
||||
public:
|
||||
bool has_gizmo(Node3D *p_spatial) override;
|
||||
String get_gizmo_name() const override;
|
||||
int get_priority() const override;
|
||||
void redraw(EditorNode3DGizmo *p_gizmo) override;
|
||||
|
||||
String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
|
||||
Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
|
||||
void begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) override;
|
||||
void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) override;
|
||||
void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false) override;
|
||||
|
||||
DecalGizmoPlugin();
|
||||
};
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* fog_volume_gizmo_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 "fog_volume_gizmo_plugin.h"
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/plugins/gizmos/gizmo_3d_helper.h"
|
||||
#include "scene/3d/fog_volume.h"
|
||||
|
||||
FogVolumeGizmoPlugin::FogVolumeGizmoPlugin() {
|
||||
helper.instantiate();
|
||||
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/fog_volume");
|
||||
create_material("shape_material", gizmo_color);
|
||||
gizmo_color.a = 0.15;
|
||||
create_material("shape_material_internal", gizmo_color);
|
||||
|
||||
create_icon_material("fog_volume_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoFogVolume"), EditorStringName(EditorIcons)));
|
||||
|
||||
create_handle_material("handles");
|
||||
}
|
||||
|
||||
bool FogVolumeGizmoPlugin::has_gizmo(Node3D *p_spatial) {
|
||||
return (Object::cast_to<FogVolume>(p_spatial) != nullptr);
|
||||
}
|
||||
|
||||
String FogVolumeGizmoPlugin::get_gizmo_name() const {
|
||||
return "FogVolume";
|
||||
}
|
||||
|
||||
int FogVolumeGizmoPlugin::get_priority() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
String FogVolumeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
|
||||
return helper->box_get_handle_name(p_id);
|
||||
}
|
||||
|
||||
Variant FogVolumeGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
|
||||
return Vector3(p_gizmo->get_node_3d()->call("get_size"));
|
||||
}
|
||||
|
||||
void FogVolumeGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) {
|
||||
helper->initialize_handle_action(get_handle_value(p_gizmo, p_id, p_secondary), p_gizmo->get_node_3d()->get_global_transform());
|
||||
}
|
||||
|
||||
void FogVolumeGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
|
||||
FogVolume *fog_volume = Object::cast_to<FogVolume>(p_gizmo->get_node_3d());
|
||||
Vector3 size = fog_volume->get_size();
|
||||
|
||||
Vector3 sg[2];
|
||||
helper->get_segment(p_camera, p_point, sg);
|
||||
|
||||
Vector3 position;
|
||||
helper->box_set_handle(sg, p_id, size, position);
|
||||
fog_volume->set_size(size);
|
||||
fog_volume->set_global_position(position);
|
||||
}
|
||||
|
||||
void FogVolumeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
|
||||
helper->box_commit_handle(TTR("Change FogVolume Size"), p_cancel, p_gizmo->get_node_3d());
|
||||
}
|
||||
|
||||
void FogVolumeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
FogVolume *fog_volume = Object::cast_to<FogVolume>(p_gizmo->get_node_3d());
|
||||
|
||||
p_gizmo->clear();
|
||||
|
||||
if (fog_volume->get_shape() != RS::FOG_VOLUME_SHAPE_WORLD) {
|
||||
const Ref<Material> material =
|
||||
get_material("shape_material", p_gizmo);
|
||||
const Ref<Material> material_internal =
|
||||
get_material("shape_material_internal", p_gizmo);
|
||||
|
||||
Ref<Material> handles_material = get_material("handles");
|
||||
|
||||
Vector<Vector3> lines;
|
||||
AABB aabb;
|
||||
aabb.size = fog_volume->get_size();
|
||||
aabb.position = aabb.size / -2;
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
Vector3 a, b;
|
||||
aabb.get_edge(i, a, b);
|
||||
lines.push_back(a);
|
||||
lines.push_back(b);
|
||||
}
|
||||
|
||||
Vector<Vector3> handles = helper->box_get_handles(fog_volume->get_size());
|
||||
|
||||
p_gizmo->add_lines(lines, material);
|
||||
p_gizmo->add_collision_segments(lines);
|
||||
const Ref<Material> icon = get_material("fog_volume_icon", p_gizmo);
|
||||
p_gizmo->add_unscaled_billboard(icon, 0.05);
|
||||
p_gizmo->add_handles(handles, handles_material);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* fog_volume_gizmo_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class Gizmo3DHelper;
|
||||
|
||||
class FogVolumeGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(FogVolumeGizmoPlugin, EditorNode3DGizmoPlugin);
|
||||
|
||||
Ref<Gizmo3DHelper> helper;
|
||||
|
||||
public:
|
||||
bool has_gizmo(Node3D *p_spatial) override;
|
||||
String get_gizmo_name() const override;
|
||||
int get_priority() const override;
|
||||
void redraw(EditorNode3DGizmo *p_gizmo) override;
|
||||
|
||||
String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
|
||||
Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
|
||||
void begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) override;
|
||||
void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) override;
|
||||
void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false) override;
|
||||
|
||||
FogVolumeGizmoPlugin();
|
||||
};
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* geometry_instance_3d_gizmo_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 "geometry_instance_3d_gizmo_plugin.h"
|
||||
|
||||
#include "editor/editor_settings.h"
|
||||
#include "scene/3d/visual_instance_3d.h"
|
||||
|
||||
bool GeometryInstance3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
|
||||
return Object::cast_to<GeometryInstance3D>(p_spatial) != nullptr;
|
||||
}
|
||||
|
||||
String GeometryInstance3DGizmoPlugin::get_gizmo_name() const {
|
||||
return "MeshInstance3DCustomAABB";
|
||||
}
|
||||
|
||||
int GeometryInstance3DGizmoPlugin::get_priority() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void GeometryInstance3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
GeometryInstance3D *geometry = Object::cast_to<GeometryInstance3D>(p_gizmo->get_node_3d());
|
||||
|
||||
p_gizmo->clear();
|
||||
|
||||
if (p_gizmo->is_selected()) {
|
||||
AABB aabb = geometry->get_custom_aabb();
|
||||
|
||||
Vector<Vector3> lines;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
Vector3 a;
|
||||
Vector3 b;
|
||||
aabb.get_edge(i, a, b);
|
||||
|
||||
lines.push_back(a);
|
||||
lines.push_back(b);
|
||||
}
|
||||
|
||||
Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D);
|
||||
mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
|
||||
mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
|
||||
const Color selection_box_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/aabb");
|
||||
mat->set_albedo(selection_box_color);
|
||||
mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
|
||||
p_gizmo->add_lines(lines, mat);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* geometry_instance_3d_gizmo_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class GeometryInstance3DGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(GeometryInstance3DGizmoPlugin, EditorNode3DGizmoPlugin);
|
||||
|
||||
public:
|
||||
virtual bool has_gizmo(Node3D *p_spatial) override;
|
||||
virtual String get_gizmo_name() const override;
|
||||
virtual int get_priority() const override;
|
||||
|
||||
virtual void redraw(EditorNode3DGizmo *p_gizmo) override;
|
||||
};
|
||||
|
|
@ -1,243 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* gizmo_3d_helper.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 "gizmo_3d_helper.h"
|
||||
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||
#include "scene/3d/camera_3d.h"
|
||||
|
||||
void Gizmo3DHelper::initialize_handle_action(const Variant &p_initial_value, const Transform3D &p_initial_transform) {
|
||||
initial_value = p_initial_value;
|
||||
initial_transform = p_initial_transform;
|
||||
}
|
||||
|
||||
void Gizmo3DHelper::get_segment(Camera3D *p_camera, const Point2 &p_point, Vector3 *r_segment) {
|
||||
Transform3D gt = initial_transform;
|
||||
Transform3D gi = gt.affine_inverse();
|
||||
|
||||
Vector3 ray_from = p_camera->project_ray_origin(p_point);
|
||||
Vector3 ray_dir = p_camera->project_ray_normal(p_point);
|
||||
|
||||
r_segment[0] = gi.xform(ray_from);
|
||||
r_segment[1] = gi.xform(ray_from + ray_dir * 4096);
|
||||
}
|
||||
|
||||
Vector<Vector3> Gizmo3DHelper::box_get_handles(const Vector3 &p_box_size) {
|
||||
Vector<Vector3> handles;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Vector3 ax;
|
||||
ax[i] = p_box_size[i] / 2;
|
||||
handles.push_back(ax);
|
||||
handles.push_back(-ax);
|
||||
}
|
||||
return handles;
|
||||
}
|
||||
|
||||
String Gizmo3DHelper::box_get_handle_name(int p_id) const {
|
||||
switch (p_id) {
|
||||
case 0:
|
||||
case 1:
|
||||
return "Size X";
|
||||
case 2:
|
||||
case 3:
|
||||
return "Size Y";
|
||||
case 4:
|
||||
case 5:
|
||||
return "Size Z";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void Gizmo3DHelper::box_set_handle(const Vector3 p_segment[2], int p_id, Vector3 &r_box_size, Vector3 &r_box_position) {
|
||||
int axis = p_id / 2;
|
||||
int sign = p_id % 2 * -2 + 1;
|
||||
|
||||
Vector3 initial_size = initial_value;
|
||||
float neg_end = initial_size[axis] * -0.5;
|
||||
float pos_end = initial_size[axis] * 0.5;
|
||||
|
||||
Vector3 axis_segment[2] = { Vector3(), Vector3() };
|
||||
axis_segment[0][axis] = 4096.0;
|
||||
axis_segment[1][axis] = -4096.0;
|
||||
Vector3 ra, rb;
|
||||
Geometry3D::get_closest_points_between_segments(axis_segment[0], axis_segment[1], p_segment[0], p_segment[1], ra, rb);
|
||||
|
||||
// Calculate new size.
|
||||
r_box_size = initial_size;
|
||||
if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
|
||||
r_box_size[axis] = ra[axis] * sign * 2;
|
||||
} else {
|
||||
r_box_size[axis] = sign > 0 ? ra[axis] - neg_end : pos_end - ra[axis];
|
||||
}
|
||||
|
||||
// Snap to grid.
|
||||
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
|
||||
r_box_size[axis] = Math::snapped(r_box_size[axis], Node3DEditor::get_singleton()->get_translate_snap());
|
||||
}
|
||||
r_box_size[axis] = MAX(r_box_size[axis], 0.001);
|
||||
|
||||
// Adjust position.
|
||||
if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
|
||||
r_box_position = initial_transform.get_origin();
|
||||
} else {
|
||||
if (sign > 0) {
|
||||
pos_end = neg_end + r_box_size[axis];
|
||||
} else {
|
||||
neg_end = pos_end - r_box_size[axis];
|
||||
}
|
||||
|
||||
Vector3 offset;
|
||||
offset[axis] = (pos_end + neg_end) * 0.5;
|
||||
r_box_position = initial_transform.xform(offset);
|
||||
}
|
||||
}
|
||||
|
||||
void Gizmo3DHelper::box_commit_handle(const String &p_action_name, bool p_cancel, Object *p_position_object, Object *p_size_object, const StringName &p_position_property, const StringName &p_size_property) {
|
||||
if (!p_size_object) {
|
||||
p_size_object = p_position_object;
|
||||
}
|
||||
|
||||
if (p_cancel) {
|
||||
p_size_object->set(p_size_property, initial_value);
|
||||
p_position_object->set(p_position_property, initial_transform.get_origin());
|
||||
return;
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
ur->create_action(p_action_name);
|
||||
ur->add_do_property(p_size_object, p_size_property, p_size_object->get(p_size_property));
|
||||
ur->add_do_property(p_position_object, p_position_property, p_position_object->get(p_position_property));
|
||||
ur->add_undo_property(p_size_object, p_size_property, initial_value);
|
||||
ur->add_undo_property(p_position_object, p_position_property, initial_transform.get_origin());
|
||||
ur->commit_action();
|
||||
}
|
||||
|
||||
Vector<Vector3> Gizmo3DHelper::cylinder_get_handles(real_t p_height, real_t p_radius) {
|
||||
Vector<Vector3> handles;
|
||||
handles.push_back(Vector3(p_radius, 0, 0));
|
||||
handles.push_back(Vector3(0, p_height * 0.5, 0));
|
||||
handles.push_back(Vector3(0, p_height * -0.5, 0));
|
||||
return handles;
|
||||
}
|
||||
|
||||
String Gizmo3DHelper::cylinder_get_handle_name(int p_id) const {
|
||||
if (p_id == 0) {
|
||||
return "Radius";
|
||||
} else {
|
||||
return "Height";
|
||||
}
|
||||
}
|
||||
|
||||
void Gizmo3DHelper::_cylinder_or_capsule_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_cylinder_position, bool p_is_capsule) {
|
||||
real_t initial_radius = initial_value.operator Vector2().x;
|
||||
real_t initial_height = initial_value.operator Vector2().y;
|
||||
|
||||
int sign = p_id == 2 ? -1 : 1;
|
||||
int axis = p_id == 0 ? 0 : 1;
|
||||
|
||||
Vector3 axis_vector;
|
||||
axis_vector[axis] = sign;
|
||||
Vector3 ra, rb;
|
||||
Geometry3D::get_closest_points_between_segments(axis_vector * -4096, axis_vector * 4096, p_segment[0], p_segment[1], ra, rb);
|
||||
float d = axis_vector.dot(ra);
|
||||
|
||||
// Snap to grid.
|
||||
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
|
||||
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
|
||||
}
|
||||
|
||||
if (p_id == 0) {
|
||||
// Adjust radius.
|
||||
if (d < 0.001) {
|
||||
d = 0.001;
|
||||
}
|
||||
r_radius = d;
|
||||
r_cylinder_position = initial_transform.get_origin();
|
||||
|
||||
if (p_is_capsule) {
|
||||
r_height = MAX(initial_height, r_radius * 2.0);
|
||||
} else {
|
||||
r_height = initial_height;
|
||||
}
|
||||
} else if (p_id == 1 || p_id == 2) {
|
||||
// Adjust height.
|
||||
if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
|
||||
r_height = d * 2.0;
|
||||
} else {
|
||||
r_height = (initial_height * 0.5) + d;
|
||||
}
|
||||
|
||||
if (r_height < 0.001) {
|
||||
r_height = 0.001;
|
||||
}
|
||||
|
||||
// Adjust position.
|
||||
if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
|
||||
r_cylinder_position = initial_transform.get_origin();
|
||||
} else {
|
||||
Vector3 offset;
|
||||
offset[axis] = (r_height - initial_height) * 0.5 * sign;
|
||||
r_cylinder_position = initial_transform.xform(offset);
|
||||
}
|
||||
|
||||
if (p_is_capsule) {
|
||||
r_radius = MIN(initial_radius, r_height / 2.0);
|
||||
} else {
|
||||
r_radius = initial_radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Gizmo3DHelper::cylinder_commit_handle(int p_id, const String &p_radius_action_name, const String &p_height_action_name, bool p_cancel, Object *p_position_object, Object *p_height_object, Object *p_radius_object, const StringName &p_position_property, const StringName &p_height_property, const StringName &p_radius_property) {
|
||||
if (!p_height_object) {
|
||||
p_height_object = p_position_object;
|
||||
}
|
||||
if (!p_radius_object) {
|
||||
p_radius_object = p_position_object;
|
||||
}
|
||||
|
||||
if (p_cancel) {
|
||||
p_radius_object->set(p_radius_property, initial_value.operator Vector2().x);
|
||||
p_height_object->set(p_height_property, initial_value.operator Vector2().y);
|
||||
p_position_object->set(p_position_property, initial_transform.get_origin());
|
||||
return;
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
ur->create_action(p_id == 0 ? p_radius_action_name : p_height_action_name);
|
||||
ur->add_do_property(p_radius_object, p_radius_property, p_radius_object->get(p_radius_property));
|
||||
ur->add_undo_property(p_radius_object, p_radius_property, initial_value.operator Vector2().x);
|
||||
ur->add_do_property(p_height_object, p_height_property, p_height_object->get(p_height_property));
|
||||
ur->add_undo_property(p_height_object, p_height_property, initial_value.operator Vector2().y);
|
||||
ur->add_do_property(p_position_object, p_position_property, p_position_object->get(p_position_property));
|
||||
ur->add_undo_property(p_position_object, p_position_property, initial_transform.get_origin());
|
||||
ur->commit_action();
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* gizmo_3d_helper.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/object/ref_counted.h"
|
||||
|
||||
class Camera3D;
|
||||
|
||||
class Gizmo3DHelper : public RefCounted {
|
||||
GDCLASS(Gizmo3DHelper, RefCounted);
|
||||
|
||||
int current_handle_id;
|
||||
Variant initial_value;
|
||||
Transform3D initial_transform;
|
||||
|
||||
private:
|
||||
void _cylinder_or_capsule_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_cylinder_position, bool p_is_capsule);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Initializes a new action involving a handle.
|
||||
*
|
||||
* Depending on the type of gizmo that will be used, different formats for the `p_initial_value` are required:
|
||||
* Box: The size of the box as `Vector3`
|
||||
* Cylinder or Capsule: A `Vector2` of the form `Vector2(radius, height)`
|
||||
*/
|
||||
void initialize_handle_action(const Variant &p_initial_value, const Transform3D &p_initial_transform);
|
||||
void get_segment(Camera3D *p_camera, const Point2 &p_point, Vector3 *r_segment);
|
||||
|
||||
// Box
|
||||
|
||||
Vector<Vector3> box_get_handles(const Vector3 &p_box_size);
|
||||
String box_get_handle_name(int p_id) const;
|
||||
void box_set_handle(const Vector3 p_segment[2], int p_id, Vector3 &r_box_size, Vector3 &r_box_position);
|
||||
void box_commit_handle(const String &p_action_name, bool p_cancel, Object *p_position_object, Object *p_size_object = nullptr, const StringName &p_position_property = "global_position", const StringName &p_size_property = "size");
|
||||
|
||||
// Cylinder
|
||||
|
||||
Vector<Vector3> cylinder_get_handles(real_t p_height, real_t p_radius);
|
||||
String cylinder_get_handle_name(int p_id) const;
|
||||
_FORCE_INLINE_ void cylinder_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_cylinder_position) {
|
||||
_cylinder_or_capsule_set_handle(p_segment, p_id, r_height, r_radius, r_cylinder_position, false);
|
||||
}
|
||||
void cylinder_commit_handle(int p_id, const String &p_radius_action_name, const String &p_height_action_name, bool p_cancel, Object *p_position_object, Object *p_height_object = nullptr, Object *p_radius_object = nullptr, const StringName &p_position_property = "global_position", const StringName &p_height_property = "height", const StringName &p_radius_property = "radius");
|
||||
|
||||
// Capsule
|
||||
|
||||
_FORCE_INLINE_ Vector<Vector3> capsule_get_handles(real_t p_height, real_t p_radius) { return cylinder_get_handles(p_height, p_radius); }
|
||||
_FORCE_INLINE_ String capsule_get_handle_name(int p_id) { return cylinder_get_handle_name(p_id); }
|
||||
_FORCE_INLINE_ void capsule_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_capsule_position) {
|
||||
_cylinder_or_capsule_set_handle(p_segment, p_id, r_height, r_radius, r_capsule_position, true);
|
||||
}
|
||||
_FORCE_INLINE_ void capsule_commit_handle(int p_id, const String &p_radius_action_name, const String &p_height_action_name, bool p_cancel, Object *p_position_object, Object *p_height_object = nullptr, Object *p_radius_object = nullptr, const StringName &p_position_property = "global_position", const StringName &p_height_property = "height", const StringName &p_radius_property = "radius") {
|
||||
cylinder_commit_handle(p_id, p_radius_action_name, p_height_action_name, p_cancel, p_position_object, p_height_object, p_radius_object, p_position_property, p_height_property, p_radius_property);
|
||||
}
|
||||
};
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* gpu_particles_3d_gizmo_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 "gpu_particles_3d_gizmo_plugin.h"
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "scene/3d/gpu_particles_3d.h"
|
||||
|
||||
GPUParticles3DGizmoPlugin::GPUParticles3DGizmoPlugin() {
|
||||
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/particles");
|
||||
create_material("particles_material", gizmo_color);
|
||||
gizmo_color.a = MAX((gizmo_color.a - 0.2) * 0.02, 0.0);
|
||||
create_icon_material("particles_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoGPUParticles3D"), EditorStringName(EditorIcons)));
|
||||
}
|
||||
|
||||
bool GPUParticles3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
|
||||
return Object::cast_to<GPUParticles3D>(p_spatial) != nullptr;
|
||||
}
|
||||
|
||||
String GPUParticles3DGizmoPlugin::get_gizmo_name() const {
|
||||
return "GPUParticles3D";
|
||||
}
|
||||
|
||||
int GPUParticles3DGizmoPlugin::get_priority() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool GPUParticles3DGizmoPlugin::is_selectable_when_hidden() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
p_gizmo->clear();
|
||||
|
||||
if (p_gizmo->is_selected()) {
|
||||
GPUParticles3D *particles = Object::cast_to<GPUParticles3D>(p_gizmo->get_node_3d());
|
||||
|
||||
Vector<Vector3> lines;
|
||||
AABB aabb = particles->get_visibility_aabb();
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
Vector3 a, b;
|
||||
aabb.get_edge(i, a, b);
|
||||
lines.push_back(a);
|
||||
lines.push_back(b);
|
||||
}
|
||||
|
||||
Ref<Material> material = get_material("particles_material", p_gizmo);
|
||||
|
||||
p_gizmo->add_lines(lines, material);
|
||||
}
|
||||
|
||||
Ref<Material> icon = get_material("particles_icon", p_gizmo);
|
||||
p_gizmo->add_unscaled_billboard(icon, 0.05);
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* gpu_particles_3d_gizmo_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class GPUParticles3DGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(GPUParticles3DGizmoPlugin, EditorNode3DGizmoPlugin);
|
||||
|
||||
public:
|
||||
bool has_gizmo(Node3D *p_spatial) override;
|
||||
String get_gizmo_name() const override;
|
||||
int get_priority() const override;
|
||||
bool is_selectable_when_hidden() const override;
|
||||
void redraw(EditorNode3DGizmo *p_gizmo) override;
|
||||
|
||||
GPUParticles3DGizmoPlugin();
|
||||
};
|
||||
|
|
@ -1,306 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* gpu_particles_collision_3d_gizmo_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 "gpu_particles_collision_3d_gizmo_plugin.h"
|
||||
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/plugins/gizmos/gizmo_3d_helper.h"
|
||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||
#include "scene/3d/gpu_particles_collision_3d.h"
|
||||
|
||||
GPUParticlesCollision3DGizmoPlugin::GPUParticlesCollision3DGizmoPlugin() {
|
||||
helper.instantiate();
|
||||
|
||||
Color gizmo_color_attractor = EDITOR_GET("editors/3d_gizmos/gizmo_colors/particle_attractor");
|
||||
create_material("shape_material_attractor", gizmo_color_attractor);
|
||||
gizmo_color_attractor.a = 0.15;
|
||||
create_material("shape_material_attractor_internal", gizmo_color_attractor);
|
||||
|
||||
Color gizmo_color_collision = EDITOR_GET("editors/3d_gizmos/gizmo_colors/particle_collision");
|
||||
create_material("shape_material_collision", gizmo_color_collision);
|
||||
gizmo_color_collision.a = 0.15;
|
||||
create_material("shape_material_collision_internal", gizmo_color_collision);
|
||||
|
||||
create_handle_material("handles");
|
||||
}
|
||||
|
||||
bool GPUParticlesCollision3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
|
||||
return (Object::cast_to<GPUParticlesCollision3D>(p_spatial) != nullptr) || (Object::cast_to<GPUParticlesAttractor3D>(p_spatial) != nullptr);
|
||||
}
|
||||
|
||||
String GPUParticlesCollision3DGizmoPlugin::get_gizmo_name() const {
|
||||
return "GPUParticlesCollision3D";
|
||||
}
|
||||
|
||||
int GPUParticlesCollision3DGizmoPlugin::get_priority() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
String GPUParticlesCollision3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
|
||||
const Node3D *cs = p_gizmo->get_node_3d();
|
||||
|
||||
if (Object::cast_to<GPUParticlesCollisionSphere3D>(cs) || Object::cast_to<GPUParticlesAttractorSphere3D>(cs)) {
|
||||
return "Radius";
|
||||
}
|
||||
|
||||
if (Object::cast_to<GPUParticlesCollisionBox3D>(cs) || Object::cast_to<GPUParticlesAttractorBox3D>(cs) || Object::cast_to<GPUParticlesAttractorVectorField3D>(cs) || Object::cast_to<GPUParticlesCollisionSDF3D>(cs) || Object::cast_to<GPUParticlesCollisionHeightField3D>(cs)) {
|
||||
return helper->box_get_handle_name(p_id);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
Variant GPUParticlesCollision3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
|
||||
const Node3D *cs = p_gizmo->get_node_3d();
|
||||
|
||||
if (Object::cast_to<GPUParticlesCollisionSphere3D>(cs) || Object::cast_to<GPUParticlesAttractorSphere3D>(cs)) {
|
||||
return p_gizmo->get_node_3d()->call("get_radius");
|
||||
}
|
||||
|
||||
if (Object::cast_to<GPUParticlesCollisionBox3D>(cs) || Object::cast_to<GPUParticlesAttractorBox3D>(cs) || Object::cast_to<GPUParticlesAttractorVectorField3D>(cs) || Object::cast_to<GPUParticlesCollisionSDF3D>(cs) || Object::cast_to<GPUParticlesCollisionHeightField3D>(cs)) {
|
||||
return Vector3(p_gizmo->get_node_3d()->call("get_size"));
|
||||
}
|
||||
|
||||
return Variant();
|
||||
}
|
||||
|
||||
void GPUParticlesCollision3DGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) {
|
||||
helper->initialize_handle_action(get_handle_value(p_gizmo, p_id, p_secondary), p_gizmo->get_node_3d()->get_global_transform());
|
||||
}
|
||||
|
||||
void GPUParticlesCollision3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
|
||||
Node3D *sn = p_gizmo->get_node_3d();
|
||||
|
||||
Vector3 sg[2];
|
||||
helper->get_segment(p_camera, p_point, sg);
|
||||
|
||||
if (Object::cast_to<GPUParticlesCollisionSphere3D>(sn) || Object::cast_to<GPUParticlesAttractorSphere3D>(sn)) {
|
||||
Vector3 ra, rb;
|
||||
Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb);
|
||||
float d = ra.x;
|
||||
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
|
||||
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
|
||||
}
|
||||
|
||||
if (d < 0.001) {
|
||||
d = 0.001;
|
||||
}
|
||||
|
||||
sn->call("set_radius", d);
|
||||
}
|
||||
|
||||
if (Object::cast_to<GPUParticlesCollisionBox3D>(sn) || Object::cast_to<GPUParticlesAttractorBox3D>(sn) || Object::cast_to<GPUParticlesAttractorVectorField3D>(sn) || Object::cast_to<GPUParticlesCollisionSDF3D>(sn) || Object::cast_to<GPUParticlesCollisionHeightField3D>(sn)) {
|
||||
Vector3 size = sn->call("get_size");
|
||||
Vector3 position;
|
||||
helper->box_set_handle(sg, p_id, size, position);
|
||||
sn->call("set_size", size);
|
||||
sn->set_global_position(position);
|
||||
}
|
||||
}
|
||||
|
||||
void GPUParticlesCollision3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
|
||||
Node3D *sn = p_gizmo->get_node_3d();
|
||||
|
||||
if (Object::cast_to<GPUParticlesCollisionSphere3D>(sn) || Object::cast_to<GPUParticlesAttractorSphere3D>(sn)) {
|
||||
if (p_cancel) {
|
||||
sn->call("set_radius", p_restore);
|
||||
return;
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
ur->create_action(TTR("Change Radius"));
|
||||
ur->add_do_method(sn, "set_radius", sn->call("get_radius"));
|
||||
ur->add_undo_method(sn, "set_radius", p_restore);
|
||||
ur->commit_action();
|
||||
}
|
||||
|
||||
if (Object::cast_to<GPUParticlesCollisionBox3D>(sn) || Object::cast_to<GPUParticlesAttractorBox3D>(sn) || Object::cast_to<GPUParticlesAttractorVectorField3D>(sn) || Object::cast_to<GPUParticlesCollisionSDF3D>(sn) || Object::cast_to<GPUParticlesCollisionHeightField3D>(sn)) {
|
||||
helper->box_commit_handle("Change Box Shape Size", p_cancel, sn);
|
||||
}
|
||||
}
|
||||
|
||||
void GPUParticlesCollision3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
Node3D *cs = p_gizmo->get_node_3d();
|
||||
|
||||
p_gizmo->clear();
|
||||
|
||||
Ref<Material> material;
|
||||
Ref<Material> material_internal;
|
||||
if (Object::cast_to<GPUParticlesAttractor3D>(cs)) {
|
||||
material = get_material("shape_material_attractor", p_gizmo);
|
||||
material_internal = get_material("shape_material_attractor_internal", p_gizmo);
|
||||
} else {
|
||||
material = get_material("shape_material_collision", p_gizmo);
|
||||
material_internal = get_material("shape_material_collision_internal", p_gizmo);
|
||||
}
|
||||
|
||||
const Ref<Material> handles_material = get_material("handles");
|
||||
|
||||
if (Object::cast_to<GPUParticlesCollisionSphere3D>(cs) || Object::cast_to<GPUParticlesAttractorSphere3D>(cs)) {
|
||||
float radius = cs->call("get_radius");
|
||||
|
||||
#define PUSH_QUARTER(m_from_x, m_from_y, m_to_x, m_to_y, m_y) \
|
||||
points_ptrw[index++] = Vector3(m_from_x, m_y, m_from_y); \
|
||||
points_ptrw[index++] = Vector3(m_to_x, m_y, m_to_y); \
|
||||
points_ptrw[index++] = Vector3(m_from_x, m_y, -m_from_y); \
|
||||
points_ptrw[index++] = Vector3(m_to_x, m_y, -m_to_y); \
|
||||
points_ptrw[index++] = Vector3(-m_from_x, m_y, m_from_y); \
|
||||
points_ptrw[index++] = Vector3(-m_to_x, m_y, m_to_y); \
|
||||
points_ptrw[index++] = Vector3(-m_from_x, m_y, -m_from_y); \
|
||||
points_ptrw[index++] = Vector3(-m_to_x, m_y, -m_to_y);
|
||||
|
||||
#define PUSH_QUARTER_XY(m_from_x, m_from_y, m_to_x, m_to_y) \
|
||||
points_ptrw[index++] = Vector3(m_from_x, -m_from_y, 0); \
|
||||
points_ptrw[index++] = Vector3(m_to_x, -m_to_y, 0); \
|
||||
points_ptrw[index++] = Vector3(m_from_x, m_from_y, 0); \
|
||||
points_ptrw[index++] = Vector3(m_to_x, m_to_y, 0); \
|
||||
points_ptrw[index++] = Vector3(-m_from_x, -m_from_y, 0); \
|
||||
points_ptrw[index++] = Vector3(-m_to_x, -m_to_y, 0); \
|
||||
points_ptrw[index++] = Vector3(-m_from_x, m_from_y, 0); \
|
||||
points_ptrw[index++] = Vector3(-m_to_x, m_to_y, 0);
|
||||
|
||||
#define PUSH_QUARTER_YZ(m_from_x, m_from_y, m_to_x, m_to_y) \
|
||||
points_ptrw[index++] = Vector3(0, -m_from_y, m_from_x); \
|
||||
points_ptrw[index++] = Vector3(0, -m_to_y, m_to_x); \
|
||||
points_ptrw[index++] = Vector3(0, m_from_y, m_from_x); \
|
||||
points_ptrw[index++] = Vector3(0, m_to_y, m_to_x); \
|
||||
points_ptrw[index++] = Vector3(0, -m_from_y, -m_from_x); \
|
||||
points_ptrw[index++] = Vector3(0, -m_to_y, -m_to_x); \
|
||||
points_ptrw[index++] = Vector3(0, m_from_y, -m_from_x); \
|
||||
points_ptrw[index++] = Vector3(0, m_to_y, -m_to_x);
|
||||
|
||||
// Number of points in an octant. So there will be 8 * points_in_octant points in total.
|
||||
// This corresponds to the smoothness of the circle.
|
||||
const uint32_t points_in_octant = 16;
|
||||
const real_t octant_angle = Math::PI / 4;
|
||||
const real_t inc = (Math::PI / (4 * points_in_octant));
|
||||
const real_t radius_squared = radius * radius;
|
||||
real_t r = 0;
|
||||
|
||||
Vector<Vector3> points;
|
||||
points.resize(3 * 8 * points_in_octant * 2);
|
||||
Vector3 *points_ptrw = points.ptrw();
|
||||
|
||||
uint32_t index = 0;
|
||||
float previous_x = radius;
|
||||
float previous_y = 0.f;
|
||||
|
||||
for (uint32_t i = 0; i < points_in_octant; ++i) {
|
||||
r += inc;
|
||||
real_t x = Math::cos((i == points_in_octant - 1) ? octant_angle : r) * radius;
|
||||
real_t y = Math::sqrt(radius_squared - (x * x));
|
||||
|
||||
PUSH_QUARTER(previous_x, previous_y, x, y, 0);
|
||||
PUSH_QUARTER(previous_y, previous_x, y, x, 0);
|
||||
|
||||
PUSH_QUARTER_XY(previous_x, previous_y, x, y);
|
||||
PUSH_QUARTER_XY(previous_y, previous_x, y, x);
|
||||
|
||||
PUSH_QUARTER_YZ(previous_x, previous_y, x, y);
|
||||
PUSH_QUARTER_YZ(previous_y, previous_x, y, x);
|
||||
|
||||
previous_x = x;
|
||||
previous_y = y;
|
||||
}
|
||||
|
||||
p_gizmo->add_lines(points, material);
|
||||
p_gizmo->add_collision_segments(points);
|
||||
Vector<Vector3> handles;
|
||||
handles.push_back(Vector3(r, 0, 0));
|
||||
p_gizmo->add_handles(handles, handles_material);
|
||||
|
||||
#undef PUSH_QUARTER
|
||||
#undef PUSH_QUARTER_XY
|
||||
#undef PUSH_QUARTER_YZ
|
||||
}
|
||||
|
||||
if (Object::cast_to<GPUParticlesCollisionBox3D>(cs) || Object::cast_to<GPUParticlesAttractorBox3D>(cs) || Object::cast_to<GPUParticlesAttractorVectorField3D>(cs) || Object::cast_to<GPUParticlesCollisionSDF3D>(cs) || Object::cast_to<GPUParticlesCollisionHeightField3D>(cs)) {
|
||||
Vector<Vector3> lines;
|
||||
AABB aabb;
|
||||
aabb.size = cs->call("get_size").operator Vector3();
|
||||
aabb.position = aabb.size / -2;
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
Vector3 a, b;
|
||||
aabb.get_edge(i, a, b);
|
||||
lines.push_back(a);
|
||||
lines.push_back(b);
|
||||
}
|
||||
|
||||
Vector<Vector3> handles = helper->box_get_handles(aabb.size);
|
||||
|
||||
p_gizmo->add_lines(lines, material);
|
||||
p_gizmo->add_collision_segments(lines);
|
||||
p_gizmo->add_handles(handles, handles_material);
|
||||
|
||||
GPUParticlesCollisionSDF3D *col_sdf = Object::cast_to<GPUParticlesCollisionSDF3D>(cs);
|
||||
if (col_sdf) {
|
||||
static const int subdivs[GPUParticlesCollisionSDF3D::RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 };
|
||||
int subdiv = subdivs[col_sdf->get_resolution()];
|
||||
float cell_size = aabb.get_longest_axis_size() / subdiv;
|
||||
|
||||
lines.clear();
|
||||
|
||||
for (int i = 1; i < subdiv; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
if (cell_size * i > aabb.size[j]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int j_n1 = (j + 1) % 3;
|
||||
int j_n2 = (j + 2) % 3;
|
||||
|
||||
for (int k = 0; k < 4; k++) {
|
||||
Vector3 from = aabb.position, to = aabb.position;
|
||||
from[j] += cell_size * i;
|
||||
to[j] += cell_size * i;
|
||||
|
||||
if (k & 1) {
|
||||
to[j_n1] += aabb.size[j_n1];
|
||||
} else {
|
||||
to[j_n2] += aabb.size[j_n2];
|
||||
}
|
||||
|
||||
if (k & 2) {
|
||||
from[j_n1] += aabb.size[j_n1];
|
||||
from[j_n2] += aabb.size[j_n2];
|
||||
}
|
||||
|
||||
lines.push_back(from);
|
||||
lines.push_back(to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p_gizmo->add_lines(lines, material_internal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* gpu_particles_collision_3d_gizmo_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class Gizmo3DHelper;
|
||||
|
||||
class GPUParticlesCollision3DGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(GPUParticlesCollision3DGizmoPlugin, EditorNode3DGizmoPlugin);
|
||||
|
||||
Ref<Gizmo3DHelper> helper;
|
||||
|
||||
public:
|
||||
bool has_gizmo(Node3D *p_spatial) override;
|
||||
String get_gizmo_name() const override;
|
||||
int get_priority() const override;
|
||||
void redraw(EditorNode3DGizmo *p_gizmo) override;
|
||||
|
||||
String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
|
||||
Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
|
||||
void begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) override;
|
||||
void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) override;
|
||||
void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false) override;
|
||||
|
||||
GPUParticlesCollision3DGizmoPlugin();
|
||||
};
|
||||
|
|
@ -1,723 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* joint_3d_gizmo_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 "joint_3d_gizmo_plugin.h"
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "scene/3d/physics/joints/cone_twist_joint_3d.h"
|
||||
#include "scene/3d/physics/joints/generic_6dof_joint_3d.h"
|
||||
#include "scene/3d/physics/joints/hinge_joint_3d.h"
|
||||
#include "scene/3d/physics/joints/pin_joint_3d.h"
|
||||
#include "scene/3d/physics/joints/slider_joint_3d.h"
|
||||
|
||||
#define BODY_A_RADIUS 0.25
|
||||
#define BODY_B_RADIUS 0.27
|
||||
|
||||
Basis JointGizmosDrawer::look_body(const Transform3D &p_joint_transform, const Transform3D &p_body_transform) {
|
||||
const Vector3 &p_eye(p_joint_transform.origin);
|
||||
const Vector3 &p_target(p_body_transform.origin);
|
||||
|
||||
Vector3 v_x, v_y, v_z;
|
||||
|
||||
// Look the body with X
|
||||
v_x = p_target - p_eye;
|
||||
v_x.normalize();
|
||||
|
||||
v_z = v_x.cross(Vector3(0, 1, 0));
|
||||
v_z.normalize();
|
||||
|
||||
v_y = v_z.cross(v_x);
|
||||
v_y.normalize();
|
||||
|
||||
Basis base;
|
||||
base.set_columns(v_x, v_y, v_z);
|
||||
|
||||
// Absorb current joint transform
|
||||
base = p_joint_transform.basis.inverse() * base;
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
Basis JointGizmosDrawer::look_body_toward(Vector3::Axis p_axis, const Transform3D &joint_transform, const Transform3D &body_transform) {
|
||||
switch (p_axis) {
|
||||
case Vector3::AXIS_X:
|
||||
return look_body_toward_x(joint_transform, body_transform);
|
||||
case Vector3::AXIS_Y:
|
||||
return look_body_toward_y(joint_transform, body_transform);
|
||||
case Vector3::AXIS_Z:
|
||||
return look_body_toward_z(joint_transform, body_transform);
|
||||
default:
|
||||
return Basis();
|
||||
}
|
||||
}
|
||||
|
||||
Basis JointGizmosDrawer::look_body_toward_x(const Transform3D &p_joint_transform, const Transform3D &p_body_transform) {
|
||||
const Vector3 &p_eye(p_joint_transform.origin);
|
||||
const Vector3 &p_target(p_body_transform.origin);
|
||||
|
||||
const Vector3 p_front(p_joint_transform.basis.get_column(0));
|
||||
|
||||
Vector3 v_x, v_y, v_z;
|
||||
|
||||
// Look the body with X
|
||||
v_x = p_target - p_eye;
|
||||
v_x.normalize();
|
||||
|
||||
v_y = p_front.cross(v_x);
|
||||
v_y.normalize();
|
||||
|
||||
v_z = v_y.cross(p_front);
|
||||
v_z.normalize();
|
||||
|
||||
// Clamp X to FRONT axis
|
||||
v_x = p_front;
|
||||
v_x.normalize();
|
||||
|
||||
Basis base;
|
||||
base.set_columns(v_x, v_y, v_z);
|
||||
|
||||
// Absorb current joint transform
|
||||
base = p_joint_transform.basis.inverse() * base;
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
Basis JointGizmosDrawer::look_body_toward_y(const Transform3D &p_joint_transform, const Transform3D &p_body_transform) {
|
||||
const Vector3 &p_eye(p_joint_transform.origin);
|
||||
const Vector3 &p_target(p_body_transform.origin);
|
||||
|
||||
const Vector3 p_up(p_joint_transform.basis.get_column(1));
|
||||
|
||||
Vector3 v_x, v_y, v_z;
|
||||
|
||||
// Look the body with X
|
||||
v_x = p_target - p_eye;
|
||||
v_x.normalize();
|
||||
|
||||
v_z = v_x.cross(p_up);
|
||||
v_z.normalize();
|
||||
|
||||
v_x = p_up.cross(v_z);
|
||||
v_x.normalize();
|
||||
|
||||
// Clamp Y to UP axis
|
||||
v_y = p_up;
|
||||
v_y.normalize();
|
||||
|
||||
Basis base;
|
||||
base.set_columns(v_x, v_y, v_z);
|
||||
|
||||
// Absorb current joint transform
|
||||
base = p_joint_transform.basis.inverse() * base;
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
Basis JointGizmosDrawer::look_body_toward_z(const Transform3D &p_joint_transform, const Transform3D &p_body_transform) {
|
||||
const Vector3 &p_eye(p_joint_transform.origin);
|
||||
const Vector3 &p_target(p_body_transform.origin);
|
||||
|
||||
const Vector3 p_lateral(p_joint_transform.basis.get_column(2));
|
||||
|
||||
Vector3 v_x, v_y, v_z;
|
||||
|
||||
// Look the body with X
|
||||
v_x = p_target - p_eye;
|
||||
v_x.normalize();
|
||||
|
||||
v_z = p_lateral;
|
||||
v_z.normalize();
|
||||
|
||||
v_y = v_z.cross(v_x);
|
||||
v_y.normalize();
|
||||
|
||||
// Clamp X to Z axis
|
||||
v_x = v_y.cross(v_z);
|
||||
v_x.normalize();
|
||||
|
||||
Basis base;
|
||||
base.set_columns(v_x, v_y, v_z);
|
||||
|
||||
// Absorb current joint transform
|
||||
base = p_joint_transform.basis.inverse() * base;
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
void JointGizmosDrawer::draw_circle(Vector3::Axis p_axis, real_t p_radius, const Transform3D &p_offset, const Basis &p_base, real_t p_limit_lower, real_t p_limit_upper, Vector<Vector3> &r_points, bool p_inverse) {
|
||||
if (p_limit_lower == p_limit_upper) {
|
||||
r_points.push_back(p_offset.translated_local(Vector3()).origin);
|
||||
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3(0.5, 0, 0))).origin);
|
||||
|
||||
} else {
|
||||
if (p_limit_lower > p_limit_upper) {
|
||||
p_limit_lower = -Math::PI;
|
||||
p_limit_upper = Math::PI;
|
||||
}
|
||||
|
||||
const int points = 32;
|
||||
|
||||
for (int i = 0; i < points; i++) {
|
||||
real_t s = p_limit_lower + i * (p_limit_upper - p_limit_lower) / points;
|
||||
real_t n = p_limit_lower + (i + 1) * (p_limit_upper - p_limit_lower) / points;
|
||||
|
||||
Vector3 from;
|
||||
Vector3 to;
|
||||
switch (p_axis) {
|
||||
case Vector3::AXIS_X:
|
||||
if (p_inverse) {
|
||||
from = p_base.xform(Vector3(0, Math::sin(s), Math::cos(s))) * p_radius;
|
||||
to = p_base.xform(Vector3(0, Math::sin(n), Math::cos(n))) * p_radius;
|
||||
} else {
|
||||
from = p_base.xform(Vector3(0, -Math::sin(s), Math::cos(s))) * p_radius;
|
||||
to = p_base.xform(Vector3(0, -Math::sin(n), Math::cos(n))) * p_radius;
|
||||
}
|
||||
break;
|
||||
case Vector3::AXIS_Y:
|
||||
if (p_inverse) {
|
||||
from = p_base.xform(Vector3(Math::cos(s), 0, -Math::sin(s))) * p_radius;
|
||||
to = p_base.xform(Vector3(Math::cos(n), 0, -Math::sin(n))) * p_radius;
|
||||
} else {
|
||||
from = p_base.xform(Vector3(Math::cos(s), 0, Math::sin(s))) * p_radius;
|
||||
to = p_base.xform(Vector3(Math::cos(n), 0, Math::sin(n))) * p_radius;
|
||||
}
|
||||
break;
|
||||
case Vector3::AXIS_Z:
|
||||
from = p_base.xform(Vector3(Math::cos(s), Math::sin(s), 0)) * p_radius;
|
||||
to = p_base.xform(Vector3(Math::cos(n), Math::sin(n), 0)) * p_radius;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == points - 1) {
|
||||
r_points.push_back(p_offset.translated_local(to).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3()).origin);
|
||||
}
|
||||
if (i == 0) {
|
||||
r_points.push_back(p_offset.translated_local(from).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3()).origin);
|
||||
}
|
||||
|
||||
r_points.push_back(p_offset.translated_local(from).origin);
|
||||
r_points.push_back(p_offset.translated_local(to).origin);
|
||||
}
|
||||
|
||||
r_points.push_back(p_offset.translated_local(Vector3(0, p_radius * 1.5, 0)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3()).origin);
|
||||
}
|
||||
}
|
||||
|
||||
void JointGizmosDrawer::draw_cone(const Transform3D &p_offset, const Basis &p_base, real_t p_swing, real_t p_twist, Vector<Vector3> &r_points) {
|
||||
float r = 1.0;
|
||||
float w = r * Math::sin(p_swing);
|
||||
float d = r * Math::cos(p_swing);
|
||||
|
||||
//swing
|
||||
for (int i = 0; i < 360; i += 10) {
|
||||
float ra = Math::deg_to_rad((float)i);
|
||||
float rb = Math::deg_to_rad((float)i + 10);
|
||||
Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w;
|
||||
Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w;
|
||||
|
||||
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3(d, a.x, a.y))).origin);
|
||||
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3(d, b.x, b.y))).origin);
|
||||
|
||||
if (i % 90 == 0) {
|
||||
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3(d, a.x, a.y))).origin);
|
||||
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3())).origin);
|
||||
}
|
||||
}
|
||||
|
||||
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3())).origin);
|
||||
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3(1, 0, 0))).origin);
|
||||
|
||||
/// Twist
|
||||
float ts = Math::rad_to_deg(p_twist);
|
||||
ts = MIN(ts, 720);
|
||||
|
||||
for (int i = 0; i < int(ts); i += 5) {
|
||||
float ra = Math::deg_to_rad((float)i);
|
||||
float rb = Math::deg_to_rad((float)i + 5);
|
||||
float c = i / 720.0;
|
||||
float cn = (i + 5) / 720.0;
|
||||
Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w * c;
|
||||
Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w * cn;
|
||||
|
||||
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3(c, a.x, a.y))).origin);
|
||||
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3(cn, b.x, b.y))).origin);
|
||||
}
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
Joint3DGizmoPlugin::Joint3DGizmoPlugin() {
|
||||
create_material("joint_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint"));
|
||||
create_material("joint_body_a_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint_body_a"));
|
||||
create_material("joint_body_b_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint_body_b"));
|
||||
|
||||
update_timer = memnew(Timer);
|
||||
update_timer->set_name("JointGizmoUpdateTimer");
|
||||
update_timer->set_wait_time(1.0 / 120.0);
|
||||
update_timer->connect("timeout", callable_mp(this, &Joint3DGizmoPlugin::incremental_update_gizmos));
|
||||
update_timer->set_autostart(true);
|
||||
callable_mp((Node *)EditorNode::get_singleton(), &Node::add_child).call_deferred(update_timer, false, Node::INTERNAL_MODE_DISABLED);
|
||||
}
|
||||
|
||||
void Joint3DGizmoPlugin::incremental_update_gizmos() {
|
||||
if (!current_gizmos.is_empty()) {
|
||||
HashSet<EditorNode3DGizmo *>::Iterator E = current_gizmos.find(last_drawn);
|
||||
if (E) {
|
||||
++E;
|
||||
}
|
||||
if (!E) {
|
||||
E = current_gizmos.begin();
|
||||
}
|
||||
redraw(*E);
|
||||
last_drawn = *E;
|
||||
}
|
||||
}
|
||||
|
||||
bool Joint3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
|
||||
return Object::cast_to<Joint3D>(p_spatial) != nullptr;
|
||||
}
|
||||
|
||||
String Joint3DGizmoPlugin::get_gizmo_name() const {
|
||||
return "Joint3D";
|
||||
}
|
||||
|
||||
int Joint3DGizmoPlugin::get_priority() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Joint3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
Joint3D *joint = Object::cast_to<Joint3D>(p_gizmo->get_node_3d());
|
||||
|
||||
p_gizmo->clear();
|
||||
|
||||
Node3D *node_body_a = nullptr;
|
||||
if (!joint->get_node_a().is_empty()) {
|
||||
node_body_a = Object::cast_to<Node3D>(joint->get_node(joint->get_node_a()));
|
||||
}
|
||||
|
||||
Node3D *node_body_b = nullptr;
|
||||
if (!joint->get_node_b().is_empty()) {
|
||||
node_body_b = Object::cast_to<Node3D>(joint->get_node(joint->get_node_b()));
|
||||
}
|
||||
|
||||
if (!node_body_a && !node_body_b) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<Material> common_material = get_material("joint_material", p_gizmo);
|
||||
Ref<Material> body_a_material = get_material("joint_body_a_material", p_gizmo);
|
||||
Ref<Material> body_b_material = get_material("joint_body_b_material", p_gizmo);
|
||||
|
||||
Vector<Vector3> points;
|
||||
Vector<Vector3> body_a_points;
|
||||
Vector<Vector3> body_b_points;
|
||||
|
||||
if (Object::cast_to<PinJoint3D>(joint)) {
|
||||
CreatePinJointGizmo(Transform3D(), points);
|
||||
p_gizmo->add_collision_segments(points);
|
||||
p_gizmo->add_lines(points, common_material);
|
||||
}
|
||||
|
||||
HingeJoint3D *hinge = Object::cast_to<HingeJoint3D>(joint);
|
||||
if (hinge) {
|
||||
CreateHingeJointGizmo(
|
||||
Transform3D(),
|
||||
hinge->get_global_transform(),
|
||||
node_body_a ? node_body_a->get_global_transform() : Transform3D(),
|
||||
node_body_b ? node_body_b->get_global_transform() : Transform3D(),
|
||||
hinge->get_param(HingeJoint3D::PARAM_LIMIT_LOWER),
|
||||
hinge->get_param(HingeJoint3D::PARAM_LIMIT_UPPER),
|
||||
hinge->get_flag(HingeJoint3D::FLAG_USE_LIMIT),
|
||||
points,
|
||||
node_body_a ? &body_a_points : nullptr,
|
||||
node_body_b ? &body_b_points : nullptr);
|
||||
|
||||
p_gizmo->add_collision_segments(points);
|
||||
p_gizmo->add_collision_segments(body_a_points);
|
||||
p_gizmo->add_collision_segments(body_b_points);
|
||||
|
||||
p_gizmo->add_lines(points, common_material);
|
||||
p_gizmo->add_lines(body_a_points, body_a_material);
|
||||
p_gizmo->add_lines(body_b_points, body_b_material);
|
||||
}
|
||||
|
||||
SliderJoint3D *slider = Object::cast_to<SliderJoint3D>(joint);
|
||||
if (slider) {
|
||||
CreateSliderJointGizmo(
|
||||
Transform3D(),
|
||||
slider->get_global_transform(),
|
||||
node_body_a ? node_body_a->get_global_transform() : Transform3D(),
|
||||
node_body_b ? node_body_b->get_global_transform() : Transform3D(),
|
||||
slider->get_param(SliderJoint3D::PARAM_ANGULAR_LIMIT_LOWER),
|
||||
slider->get_param(SliderJoint3D::PARAM_ANGULAR_LIMIT_UPPER),
|
||||
slider->get_param(SliderJoint3D::PARAM_LINEAR_LIMIT_LOWER),
|
||||
slider->get_param(SliderJoint3D::PARAM_LINEAR_LIMIT_UPPER),
|
||||
points,
|
||||
node_body_a ? &body_a_points : nullptr,
|
||||
node_body_b ? &body_b_points : nullptr);
|
||||
|
||||
p_gizmo->add_collision_segments(points);
|
||||
p_gizmo->add_collision_segments(body_a_points);
|
||||
p_gizmo->add_collision_segments(body_b_points);
|
||||
|
||||
p_gizmo->add_lines(points, common_material);
|
||||
p_gizmo->add_lines(body_a_points, body_a_material);
|
||||
p_gizmo->add_lines(body_b_points, body_b_material);
|
||||
}
|
||||
|
||||
ConeTwistJoint3D *cone = Object::cast_to<ConeTwistJoint3D>(joint);
|
||||
if (cone) {
|
||||
CreateConeTwistJointGizmo(
|
||||
Transform3D(),
|
||||
cone->get_global_transform(),
|
||||
node_body_a ? node_body_a->get_global_transform() : Transform3D(),
|
||||
node_body_b ? node_body_b->get_global_transform() : Transform3D(),
|
||||
cone->get_param(ConeTwistJoint3D::PARAM_SWING_SPAN),
|
||||
cone->get_param(ConeTwistJoint3D::PARAM_TWIST_SPAN),
|
||||
node_body_a ? &body_a_points : nullptr,
|
||||
node_body_b ? &body_b_points : nullptr);
|
||||
|
||||
p_gizmo->add_collision_segments(body_a_points);
|
||||
p_gizmo->add_collision_segments(body_b_points);
|
||||
|
||||
p_gizmo->add_lines(body_a_points, body_a_material);
|
||||
p_gizmo->add_lines(body_b_points, body_b_material);
|
||||
}
|
||||
|
||||
Generic6DOFJoint3D *gen = Object::cast_to<Generic6DOFJoint3D>(joint);
|
||||
if (gen) {
|
||||
CreateGeneric6DOFJointGizmo(
|
||||
Transform3D(),
|
||||
gen->get_global_transform(),
|
||||
node_body_a ? node_body_a->get_global_transform() : Transform3D(),
|
||||
node_body_b ? node_body_b->get_global_transform() : Transform3D(),
|
||||
|
||||
gen->get_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT),
|
||||
gen->get_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT),
|
||||
gen->get_param_x(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT),
|
||||
gen->get_param_x(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT),
|
||||
gen->get_flag_x(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT),
|
||||
gen->get_flag_x(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT),
|
||||
|
||||
gen->get_param_y(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT),
|
||||
gen->get_param_y(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT),
|
||||
gen->get_param_y(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT),
|
||||
gen->get_param_y(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT),
|
||||
gen->get_flag_y(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT),
|
||||
gen->get_flag_y(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT),
|
||||
|
||||
gen->get_param_z(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT),
|
||||
gen->get_param_z(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT),
|
||||
gen->get_param_z(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT),
|
||||
gen->get_param_z(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT),
|
||||
gen->get_flag_z(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT),
|
||||
gen->get_flag_z(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT),
|
||||
|
||||
points,
|
||||
node_body_a ? &body_a_points : nullptr,
|
||||
node_body_a ? &body_b_points : nullptr);
|
||||
|
||||
p_gizmo->add_collision_segments(points);
|
||||
p_gizmo->add_collision_segments(body_a_points);
|
||||
p_gizmo->add_collision_segments(body_b_points);
|
||||
|
||||
p_gizmo->add_lines(points, common_material);
|
||||
p_gizmo->add_lines(body_a_points, body_a_material);
|
||||
p_gizmo->add_lines(body_b_points, body_b_material);
|
||||
}
|
||||
}
|
||||
|
||||
void Joint3DGizmoPlugin::CreatePinJointGizmo(const Transform3D &p_offset, Vector<Vector3> &r_cursor_points) {
|
||||
float cs = 0.25;
|
||||
|
||||
r_cursor_points.push_back(p_offset.translated_local(Vector3(+cs, 0, 0)).origin);
|
||||
r_cursor_points.push_back(p_offset.translated_local(Vector3(-cs, 0, 0)).origin);
|
||||
r_cursor_points.push_back(p_offset.translated_local(Vector3(0, +cs, 0)).origin);
|
||||
r_cursor_points.push_back(p_offset.translated_local(Vector3(0, -cs, 0)).origin);
|
||||
r_cursor_points.push_back(p_offset.translated_local(Vector3(0, 0, +cs)).origin);
|
||||
r_cursor_points.push_back(p_offset.translated_local(Vector3(0, 0, -cs)).origin);
|
||||
}
|
||||
|
||||
void Joint3DGizmoPlugin::CreateHingeJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_limit_lower, real_t p_limit_upper, bool p_use_limit, Vector<Vector3> &r_common_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points) {
|
||||
r_common_points.push_back(p_offset.translated_local(Vector3(0, 0, 0.5)).origin);
|
||||
r_common_points.push_back(p_offset.translated_local(Vector3(0, 0, -0.5)).origin);
|
||||
|
||||
if (!p_use_limit) {
|
||||
p_limit_upper = -1;
|
||||
p_limit_lower = 0;
|
||||
}
|
||||
|
||||
if (r_body_a_points) {
|
||||
JointGizmosDrawer::draw_circle(Vector3::AXIS_Z,
|
||||
BODY_A_RADIUS,
|
||||
p_offset,
|
||||
JointGizmosDrawer::look_body_toward_z(p_trs_joint, p_trs_body_a),
|
||||
p_limit_lower,
|
||||
p_limit_upper,
|
||||
*r_body_a_points);
|
||||
}
|
||||
|
||||
if (r_body_b_points) {
|
||||
JointGizmosDrawer::draw_circle(Vector3::AXIS_Z,
|
||||
BODY_B_RADIUS,
|
||||
p_offset,
|
||||
JointGizmosDrawer::look_body_toward_z(p_trs_joint, p_trs_body_b),
|
||||
p_limit_lower,
|
||||
p_limit_upper,
|
||||
*r_body_b_points);
|
||||
}
|
||||
}
|
||||
|
||||
void Joint3DGizmoPlugin::CreateSliderJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_angular_limit_lower, real_t p_angular_limit_upper, real_t p_linear_limit_lower, real_t p_linear_limit_upper, Vector<Vector3> &r_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points) {
|
||||
p_linear_limit_lower = -p_linear_limit_lower;
|
||||
p_linear_limit_upper = -p_linear_limit_upper;
|
||||
|
||||
float cs = 0.25;
|
||||
r_points.push_back(p_offset.translated_local(Vector3(0, 0, 0.5)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3(0, 0, -0.5)).origin);
|
||||
|
||||
if (p_linear_limit_lower >= p_linear_limit_upper) {
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, 0, 0)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, 0, 0)).origin);
|
||||
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, -cs, -cs)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, -cs, cs)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, -cs, cs)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, cs, cs)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, cs, cs)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, cs, -cs)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, cs, -cs)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, -cs, -cs)).origin);
|
||||
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, -cs, -cs)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, -cs, cs)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, -cs, cs)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, cs, cs)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, cs, cs)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, cs, -cs)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, cs, -cs)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, -cs, -cs)).origin);
|
||||
|
||||
} else {
|
||||
r_points.push_back(p_offset.translated_local(Vector3(+cs * 2, 0, 0)).origin);
|
||||
r_points.push_back(p_offset.translated_local(Vector3(-cs * 2, 0, 0)).origin);
|
||||
}
|
||||
|
||||
if (r_body_a_points) {
|
||||
JointGizmosDrawer::draw_circle(
|
||||
Vector3::AXIS_X,
|
||||
BODY_A_RADIUS,
|
||||
p_offset,
|
||||
JointGizmosDrawer::look_body_toward(Vector3::AXIS_X, p_trs_joint, p_trs_body_a),
|
||||
p_angular_limit_lower,
|
||||
p_angular_limit_upper,
|
||||
*r_body_a_points);
|
||||
}
|
||||
|
||||
if (r_body_b_points) {
|
||||
JointGizmosDrawer::draw_circle(
|
||||
Vector3::AXIS_X,
|
||||
BODY_B_RADIUS,
|
||||
p_offset,
|
||||
JointGizmosDrawer::look_body_toward(Vector3::AXIS_X, p_trs_joint, p_trs_body_b),
|
||||
p_angular_limit_lower,
|
||||
p_angular_limit_upper,
|
||||
*r_body_b_points,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
void Joint3DGizmoPlugin::CreateConeTwistJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_swing, real_t p_twist, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points) {
|
||||
if (r_body_a_points) {
|
||||
JointGizmosDrawer::draw_cone(
|
||||
p_offset,
|
||||
JointGizmosDrawer::look_body(p_trs_joint, p_trs_body_a),
|
||||
p_swing,
|
||||
p_twist,
|
||||
*r_body_a_points);
|
||||
}
|
||||
|
||||
if (r_body_b_points) {
|
||||
JointGizmosDrawer::draw_cone(
|
||||
p_offset,
|
||||
JointGizmosDrawer::look_body(p_trs_joint, p_trs_body_b),
|
||||
p_swing,
|
||||
p_twist,
|
||||
*r_body_b_points);
|
||||
}
|
||||
}
|
||||
|
||||
void Joint3DGizmoPlugin::CreateGeneric6DOFJointGizmo(
|
||||
const Transform3D &p_offset,
|
||||
const Transform3D &p_trs_joint,
|
||||
const Transform3D &p_trs_body_a,
|
||||
const Transform3D &p_trs_body_b,
|
||||
real_t p_angular_limit_lower_x,
|
||||
real_t p_angular_limit_upper_x,
|
||||
real_t p_linear_limit_lower_x,
|
||||
real_t p_linear_limit_upper_x,
|
||||
bool p_enable_angular_limit_x,
|
||||
bool p_enable_linear_limit_x,
|
||||
real_t p_angular_limit_lower_y,
|
||||
real_t p_angular_limit_upper_y,
|
||||
real_t p_linear_limit_lower_y,
|
||||
real_t p_linear_limit_upper_y,
|
||||
bool p_enable_angular_limit_y,
|
||||
bool p_enable_linear_limit_y,
|
||||
real_t p_angular_limit_lower_z,
|
||||
real_t p_angular_limit_upper_z,
|
||||
real_t p_linear_limit_lower_z,
|
||||
real_t p_linear_limit_upper_z,
|
||||
bool p_enable_angular_limit_z,
|
||||
bool p_enable_linear_limit_z,
|
||||
Vector<Vector3> &r_points,
|
||||
Vector<Vector3> *r_body_a_points,
|
||||
Vector<Vector3> *r_body_b_points) {
|
||||
float cs = 0.25;
|
||||
|
||||
for (int ax = 0; ax < 3; ax++) {
|
||||
float ll = 0;
|
||||
float ul = 0;
|
||||
float lll = 0;
|
||||
float lul = 0;
|
||||
|
||||
int a1 = 0;
|
||||
int a2 = 0;
|
||||
int a3 = 0;
|
||||
bool enable_ang = false;
|
||||
bool enable_lin = false;
|
||||
|
||||
switch (ax) {
|
||||
case 0:
|
||||
ll = p_angular_limit_lower_x;
|
||||
ul = p_angular_limit_upper_x;
|
||||
lll = -p_linear_limit_lower_x;
|
||||
lul = -p_linear_limit_upper_x;
|
||||
enable_ang = p_enable_angular_limit_x;
|
||||
enable_lin = p_enable_linear_limit_x;
|
||||
a1 = 0;
|
||||
a2 = 1;
|
||||
a3 = 2;
|
||||
break;
|
||||
case 1:
|
||||
ll = p_angular_limit_lower_y;
|
||||
ul = p_angular_limit_upper_y;
|
||||
lll = -p_linear_limit_lower_y;
|
||||
lul = -p_linear_limit_upper_y;
|
||||
enable_ang = p_enable_angular_limit_y;
|
||||
enable_lin = p_enable_linear_limit_y;
|
||||
a1 = 1;
|
||||
a2 = 2;
|
||||
a3 = 0;
|
||||
break;
|
||||
case 2:
|
||||
ll = p_angular_limit_lower_z;
|
||||
ul = p_angular_limit_upper_z;
|
||||
lll = -p_linear_limit_lower_z;
|
||||
lul = -p_linear_limit_upper_z;
|
||||
enable_ang = p_enable_angular_limit_z;
|
||||
enable_lin = p_enable_linear_limit_z;
|
||||
a1 = 2;
|
||||
a2 = 0;
|
||||
a3 = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
#define ADD_VTX(x, y, z) \
|
||||
{ \
|
||||
Vector3 v; \
|
||||
v[a1] = (x); \
|
||||
v[a2] = (y); \
|
||||
v[a3] = (z); \
|
||||
r_points.push_back(p_offset.translated_local(v).origin); \
|
||||
}
|
||||
|
||||
if (enable_lin && lll >= lul) {
|
||||
ADD_VTX(lul, 0, 0);
|
||||
ADD_VTX(lll, 0, 0);
|
||||
|
||||
ADD_VTX(lul, -cs, -cs);
|
||||
ADD_VTX(lul, -cs, cs);
|
||||
ADD_VTX(lul, -cs, cs);
|
||||
ADD_VTX(lul, cs, cs);
|
||||
ADD_VTX(lul, cs, cs);
|
||||
ADD_VTX(lul, cs, -cs);
|
||||
ADD_VTX(lul, cs, -cs);
|
||||
ADD_VTX(lul, -cs, -cs);
|
||||
|
||||
ADD_VTX(lll, -cs, -cs);
|
||||
ADD_VTX(lll, -cs, cs);
|
||||
ADD_VTX(lll, -cs, cs);
|
||||
ADD_VTX(lll, cs, cs);
|
||||
ADD_VTX(lll, cs, cs);
|
||||
ADD_VTX(lll, cs, -cs);
|
||||
ADD_VTX(lll, cs, -cs);
|
||||
ADD_VTX(lll, -cs, -cs);
|
||||
|
||||
} else {
|
||||
ADD_VTX(+cs * 2, 0, 0);
|
||||
ADD_VTX(-cs * 2, 0, 0);
|
||||
}
|
||||
|
||||
if (!enable_ang) {
|
||||
ll = 0;
|
||||
ul = -1;
|
||||
}
|
||||
|
||||
if (r_body_a_points) {
|
||||
JointGizmosDrawer::draw_circle(
|
||||
static_cast<Vector3::Axis>(ax),
|
||||
BODY_A_RADIUS,
|
||||
p_offset,
|
||||
JointGizmosDrawer::look_body_toward(static_cast<Vector3::Axis>(ax), p_trs_joint, p_trs_body_a),
|
||||
ll,
|
||||
ul,
|
||||
*r_body_a_points,
|
||||
true);
|
||||
}
|
||||
|
||||
if (r_body_b_points) {
|
||||
JointGizmosDrawer::draw_circle(
|
||||
static_cast<Vector3::Axis>(ax),
|
||||
BODY_B_RADIUS,
|
||||
p_offset,
|
||||
JointGizmosDrawer::look_body_toward(static_cast<Vector3::Axis>(ax), p_trs_joint, p_trs_body_b),
|
||||
ll,
|
||||
ul,
|
||||
*r_body_b_points);
|
||||
}
|
||||
}
|
||||
|
||||
#undef ADD_VTX
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* joint_3d_gizmo_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class Joint3DGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(Joint3DGizmoPlugin, EditorNode3DGizmoPlugin);
|
||||
|
||||
Timer *update_timer = nullptr;
|
||||
EditorNode3DGizmo *last_drawn = nullptr;
|
||||
|
||||
void incremental_update_gizmos();
|
||||
|
||||
public:
|
||||
bool has_gizmo(Node3D *p_spatial) override;
|
||||
String get_gizmo_name() const override;
|
||||
int get_priority() const override;
|
||||
void redraw(EditorNode3DGizmo *p_gizmo) override;
|
||||
|
||||
static void CreatePinJointGizmo(const Transform3D &p_offset, Vector<Vector3> &r_cursor_points);
|
||||
static void CreateHingeJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_limit_lower, real_t p_limit_upper, bool p_use_limit, Vector<Vector3> &r_common_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points);
|
||||
static void CreateSliderJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_angular_limit_lower, real_t p_angular_limit_upper, real_t p_linear_limit_lower, real_t p_linear_limit_upper, Vector<Vector3> &r_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points);
|
||||
static void CreateConeTwistJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_swing, real_t p_twist, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points);
|
||||
static void CreateGeneric6DOFJointGizmo(
|
||||
const Transform3D &p_offset,
|
||||
const Transform3D &p_trs_joint,
|
||||
const Transform3D &p_trs_body_a,
|
||||
const Transform3D &p_trs_body_b,
|
||||
real_t p_angular_limit_lower_x,
|
||||
real_t p_angular_limit_upper_x,
|
||||
real_t p_linear_limit_lower_x,
|
||||
real_t p_linear_limit_upper_x,
|
||||
bool p_enable_angular_limit_x,
|
||||
bool p_enable_linear_limit_x,
|
||||
real_t p_angular_limit_lower_y,
|
||||
real_t p_angular_limit_upper_y,
|
||||
real_t p_linear_limit_lower_y,
|
||||
real_t p_linear_limit_upper_y,
|
||||
bool p_enable_angular_limit_y,
|
||||
bool p_enable_linear_limit_y,
|
||||
real_t p_angular_limit_lower_z,
|
||||
real_t p_angular_limit_upper_z,
|
||||
real_t p_linear_limit_lower_z,
|
||||
real_t p_linear_limit_upper_z,
|
||||
bool p_enable_angular_limit_z,
|
||||
bool p_enable_linear_limit_z,
|
||||
Vector<Vector3> &r_points,
|
||||
Vector<Vector3> *r_body_a_points,
|
||||
Vector<Vector3> *r_body_b_points);
|
||||
|
||||
Joint3DGizmoPlugin();
|
||||
};
|
||||
|
||||
class JointGizmosDrawer {
|
||||
public:
|
||||
static Basis look_body(const Transform3D &p_joint_transform, const Transform3D &p_body_transform);
|
||||
static Basis look_body_toward(Vector3::Axis p_axis, const Transform3D &joint_transform, const Transform3D &body_transform);
|
||||
static Basis look_body_toward_x(const Transform3D &p_joint_transform, const Transform3D &p_body_transform);
|
||||
static Basis look_body_toward_y(const Transform3D &p_joint_transform, const Transform3D &p_body_transform);
|
||||
/// Special function just used for physics joints, it returns a basis constrained toward Joint Z axis
|
||||
/// with axis X and Y that are looking toward the body and oriented toward up
|
||||
static Basis look_body_toward_z(const Transform3D &p_joint_transform, const Transform3D &p_body_transform);
|
||||
|
||||
// Draw circle around p_axis
|
||||
static void draw_circle(Vector3::Axis p_axis, real_t p_radius, const Transform3D &p_offset, const Basis &p_base, real_t p_limit_lower, real_t p_limit_upper, Vector<Vector3> &r_points, bool p_inverse = false);
|
||||
static void draw_cone(const Transform3D &p_offset, const Basis &p_base, real_t p_swing, real_t p_twist, Vector<Vector3> &r_points);
|
||||
};
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* label_3d_gizmo_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 "label_3d_gizmo_plugin.h"
|
||||
|
||||
#include "scene/3d/label_3d.h"
|
||||
|
||||
bool Label3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
|
||||
return Object::cast_to<Label3D>(p_spatial) != nullptr;
|
||||
}
|
||||
|
||||
String Label3DGizmoPlugin::get_gizmo_name() const {
|
||||
return "Label3D";
|
||||
}
|
||||
|
||||
int Label3DGizmoPlugin::get_priority() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Label3DGizmoPlugin::can_be_hidden() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Label3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
Label3D *label = Object::cast_to<Label3D>(p_gizmo->get_node_3d());
|
||||
|
||||
p_gizmo->clear();
|
||||
|
||||
Ref<TriangleMesh> tm = label->generate_triangle_mesh();
|
||||
if (tm.is_valid()) {
|
||||
p_gizmo->add_collision_triangles(tm);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* label_3d_gizmo_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class Label3DGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(Label3DGizmoPlugin, EditorNode3DGizmoPlugin);
|
||||
|
||||
public:
|
||||
bool has_gizmo(Node3D *p_spatial) override;
|
||||
String get_gizmo_name() const override;
|
||||
int get_priority() const override;
|
||||
bool can_be_hidden() const override;
|
||||
void redraw(EditorNode3DGizmo *p_gizmo) override;
|
||||
};
|
||||
|
|
@ -1,316 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* light_3d_gizmo_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 "light_3d_gizmo_plugin.h"
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||
#include "scene/3d/light_3d.h"
|
||||
|
||||
Light3DGizmoPlugin::Light3DGizmoPlugin() {
|
||||
// Enable vertex colors for the materials below as the gizmo color depends on the light color.
|
||||
create_material("lines_primary", Color(1, 1, 1), false, false, true);
|
||||
create_material("lines_secondary", Color(1, 1, 1, 0.35), false, false, true);
|
||||
create_material("lines_billboard", Color(1, 1, 1), true, false, true);
|
||||
|
||||
create_icon_material("light_directional_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoDirectionalLight"), EditorStringName(EditorIcons)));
|
||||
create_icon_material("light_omni_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoLight"), EditorStringName(EditorIcons)));
|
||||
create_icon_material("light_spot_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoSpotLight"), EditorStringName(EditorIcons)));
|
||||
|
||||
create_handle_material("handles");
|
||||
create_handle_material("handles_billboard", true);
|
||||
}
|
||||
|
||||
bool Light3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
|
||||
return Object::cast_to<Light3D>(p_spatial) != nullptr;
|
||||
}
|
||||
|
||||
String Light3DGizmoPlugin::get_gizmo_name() const {
|
||||
return "Light3D";
|
||||
}
|
||||
|
||||
int Light3DGizmoPlugin::get_priority() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
String Light3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
|
||||
if (p_id == 0) {
|
||||
return "Radius";
|
||||
} else {
|
||||
return "Aperture";
|
||||
}
|
||||
}
|
||||
|
||||
Variant Light3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
|
||||
Light3D *light = Object::cast_to<Light3D>(p_gizmo->get_node_3d());
|
||||
if (p_id == 0) {
|
||||
return light->get_param(Light3D::PARAM_RANGE);
|
||||
}
|
||||
if (p_id == 1) {
|
||||
return light->get_param(Light3D::PARAM_SPOT_ANGLE);
|
||||
}
|
||||
|
||||
return Variant();
|
||||
}
|
||||
|
||||
void Light3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
|
||||
Light3D *light = Object::cast_to<Light3D>(p_gizmo->get_node_3d());
|
||||
Transform3D gt = light->get_global_transform();
|
||||
Transform3D gi = gt.affine_inverse();
|
||||
|
||||
Vector3 ray_from = p_camera->project_ray_origin(p_point);
|
||||
Vector3 ray_dir = p_camera->project_ray_normal(p_point);
|
||||
|
||||
Vector3 s[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) };
|
||||
if (p_id == 0) {
|
||||
if (Object::cast_to<SpotLight3D>(light)) {
|
||||
Vector3 ra, rb;
|
||||
Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(0, 0, -4096), s[0], s[1], ra, rb);
|
||||
|
||||
float d = -ra.z;
|
||||
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
|
||||
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
|
||||
}
|
||||
|
||||
if (d <= 0) { // Equal is here for negative zero.
|
||||
d = 0;
|
||||
}
|
||||
|
||||
light->set_param(Light3D::PARAM_RANGE, d);
|
||||
} else if (Object::cast_to<OmniLight3D>(light)) {
|
||||
Plane cp = Plane(p_camera->get_transform().basis.get_column(2), gt.origin);
|
||||
|
||||
Vector3 inters;
|
||||
if (cp.intersects_ray(ray_from, ray_dir, &inters)) {
|
||||
float r = inters.distance_to(gt.origin);
|
||||
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
|
||||
r = Math::snapped(r, Node3DEditor::get_singleton()->get_translate_snap());
|
||||
}
|
||||
|
||||
light->set_param(Light3D::PARAM_RANGE, r);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (p_id == 1) {
|
||||
float a = _find_closest_angle_to_half_pi_arc(s[0], s[1], light->get_param(Light3D::PARAM_RANGE), gt);
|
||||
light->set_param(Light3D::PARAM_SPOT_ANGLE, CLAMP(a, 0.01, 89.99));
|
||||
}
|
||||
}
|
||||
|
||||
void Light3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
|
||||
Light3D *light = Object::cast_to<Light3D>(p_gizmo->get_node_3d());
|
||||
if (p_cancel) {
|
||||
light->set_param(p_id == 0 ? Light3D::PARAM_RANGE : Light3D::PARAM_SPOT_ANGLE, p_restore);
|
||||
|
||||
} else if (p_id == 0) {
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
ur->create_action(TTR("Change Light Radius"));
|
||||
ur->add_do_method(light, "set_param", Light3D::PARAM_RANGE, light->get_param(Light3D::PARAM_RANGE));
|
||||
ur->add_undo_method(light, "set_param", Light3D::PARAM_RANGE, p_restore);
|
||||
ur->commit_action();
|
||||
} else if (p_id == 1) {
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
ur->create_action(TTR("Change Light Radius"));
|
||||
ur->add_do_method(light, "set_param", Light3D::PARAM_SPOT_ANGLE, light->get_param(Light3D::PARAM_SPOT_ANGLE));
|
||||
ur->add_undo_method(light, "set_param", Light3D::PARAM_SPOT_ANGLE, p_restore);
|
||||
ur->commit_action();
|
||||
}
|
||||
}
|
||||
|
||||
void Light3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
Light3D *light = Object::cast_to<Light3D>(p_gizmo->get_node_3d());
|
||||
|
||||
Color color = light->get_color().srgb_to_linear() * light->get_correlated_color().srgb_to_linear();
|
||||
color = color.linear_to_srgb();
|
||||
// Make the gizmo color as bright as possible for better visibility
|
||||
color.set_hsv(color.get_h(), color.get_s(), 1);
|
||||
|
||||
p_gizmo->clear();
|
||||
|
||||
if (Object::cast_to<DirectionalLight3D>(light)) {
|
||||
if (p_gizmo->is_selected()) {
|
||||
Ref<Material> material = get_material("lines_primary", p_gizmo);
|
||||
|
||||
const int arrow_points = 7;
|
||||
const float arrow_length = 1.5;
|
||||
|
||||
Vector3 arrow[arrow_points] = {
|
||||
Vector3(0, 0, -1),
|
||||
Vector3(0, 0.8, 0),
|
||||
Vector3(0, 0.3, 0),
|
||||
Vector3(0, 0.3, arrow_length),
|
||||
Vector3(0, -0.3, arrow_length),
|
||||
Vector3(0, -0.3, 0),
|
||||
Vector3(0, -0.8, 0)
|
||||
};
|
||||
|
||||
int arrow_sides = 2;
|
||||
|
||||
Vector<Vector3> lines;
|
||||
|
||||
for (int i = 0; i < arrow_sides; i++) {
|
||||
for (int j = 0; j < arrow_points; j++) {
|
||||
Basis ma(Vector3(0, 0, 1), Math::PI * i / arrow_sides);
|
||||
|
||||
Vector3 v1 = arrow[j] - Vector3(0, 0, arrow_length);
|
||||
Vector3 v2 = arrow[(j + 1) % arrow_points] - Vector3(0, 0, arrow_length);
|
||||
|
||||
lines.push_back(ma.xform(v1));
|
||||
lines.push_back(ma.xform(v2));
|
||||
}
|
||||
}
|
||||
|
||||
p_gizmo->add_lines(lines, material, false, color);
|
||||
}
|
||||
|
||||
Ref<Material> icon = get_material("light_directional_icon", p_gizmo);
|
||||
p_gizmo->add_unscaled_billboard(icon, 0.05, color);
|
||||
}
|
||||
|
||||
if (Object::cast_to<OmniLight3D>(light)) {
|
||||
if (p_gizmo->is_selected()) {
|
||||
// Use both a billboard circle and 3 non-billboard circles for a better sphere-like representation
|
||||
const Ref<Material> lines_material = get_material("lines_secondary", p_gizmo);
|
||||
const Ref<Material> lines_billboard_material = get_material("lines_billboard", p_gizmo);
|
||||
|
||||
OmniLight3D *on = Object::cast_to<OmniLight3D>(light);
|
||||
const float r = on->get_param(Light3D::PARAM_RANGE);
|
||||
Vector<Vector3> points;
|
||||
Vector<Vector3> points_billboard;
|
||||
|
||||
for (int i = 0; i < 120; i++) {
|
||||
// Create a circle
|
||||
const float ra = Math::deg_to_rad((float)(i * 3));
|
||||
const float rb = Math::deg_to_rad((float)((i + 1) * 3));
|
||||
const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r;
|
||||
const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r;
|
||||
|
||||
// Draw axis-aligned circles
|
||||
points.push_back(Vector3(a.x, 0, a.y));
|
||||
points.push_back(Vector3(b.x, 0, b.y));
|
||||
points.push_back(Vector3(0, a.x, a.y));
|
||||
points.push_back(Vector3(0, b.x, b.y));
|
||||
points.push_back(Vector3(a.x, a.y, 0));
|
||||
points.push_back(Vector3(b.x, b.y, 0));
|
||||
|
||||
// Draw a billboarded circle
|
||||
points_billboard.push_back(Vector3(a.x, a.y, 0));
|
||||
points_billboard.push_back(Vector3(b.x, b.y, 0));
|
||||
}
|
||||
|
||||
p_gizmo->add_lines(points, lines_material, true, color);
|
||||
p_gizmo->add_lines(points_billboard, lines_billboard_material, true, color);
|
||||
|
||||
Vector<Vector3> handles;
|
||||
handles.push_back(Vector3(r, 0, 0));
|
||||
p_gizmo->add_handles(handles, get_material("handles_billboard"), Vector<int>(), true);
|
||||
}
|
||||
|
||||
const Ref<Material> icon = get_material("light_omni_icon", p_gizmo);
|
||||
p_gizmo->add_unscaled_billboard(icon, 0.05, color);
|
||||
}
|
||||
|
||||
if (Object::cast_to<SpotLight3D>(light)) {
|
||||
if (p_gizmo->is_selected()) {
|
||||
const Ref<Material> material_primary = get_material("lines_primary", p_gizmo);
|
||||
const Ref<Material> material_secondary = get_material("lines_secondary", p_gizmo);
|
||||
|
||||
Vector<Vector3> points_primary;
|
||||
Vector<Vector3> points_secondary;
|
||||
SpotLight3D *sl = Object::cast_to<SpotLight3D>(light);
|
||||
|
||||
float r = sl->get_param(Light3D::PARAM_RANGE);
|
||||
float w = r * Math::sin(Math::deg_to_rad(sl->get_param(Light3D::PARAM_SPOT_ANGLE)));
|
||||
float d = r * Math::cos(Math::deg_to_rad(sl->get_param(Light3D::PARAM_SPOT_ANGLE)));
|
||||
|
||||
for (int i = 0; i < 120; i++) {
|
||||
// Draw a circle
|
||||
const float ra = Math::deg_to_rad((float)(i * 3));
|
||||
const float rb = Math::deg_to_rad((float)((i + 1) * 3));
|
||||
const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w;
|
||||
const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w;
|
||||
|
||||
points_primary.push_back(Vector3(a.x, a.y, -d));
|
||||
points_primary.push_back(Vector3(b.x, b.y, -d));
|
||||
|
||||
if (i % 15 == 0) {
|
||||
// Draw 8 lines from the cone origin to the sides of the circle
|
||||
points_secondary.push_back(Vector3(a.x, a.y, -d));
|
||||
points_secondary.push_back(Vector3());
|
||||
}
|
||||
}
|
||||
|
||||
points_primary.push_back(Vector3(0, 0, -r));
|
||||
points_primary.push_back(Vector3());
|
||||
|
||||
p_gizmo->add_lines(points_primary, material_primary, false, color);
|
||||
p_gizmo->add_lines(points_secondary, material_secondary, false, color);
|
||||
|
||||
Vector<Vector3> handles = {
|
||||
Vector3(0, 0, -r),
|
||||
Vector3(w, 0, -d)
|
||||
};
|
||||
|
||||
p_gizmo->add_handles(handles, get_material("handles"));
|
||||
}
|
||||
|
||||
const Ref<Material> icon = get_material("light_spot_icon", p_gizmo);
|
||||
p_gizmo->add_unscaled_billboard(icon, 0.05, color);
|
||||
}
|
||||
}
|
||||
|
||||
float Light3DGizmoPlugin::_find_closest_angle_to_half_pi_arc(const Vector3 &p_from, const Vector3 &p_to, float p_arc_radius, const Transform3D &p_arc_xform) {
|
||||
//bleh, discrete is simpler
|
||||
static const int arc_test_points = 64;
|
||||
float min_d = 1e20;
|
||||
Vector3 min_p;
|
||||
|
||||
for (int i = 0; i < arc_test_points; i++) {
|
||||
float a = i * Math::PI * 0.5 / arc_test_points;
|
||||
float an = (i + 1) * Math::PI * 0.5 / arc_test_points;
|
||||
Vector3 p = Vector3(Math::cos(a), 0, -Math::sin(a)) * p_arc_radius;
|
||||
Vector3 n = Vector3(Math::cos(an), 0, -Math::sin(an)) * p_arc_radius;
|
||||
|
||||
Vector3 ra, rb;
|
||||
Geometry3D::get_closest_points_between_segments(p, n, p_from, p_to, ra, rb);
|
||||
|
||||
float d = ra.distance_to(rb);
|
||||
if (d < min_d) {
|
||||
min_d = d;
|
||||
min_p = ra;
|
||||
}
|
||||
}
|
||||
|
||||
//min_p = p_arc_xform.affine_inverse().xform(min_p);
|
||||
float a = (Math::PI * 0.5) - Vector2(min_p.x, -min_p.z).angle();
|
||||
return Math::rad_to_deg(a);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue