feat: godot-engine-source-4.3-stable
This commit is contained in:
parent
c59a7dcade
commit
7125d019b5
11149 changed files with 5070401 additions and 0 deletions
8
engine/editor/plugins/SCsub
Normal file
8
engine/editor/plugins/SCsub
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.editor_sources, "*.cpp")
|
||||
|
||||
SConscript("gizmos/SCsub")
|
||||
SConscript("tiles/SCsub")
|
||||
778
engine/editor/plugins/abstract_polygon_2d_editor.cpp
Normal file
778
engine/editor/plugins/abstract_polygon_2d_editor.cpp
Normal file
|
|
@ -0,0 +1,778 @@
|
|||
/**************************************************************************/
|
||||
/* 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"
|
||||
#include "scene/gui/separator.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() {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
button_create->set_icon(get_editor_theme_icon(SNAME("CurveCreate")));
|
||||
button_edit->set_icon(get_editor_theme_icon(SNAME("CurveEdit")));
|
||||
button_delete->set_icon(get_editor_theme_icon(SNAME("CurveDelete")));
|
||||
} 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();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (p_disable) {
|
||||
button_create->set_tooltip_text(p_reason);
|
||||
button_edit->set_tooltip_text(p_reason);
|
||||
button_delete->set_tooltip_text(p_reason);
|
||||
} else {
|
||||
button_create->set_tooltip_text(TTR("Create points."));
|
||||
button_edit->set_tooltip_text(TTR("Edit points.\nLMB: Move Point\nRMB: Erase Point"));
|
||||
button_delete->set_tooltip_text(TTR("Erase points."));
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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_global_transform();
|
||||
|
||||
Vector2 gpoint = mb->get_position();
|
||||
Vector2 cpoint = _get_node()->to_local(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mb->get_position())));
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
|
||||
if (mm.is_valid()) {
|
||||
Vector2 gpoint = mm->get_position();
|
||||
|
||||
if (edited_point.valid() && (wip_active || mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
|
||||
Vector2 cpoint = _get_node()->to_local(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)));
|
||||
|
||||
//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 (ABS(cpoint.x - old_point.x) > 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;
|
||||
}
|
||||
|
||||
Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform();
|
||||
// All polygon points are sharp, so use the sharp handle icon
|
||||
const Ref<Texture2D> handle = get_editor_theme_icon(SNAME("EditorPathSharpHandle"));
|
||||
|
||||
const Vertex active_point = get_active_point();
|
||||
const int n_polygons = _get_polygon_count();
|
||||
const bool is_closed = !_is_line();
|
||||
|
||||
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) {
|
||||
Ref<Font> font = get_theme_font(SNAME("bold"), EditorStringName(EditorFonts));
|
||||
int font_size = 1.3 * get_theme_font_size(SNAME("bold_size"), EditorStringName(EditorFonts));
|
||||
String num = String::num(vertex.vertex);
|
||||
Size2 num_size = font->get_string_size(num, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size);
|
||||
const float outline_size = 4;
|
||||
Color font_color = get_theme_color(SceneStringName(font_color), EditorStringName(Editor));
|
||||
Color outline_color = font_color.inverted();
|
||||
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::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();
|
||||
} else {
|
||||
_set_node(nullptr);
|
||||
}
|
||||
|
||||
canvas_item_editor->update_viewport();
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditor::_bind_methods() {
|
||||
}
|
||||
|
||||
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_global_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_global_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++) {
|
||||
Vector2 segment[2] = { xform.xform(points[i] + offset),
|
||||
xform.xform(points[(i + 1) % n_points] + offset) };
|
||||
|
||||
Vector2 cp = Geometry2D::get_closest_point_to_segment(p_pos, segment);
|
||||
|
||||
if (cp.distance_squared_to(segment[0]) < eps2 || cp.distance_squared_to(segment[1]) < 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();
|
||||
wip_destructive = p_wip_destructive;
|
||||
|
||||
hover_point = Vertex();
|
||||
selected_point = Vertex();
|
||||
edge_point = PosVertex();
|
||||
|
||||
button_create = memnew(Button);
|
||||
button_create->set_theme_type_variation("FlatButton");
|
||||
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("FlatButton");
|
||||
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("FlatButton");
|
||||
add_child(button_delete);
|
||||
button_delete->connect(SceneStringName(pressed), callable_mp(this, &AbstractPolygon2DEditor::_menu_option).bind(MODE_DELETE));
|
||||
button_delete->set_toggle_mode(true);
|
||||
|
||||
create_resource = memnew(ConfirmationDialog);
|
||||
add_child(create_resource);
|
||||
create_resource->set_ok_button_text(TTR("Create"));
|
||||
}
|
||||
|
||||
void AbstractPolygon2DEditorPlugin::edit(Object *p_object) {
|
||||
polygon_editor->edit(Object::cast_to<Node>(p_object));
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
AbstractPolygon2DEditorPlugin::~AbstractPolygon2DEditorPlugin() {
|
||||
}
|
||||
170
engine/editor/plugins/abstract_polygon_2d_editor.h
Normal file
170
engine/editor/plugins/abstract_polygon_2d_editor.h
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ABSTRACT_POLYGON_2D_EDITOR_H
|
||||
#define ABSTRACT_POLYGON_2D_EDITOR_H
|
||||
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/2d/polygon_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;
|
||||
|
||||
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;
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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);
|
||||
static void _bind_methods();
|
||||
|
||||
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 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 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_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);
|
||||
~AbstractPolygon2DEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // ABSTRACT_POLYGON_2D_EDITOR_H
|
||||
816
engine/editor/plugins/animation_blend_space_1d_editor.cpp
Normal file
816
engine/editor/plugins/animation_blend_space_1d_editor.cpp
Normal file
|
|
@ -0,0 +1,816 @@
|
|||
/**************************************************************************/
|
||||
/* 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();
|
||||
|
||||
List<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_valid()) {
|
||||
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_ENTER_TREE:
|
||||
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_icon(get_editor_theme_icon(SNAME("EditPivot")));
|
||||
tool_select->set_icon(get_editor_theme_icon(SNAME("ToolSelect")));
|
||||
tool_create->set_icon(get_editor_theme_icon(SNAME("EditKey")));
|
||||
tool_erase->set_icon(get_editor_theme_icon(SNAME("Remove")));
|
||||
snap->set_icon(get_editor_theme_icon(SNAME("SnapGrid")));
|
||||
open_editor->set_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_null()) {
|
||||
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("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("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("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("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("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);
|
||||
|
||||
top_hb->add_child(memnew(VSeparator));
|
||||
top_hb->add_child(memnew(Label(TTR("Sync:"))));
|
||||
sync = memnew(CheckBox);
|
||||
top_hb->add_child(sync);
|
||||
sync->connect("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->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);
|
||||
|
||||
max_value = memnew(SpinBox);
|
||||
max_value->set_min(0.01);
|
||||
max_value->set_max(10000);
|
||||
max_value->set_step(0.01);
|
||||
|
||||
label_value = memnew(LineEdit);
|
||||
label_value->set_expand_to_text_length_enabled(true);
|
||||
|
||||
// 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_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));
|
||||
}
|
||||
141
engine/editor/plugins/animation_blend_space_1d_editor.h
Normal file
141
engine/editor/plugins/animation_blend_space_1d_editor.h
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ANIMATION_BLEND_SPACE_1D_EDITOR_H
|
||||
#define ANIMATION_BLEND_SPACE_1D_EDITOR_H
|
||||
|
||||
#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"
|
||||
#include "scene/gui/popup.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();
|
||||
};
|
||||
|
||||
#endif // ANIMATION_BLEND_SPACE_1D_EDITOR_H
|
||||
1099
engine/editor/plugins/animation_blend_space_2d_editor.cpp
Normal file
1099
engine/editor/plugins/animation_blend_space_2d_editor.cpp
Normal file
File diff suppressed because it is too large
Load diff
152
engine/editor/plugins/animation_blend_space_2d_editor.h
Normal file
152
engine/editor/plugins/animation_blend_space_2d_editor.h
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ANIMATION_BLEND_SPACE_2D_EDITOR_H
|
||||
#define ANIMATION_BLEND_SPACE_2D_EDITOR_H
|
||||
|
||||
#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"
|
||||
#include "scene/gui/popup.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();
|
||||
};
|
||||
|
||||
#endif // ANIMATION_BLEND_SPACE_2D_EDITOR_H
|
||||
1265
engine/editor/plugins/animation_blend_tree_editor_plugin.cpp
Normal file
1265
engine/editor/plugins/animation_blend_tree_editor_plugin.cpp
Normal file
File diff suppressed because it is too large
Load diff
170
engine/editor/plugins/animation_blend_tree_editor_plugin.h
Normal file
170
engine/editor/plugins/animation_blend_tree_editor_plugin.h
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ANIMATION_BLEND_TREE_EDITOR_PLUGIN_H
|
||||
#define ANIMATION_BLEND_TREE_EDITOR_PLUGIN_H
|
||||
|
||||
#include "core/object/script_language.h"
|
||||
#include "editor/plugins/animation_tree_editor_plugin.h"
|
||||
#include "scene/animation/animation_blend_tree.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/graph_edit.h"
|
||||
#include "scene/gui/panel_container.h"
|
||||
#include "scene/gui/popup.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
class AcceptDialog;
|
||||
class CheckBox;
|
||||
class ProgressBar;
|
||||
class EditorFileDialog;
|
||||
class EditorProperty;
|
||||
class MenuButton;
|
||||
class PanelContainer;
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
#endif // ANIMATION_BLEND_TREE_EDITOR_PLUGIN_H
|
||||
871
engine/editor/plugins/animation_library_editor.cpp
Normal file
871
engine/editor/plugins/animation_library_editor.cpp
Normal file
|
|
@ -0,0 +1,871 @@
|
|||
/**************************************************************************/
|
||||
/* animation_library_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_library_editor.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_mixer.h"
|
||||
|
||||
void AnimationLibraryEditor::set_animation_mixer(Object *p_mixer) {
|
||||
mixer = Object::cast_to<AnimationMixer>(p_mixer);
|
||||
}
|
||||
|
||||
void AnimationLibraryEditor::_add_library() {
|
||||
add_library_dialog->set_title(TTR("Library Name:"));
|
||||
add_library_name->set_text("");
|
||||
add_library_dialog->popup_centered();
|
||||
add_library_name->grab_focus();
|
||||
adding_animation = false;
|
||||
adding_animation_to_library = StringName();
|
||||
_add_library_validate("");
|
||||
}
|
||||
|
||||
void AnimationLibraryEditor::_add_library_validate(const String &p_name) {
|
||||
String error;
|
||||
|
||||
if (adding_animation) {
|
||||
Ref<AnimationLibrary> al = mixer->get_animation_library(adding_animation_to_library);
|
||||
ERR_FAIL_COND(al.is_null());
|
||||
if (p_name == "") {
|
||||
error = TTR("Animation name can't be empty.");
|
||||
} else if (!AnimationLibrary::is_valid_animation_name(p_name)) {
|
||||
error = TTR("Animation name contains invalid characters: '/', ':', ',' or '['.");
|
||||
} else if (al->has_animation(p_name)) {
|
||||
error = TTR("Animation with the same name already exists.");
|
||||
}
|
||||
} else {
|
||||
if (p_name == "" && mixer->has_animation_library("")) {
|
||||
error = TTR("Enter a library name.");
|
||||
} else if (!AnimationLibrary::is_valid_library_name(p_name)) {
|
||||
error = TTR("Library name contains invalid characters: '/', ':', ',' or '['.");
|
||||
} else if (mixer->has_animation_library(p_name)) {
|
||||
error = TTR("Library with the same name already exists.");
|
||||
}
|
||||
}
|
||||
|
||||
if (error != "") {
|
||||
add_library_validate->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
|
||||
add_library_validate->set_text(error);
|
||||
add_library_dialog->get_ok_button()->set_disabled(true);
|
||||
} else {
|
||||
if (adding_animation) {
|
||||
add_library_validate->set_text(TTR("Animation name is valid."));
|
||||
} else {
|
||||
if (p_name == "") {
|
||||
add_library_validate->set_text(TTR("Global library will be created."));
|
||||
} else {
|
||||
add_library_validate->set_text(TTR("Library name is valid."));
|
||||
}
|
||||
}
|
||||
add_library_validate->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("success_color"), EditorStringName(Editor)));
|
||||
add_library_dialog->get_ok_button()->set_disabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationLibraryEditor::_add_library_confirm() {
|
||||
if (adding_animation) {
|
||||
String anim_name = add_library_name->get_text();
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
|
||||
Ref<AnimationLibrary> al = mixer->get_animation_library(adding_animation_to_library);
|
||||
ERR_FAIL_COND(!al.is_valid());
|
||||
|
||||
Ref<Animation> anim;
|
||||
anim.instantiate();
|
||||
|
||||
undo_redo->create_action(vformat(TTR("Add Animation to Library: %s"), anim_name));
|
||||
undo_redo->add_do_method(al.ptr(), "add_animation", anim_name, anim);
|
||||
undo_redo->add_undo_method(al.ptr(), "remove_animation", anim_name);
|
||||
undo_redo->add_do_method(this, "_update_editor", mixer);
|
||||
undo_redo->add_undo_method(this, "_update_editor", mixer);
|
||||
undo_redo->commit_action();
|
||||
|
||||
} else {
|
||||
String lib_name = add_library_name->get_text();
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
|
||||
Ref<AnimationLibrary> al;
|
||||
al.instantiate();
|
||||
|
||||
undo_redo->create_action(vformat(TTR("Add Animation Library: %s"), lib_name));
|
||||
undo_redo->add_do_method(mixer, "add_animation_library", lib_name, al);
|
||||
undo_redo->add_undo_method(mixer, "remove_animation_library", lib_name);
|
||||
undo_redo->add_do_method(this, "_update_editor", mixer);
|
||||
undo_redo->add_undo_method(this, "_update_editor", mixer);
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationLibraryEditor::_load_library() {
|
||||
List<String> extensions;
|
||||
ResourceLoader::get_recognized_extensions_for_type("AnimationLibrary", &extensions);
|
||||
|
||||
file_dialog->set_title(TTR("Load Animation"));
|
||||
file_dialog->clear_filters();
|
||||
for (const String &K : extensions) {
|
||||
file_dialog->add_filter("*." + K);
|
||||
}
|
||||
|
||||
file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILES);
|
||||
file_dialog->set_current_file("");
|
||||
file_dialog->popup_centered_ratio();
|
||||
|
||||
file_dialog_action = FILE_DIALOG_ACTION_OPEN_LIBRARY;
|
||||
}
|
||||
|
||||
void AnimationLibraryEditor::_file_popup_selected(int p_id) {
|
||||
Ref<AnimationLibrary> al = mixer->get_animation_library(file_dialog_library);
|
||||
Ref<Animation> anim;
|
||||
if (file_dialog_animation != StringName()) {
|
||||
anim = al->get_animation(file_dialog_animation);
|
||||
ERR_FAIL_COND(anim.is_null());
|
||||
}
|
||||
switch (p_id) {
|
||||
case FILE_MENU_SAVE_LIBRARY: {
|
||||
if (al->get_path().is_resource_file() && !FileAccess::exists(al->get_path() + ".import")) {
|
||||
EditorNode::get_singleton()->save_resource(al);
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
}
|
||||
case FILE_MENU_SAVE_AS_LIBRARY: {
|
||||
// Check if we're allowed to save this
|
||||
{
|
||||
String al_path = al->get_path();
|
||||
if (!al_path.is_resource_file()) {
|
||||
int srpos = al_path.find("::");
|
||||
if (srpos != -1) {
|
||||
String base = al_path.substr(0, srpos);
|
||||
if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
|
||||
error_dialog->set_text(TTR("This animation library can't be saved because it does not belong to the edited scene. Make it unique first."));
|
||||
error_dialog->popup_centered();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (FileAccess::exists(al_path + ".import")) {
|
||||
error_dialog->set_text(TTR("This animation library can't be saved because it was imported from another file. Make it unique first."));
|
||||
error_dialog->popup_centered();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
|
||||
file_dialog->set_title(TTR("Save Library"));
|
||||
if (al->get_path().is_resource_file()) {
|
||||
file_dialog->set_current_path(al->get_path());
|
||||
} else {
|
||||
file_dialog->set_current_file(String(file_dialog_library) + ".res");
|
||||
}
|
||||
file_dialog->clear_filters();
|
||||
List<String> exts;
|
||||
ResourceLoader::get_recognized_extensions_for_type("AnimationLibrary", &exts);
|
||||
for (const String &K : exts) {
|
||||
file_dialog->add_filter("*." + K);
|
||||
}
|
||||
|
||||
file_dialog->popup_centered_ratio();
|
||||
file_dialog_action = FILE_DIALOG_ACTION_SAVE_LIBRARY;
|
||||
} break;
|
||||
case FILE_MENU_MAKE_LIBRARY_UNIQUE: {
|
||||
StringName lib_name = file_dialog_library;
|
||||
List<StringName> animation_list;
|
||||
|
||||
Ref<AnimationLibrary> ald = memnew(AnimationLibrary);
|
||||
al->get_animation_list(&animation_list);
|
||||
for (const StringName &animation_name : animation_list) {
|
||||
Ref<Animation> animation = al->get_animation(animation_name);
|
||||
if (EditorNode::get_singleton()->is_resource_read_only(animation)) {
|
||||
animation = animation->duplicate();
|
||||
}
|
||||
ald->add_animation(animation_name, animation);
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(vformat(TTR("Make Animation Library Unique: %s"), lib_name));
|
||||
undo_redo->add_do_method(mixer, "remove_animation_library", lib_name);
|
||||
undo_redo->add_do_method(mixer, "add_animation_library", lib_name, ald);
|
||||
undo_redo->add_undo_method(mixer, "remove_animation_library", lib_name);
|
||||
undo_redo->add_undo_method(mixer, "add_animation_library", lib_name, al);
|
||||
undo_redo->add_do_method(this, "_update_editor", mixer);
|
||||
undo_redo->add_undo_method(this, "_update_editor", mixer);
|
||||
undo_redo->commit_action();
|
||||
|
||||
update_tree();
|
||||
|
||||
} break;
|
||||
case FILE_MENU_EDIT_LIBRARY: {
|
||||
EditorNode::get_singleton()->push_item(al.ptr());
|
||||
} break;
|
||||
|
||||
case FILE_MENU_SAVE_ANIMATION: {
|
||||
if (anim->get_path().is_resource_file() && !FileAccess::exists(anim->get_path() + ".import")) {
|
||||
EditorNode::get_singleton()->save_resource(anim);
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
}
|
||||
case FILE_MENU_SAVE_AS_ANIMATION: {
|
||||
// Check if we're allowed to save this
|
||||
{
|
||||
String anim_path = al->get_path();
|
||||
if (!anim_path.is_resource_file()) {
|
||||
int srpos = anim_path.find("::");
|
||||
if (srpos != -1) {
|
||||
String base = anim_path.substr(0, srpos);
|
||||
if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
|
||||
error_dialog->set_text(TTR("This animation can't be saved because it does not belong to the edited scene. Make it unique first."));
|
||||
error_dialog->popup_centered();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (FileAccess::exists(anim_path + ".import")) {
|
||||
error_dialog->set_text(TTR("This animation can't be saved because it was imported from another file. Make it unique first."));
|
||||
error_dialog->popup_centered();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
|
||||
file_dialog->set_title(TTR("Save Animation"));
|
||||
if (anim->get_path().is_resource_file()) {
|
||||
file_dialog->set_current_path(anim->get_path());
|
||||
} else {
|
||||
file_dialog->set_current_file(String(file_dialog_animation) + ".res");
|
||||
}
|
||||
file_dialog->clear_filters();
|
||||
List<String> exts;
|
||||
ResourceLoader::get_recognized_extensions_for_type("Animation", &exts);
|
||||
for (const String &K : exts) {
|
||||
file_dialog->add_filter("*." + K);
|
||||
}
|
||||
|
||||
file_dialog->popup_centered_ratio();
|
||||
file_dialog_action = FILE_DIALOG_ACTION_SAVE_ANIMATION;
|
||||
} break;
|
||||
case FILE_MENU_MAKE_ANIMATION_UNIQUE: {
|
||||
StringName anim_name = file_dialog_animation;
|
||||
|
||||
Ref<Animation> animd = anim->duplicate();
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(vformat(TTR("Make Animation Unique: %s"), anim_name));
|
||||
undo_redo->add_do_method(al.ptr(), "remove_animation", anim_name);
|
||||
undo_redo->add_do_method(al.ptr(), "add_animation", anim_name, animd);
|
||||
undo_redo->add_undo_method(al.ptr(), "remove_animation", anim_name);
|
||||
undo_redo->add_undo_method(al.ptr(), "add_animation", anim_name, anim);
|
||||
undo_redo->add_do_method(this, "_update_editor", mixer);
|
||||
undo_redo->add_undo_method(this, "_update_editor", mixer);
|
||||
undo_redo->commit_action();
|
||||
|
||||
update_tree();
|
||||
} break;
|
||||
case FILE_MENU_EDIT_ANIMATION: {
|
||||
EditorNode::get_singleton()->push_item(anim.ptr());
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationLibraryEditor::_load_file(const String &p_path) {
|
||||
switch (file_dialog_action) {
|
||||
case FILE_DIALOG_ACTION_SAVE_LIBRARY: {
|
||||
Ref<AnimationLibrary> al = mixer->get_animation_library(file_dialog_library);
|
||||
String prev_path = al->get_path();
|
||||
EditorNode::get_singleton()->save_resource_in_path(al, p_path);
|
||||
|
||||
if (al->get_path() != prev_path) { // Save successful.
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
|
||||
undo_redo->create_action(vformat(TTR("Save Animation library to File: %s"), file_dialog_library));
|
||||
undo_redo->add_do_method(al.ptr(), "set_path", al->get_path());
|
||||
undo_redo->add_undo_method(al.ptr(), "set_path", prev_path);
|
||||
undo_redo->add_do_method(this, "_update_editor", mixer);
|
||||
undo_redo->add_undo_method(this, "_update_editor", mixer);
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
} break;
|
||||
case FILE_DIALOG_ACTION_SAVE_ANIMATION: {
|
||||
Ref<AnimationLibrary> al = mixer->get_animation_library(file_dialog_library);
|
||||
Ref<Animation> anim;
|
||||
if (file_dialog_animation != StringName()) {
|
||||
anim = al->get_animation(file_dialog_animation);
|
||||
ERR_FAIL_COND(anim.is_null());
|
||||
}
|
||||
String prev_path = anim->get_path();
|
||||
EditorNode::get_singleton()->save_resource_in_path(anim, p_path);
|
||||
if (anim->get_path() != prev_path) { // Save successful.
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
|
||||
undo_redo->create_action(vformat(TTR("Save Animation to File: %s"), file_dialog_animation));
|
||||
undo_redo->add_do_method(anim.ptr(), "set_path", anim->get_path());
|
||||
undo_redo->add_undo_method(anim.ptr(), "set_path", prev_path);
|
||||
undo_redo->add_do_method(this, "_update_editor", mixer);
|
||||
undo_redo->add_undo_method(this, "_update_editor", mixer);
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationLibraryEditor::_load_files(const PackedStringArray &p_paths) {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
bool has_created_action = false;
|
||||
bool show_error_diag = false;
|
||||
List<String> name_list;
|
||||
|
||||
switch (file_dialog_action) {
|
||||
case FILE_DIALOG_ACTION_OPEN_LIBRARY: {
|
||||
for (const String &path : p_paths) {
|
||||
Ref<AnimationLibrary> al = ResourceLoader::load(path);
|
||||
if (al.is_null()) {
|
||||
show_error_diag = true;
|
||||
error_dialog->set_text(TTR("Some AnimationLibrary files were invalid."));
|
||||
continue;
|
||||
}
|
||||
|
||||
List<StringName> libs;
|
||||
mixer->get_animation_library_list(&libs);
|
||||
bool is_already_added = false;
|
||||
for (const StringName &K : libs) {
|
||||
if (mixer->get_animation_library(K) == al) {
|
||||
// Prioritize the "invalid" error message.
|
||||
if (!show_error_diag) {
|
||||
show_error_diag = true;
|
||||
error_dialog->set_text(TTR("Some of the selected libraries were already added to the mixer."));
|
||||
}
|
||||
|
||||
is_already_added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_already_added) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = AnimationLibrary::validate_library_name(path.get_file().get_basename());
|
||||
int attempt = 1;
|
||||
while (bool(mixer->has_animation_library(name)) || name_list.find(name)) {
|
||||
attempt++;
|
||||
name = path.get_file().get_basename() + " " + itos(attempt);
|
||||
}
|
||||
name_list.push_back(name);
|
||||
|
||||
if (!has_created_action) {
|
||||
has_created_action = true;
|
||||
undo_redo->create_action(p_paths.size() > 1 ? TTR("Add Animation Libraries") : vformat(TTR("Add Animation Library: %s"), name));
|
||||
}
|
||||
undo_redo->add_do_method(mixer, "add_animation_library", name, al);
|
||||
undo_redo->add_undo_method(mixer, "remove_animation_library", name);
|
||||
}
|
||||
} break;
|
||||
case FILE_DIALOG_ACTION_OPEN_ANIMATION: {
|
||||
Ref<AnimationLibrary> al = mixer->get_animation_library(adding_animation_to_library);
|
||||
for (const String &path : p_paths) {
|
||||
Ref<Animation> anim = ResourceLoader::load(path);
|
||||
if (anim.is_null()) {
|
||||
show_error_diag = true;
|
||||
error_dialog->set_text(TTR("Some Animation files were invalid."));
|
||||
continue;
|
||||
}
|
||||
|
||||
List<StringName> anims;
|
||||
al->get_animation_list(&anims);
|
||||
bool is_already_added = false;
|
||||
for (const StringName &K : anims) {
|
||||
if (al->get_animation(K) == anim) {
|
||||
// Prioritize the "invalid" error message.
|
||||
if (!show_error_diag) {
|
||||
show_error_diag = true;
|
||||
error_dialog->set_text(TTR("Some of the selected animations were already added to the library."));
|
||||
}
|
||||
|
||||
is_already_added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_already_added) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = path.get_file().get_basename();
|
||||
int attempt = 1;
|
||||
while (al->has_animation(name) || name_list.find(name)) {
|
||||
attempt++;
|
||||
name = path.get_file().get_basename() + " " + itos(attempt);
|
||||
}
|
||||
name_list.push_back(name);
|
||||
|
||||
if (!has_created_action) {
|
||||
has_created_action = true;
|
||||
undo_redo->create_action(p_paths.size() > 1 ? TTR("Load Animations into Library") : vformat(TTR("Load Animation into Library: %s"), name));
|
||||
}
|
||||
undo_redo->add_do_method(al.ptr(), "add_animation", name, anim);
|
||||
undo_redo->add_undo_method(al.ptr(), "remove_animation", name);
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
|
||||
if (has_created_action) {
|
||||
undo_redo->add_do_method(this, "_update_editor", mixer);
|
||||
undo_redo->add_undo_method(this, "_update_editor", mixer);
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
if (show_error_diag) {
|
||||
error_dialog->popup_centered();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationLibraryEditor::_item_renamed() {
|
||||
TreeItem *ti = tree->get_edited();
|
||||
String text = ti->get_text(0);
|
||||
String old_text = ti->get_metadata(0);
|
||||
bool restore_text = false;
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
|
||||
if (String(text).contains("/") || String(text).contains(":") || String(text).contains(",") || String(text).contains("[")) {
|
||||
restore_text = true;
|
||||
} else {
|
||||
if (ti->get_parent() == tree->get_root()) {
|
||||
// Renamed library
|
||||
|
||||
if (mixer->has_animation_library(text)) {
|
||||
restore_text = true;
|
||||
} else {
|
||||
undo_redo->create_action(vformat(TTR("Rename Animation Library: %s"), text));
|
||||
undo_redo->add_do_method(mixer, "rename_animation_library", old_text, text);
|
||||
undo_redo->add_undo_method(mixer, "rename_animation_library", text, old_text);
|
||||
undo_redo->add_do_method(this, "_update_editor", mixer);
|
||||
undo_redo->add_undo_method(this, "_update_editor", mixer);
|
||||
updating = true;
|
||||
undo_redo->commit_action();
|
||||
updating = false;
|
||||
ti->set_metadata(0, text);
|
||||
if (text == "") {
|
||||
ti->set_suffix(0, TTR("[Global]"));
|
||||
} else {
|
||||
ti->set_suffix(0, "");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Renamed anim
|
||||
StringName library = ti->get_parent()->get_metadata(0);
|
||||
Ref<AnimationLibrary> al = mixer->get_animation_library(library);
|
||||
|
||||
if (al.is_valid()) {
|
||||
if (al->has_animation(text)) {
|
||||
restore_text = true;
|
||||
} else {
|
||||
undo_redo->create_action(vformat(TTR("Rename Animation: %s"), text));
|
||||
undo_redo->add_do_method(al.ptr(), "rename_animation", old_text, text);
|
||||
undo_redo->add_undo_method(al.ptr(), "rename_animation", text, old_text);
|
||||
undo_redo->add_do_method(this, "_update_editor", mixer);
|
||||
undo_redo->add_undo_method(this, "_update_editor", mixer);
|
||||
updating = true;
|
||||
undo_redo->commit_action();
|
||||
updating = false;
|
||||
|
||||
ti->set_metadata(0, text);
|
||||
}
|
||||
} else {
|
||||
restore_text = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (restore_text) {
|
||||
ti->set_text(0, old_text);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int p_id, MouseButton p_button) {
|
||||
if (p_item->get_parent() == tree->get_root()) {
|
||||
// Library
|
||||
StringName lib_name = p_item->get_metadata(0);
|
||||
Ref<AnimationLibrary> al = mixer->get_animation_library(lib_name);
|
||||
switch (p_id) {
|
||||
case LIB_BUTTON_ADD: {
|
||||
add_library_dialog->set_title(TTR("Animation Name:"));
|
||||
add_library_name->set_text("");
|
||||
add_library_dialog->popup_centered();
|
||||
add_library_name->grab_focus();
|
||||
adding_animation = true;
|
||||
adding_animation_to_library = p_item->get_metadata(0);
|
||||
_add_library_validate("");
|
||||
} break;
|
||||
case LIB_BUTTON_LOAD: {
|
||||
adding_animation_to_library = p_item->get_metadata(0);
|
||||
List<String> extensions;
|
||||
ResourceLoader::get_recognized_extensions_for_type("Animation", &extensions);
|
||||
|
||||
file_dialog->clear_filters();
|
||||
for (const String &K : extensions) {
|
||||
file_dialog->add_filter("*." + K);
|
||||
}
|
||||
|
||||
file_dialog->set_title(TTR("Load Animation"));
|
||||
file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILES);
|
||||
file_dialog->set_current_file("");
|
||||
file_dialog->popup_centered_ratio();
|
||||
|
||||
file_dialog_action = FILE_DIALOG_ACTION_OPEN_ANIMATION;
|
||||
|
||||
} break;
|
||||
case LIB_BUTTON_PASTE: {
|
||||
Ref<Animation> anim = EditorSettings::get_singleton()->get_resource_clipboard();
|
||||
if (!anim.is_valid()) {
|
||||
error_dialog->set_text(TTR("No animation resource in clipboard!"));
|
||||
error_dialog->popup_centered();
|
||||
return;
|
||||
}
|
||||
|
||||
anim = anim->duplicate(); // Users simply dont care about referencing, so making a copy works better here.
|
||||
|
||||
String base_name;
|
||||
if (anim->get_name() != "") {
|
||||
base_name = anim->get_name();
|
||||
} else {
|
||||
base_name = TTR("Pasted Animation");
|
||||
}
|
||||
|
||||
String name = base_name;
|
||||
int attempt = 1;
|
||||
while (al->has_animation(name)) {
|
||||
attempt++;
|
||||
name = base_name + " (" + itos(attempt) + ")";
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
|
||||
undo_redo->create_action(vformat(TTR("Add Animation to Library: %s"), name));
|
||||
undo_redo->add_do_method(al.ptr(), "add_animation", name, anim);
|
||||
undo_redo->add_undo_method(al.ptr(), "remove_animation", name);
|
||||
undo_redo->add_do_method(this, "_update_editor", mixer);
|
||||
undo_redo->add_undo_method(this, "_update_editor", mixer);
|
||||
undo_redo->commit_action();
|
||||
|
||||
} break;
|
||||
case LIB_BUTTON_FILE: {
|
||||
file_popup->clear();
|
||||
file_popup->add_item(TTR("Save"), FILE_MENU_SAVE_LIBRARY);
|
||||
file_popup->add_item(TTR("Save As"), FILE_MENU_SAVE_AS_LIBRARY);
|
||||
file_popup->add_separator();
|
||||
file_popup->add_item(TTR("Make Unique"), FILE_MENU_MAKE_LIBRARY_UNIQUE);
|
||||
file_popup->add_separator();
|
||||
file_popup->add_item(TTR("Open in Inspector"), FILE_MENU_EDIT_LIBRARY);
|
||||
Rect2 pos = tree->get_item_rect(p_item, 1, 0);
|
||||
Vector2 popup_pos = tree->get_screen_transform().xform(pos.position + Vector2(0, pos.size.height));
|
||||
file_popup->popup(Rect2(popup_pos, Size2()));
|
||||
|
||||
file_dialog_animation = StringName();
|
||||
file_dialog_library = lib_name;
|
||||
} break;
|
||||
case LIB_BUTTON_DELETE: {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(vformat(TTR("Remove Animation Library: %s"), lib_name));
|
||||
undo_redo->add_do_method(mixer, "remove_animation_library", lib_name);
|
||||
undo_redo->add_undo_method(mixer, "add_animation_library", lib_name, al);
|
||||
undo_redo->add_do_method(this, "_update_editor", mixer);
|
||||
undo_redo->add_undo_method(this, "_update_editor", mixer);
|
||||
undo_redo->commit_action();
|
||||
} break;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Animation
|
||||
StringName lib_name = p_item->get_parent()->get_metadata(0);
|
||||
StringName anim_name = p_item->get_metadata(0);
|
||||
Ref<AnimationLibrary> al = mixer->get_animation_library(lib_name);
|
||||
Ref<Animation> anim = al->get_animation(anim_name);
|
||||
ERR_FAIL_COND(!anim.is_valid());
|
||||
switch (p_id) {
|
||||
case ANIM_BUTTON_COPY: {
|
||||
if (anim->get_name() == "") {
|
||||
anim->set_name(anim_name); // Keep the name around
|
||||
}
|
||||
EditorSettings::get_singleton()->set_resource_clipboard(anim);
|
||||
} break;
|
||||
case ANIM_BUTTON_FILE: {
|
||||
file_popup->clear();
|
||||
file_popup->add_item(TTR("Save"), FILE_MENU_SAVE_ANIMATION);
|
||||
file_popup->add_item(TTR("Save As"), FILE_MENU_SAVE_AS_ANIMATION);
|
||||
file_popup->add_separator();
|
||||
file_popup->add_item(TTR("Make Unique"), FILE_MENU_MAKE_ANIMATION_UNIQUE);
|
||||
file_popup->add_separator();
|
||||
file_popup->add_item(TTR("Open in Inspector"), FILE_MENU_EDIT_ANIMATION);
|
||||
Rect2 pos = tree->get_item_rect(p_item, 1, 0);
|
||||
Vector2 popup_pos = tree->get_screen_transform().xform(pos.position + Vector2(0, pos.size.height));
|
||||
file_popup->popup(Rect2(popup_pos, Size2()));
|
||||
|
||||
file_dialog_animation = anim_name;
|
||||
file_dialog_library = lib_name;
|
||||
|
||||
} break;
|
||||
case ANIM_BUTTON_DELETE: {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(vformat(TTR("Remove Animation from Library: %s"), anim_name));
|
||||
undo_redo->add_do_method(al.ptr(), "remove_animation", anim_name);
|
||||
undo_redo->add_undo_method(al.ptr(), "add_animation", anim_name, anim);
|
||||
undo_redo->add_do_method(this, "_update_editor", mixer);
|
||||
undo_redo->add_undo_method(this, "_update_editor", mixer);
|
||||
undo_redo->commit_action();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationLibraryEditor::update_tree() {
|
||||
if (updating) {
|
||||
return;
|
||||
}
|
||||
|
||||
tree->clear();
|
||||
ERR_FAIL_NULL(mixer);
|
||||
|
||||
Color ss_color = get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor));
|
||||
|
||||
TreeItem *root = tree->create_item();
|
||||
List<StringName> libs;
|
||||
mixer->get_animation_library_list(&libs);
|
||||
|
||||
for (const StringName &K : libs) {
|
||||
TreeItem *libitem = tree->create_item(root);
|
||||
libitem->set_text(0, K);
|
||||
if (K == StringName()) {
|
||||
libitem->set_suffix(0, TTR("[Global]"));
|
||||
} else {
|
||||
libitem->set_suffix(0, "");
|
||||
}
|
||||
|
||||
Ref<AnimationLibrary> al = mixer->get_animation_library(K);
|
||||
bool animation_library_is_foreign = false;
|
||||
String al_path = al->get_path();
|
||||
if (!al_path.is_resource_file()) {
|
||||
libitem->set_text(1, TTR("[built-in]"));
|
||||
libitem->set_tooltip_text(1, al_path);
|
||||
int srpos = al_path.find("::");
|
||||
if (srpos != -1) {
|
||||
String base = al_path.substr(0, srpos);
|
||||
if (ResourceLoader::get_resource_type(base) == "PackedScene") {
|
||||
if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
|
||||
animation_library_is_foreign = true;
|
||||
libitem->set_text(1, TTR("[foreign]"));
|
||||
}
|
||||
} else {
|
||||
if (FileAccess::exists(base + ".import")) {
|
||||
animation_library_is_foreign = true;
|
||||
libitem->set_text(1, TTR("[imported]"));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (FileAccess::exists(al_path + ".import")) {
|
||||
animation_library_is_foreign = true;
|
||||
libitem->set_text(1, TTR("[imported]"));
|
||||
} else {
|
||||
libitem->set_text(1, al_path.get_file());
|
||||
}
|
||||
}
|
||||
|
||||
libitem->set_editable(0, true);
|
||||
libitem->set_metadata(0, K);
|
||||
libitem->set_icon(0, get_editor_theme_icon("AnimationLibrary"));
|
||||
|
||||
libitem->add_button(0, get_editor_theme_icon("Add"), LIB_BUTTON_ADD, animation_library_is_foreign, TTR("Add animation to library."));
|
||||
libitem->add_button(0, get_editor_theme_icon("Load"), LIB_BUTTON_LOAD, animation_library_is_foreign, TTR("Load animation from file and add to library."));
|
||||
libitem->add_button(0, get_editor_theme_icon("ActionPaste"), LIB_BUTTON_PASTE, animation_library_is_foreign, TTR("Paste animation to library from clipboard."));
|
||||
|
||||
libitem->add_button(1, get_editor_theme_icon("Save"), LIB_BUTTON_FILE, false, TTR("Save animation library to resource on disk."));
|
||||
libitem->add_button(1, get_editor_theme_icon("Remove"), LIB_BUTTON_DELETE, false, TTR("Remove animation library."));
|
||||
|
||||
libitem->set_custom_bg_color(0, ss_color);
|
||||
|
||||
List<StringName> animations;
|
||||
al->get_animation_list(&animations);
|
||||
for (const StringName &L : animations) {
|
||||
TreeItem *anitem = tree->create_item(libitem);
|
||||
anitem->set_text(0, L);
|
||||
anitem->set_editable(0, !animation_library_is_foreign);
|
||||
anitem->set_metadata(0, L);
|
||||
anitem->set_icon(0, get_editor_theme_icon("Animation"));
|
||||
anitem->add_button(0, get_editor_theme_icon("ActionCopy"), ANIM_BUTTON_COPY, animation_library_is_foreign, TTR("Copy animation to clipboard."));
|
||||
|
||||
Ref<Animation> anim = al->get_animation(L);
|
||||
String anim_path = anim->get_path();
|
||||
if (!anim_path.is_resource_file()) {
|
||||
anitem->set_text(1, TTR("[built-in]"));
|
||||
anitem->set_tooltip_text(1, anim_path);
|
||||
int srpos = anim_path.find("::");
|
||||
if (srpos != -1) {
|
||||
String base = anim_path.substr(0, srpos);
|
||||
if (ResourceLoader::get_resource_type(base) == "PackedScene") {
|
||||
if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
|
||||
anitem->set_text(1, TTR("[foreign]"));
|
||||
}
|
||||
} else {
|
||||
if (FileAccess::exists(base + ".import")) {
|
||||
anitem->set_text(1, TTR("[imported]"));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (FileAccess::exists(anim_path + ".import")) {
|
||||
anitem->set_text(1, TTR("[imported]"));
|
||||
} else {
|
||||
anitem->set_text(1, anim_path.get_file());
|
||||
}
|
||||
}
|
||||
anitem->add_button(1, get_editor_theme_icon("Save"), ANIM_BUTTON_FILE, animation_library_is_foreign, TTR("Save animation to resource on disk."));
|
||||
anitem->add_button(1, get_editor_theme_icon("Remove"), ANIM_BUTTON_DELETE, animation_library_is_foreign, TTR("Remove animation from Library."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationLibraryEditor::show_dialog() {
|
||||
update_tree();
|
||||
popup_centered_ratio(0.5);
|
||||
}
|
||||
|
||||
void AnimationLibraryEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
new_library_button->set_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
load_library_button->set_icon(get_editor_theme_icon(SNAME("Load")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationLibraryEditor::_update_editor(Object *p_mixer) {
|
||||
emit_signal("update_editor", p_mixer);
|
||||
}
|
||||
|
||||
void AnimationLibraryEditor::shortcut_input(const Ref<InputEvent> &p_event) {
|
||||
const Ref<InputEventKey> k = p_event;
|
||||
if (k.is_valid() && k->is_pressed()) {
|
||||
bool handled = false;
|
||||
|
||||
if (ED_IS_SHORTCUT("ui_undo", p_event)) {
|
||||
EditorNode::get_singleton()->undo();
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (ED_IS_SHORTCUT("ui_redo", p_event)) {
|
||||
EditorNode::get_singleton()->redo();
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
set_input_as_handled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationLibraryEditor::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_update_editor", "mixer"), &AnimationLibraryEditor::_update_editor);
|
||||
ADD_SIGNAL(MethodInfo("update_editor"));
|
||||
}
|
||||
|
||||
AnimationLibraryEditor::AnimationLibraryEditor() {
|
||||
set_title(TTR("Edit Animation Libraries"));
|
||||
set_process_shortcut_input(true);
|
||||
|
||||
file_dialog = memnew(EditorFileDialog);
|
||||
add_child(file_dialog);
|
||||
file_dialog->connect("file_selected", callable_mp(this, &AnimationLibraryEditor::_load_file));
|
||||
file_dialog->connect("files_selected", callable_mp(this, &AnimationLibraryEditor::_load_files));
|
||||
|
||||
add_library_dialog = memnew(ConfirmationDialog);
|
||||
VBoxContainer *dialog_vb = memnew(VBoxContainer);
|
||||
add_library_name = memnew(LineEdit);
|
||||
dialog_vb->add_child(add_library_name);
|
||||
add_library_name->connect(SceneStringName(text_changed), callable_mp(this, &AnimationLibraryEditor::_add_library_validate));
|
||||
add_child(add_library_dialog);
|
||||
|
||||
add_library_validate = memnew(Label);
|
||||
dialog_vb->add_child(add_library_validate);
|
||||
add_library_dialog->add_child(dialog_vb);
|
||||
add_library_dialog->connect(SceneStringName(confirmed), callable_mp(this, &AnimationLibraryEditor::_add_library_confirm));
|
||||
add_library_dialog->register_text_enter(add_library_name);
|
||||
|
||||
VBoxContainer *vb = memnew(VBoxContainer);
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
hb->add_spacer(true);
|
||||
new_library_button = memnew(Button(TTR("New Library")));
|
||||
new_library_button->set_tooltip_text(TTR("Create new empty animation library."));
|
||||
new_library_button->connect(SceneStringName(pressed), callable_mp(this, &AnimationLibraryEditor::_add_library));
|
||||
hb->add_child(new_library_button);
|
||||
load_library_button = memnew(Button(TTR("Load Library")));
|
||||
load_library_button->set_tooltip_text(TTR("Load animation library from disk."));
|
||||
load_library_button->connect(SceneStringName(pressed), callable_mp(this, &AnimationLibraryEditor::_load_library));
|
||||
hb->add_child(load_library_button);
|
||||
vb->add_child(hb);
|
||||
tree = memnew(Tree);
|
||||
vb->add_child(tree);
|
||||
|
||||
tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
tree->set_columns(2);
|
||||
tree->set_column_titles_visible(true);
|
||||
tree->set_column_title(0, TTR("Resource"));
|
||||
tree->set_column_title(1, TTR("Storage"));
|
||||
tree->set_column_expand(0, true);
|
||||
tree->set_column_custom_minimum_width(1, EDSCALE * 250);
|
||||
tree->set_column_expand(1, false);
|
||||
tree->set_hide_root(true);
|
||||
tree->set_hide_folding(true);
|
||||
tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
|
||||
tree->connect("item_edited", callable_mp(this, &AnimationLibraryEditor::_item_renamed));
|
||||
tree->connect("button_clicked", callable_mp(this, &AnimationLibraryEditor::_button_pressed));
|
||||
|
||||
file_popup = memnew(PopupMenu);
|
||||
add_child(file_popup);
|
||||
file_popup->connect(SceneStringName(id_pressed), callable_mp(this, &AnimationLibraryEditor::_file_popup_selected));
|
||||
|
||||
add_child(vb);
|
||||
|
||||
error_dialog = memnew(AcceptDialog);
|
||||
error_dialog->set_title(TTR("Error:"));
|
||||
add_child(error_dialog);
|
||||
}
|
||||
126
engine/editor/plugins/animation_library_editor.h
Normal file
126
engine/editor/plugins/animation_library_editor.h
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ANIMATION_LIBRARY_EDITOR_H
|
||||
#define ANIMATION_LIBRARY_EDITOR_H
|
||||
|
||||
#include "editor/animation_track_editor.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 _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();
|
||||
};
|
||||
|
||||
#endif // ANIMATION_LIBRARY_EDITOR_H
|
||||
2314
engine/editor/plugins/animation_player_editor_plugin.cpp
Normal file
2314
engine/editor/plugins/animation_player_editor_plugin.cpp
Normal file
File diff suppressed because it is too large
Load diff
337
engine/editor/plugins/animation_player_editor_plugin.h
Normal file
337
engine/editor/plugins/animation_player_editor_plugin.h
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ANIMATION_PLAYER_EDITOR_PLUGIN_H
|
||||
#define ANIMATION_PLAYER_EDITOR_PLUGIN_H
|
||||
|
||||
#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.
|
||||
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 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 _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 _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);
|
||||
|
||||
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;
|
||||
|
||||
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 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 String get_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_name() const override { return "AnimationTrackKeyEdit"; }
|
||||
|
||||
AnimationTrackKeyEditEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // ANIMATION_PLAYER_EDITOR_PLUGIN_H
|
||||
1888
engine/editor/plugins/animation_state_machine_editor.cpp
Normal file
1888
engine/editor/plugins/animation_state_machine_editor.cpp
Normal file
File diff suppressed because it is too large
Load diff
328
engine/editor/plugins/animation_state_machine_editor.h
Normal file
328
engine/editor/plugins/animation_state_machine_editor.h
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ANIMATION_STATE_MACHINE_EDITOR_H
|
||||
#define ANIMATION_STATE_MACHINE_EDITOR_H
|
||||
|
||||
#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"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
class ConfirmationDialog;
|
||||
class EditorFileDialog;
|
||||
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 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);
|
||||
|
||||
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;
|
||||
|
||||
ConfirmationDialog *delete_window = nullptr;
|
||||
Tree *delete_tree = nullptr;
|
||||
|
||||
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();
|
||||
|
||||
void _delete_selected();
|
||||
void _delete_all();
|
||||
void _delete_tree_draw();
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
EditorAnimationMultiTransitionEdit(){};
|
||||
};
|
||||
|
||||
#endif // ANIMATION_STATE_MACHINE_EDITOR_H
|
||||
315
engine/editor/plugins/animation_tree_editor_plugin.cpp
Normal file
315
engine/editor/plugins/animation_tree_editor_plugin.cpp
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
/**************************************************************************/
|
||||
/* 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_NONE);
|
||||
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_NONE);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationTreeEditor::_bind_methods() {
|
||||
}
|
||||
|
||||
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() {
|
||||
if (!singleton->tree || !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(TTR("AnimationTree"), anim_tree_editor, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_animation_tree_bottom_panel", TTR("Toggle AnimationTree Bottom Panel")));
|
||||
button->hide();
|
||||
}
|
||||
|
||||
AnimationTreeEditorPlugin::~AnimationTreeEditorPlugin() {
|
||||
}
|
||||
114
engine/editor/plugins/animation_tree_editor_plugin.h
Normal file
114
engine/editor/plugins/animation_tree_editor_plugin.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ANIMATION_TREE_EDITOR_PLUGIN_H
|
||||
#define ANIMATION_TREE_EDITOR_PLUGIN_H
|
||||
|
||||
#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 void _bind_methods();
|
||||
|
||||
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_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();
|
||||
~AnimationTreeEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // ANIMATION_TREE_EDITOR_PLUGIN_H
|
||||
1803
engine/editor/plugins/asset_library_editor_plugin.cpp
Normal file
1803
engine/editor/plugins/asset_library_editor_plugin.cpp
Normal file
File diff suppressed because it is too large
Load diff
358
engine/editor/plugins/asset_library_editor_plugin.h
Normal file
358
engine/editor/plugins/asset_library_editor_plugin.h
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ASSET_LIBRARY_EDITOR_PLUGIN_H
|
||||
#define ASSET_LIBRARY_EDITOR_PLUGIN_H
|
||||
|
||||
#include "editor/editor_asset_installer.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "editor/plugins/editor_plugin_settings.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/check_box.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/separator.h"
|
||||
#include "scene/gui/tab_container.h"
|
||||
#include "scene/gui/texture_button.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_name() const override { return "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();
|
||||
~AssetLibraryEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // ASSET_LIBRARY_EDITOR_PLUGIN_H
|
||||
287
engine/editor/plugins/audio_stream_editor_plugin.cpp
Normal file
287
engine/editor/plugins/audio_stream_editor_plugin.cpp
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
/**************************************************************************/
|
||||
/* 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:
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
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_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
_stop_button->set_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_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
set_process(false);
|
||||
} else {
|
||||
_pausing = false;
|
||||
_player->play(_current);
|
||||
_play_button->set_icon(get_editor_theme_icon(SNAME("Pause")));
|
||||
set_process(true);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamEditor::_stop() {
|
||||
_player->stop();
|
||||
_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
_current = 0;
|
||||
_indicator->queue_redraw();
|
||||
set_process(false);
|
||||
}
|
||||
|
||||
void AudioStreamEditor::_on_finished() {
|
||||
_play_button->set_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_NONE);
|
||||
_play_button->connect(SceneStringName(pressed), callable_mp(this, &AudioStreamEditor::_play));
|
||||
_play_button->set_shortcut(ED_SHORTCUT("audio_stream_editor/audio_preview_play_pause", TTR("Audio Preview Play/Pause"), Key::SPACE));
|
||||
|
||||
_stop_button = memnew(Button);
|
||||
hbox->add_child(_stop_button);
|
||||
_stop_button->set_flat(true);
|
||||
_stop_button->set_focus_mode(Control::FOCUS_NONE);
|
||||
_stop_button->connect(SceneStringName(pressed), callable_mp(this, &AudioStreamEditor::_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);
|
||||
}
|
||||
93
engine/editor/plugins/audio_stream_editor_plugin.h
Normal file
93
engine/editor/plugins/audio_stream_editor_plugin.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef AUDIO_STREAM_EDITOR_PLUGIN_H
|
||||
#define AUDIO_STREAM_EDITOR_PLUGIN_H
|
||||
|
||||
#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();
|
||||
};
|
||||
|
||||
#endif // AUDIO_STREAM_EDITOR_PLUGIN_H
|
||||
122
engine/editor/plugins/audio_stream_randomizer_editor_plugin.cpp
Normal file
122
engine/editor/plugins/audio_stream_randomizer_editor_plugin.cpp
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/**************************************************************************/
|
||||
/* 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"
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
AudioStreamRandomizerEditorPlugin::~AudioStreamRandomizerEditorPlugin() {}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef AUDIO_STREAM_RANDOMIZER_EDITOR_PLUGIN_H
|
||||
#define AUDIO_STREAM_RANDOMIZER_EDITOR_PLUGIN_H
|
||||
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "servers/audio/audio_stream.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_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();
|
||||
~AudioStreamRandomizerEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // AUDIO_STREAM_RANDOMIZER_EDITOR_PLUGIN_H
|
||||
85
engine/editor/plugins/bit_map_editor_plugin.cpp
Normal file
85
engine/editor/plugins/bit_map_editor_plugin.cpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/**************************************************************************/
|
||||
/* 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/themes/editor_scale.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/texture_rect.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
|
||||
void BitMapEditor::setup(const Ref<BitMap> &p_bitmap) {
|
||||
texture_rect->set_texture(ImageTexture::create_from_image(p_bitmap->convert_to_image()));
|
||||
size_label->set_text(vformat(U"%s×%s", p_bitmap->get_size().width, p_bitmap->get_size().height));
|
||||
}
|
||||
|
||||
BitMapEditor::BitMapEditor() {
|
||||
texture_rect = memnew(TextureRect);
|
||||
texture_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
|
||||
texture_rect->set_texture_filter(TEXTURE_FILTER_NEAREST);
|
||||
texture_rect->set_custom_minimum_size(Size2(0, 250) * EDSCALE);
|
||||
add_child(texture_rect);
|
||||
|
||||
size_label = memnew(Label);
|
||||
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);
|
||||
}
|
||||
67
engine/editor/plugins/bit_map_editor_plugin.h
Normal file
67
engine/editor/plugins/bit_map_editor_plugin.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef BIT_MAP_EDITOR_PLUGIN_H
|
||||
#define BIT_MAP_EDITOR_PLUGIN_H
|
||||
|
||||
#include "editor/editor_inspector.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/resources/bit_map.h"
|
||||
|
||||
class TextureRect;
|
||||
|
||||
class BitMapEditor : public VBoxContainer {
|
||||
GDCLASS(BitMapEditor, VBoxContainer);
|
||||
|
||||
TextureRect *texture_rect = nullptr;
|
||||
Label *size_label = nullptr;
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
#endif // BIT_MAP_EDITOR_PLUGIN_H
|
||||
1502
engine/editor/plugins/bone_map_editor_plugin.cpp
Normal file
1502
engine/editor/plugins/bone_map_editor_plugin.cpp
Normal file
File diff suppressed because it is too large
Load diff
240
engine/editor/plugins/bone_map_editor_plugin.h
Normal file
240
engine/editor/plugins/bone_map_editor_plugin.h
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef BONE_MAP_EDITOR_PLUGIN_H
|
||||
#define BONE_MAP_EDITOR_PLUGIN_H
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_properties.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For regex.
|
||||
#ifdef MODULE_REGEX_ENABLED
|
||||
#include "modules/regex/regex.h"
|
||||
#endif
|
||||
|
||||
#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);
|
||||
~BoneMapperButton();
|
||||
};
|
||||
|
||||
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());
|
||||
~BoneMapperItem();
|
||||
};
|
||||
|
||||
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);
|
||||
static void _bind_methods();
|
||||
|
||||
void _confirm();
|
||||
|
||||
private:
|
||||
void create_editors();
|
||||
void create_bones_tree(Skeleton3D *p_skeleton);
|
||||
|
||||
public:
|
||||
BonePicker(Skeleton3D *p_skeleton);
|
||||
~BonePicker();
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
#ifdef MODULE_REGEX_ENABLED
|
||||
/* 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();
|
||||
#endif // MODULE_REGEX_ENABLED
|
||||
|
||||
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);
|
||||
~BoneMapper();
|
||||
};
|
||||
|
||||
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);
|
||||
~BoneMapEditor();
|
||||
};
|
||||
|
||||
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_name() const override { return "BoneMap"; }
|
||||
BoneMapEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // BONE_MAP_EDITOR_PLUGIN_H
|
||||
116
engine/editor/plugins/camera_3d_editor_plugin.cpp
Normal file
116
engine/editor/plugins/camera_3d_editor_plugin.cpp
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "editor/editor_node.h"
|
||||
#include "node_3d_editor_plugin.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::_bind_methods() {
|
||||
}
|
||||
|
||||
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 Camera3DEditorPlugin::edit(Object *p_object) {
|
||||
Node3DEditor::get_singleton()->set_can_preview(Object::cast_to<Camera3D>(p_object));
|
||||
//camera_editor->edit(Object::cast_to<Node>(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(Object::cast_to<Camera3D>(p_object));
|
||||
} else {
|
||||
Node3DEditor::get_singleton()->set_can_preview(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
Camera3DEditorPlugin::Camera3DEditorPlugin() {
|
||||
/* camera_editor = memnew( CameraEditor );
|
||||
EditorNode::get_singleton()->get_main_screen_control()->add_child(camera_editor);
|
||||
|
||||
camera_editor->set_anchor(SIDE_LEFT,Control::ANCHOR_END);
|
||||
camera_editor->set_anchor(SIDE_RIGHT,Control::ANCHOR_END);
|
||||
camera_editor->set_offset(SIDE_LEFT,60);
|
||||
camera_editor->set_offset(SIDE_RIGHT,0);
|
||||
camera_editor->set_offset(SIDE_TOP,0);
|
||||
camera_editor->set_offset(SIDE_BOTTOM,10);
|
||||
|
||||
|
||||
camera_editor->hide();
|
||||
*/
|
||||
}
|
||||
|
||||
Camera3DEditorPlugin::~Camera3DEditorPlugin() {
|
||||
}
|
||||
71
engine/editor/plugins/camera_3d_editor_plugin.h
Normal file
71
engine/editor/plugins/camera_3d_editor_plugin.h
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CAMERA_3D_EDITOR_PLUGIN_H
|
||||
#define CAMERA_3D_EDITOR_PLUGIN_H
|
||||
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/3d/camera_3d.h"
|
||||
|
||||
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);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void edit(Node *p_camera);
|
||||
Camera3DEditor();
|
||||
};
|
||||
|
||||
class Camera3DEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(Camera3DEditorPlugin, EditorPlugin);
|
||||
|
||||
//CameraEditor *camera_editor;
|
||||
|
||||
public:
|
||||
virtual String get_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();
|
||||
~Camera3DEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // CAMERA_3D_EDITOR_PLUGIN_H
|
||||
6343
engine/editor/plugins/canvas_item_editor_plugin.cpp
Normal file
6343
engine/editor/plugins/canvas_item_editor_plugin.cpp
Normal file
File diff suppressed because it is too large
Load diff
673
engine/editor/plugins/canvas_item_editor_plugin.h
Normal file
673
engine/editor/plugins/canvas_item_editor_plugin.h
Normal file
|
|
@ -0,0 +1,673 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CANVAS_ITEM_EDITOR_PLUGIN_H
|
||||
#define CANVAS_ITEM_EDITOR_PLUGIN_H
|
||||
|
||||
#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;
|
||||
|
||||
CanvasItemEditorSelectedItem() {}
|
||||
};
|
||||
|
||||
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(INFINITY, INFINITY);
|
||||
|
||||
bool ruler_tool_active = false;
|
||||
Point2 ruler_tool_origin;
|
||||
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;
|
||||
|
||||
Button *override_camera_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;
|
||||
|
||||
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;
|
||||
bool warped_panning = true;
|
||||
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 _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_override_camera(bool p_pressed);
|
||||
void _button_tool_select(int p_index);
|
||||
|
||||
void _update_override_camera_button(bool p_game_running);
|
||||
|
||||
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);
|
||||
|
||||
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_name() const override { return "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();
|
||||
~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();
|
||||
};
|
||||
|
||||
#endif // CANVAS_ITEM_EDITOR_PLUGIN_H
|
||||
156
engine/editor/plugins/cast_2d_editor_plugin.cpp
Normal file
156
engine/editor/plugins/cast_2d_editor_plugin.cpp
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
/**************************************************************************/
|
||||
/* 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"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_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_global_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;
|
||||
}
|
||||
|
||||
Transform2D gt = canvas_item_editor->get_canvas_transform() * node->get_global_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);
|
||||
}
|
||||
77
engine/editor/plugins/cast_2d_editor_plugin.h
Normal file
77
engine/editor/plugins/cast_2d_editor_plugin.h
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CAST_2D_EDITOR_PLUGIN_H
|
||||
#define CAST_2D_EDITOR_PLUGIN_H
|
||||
|
||||
#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_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();
|
||||
};
|
||||
|
||||
#endif // CAST_2D_EDITOR_PLUGIN_H
|
||||
45
engine/editor/plugins/collision_polygon_2d_editor_plugin.cpp
Normal file
45
engine/editor/plugins/collision_polygon_2d_editor_plugin.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/**************************************************************************/
|
||||
/* 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() {}
|
||||
|
||||
CollisionPolygon2DEditorPlugin::CollisionPolygon2DEditorPlugin() :
|
||||
AbstractPolygon2DEditorPlugin(memnew(CollisionPolygon2DEditor), "CollisionPolygon2D") {
|
||||
}
|
||||
57
engine/editor/plugins/collision_polygon_2d_editor_plugin.h
Normal file
57
engine/editor/plugins/collision_polygon_2d_editor_plugin.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef COLLISION_POLYGON_2D_EDITOR_PLUGIN_H
|
||||
#define COLLISION_POLYGON_2D_EDITOR_PLUGIN_H
|
||||
|
||||
#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();
|
||||
};
|
||||
|
||||
#endif // COLLISION_POLYGON_2D_EDITOR_PLUGIN_H
|
||||
589
engine/editor/plugins/collision_shape_2d_editor_plugin.cpp
Normal file
589
engine/editor/plugins/collision_shape_2d_editor_plugin.cpp
Normal file
|
|
@ -0,0 +1,589 @@
|
|||
/**************************************************************************/
|
||||
/* 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/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: {
|
||||
} break;
|
||||
|
||||
case CONVEX_POLYGON_SHAPE: {
|
||||
} 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: {
|
||||
} break;
|
||||
|
||||
case CONVEX_POLYGON_SHAPE: {
|
||||
} 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 {
|
||||
world_boundary->set_normal(p_point.normalized());
|
||||
}
|
||||
}
|
||||
} 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: {
|
||||
// Cannot be edited directly, use CollisionPolygon2D instead.
|
||||
} break;
|
||||
|
||||
case CONVEX_POLYGON_SHAPE: {
|
||||
// Cannot be edited directly, use CollisionPolygon2D instead.
|
||||
} 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;
|
||||
}
|
||||
|
||||
if (shape_type == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Ref<InputEventMouseButton> mb = p_event;
|
||||
Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_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 = 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;
|
||||
}
|
||||
|
||||
if (shape_type == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Transform2D gt = canvas_item_editor->get_canvas_transform() * node->get_global_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: {
|
||||
} break;
|
||||
|
||||
case CONVEX_POLYGON_SHAPE: {
|
||||
} 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);
|
||||
}
|
||||
|
||||
CollisionShape2DEditorPlugin::~CollisionShape2DEditorPlugin() {
|
||||
}
|
||||
118
engine/editor/plugins/collision_shape_2d_editor_plugin.h
Normal file
118
engine/editor/plugins/collision_shape_2d_editor_plugin.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef COLLISION_SHAPE_2D_EDITOR_PLUGIN_H
|
||||
#define COLLISION_SHAPE_2D_EDITOR_PLUGIN_H
|
||||
|
||||
#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_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();
|
||||
~CollisionShape2DEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // COLLISION_SHAPE_2D_EDITOR_PLUGIN_H
|
||||
1132
engine/editor/plugins/control_editor_plugin.cpp
Normal file
1132
engine/editor/plugins/control_editor_plugin.cpp
Normal file
File diff suppressed because it is too large
Load diff
263
engine/editor/plugins/control_editor_plugin.h
Normal file
263
engine/editor/plugins/control_editor_plugin.h
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CONTROL_EDITOR_PLUGIN_H
|
||||
#define CONTROL_EDITOR_PLUGIN_H
|
||||
|
||||
#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/check_box.h"
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/margin_container.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
#include "scene/gui/panel_container.h"
|
||||
#include "scene/gui/popup.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/gui/texture_rect.h"
|
||||
|
||||
class CheckButton;
|
||||
class EditorSelection;
|
||||
class GridContainer;
|
||||
|
||||
// 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;
|
||||
|
||||
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);
|
||||
|
||||
public:
|
||||
ControlEditorPresetPicker() {}
|
||||
};
|
||||
|
||||
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_name() const override { return "Control"; }
|
||||
|
||||
ControlEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // CONTROL_EDITOR_PLUGIN_H
|
||||
312
engine/editor/plugins/cpu_particles_2d_editor_plugin.cpp
Normal file
312
engine/editor/plugins/cpu_particles_2d_editor_plugin.cpp
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
/**************************************************************************/
|
||||
/* cpu_particles_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 "cpu_particles_2d_editor_plugin.h"
|
||||
|
||||
#include "canvas_item_editor_plugin.h"
|
||||
#include "core/io/image_loader.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/gui/editor_file_dialog.h"
|
||||
#include "editor/scene_tree_dock.h"
|
||||
#include "scene/2d/cpu_particles_2d.h"
|
||||
#include "scene/2d/gpu_particles_2d.h"
|
||||
#include "scene/gui/check_box.h"
|
||||
#include "scene/gui/menu_button.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/gui/spin_box.h"
|
||||
#include "scene/resources/particle_process_material.h"
|
||||
|
||||
void CPUParticles2DEditorPlugin::edit(Object *p_object) {
|
||||
particles = Object::cast_to<CPUParticles2D>(p_object);
|
||||
}
|
||||
|
||||
bool CPUParticles2DEditorPlugin::handles(Object *p_object) const {
|
||||
return p_object->is_class("CPUParticles2D");
|
||||
}
|
||||
|
||||
void CPUParticles2DEditorPlugin::make_visible(bool p_visible) {
|
||||
if (p_visible) {
|
||||
toolbar->show();
|
||||
} else {
|
||||
toolbar->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void CPUParticles2DEditorPlugin::_file_selected(const String &p_file) {
|
||||
source_emission_file = p_file;
|
||||
emission_mask->popup_centered();
|
||||
}
|
||||
|
||||
void CPUParticles2DEditorPlugin::_menu_callback(int p_idx) {
|
||||
switch (p_idx) {
|
||||
case MENU_LOAD_EMISSION_MASK: {
|
||||
file->popup_file_dialog();
|
||||
} break;
|
||||
case MENU_CLEAR_EMISSION_MASK: {
|
||||
emission_mask->popup_centered();
|
||||
} break;
|
||||
case MENU_RESTART: {
|
||||
particles->restart();
|
||||
} break;
|
||||
case MENU_CONVERT_TO_GPU_PARTICLES: {
|
||||
GPUParticles2D *gpu_particles = memnew(GPUParticles2D);
|
||||
gpu_particles->convert_from_particles(particles);
|
||||
gpu_particles->set_name(particles->get_name());
|
||||
gpu_particles->set_transform(particles->get_transform());
|
||||
gpu_particles->set_visible(particles->is_visible());
|
||||
gpu_particles->set_process_mode(particles->get_process_mode());
|
||||
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
ur->create_action(TTR("Convert to GPUParticles3D"), UndoRedo::MERGE_DISABLE, particles);
|
||||
SceneTreeDock::get_singleton()->replace_node(particles, gpu_particles);
|
||||
ur->commit_action(false);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void CPUParticles2DEditorPlugin::_generate_emission_mask() {
|
||||
Ref<Image> img;
|
||||
img.instantiate();
|
||||
Error err = ImageLoader::load_image(source_emission_file, img);
|
||||
ERR_FAIL_COND_MSG(err != OK, "Error loading image '" + source_emission_file + "'.");
|
||||
|
||||
if (img->is_compressed()) {
|
||||
img->decompress();
|
||||
}
|
||||
img->convert(Image::FORMAT_RGBA8);
|
||||
ERR_FAIL_COND(img->get_format() != Image::FORMAT_RGBA8);
|
||||
Size2i s = img->get_size();
|
||||
ERR_FAIL_COND(s.width == 0 || s.height == 0);
|
||||
|
||||
Vector<Point2> valid_positions;
|
||||
Vector<Point2> valid_normals;
|
||||
Vector<uint8_t> valid_colors;
|
||||
|
||||
valid_positions.resize(s.width * s.height);
|
||||
|
||||
EmissionMode emode = (EmissionMode)emission_mask_mode->get_selected();
|
||||
|
||||
if (emode == EMISSION_MODE_BORDER_DIRECTED) {
|
||||
valid_normals.resize(s.width * s.height);
|
||||
}
|
||||
|
||||
bool capture_colors = emission_colors->is_pressed();
|
||||
|
||||
if (capture_colors) {
|
||||
valid_colors.resize(s.width * s.height * 4);
|
||||
}
|
||||
|
||||
int vpc = 0;
|
||||
|
||||
{
|
||||
Vector<uint8_t> img_data = img->get_data();
|
||||
const uint8_t *r = img_data.ptr();
|
||||
|
||||
for (int i = 0; i < s.width; i++) {
|
||||
for (int j = 0; j < s.height; j++) {
|
||||
uint8_t a = r[(j * s.width + i) * 4 + 3];
|
||||
|
||||
if (a > 128) {
|
||||
if (emode == EMISSION_MODE_SOLID) {
|
||||
if (capture_colors) {
|
||||
valid_colors.write[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0];
|
||||
valid_colors.write[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1];
|
||||
valid_colors.write[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2];
|
||||
valid_colors.write[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3];
|
||||
}
|
||||
valid_positions.write[vpc++] = Point2(i, j);
|
||||
|
||||
} else {
|
||||
bool on_border = false;
|
||||
for (int x = i - 1; x <= i + 1; x++) {
|
||||
for (int y = j - 1; y <= j + 1; y++) {
|
||||
if (x < 0 || y < 0 || x >= s.width || y >= s.height || r[(y * s.width + x) * 4 + 3] <= 128) {
|
||||
on_border = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (on_border) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (on_border) {
|
||||
valid_positions.write[vpc] = Point2(i, j);
|
||||
|
||||
if (emode == EMISSION_MODE_BORDER_DIRECTED) {
|
||||
Vector2 normal;
|
||||
for (int x = i - 2; x <= i + 2; x++) {
|
||||
for (int y = j - 2; y <= j + 2; y++) {
|
||||
if (x == i && y == j) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (x < 0 || y < 0 || x >= s.width || y >= s.height || r[(y * s.width + x) * 4 + 3] <= 128) {
|
||||
normal += Vector2(x - i, y - j).normalized();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
normal.normalize();
|
||||
valid_normals.write[vpc] = normal;
|
||||
}
|
||||
|
||||
if (capture_colors) {
|
||||
valid_colors.write[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0];
|
||||
valid_colors.write[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1];
|
||||
valid_colors.write[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2];
|
||||
valid_colors.write[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3];
|
||||
}
|
||||
|
||||
vpc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
valid_positions.resize(vpc);
|
||||
if (valid_normals.size()) {
|
||||
valid_normals.resize(vpc);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_MSG(valid_positions.is_empty(), "No pixels with transparency > 128 in image...");
|
||||
|
||||
if (capture_colors) {
|
||||
PackedColorArray pca;
|
||||
pca.resize(vpc);
|
||||
Color *pcaw = pca.ptrw();
|
||||
for (int i = 0; i < vpc; i += 1) {
|
||||
Color color;
|
||||
color.r = valid_colors[i * 4 + 0] / 255.0f;
|
||||
color.g = valid_colors[i * 4 + 1] / 255.0f;
|
||||
color.b = valid_colors[i * 4 + 2] / 255.0f;
|
||||
color.a = valid_colors[i * 4 + 3] / 255.0f;
|
||||
pcaw[i] = color;
|
||||
}
|
||||
particles->set_emission_colors(pca);
|
||||
}
|
||||
|
||||
if (valid_normals.size()) {
|
||||
particles->set_emission_shape(CPUParticles2D::EMISSION_SHAPE_DIRECTED_POINTS);
|
||||
PackedVector2Array norms;
|
||||
norms.resize(valid_normals.size());
|
||||
Vector2 *normsw = norms.ptrw();
|
||||
for (int i = 0; i < valid_normals.size(); i += 1) {
|
||||
normsw[i] = valid_normals[i];
|
||||
}
|
||||
particles->set_emission_normals(norms);
|
||||
} else {
|
||||
particles->set_emission_shape(CPUParticles2D::EMISSION_SHAPE_POINTS);
|
||||
}
|
||||
|
||||
{
|
||||
Vector2 offset;
|
||||
if (emission_mask_centered->is_pressed()) {
|
||||
offset = Vector2(-s.width * 0.5, -s.height * 0.5);
|
||||
}
|
||||
|
||||
PackedVector2Array points;
|
||||
points.resize(valid_positions.size());
|
||||
Vector2 *pointsw = points.ptrw();
|
||||
for (int i = 0; i < valid_positions.size(); i += 1) {
|
||||
pointsw[i] = valid_positions[i] + offset;
|
||||
}
|
||||
particles->set_emission_points(points);
|
||||
}
|
||||
}
|
||||
|
||||
void CPUParticles2DEditorPlugin::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &CPUParticles2DEditorPlugin::_menu_callback));
|
||||
menu->set_icon(file->get_editor_theme_icon(SNAME("CPUParticles2D")));
|
||||
file->connect("file_selected", callable_mp(this, &CPUParticles2DEditorPlugin::_file_selected));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void CPUParticles2DEditorPlugin::_bind_methods() {
|
||||
}
|
||||
|
||||
CPUParticles2DEditorPlugin::CPUParticles2DEditorPlugin() {
|
||||
particles = nullptr;
|
||||
|
||||
toolbar = memnew(HBoxContainer);
|
||||
add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, toolbar);
|
||||
toolbar->hide();
|
||||
|
||||
menu = memnew(MenuButton);
|
||||
menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("particles/restart_emission"), MENU_RESTART);
|
||||
menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK);
|
||||
menu->get_popup()->add_item(TTR("Convert to GPUParticles2D"), MENU_CONVERT_TO_GPU_PARTICLES);
|
||||
menu->set_text(TTR("CPUParticles2D"));
|
||||
menu->set_switch_on_hover(true);
|
||||
toolbar->add_child(menu);
|
||||
|
||||
file = memnew(EditorFileDialog);
|
||||
List<String> ext;
|
||||
ImageLoader::get_recognized_extensions(&ext);
|
||||
for (const String &E : ext) {
|
||||
file->add_filter("*." + E, E.to_upper());
|
||||
}
|
||||
file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
|
||||
toolbar->add_child(file);
|
||||
|
||||
emission_mask = memnew(ConfirmationDialog);
|
||||
emission_mask->set_title(TTR("Load Emission Mask"));
|
||||
VBoxContainer *emvb = memnew(VBoxContainer);
|
||||
emission_mask->add_child(emvb);
|
||||
emission_mask_mode = memnew(OptionButton);
|
||||
emvb->add_margin_child(TTR("Emission Mask"), emission_mask_mode);
|
||||
emission_mask_mode->add_item(TTR("Solid Pixels"), EMISSION_MODE_SOLID);
|
||||
emission_mask_mode->add_item(TTR("Border Pixels"), EMISSION_MODE_BORDER);
|
||||
emission_mask_mode->add_item(TTR("Directed Border Pixels"), EMISSION_MODE_BORDER_DIRECTED);
|
||||
VBoxContainer *optionsvb = memnew(VBoxContainer);
|
||||
emvb->add_margin_child(TTR("Options"), optionsvb);
|
||||
emission_mask_centered = memnew(CheckBox);
|
||||
emission_mask_centered->set_text(TTR("Centered"));
|
||||
optionsvb->add_child(emission_mask_centered);
|
||||
emission_colors = memnew(CheckBox);
|
||||
emission_colors->set_text(TTR("Capture Colors from Pixel"));
|
||||
optionsvb->add_child(emission_colors);
|
||||
|
||||
toolbar->add_child(emission_mask);
|
||||
|
||||
emission_mask->connect(SceneStringName(confirmed), callable_mp(this, &CPUParticles2DEditorPlugin::_generate_emission_mask));
|
||||
}
|
||||
|
||||
CPUParticles2DEditorPlugin::~CPUParticles2DEditorPlugin() {
|
||||
}
|
||||
95
engine/editor/plugins/cpu_particles_2d_editor_plugin.h
Normal file
95
engine/editor/plugins/cpu_particles_2d_editor_plugin.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/**************************************************************************/
|
||||
/* cpu_particles_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CPU_PARTICLES_2D_EDITOR_PLUGIN_H
|
||||
#define CPU_PARTICLES_2D_EDITOR_PLUGIN_H
|
||||
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/2d/cpu_particles_2d.h"
|
||||
#include "scene/2d/physics/collision_polygon_2d.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
|
||||
class CheckBox;
|
||||
class ConfirmationDialog;
|
||||
class SpinBox;
|
||||
class EditorFileDialog;
|
||||
class MenuButton;
|
||||
class OptionButton;
|
||||
|
||||
class CPUParticles2DEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(CPUParticles2DEditorPlugin, EditorPlugin);
|
||||
|
||||
enum {
|
||||
MENU_LOAD_EMISSION_MASK,
|
||||
MENU_CLEAR_EMISSION_MASK,
|
||||
MENU_RESTART,
|
||||
MENU_CONVERT_TO_GPU_PARTICLES,
|
||||
};
|
||||
|
||||
enum EmissionMode {
|
||||
EMISSION_MODE_SOLID,
|
||||
EMISSION_MODE_BORDER,
|
||||
EMISSION_MODE_BORDER_DIRECTED
|
||||
};
|
||||
|
||||
CPUParticles2D *particles = nullptr;
|
||||
|
||||
EditorFileDialog *file = nullptr;
|
||||
|
||||
HBoxContainer *toolbar = nullptr;
|
||||
MenuButton *menu = nullptr;
|
||||
|
||||
ConfirmationDialog *emission_mask = nullptr;
|
||||
OptionButton *emission_mask_mode = nullptr;
|
||||
CheckBox *emission_mask_centered = nullptr;
|
||||
CheckBox *emission_colors = nullptr;
|
||||
|
||||
String source_emission_file;
|
||||
|
||||
void _file_selected(const String &p_file);
|
||||
void _menu_callback(int p_idx);
|
||||
void _generate_emission_mask();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual String get_name() const override { return "CPUParticles2D"; }
|
||||
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;
|
||||
|
||||
CPUParticles2DEditorPlugin();
|
||||
~CPUParticles2DEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // CPU_PARTICLES_2D_EDITOR_PLUGIN_H
|
||||
220
engine/editor/plugins/cpu_particles_3d_editor_plugin.cpp
Normal file
220
engine/editor/plugins/cpu_particles_3d_editor_plugin.cpp
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
/**************************************************************************/
|
||||
/* cpu_particles_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 "cpu_particles_3d_editor_plugin.h"
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/gui/scene_tree_editor.h"
|
||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||
#include "editor/scene_tree_dock.h"
|
||||
#include "scene/gui/menu_button.h"
|
||||
|
||||
void CPUParticles3DEditor::_node_removed(Node *p_node) {
|
||||
if (p_node == node) {
|
||||
node = nullptr;
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
void CPUParticles3DEditor::_notification(int p_notification) {
|
||||
switch (p_notification) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
options->set_icon(get_editor_theme_icon(SNAME("CPUParticles3D")));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void CPUParticles3DEditor::_menu_option(int p_option) {
|
||||
switch (p_option) {
|
||||
case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE: {
|
||||
emission_tree_dialog->popup_scenetree_dialog();
|
||||
} break;
|
||||
|
||||
case MENU_OPTION_RESTART: {
|
||||
node->restart();
|
||||
} break;
|
||||
|
||||
case MENU_OPTION_CONVERT_TO_GPU_PARTICLES: {
|
||||
GPUParticles3D *gpu_particles = memnew(GPUParticles3D);
|
||||
gpu_particles->convert_from_particles(node);
|
||||
gpu_particles->set_name(node->get_name());
|
||||
gpu_particles->set_transform(node->get_transform());
|
||||
gpu_particles->set_visible(node->is_visible());
|
||||
gpu_particles->set_process_mode(node->get_process_mode());
|
||||
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
ur->create_action(TTR("Convert to GPUParticles3D"), UndoRedo::MERGE_DISABLE, node);
|
||||
SceneTreeDock::get_singleton()->replace_node(node, gpu_particles);
|
||||
ur->commit_action(false);
|
||||
|
||||
} break;
|
||||
case MENU_OPTION_GENERATE_AABB: {
|
||||
// Add one second to the default generation lifetime, since the progress is updated every second.
|
||||
generate_seconds->set_value(MAX(1.0, trunc(node->get_lifetime()) + 1.0));
|
||||
|
||||
if (generate_seconds->get_value() >= 11.0 + CMP_EPSILON) {
|
||||
// Only pop up the time dialog if the particle's lifetime is long enough to warrant shortening it.
|
||||
generate_aabb->popup_centered();
|
||||
} else {
|
||||
// Generate the visibility AABB immediately.
|
||||
_generate_aabb();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void CPUParticles3DEditor::_generate_aabb() {
|
||||
double time = generate_seconds->get_value();
|
||||
|
||||
double running = 0.0;
|
||||
|
||||
EditorProgress ep("gen_aabb", TTR("Generating Visibility AABB (Waiting for Particle Simulation)"), int(time));
|
||||
|
||||
bool was_emitting = node->is_emitting();
|
||||
if (!was_emitting) {
|
||||
node->set_emitting(true);
|
||||
OS::get_singleton()->delay_usec(1000);
|
||||
}
|
||||
|
||||
AABB rect;
|
||||
|
||||
while (running < time) {
|
||||
uint64_t ticks = OS::get_singleton()->get_ticks_usec();
|
||||
ep.step(TTR("Generating..."), int(running), true);
|
||||
OS::get_singleton()->delay_usec(1000);
|
||||
|
||||
AABB capture = node->capture_aabb();
|
||||
if (rect == AABB()) {
|
||||
rect = capture;
|
||||
} else {
|
||||
rect.merge_with(capture);
|
||||
}
|
||||
|
||||
running += (OS::get_singleton()->get_ticks_usec() - ticks) / 1000000.0;
|
||||
}
|
||||
|
||||
if (!was_emitting) {
|
||||
node->set_emitting(false);
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
ur->create_action(TTR("Generate Visibility AABB"));
|
||||
ur->add_do_method(node, "set_visibility_aabb", rect);
|
||||
ur->add_undo_method(node, "set_visibility_aabb", node->get_visibility_aabb());
|
||||
ur->commit_action();
|
||||
}
|
||||
|
||||
void CPUParticles3DEditor::edit(CPUParticles3D *p_particles) {
|
||||
base_node = p_particles;
|
||||
node = p_particles;
|
||||
}
|
||||
|
||||
void CPUParticles3DEditor::_generate_emission_points() {
|
||||
/// hacer codigo aca
|
||||
Vector<Vector3> points;
|
||||
Vector<Vector3> normals;
|
||||
|
||||
if (!_generate(points, normals)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (normals.size() == 0) {
|
||||
node->set_emission_shape(CPUParticles3D::EMISSION_SHAPE_POINTS);
|
||||
node->set_emission_points(points);
|
||||
} else {
|
||||
node->set_emission_shape(CPUParticles3D::EMISSION_SHAPE_DIRECTED_POINTS);
|
||||
node->set_emission_points(points);
|
||||
node->set_emission_normals(normals);
|
||||
}
|
||||
}
|
||||
|
||||
void CPUParticles3DEditor::_bind_methods() {
|
||||
}
|
||||
|
||||
CPUParticles3DEditor::CPUParticles3DEditor() {
|
||||
particles_editor_hb = memnew(HBoxContainer);
|
||||
Node3DEditor::get_singleton()->add_control_to_menu_panel(particles_editor_hb);
|
||||
options = memnew(MenuButton);
|
||||
options->set_switch_on_hover(true);
|
||||
particles_editor_hb->add_child(options);
|
||||
particles_editor_hb->hide();
|
||||
|
||||
options->set_text(TTR("CPUParticles3D"));
|
||||
options->get_popup()->add_shortcut(ED_GET_SHORTCUT("particles/restart_emission"), MENU_OPTION_RESTART);
|
||||
options->get_popup()->add_item(TTR("Generate AABB"), MENU_OPTION_GENERATE_AABB);
|
||||
options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE);
|
||||
options->get_popup()->add_item(TTR("Convert to GPUParticles3D"), MENU_OPTION_CONVERT_TO_GPU_PARTICLES);
|
||||
options->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &CPUParticles3DEditor::_menu_option));
|
||||
|
||||
generate_aabb = memnew(ConfirmationDialog);
|
||||
generate_aabb->set_title(TTR("Generate Visibility AABB"));
|
||||
VBoxContainer *genvb = memnew(VBoxContainer);
|
||||
generate_aabb->add_child(genvb);
|
||||
generate_seconds = memnew(SpinBox);
|
||||
genvb->add_margin_child(TTR("Generation Time (sec):"), generate_seconds);
|
||||
generate_seconds->set_min(0.1);
|
||||
generate_seconds->set_max(25);
|
||||
generate_seconds->set_value(2);
|
||||
|
||||
add_child(generate_aabb);
|
||||
|
||||
generate_aabb->connect(SceneStringName(confirmed), callable_mp(this, &CPUParticles3DEditor::_generate_aabb));
|
||||
}
|
||||
|
||||
void CPUParticles3DEditorPlugin::edit(Object *p_object) {
|
||||
particles_editor->edit(Object::cast_to<CPUParticles3D>(p_object));
|
||||
}
|
||||
|
||||
bool CPUParticles3DEditorPlugin::handles(Object *p_object) const {
|
||||
return p_object->is_class("CPUParticles3D");
|
||||
}
|
||||
|
||||
void CPUParticles3DEditorPlugin::make_visible(bool p_visible) {
|
||||
if (p_visible) {
|
||||
particles_editor->show();
|
||||
particles_editor->particles_editor_hb->show();
|
||||
} else {
|
||||
particles_editor->particles_editor_hb->hide();
|
||||
particles_editor->hide();
|
||||
particles_editor->edit(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
CPUParticles3DEditorPlugin::CPUParticles3DEditorPlugin() {
|
||||
particles_editor = memnew(CPUParticles3DEditor);
|
||||
EditorNode::get_singleton()->get_main_screen_control()->add_child(particles_editor);
|
||||
|
||||
particles_editor->hide();
|
||||
}
|
||||
|
||||
CPUParticles3DEditorPlugin::~CPUParticles3DEditorPlugin() {
|
||||
}
|
||||
86
engine/editor/plugins/cpu_particles_3d_editor_plugin.h
Normal file
86
engine/editor/plugins/cpu_particles_3d_editor_plugin.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/**************************************************************************/
|
||||
/* cpu_particles_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CPU_PARTICLES_3D_EDITOR_PLUGIN_H
|
||||
#define CPU_PARTICLES_3D_EDITOR_PLUGIN_H
|
||||
|
||||
#include "editor/plugins/gpu_particles_3d_editor_plugin.h"
|
||||
#include "scene/3d/cpu_particles_3d.h"
|
||||
|
||||
class CPUParticles3DEditor : public GPUParticles3DEditorBase {
|
||||
GDCLASS(CPUParticles3DEditor, GPUParticles3DEditorBase);
|
||||
|
||||
enum Menu {
|
||||
MENU_OPTION_GENERATE_AABB,
|
||||
MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE,
|
||||
MENU_OPTION_CLEAR_EMISSION_VOLUME,
|
||||
MENU_OPTION_RESTART,
|
||||
MENU_OPTION_CONVERT_TO_GPU_PARTICLES,
|
||||
};
|
||||
|
||||
ConfirmationDialog *generate_aabb = nullptr;
|
||||
SpinBox *generate_seconds = nullptr;
|
||||
CPUParticles3D *node = nullptr;
|
||||
|
||||
void _generate_aabb();
|
||||
|
||||
void _menu_option(int);
|
||||
|
||||
friend class CPUParticles3DEditorPlugin;
|
||||
|
||||
virtual void _generate_emission_points() override;
|
||||
|
||||
protected:
|
||||
void _notification(int p_notification);
|
||||
void _node_removed(Node *p_node);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void edit(CPUParticles3D *p_particles);
|
||||
CPUParticles3DEditor();
|
||||
};
|
||||
|
||||
class CPUParticles3DEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(CPUParticles3DEditorPlugin, EditorPlugin);
|
||||
|
||||
CPUParticles3DEditor *particles_editor = nullptr;
|
||||
|
||||
public:
|
||||
virtual String get_name() const override { return "CPUParticles3D"; }
|
||||
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;
|
||||
|
||||
CPUParticles3DEditorPlugin();
|
||||
~CPUParticles3DEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // CPU_PARTICLES_3D_EDITOR_PLUGIN_H
|
||||
1101
engine/editor/plugins/curve_editor_plugin.cpp
Normal file
1101
engine/editor/plugins/curve_editor_plugin.cpp
Normal file
File diff suppressed because it is too large
Load diff
199
engine/editor/plugins/curve_editor_plugin.h
Normal file
199
engine/editor/plugins/curve_editor_plugin.h
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CURVE_EDITOR_PLUGIN_H
|
||||
#define CURVE_EDITOR_PLUGIN_H
|
||||
|
||||
#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(Vector2 p_pos) const;
|
||||
TangentIndex get_tangent_at(Vector2 p_pos) const;
|
||||
|
||||
float get_offset_without_collision(int p_current_index, float p_offset, bool p_prioritize_right = true);
|
||||
|
||||
void add_point(Vector2 p_pos);
|
||||
void remove_point(int p_index);
|
||||
void set_point_position(int p_index, 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 set_selected_index(int p_index);
|
||||
void set_selected_tangent_index(TangentIndex p_tangent);
|
||||
|
||||
Vector2 get_tangent_view_pos(int p_index, TangentIndex p_tangent) const;
|
||||
Vector2 get_view_pos(Vector2 p_world_pos) const;
|
||||
Vector2 get_world_pos(Vector2 p_view_pos) const;
|
||||
|
||||
void _redraw();
|
||||
|
||||
private:
|
||||
const float ASPECT_RATIO = 6.f / 13.f;
|
||||
|
||||
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;
|
||||
float initial_grab_left_tangent;
|
||||
float initial_grab_right_tangent;
|
||||
|
||||
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_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;
|
||||
};
|
||||
|
||||
#endif // CURVE_EDITOR_PLUGIN_H
|
||||
272
engine/editor/plugins/debugger_editor_plugin.cpp
Normal file
272
engine/editor/plugins/debugger_editor_plugin.cpp
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/menu_button.h"
|
||||
|
||||
DebuggerEditorPlugin::DebuggerEditorPlugin(PopupMenu *p_debug_menu) {
|
||||
EditorDebuggerServer::initialize();
|
||||
|
||||
ED_SHORTCUT("debugger/step_into", TTR("Step Into"), Key::F11);
|
||||
ED_SHORTCUT("debugger/step_over", TTR("Step Over"), Key::F10);
|
||||
ED_SHORTCUT("debugger/break", TTR("Break"));
|
||||
ED_SHORTCUT("debugger/continue", TTR("Continue"), Key::F12);
|
||||
ED_SHORTCUT("debugger/debug_with_external_editor", TTR("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(TTR("Debugger"), debugger, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_debugger_bottom_panel", TTR("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", TTR("Deploy with Remote Debug")), RUN_DEPLOY_REMOTE_DEBUG);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTR("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", TTR("Small Deploy with Network Filesystem")), RUN_FILE_SERVER);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTR("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", TTR("Visible Collision Shapes")), RUN_DEBUG_COLLISIONS);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTR("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", TTR("Visible Paths")), RUN_DEBUG_PATHS);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTR("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", TTR("Visible Navigation")), RUN_DEBUG_NAVIGATION);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTR("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", TTR("Visible Avoidance")), RUN_DEBUG_AVOIDANCE);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTR("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", TTR("Debug CanvasItem Redraws")), RUN_DEBUG_CANVAS_REDRAW);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTR("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", TTR("Synchronize Scene Changes")), RUN_LIVE_DEBUG);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTR("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", TTR("Synchronize Script Changes")), RUN_RELOAD_SCRIPTS);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTR("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", TTR("Keep Debug Server Open")), SERVER_KEEP_OPEN);
|
||||
debug_menu->set_item_tooltip(-1,
|
||||
TTR("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(TTR("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", false);
|
||||
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);
|
||||
}
|
||||
}
|
||||
77
engine/editor/plugins/debugger_editor_plugin.h
Normal file
77
engine/editor/plugins/debugger_editor_plugin.h
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DEBUGGER_EDITOR_PLUGIN_H
|
||||
#define DEBUGGER_EDITOR_PLUGIN_H
|
||||
|
||||
#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_name() const override { return "Debugger"; }
|
||||
bool has_main_screen() const override { return false; }
|
||||
|
||||
DebuggerEditorPlugin(PopupMenu *p_menu);
|
||||
~DebuggerEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // DEBUGGER_EDITOR_PLUGIN_H
|
||||
139
engine/editor/plugins/dedicated_server_export_plugin.cpp
Normal file
139
engine/editor/plugins/dedicated_server_export_plugin.cpp
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/**************************************************************************/
|
||||
/* 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 = const_cast<Resource *>(p_resource.ptr())->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;
|
||||
}
|
||||
58
engine/editor/plugins/dedicated_server_export_plugin.h
Normal file
58
engine/editor/plugins/dedicated_server_export_plugin.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DEDICATED_SERVER_EXPORT_PLUGIN_H
|
||||
#define DEDICATED_SERVER_EXPORT_PLUGIN_H
|
||||
|
||||
#include "editor/export/editor_export.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;
|
||||
};
|
||||
|
||||
#endif // DEDICATED_SERVER_EXPORT_PLUGIN_H
|
||||
220
engine/editor/plugins/editor_debugger_plugin.cpp
Normal file
220
engine/editor/plugins/editor_debugger_plugin.cpp
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
/**************************************************************************/
|
||||
/* 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));
|
||||
}
|
||||
107
engine/editor/plugins/editor_debugger_plugin.h
Normal file
107
engine/editor/plugins/editor_debugger_plugin.h
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef EDITOR_DEBUGGER_PLUGIN_H
|
||||
#define EDITOR_DEBUGGER_PLUGIN_H
|
||||
|
||||
#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();
|
||||
};
|
||||
|
||||
#endif // EDITOR_DEBUGGER_PLUGIN_H
|
||||
46
engine/editor/plugins/editor_plugin.compat.inc
Normal file
46
engine/editor/plugins/editor_plugin.compat.inc
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/**************************************************************************/
|
||||
/* editor_plugin.compat.inc */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
Button *EditorPlugin::_add_control_to_bottom_panel_compat_88081(Control *p_control, const String &p_title) {
|
||||
return add_control_to_bottom_panel(p_control, p_title, nullptr);
|
||||
}
|
||||
|
||||
void EditorPlugin::_add_control_to_dock_compat_88081(DockSlot p_slot, Control *p_control) {
|
||||
return add_control_to_dock(p_slot, p_control, nullptr);
|
||||
}
|
||||
|
||||
void EditorPlugin::_bind_compatibility_methods() {
|
||||
ClassDB::bind_compatibility_method(D_METHOD("add_control_to_bottom_panel", "control", "title"), &EditorPlugin::_add_control_to_bottom_panel_compat_88081);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("add_control_to_dock", "slot", "control"), &EditorPlugin::_add_control_to_dock_compat_88081);
|
||||
}
|
||||
|
||||
#endif
|
||||
692
engine/editor/plugins/editor_plugin.cpp
Normal file
692
engine/editor/plugins/editor_plugin.cpp
Normal file
|
|
@ -0,0 +1,692 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "editor_plugin.h"
|
||||
#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/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/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/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 "scene/3d/camera_3d.h"
|
||||
#include "scene/gui/popup_menu.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
void EditorPlugin::add_custom_type(const String &p_type, const String &p_base, const Ref<Script> &p_script, const Ref<Texture2D> &p_icon) {
|
||||
EditorNode::get_editor_data().add_custom_type(p_type, p_base, p_script, p_icon);
|
||||
}
|
||||
|
||||
void EditorPlugin::remove_custom_type(const String &p_type) {
|
||||
EditorNode::get_editor_data().remove_custom_type(p_type);
|
||||
}
|
||||
|
||||
void EditorPlugin::add_autoload_singleton(const String &p_name, const String &p_path) {
|
||||
if (p_path.begins_with("res://")) {
|
||||
EditorNode::get_singleton()->get_project_settings()->get_autoload_settings()->autoload_add(p_name, p_path);
|
||||
} else {
|
||||
const Ref<Script> plugin_script = static_cast<Ref<Script>>(get_script());
|
||||
ERR_FAIL_COND(plugin_script.is_null());
|
||||
const String script_base_path = plugin_script->get_path().get_base_dir();
|
||||
EditorNode::get_singleton()->get_project_settings()->get_autoload_settings()->autoload_add(p_name, script_base_path.path_join(p_path));
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPlugin::remove_autoload_singleton(const String &p_name) {
|
||||
EditorNode::get_singleton()->get_project_settings()->get_autoload_settings()->autoload_remove(p_name);
|
||||
}
|
||||
|
||||
Button *EditorPlugin::add_control_to_bottom_panel(Control *p_control, const String &p_title, const Ref<Shortcut> &p_shortcut) {
|
||||
ERR_FAIL_NULL_V(p_control, nullptr);
|
||||
return EditorNode::get_bottom_panel()->add_item(p_title, p_control, p_shortcut);
|
||||
}
|
||||
|
||||
void EditorPlugin::add_control_to_dock(DockSlot p_slot, Control *p_control, const Ref<Shortcut> &p_shortcut) {
|
||||
ERR_FAIL_NULL(p_control);
|
||||
EditorDockManager::get_singleton()->add_dock(p_control, String(), EditorDockManager::DockSlot(p_slot), p_shortcut);
|
||||
}
|
||||
|
||||
void EditorPlugin::remove_control_from_docks(Control *p_control) {
|
||||
ERR_FAIL_NULL(p_control);
|
||||
EditorDockManager::get_singleton()->remove_dock(p_control);
|
||||
}
|
||||
|
||||
void EditorPlugin::remove_control_from_bottom_panel(Control *p_control) {
|
||||
ERR_FAIL_NULL(p_control);
|
||||
EditorNode::get_bottom_panel()->remove_item(p_control);
|
||||
}
|
||||
|
||||
void EditorPlugin::set_dock_tab_icon(Control *p_control, const Ref<Texture2D> &p_icon) {
|
||||
ERR_FAIL_NULL(p_control);
|
||||
EditorDockManager::get_singleton()->set_dock_tab_icon(p_control, p_icon);
|
||||
}
|
||||
|
||||
void EditorPlugin::add_control_to_container(CustomControlContainer p_location, Control *p_control) {
|
||||
ERR_FAIL_NULL(p_control);
|
||||
|
||||
switch (p_location) {
|
||||
case CONTAINER_TOOLBAR: {
|
||||
EditorNode::get_title_bar()->add_child(p_control);
|
||||
} break;
|
||||
|
||||
case CONTAINER_SPATIAL_EDITOR_MENU: {
|
||||
Node3DEditor::get_singleton()->add_control_to_menu_panel(p_control);
|
||||
|
||||
} break;
|
||||
case CONTAINER_SPATIAL_EDITOR_SIDE_LEFT: {
|
||||
Node3DEditor::get_singleton()->add_control_to_left_panel(p_control);
|
||||
} break;
|
||||
case CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT: {
|
||||
Node3DEditor::get_singleton()->add_control_to_right_panel(p_control);
|
||||
} break;
|
||||
case CONTAINER_SPATIAL_EDITOR_BOTTOM: {
|
||||
Node3DEditor::get_singleton()->get_shader_split()->add_child(p_control);
|
||||
|
||||
} break;
|
||||
case CONTAINER_CANVAS_EDITOR_MENU: {
|
||||
CanvasItemEditor::get_singleton()->add_control_to_menu_panel(p_control);
|
||||
|
||||
} break;
|
||||
case CONTAINER_CANVAS_EDITOR_SIDE_LEFT: {
|
||||
CanvasItemEditor::get_singleton()->add_control_to_left_panel(p_control);
|
||||
} break;
|
||||
case CONTAINER_CANVAS_EDITOR_SIDE_RIGHT: {
|
||||
CanvasItemEditor::get_singleton()->add_control_to_right_panel(p_control);
|
||||
} break;
|
||||
case CONTAINER_CANVAS_EDITOR_BOTTOM: {
|
||||
CanvasItemEditor::get_singleton()->get_bottom_split()->add_child(p_control);
|
||||
|
||||
} break;
|
||||
case CONTAINER_INSPECTOR_BOTTOM: {
|
||||
InspectorDock::get_singleton()->get_addon_area()->add_child(p_control);
|
||||
|
||||
} break;
|
||||
case CONTAINER_PROJECT_SETTING_TAB_LEFT: {
|
||||
ProjectSettingsEditor::get_singleton()->get_tabs()->add_child(p_control);
|
||||
ProjectSettingsEditor::get_singleton()->get_tabs()->move_child(p_control, 0);
|
||||
|
||||
} break;
|
||||
case CONTAINER_PROJECT_SETTING_TAB_RIGHT: {
|
||||
ProjectSettingsEditor::get_singleton()->get_tabs()->add_child(p_control);
|
||||
ProjectSettingsEditor::get_singleton()->get_tabs()->move_child(p_control, 1);
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPlugin::remove_control_from_container(CustomControlContainer p_location, Control *p_control) {
|
||||
ERR_FAIL_NULL(p_control);
|
||||
|
||||
switch (p_location) {
|
||||
case CONTAINER_TOOLBAR: {
|
||||
EditorNode::get_title_bar()->remove_child(p_control);
|
||||
} break;
|
||||
|
||||
case CONTAINER_SPATIAL_EDITOR_MENU: {
|
||||
Node3DEditor::get_singleton()->remove_control_from_menu_panel(p_control);
|
||||
|
||||
} break;
|
||||
case CONTAINER_SPATIAL_EDITOR_SIDE_LEFT: {
|
||||
Node3DEditor::get_singleton()->remove_control_from_left_panel(p_control);
|
||||
} break;
|
||||
case CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT: {
|
||||
Node3DEditor::get_singleton()->remove_control_from_right_panel(p_control);
|
||||
} break;
|
||||
case CONTAINER_SPATIAL_EDITOR_BOTTOM: {
|
||||
Node3DEditor::get_singleton()->get_shader_split()->remove_child(p_control);
|
||||
|
||||
} break;
|
||||
case CONTAINER_CANVAS_EDITOR_MENU: {
|
||||
CanvasItemEditor::get_singleton()->remove_control_from_menu_panel(p_control);
|
||||
|
||||
} break;
|
||||
case CONTAINER_CANVAS_EDITOR_SIDE_LEFT: {
|
||||
CanvasItemEditor::get_singleton()->remove_control_from_left_panel(p_control);
|
||||
} break;
|
||||
case CONTAINER_CANVAS_EDITOR_SIDE_RIGHT: {
|
||||
CanvasItemEditor::get_singleton()->remove_control_from_right_panel(p_control);
|
||||
} break;
|
||||
case CONTAINER_CANVAS_EDITOR_BOTTOM: {
|
||||
CanvasItemEditor::get_singleton()->get_bottom_split()->remove_child(p_control);
|
||||
|
||||
} break;
|
||||
case CONTAINER_INSPECTOR_BOTTOM: {
|
||||
InspectorDock::get_singleton()->get_addon_area()->remove_child(p_control);
|
||||
|
||||
} break;
|
||||
case CONTAINER_PROJECT_SETTING_TAB_LEFT:
|
||||
case CONTAINER_PROJECT_SETTING_TAB_RIGHT: {
|
||||
ProjectSettingsEditor::get_singleton()->get_tabs()->remove_child(p_control);
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPlugin::add_tool_menu_item(const String &p_name, const Callable &p_callable) {
|
||||
EditorNode::get_singleton()->add_tool_menu_item(p_name, p_callable);
|
||||
}
|
||||
|
||||
void EditorPlugin::add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu) {
|
||||
ERR_FAIL_NULL(p_submenu);
|
||||
EditorNode::get_singleton()->add_tool_submenu_item(p_name, p_submenu);
|
||||
}
|
||||
|
||||
void EditorPlugin::remove_tool_menu_item(const String &p_name) {
|
||||
EditorNode::get_singleton()->remove_tool_menu_item(p_name);
|
||||
}
|
||||
|
||||
PopupMenu *EditorPlugin::get_export_as_menu() {
|
||||
return EditorNode::get_singleton()->get_export_as_menu();
|
||||
}
|
||||
|
||||
void EditorPlugin::set_input_event_forwarding_always_enabled() {
|
||||
input_event_forwarding_always_enabled = true;
|
||||
EditorPluginList *always_input_forwarding_list = EditorNode::get_singleton()->get_editor_plugins_force_input_forwarding();
|
||||
always_input_forwarding_list->add_plugin(this);
|
||||
}
|
||||
|
||||
void EditorPlugin::set_force_draw_over_forwarding_enabled() {
|
||||
force_draw_over_forwarding_enabled = true;
|
||||
EditorPluginList *always_draw_over_forwarding_list = EditorNode::get_singleton()->get_editor_plugins_force_over();
|
||||
always_draw_over_forwarding_list->add_plugin(this);
|
||||
}
|
||||
|
||||
void EditorPlugin::notify_scene_changed(const Node *scn_root) {
|
||||
emit_signal(SNAME("scene_changed"), scn_root);
|
||||
}
|
||||
|
||||
void EditorPlugin::notify_main_screen_changed(const String &screen_name) {
|
||||
if (screen_name == last_main_screen_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit_signal(SNAME("main_screen_changed"), screen_name);
|
||||
last_main_screen_name = screen_name;
|
||||
}
|
||||
|
||||
void EditorPlugin::notify_scene_closed(const String &scene_filepath) {
|
||||
emit_signal(SNAME("scene_closed"), scene_filepath);
|
||||
}
|
||||
|
||||
void EditorPlugin::notify_resource_saved(const Ref<Resource> &p_resource) {
|
||||
emit_signal(SNAME("resource_saved"), p_resource);
|
||||
}
|
||||
|
||||
void EditorPlugin::notify_scene_saved(const String &p_scene_filepath) {
|
||||
emit_signal(SNAME("scene_saved"), p_scene_filepath);
|
||||
}
|
||||
|
||||
bool EditorPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
|
||||
bool success = false;
|
||||
GDVIRTUAL_CALL(_forward_canvas_gui_input, p_event, success);
|
||||
return success;
|
||||
}
|
||||
|
||||
void EditorPlugin::forward_canvas_draw_over_viewport(Control *p_overlay) {
|
||||
GDVIRTUAL_CALL(_forward_canvas_draw_over_viewport, p_overlay);
|
||||
}
|
||||
|
||||
void EditorPlugin::forward_canvas_force_draw_over_viewport(Control *p_overlay) {
|
||||
GDVIRTUAL_CALL(_forward_canvas_force_draw_over_viewport, p_overlay);
|
||||
}
|
||||
|
||||
// Updates the overlays of the 2D viewport or, if in 3D mode, of every 3D viewport.
|
||||
int EditorPlugin::update_overlays() const {
|
||||
if (Node3DEditor::get_singleton()->is_visible()) {
|
||||
int count = 0;
|
||||
for (uint32_t i = 0; i < Node3DEditor::VIEWPORTS_COUNT; i++) {
|
||||
Node3DEditorViewport *vp = Node3DEditor::get_singleton()->get_editor_viewport(i);
|
||||
if (vp->is_visible()) {
|
||||
vp->update_surface();
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
} else {
|
||||
// This will update the normal viewport itself as well
|
||||
CanvasItemEditor::get_singleton()->get_viewport_control()->queue_redraw();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
EditorPlugin::AfterGUIInput EditorPlugin::forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
|
||||
int success = EditorPlugin::AFTER_GUI_INPUT_PASS;
|
||||
GDVIRTUAL_CALL(_forward_3d_gui_input, p_camera, p_event, success);
|
||||
return static_cast<EditorPlugin::AfterGUIInput>(success);
|
||||
}
|
||||
|
||||
void EditorPlugin::forward_3d_draw_over_viewport(Control *p_overlay) {
|
||||
GDVIRTUAL_CALL(_forward_3d_draw_over_viewport, p_overlay);
|
||||
}
|
||||
|
||||
void EditorPlugin::forward_3d_force_draw_over_viewport(Control *p_overlay) {
|
||||
GDVIRTUAL_CALL(_forward_3d_force_draw_over_viewport, p_overlay);
|
||||
}
|
||||
|
||||
String EditorPlugin::get_name() const {
|
||||
String name;
|
||||
GDVIRTUAL_CALL(_get_plugin_name, name);
|
||||
return name;
|
||||
}
|
||||
|
||||
const Ref<Texture2D> EditorPlugin::get_icon() const {
|
||||
Ref<Texture2D> icon;
|
||||
GDVIRTUAL_CALL(_get_plugin_icon, icon);
|
||||
return icon;
|
||||
}
|
||||
|
||||
String EditorPlugin::get_plugin_version() const {
|
||||
return plugin_version;
|
||||
}
|
||||
|
||||
void EditorPlugin::set_plugin_version(const String &p_version) {
|
||||
plugin_version = p_version;
|
||||
}
|
||||
|
||||
bool EditorPlugin::has_main_screen() const {
|
||||
bool success = false;
|
||||
GDVIRTUAL_CALL(_has_main_screen, success);
|
||||
return success;
|
||||
}
|
||||
|
||||
void EditorPlugin::make_visible(bool p_visible) {
|
||||
GDVIRTUAL_CALL(_make_visible, p_visible);
|
||||
}
|
||||
|
||||
void EditorPlugin::edit(Object *p_object) {
|
||||
GDVIRTUAL_CALL(_edit, p_object);
|
||||
}
|
||||
|
||||
bool EditorPlugin::handles(Object *p_object) const {
|
||||
bool success = false;
|
||||
GDVIRTUAL_CALL(_handles, p_object, success);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool EditorPlugin::can_auto_hide() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
Dictionary EditorPlugin::get_state() const {
|
||||
Dictionary state;
|
||||
GDVIRTUAL_CALL(_get_state, state);
|
||||
return state;
|
||||
}
|
||||
|
||||
void EditorPlugin::set_state(const Dictionary &p_state) {
|
||||
GDVIRTUAL_CALL(_set_state, p_state);
|
||||
}
|
||||
|
||||
void EditorPlugin::clear() {
|
||||
GDVIRTUAL_CALL(_clear);
|
||||
}
|
||||
|
||||
String EditorPlugin::get_unsaved_status(const String &p_for_scene) const {
|
||||
String ret;
|
||||
GDVIRTUAL_CALL(_get_unsaved_status, p_for_scene, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EditorPlugin::save_external_data() {
|
||||
GDVIRTUAL_CALL(_save_external_data);
|
||||
}
|
||||
|
||||
// if changes are pending in editor, apply them
|
||||
void EditorPlugin::apply_changes() {
|
||||
GDVIRTUAL_CALL(_apply_changes);
|
||||
}
|
||||
|
||||
void EditorPlugin::get_breakpoints(List<String> *p_breakpoints) {
|
||||
PackedStringArray arr;
|
||||
if (GDVIRTUAL_CALL(_get_breakpoints, arr)) {
|
||||
for (int i = 0; i < arr.size(); i++) {
|
||||
p_breakpoints->push_back(arr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorPlugin::get_remove_list(List<Node *> *p_list) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void EditorPlugin::add_undo_redo_inspector_hook_callback(Callable p_callable) {
|
||||
EditorNode::get_editor_data().add_undo_redo_inspector_hook_callback(p_callable);
|
||||
}
|
||||
|
||||
void EditorPlugin::remove_undo_redo_inspector_hook_callback(Callable p_callable) {
|
||||
EditorNode::get_editor_data().remove_undo_redo_inspector_hook_callback(p_callable);
|
||||
}
|
||||
|
||||
void EditorPlugin::add_translation_parser_plugin(const Ref<EditorTranslationParserPlugin> &p_parser) {
|
||||
ERR_FAIL_COND(!p_parser.is_valid());
|
||||
EditorTranslationParser::get_singleton()->add_parser(p_parser, EditorTranslationParser::CUSTOM);
|
||||
}
|
||||
|
||||
void EditorPlugin::remove_translation_parser_plugin(const Ref<EditorTranslationParserPlugin> &p_parser) {
|
||||
ERR_FAIL_COND(!p_parser.is_valid());
|
||||
EditorTranslationParser::get_singleton()->remove_parser(p_parser, EditorTranslationParser::CUSTOM);
|
||||
}
|
||||
|
||||
void EditorPlugin::add_import_plugin(const Ref<EditorImportPlugin> &p_importer, bool p_first_priority) {
|
||||
ERR_FAIL_COND(!p_importer.is_valid());
|
||||
ResourceFormatImporter::get_singleton()->add_importer(p_importer, p_first_priority);
|
||||
// Plugins are now loaded during the first scan. It's important not to start another scan,
|
||||
// even a deferred one, as it would cause a scan during a scan at the next main thread iteration.
|
||||
if (!EditorFileSystem::get_singleton()->doing_first_scan()) {
|
||||
callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::scan).call_deferred();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPlugin::remove_import_plugin(const Ref<EditorImportPlugin> &p_importer) {
|
||||
ERR_FAIL_COND(!p_importer.is_valid());
|
||||
ResourceFormatImporter::get_singleton()->remove_importer(p_importer);
|
||||
// Plugins are now loaded during the first scan. It's important not to start another scan,
|
||||
// even a deferred one, as it would cause a scan during a scan at the next main thread iteration.
|
||||
if (!EditorNode::get_singleton()->is_exiting() && !EditorFileSystem::get_singleton()->doing_first_scan()) {
|
||||
callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::scan).call_deferred();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPlugin::add_export_plugin(const Ref<EditorExportPlugin> &p_exporter) {
|
||||
ERR_FAIL_COND(!p_exporter.is_valid());
|
||||
EditorExport::get_singleton()->add_export_plugin(p_exporter);
|
||||
}
|
||||
|
||||
void EditorPlugin::remove_export_plugin(const Ref<EditorExportPlugin> &p_exporter) {
|
||||
ERR_FAIL_COND(!p_exporter.is_valid());
|
||||
EditorExport::get_singleton()->remove_export_plugin(p_exporter);
|
||||
}
|
||||
|
||||
void EditorPlugin::add_node_3d_gizmo_plugin(const Ref<EditorNode3DGizmoPlugin> &p_gizmo_plugin) {
|
||||
ERR_FAIL_COND(!p_gizmo_plugin.is_valid());
|
||||
Node3DEditor::get_singleton()->add_gizmo_plugin(p_gizmo_plugin);
|
||||
}
|
||||
|
||||
void EditorPlugin::remove_node_3d_gizmo_plugin(const Ref<EditorNode3DGizmoPlugin> &p_gizmo_plugin) {
|
||||
ERR_FAIL_COND(!p_gizmo_plugin.is_valid());
|
||||
Node3DEditor::get_singleton()->remove_gizmo_plugin(p_gizmo_plugin);
|
||||
}
|
||||
|
||||
void EditorPlugin::add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin) {
|
||||
ERR_FAIL_COND(!p_plugin.is_valid());
|
||||
EditorInspector::add_inspector_plugin(p_plugin);
|
||||
}
|
||||
|
||||
void EditorPlugin::remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin) {
|
||||
ERR_FAIL_COND(!p_plugin.is_valid());
|
||||
EditorInspector::remove_inspector_plugin(p_plugin);
|
||||
}
|
||||
|
||||
void EditorPlugin::add_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer, bool p_first_priority) {
|
||||
ERR_FAIL_COND(!p_importer.is_valid());
|
||||
ResourceImporterScene::add_scene_importer(p_importer, p_first_priority);
|
||||
}
|
||||
|
||||
void EditorPlugin::remove_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer) {
|
||||
ERR_FAIL_COND(!p_importer.is_valid());
|
||||
ResourceImporterScene::remove_scene_importer(p_importer);
|
||||
}
|
||||
|
||||
void EditorPlugin::add_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin, bool p_first_priority) {
|
||||
ResourceImporterScene::add_post_importer_plugin(p_plugin, p_first_priority);
|
||||
}
|
||||
|
||||
void EditorPlugin::remove_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin) {
|
||||
ResourceImporterScene::remove_post_importer_plugin(p_plugin);
|
||||
}
|
||||
|
||||
int find(const PackedStringArray &a, const String &v) {
|
||||
const String *r = a.ptr();
|
||||
for (int j = 0; j < a.size(); ++j) {
|
||||
if (r[j] == v) {
|
||||
return j;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void EditorPlugin::enable_plugin() {
|
||||
// Called when the plugin gets enabled in project settings, after it's added to the tree.
|
||||
// You can implement it to register autoloads.
|
||||
GDVIRTUAL_CALL(_enable_plugin);
|
||||
}
|
||||
|
||||
void EditorPlugin::disable_plugin() {
|
||||
// Last function called when the plugin gets disabled in project settings.
|
||||
// Implement it to cleanup things from the project, such as unregister autoloads.
|
||||
GDVIRTUAL_CALL(_disable_plugin);
|
||||
}
|
||||
|
||||
void EditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) {
|
||||
GDVIRTUAL_CALL(_set_window_layout, p_layout);
|
||||
}
|
||||
|
||||
void EditorPlugin::get_window_layout(Ref<ConfigFile> p_layout) {
|
||||
GDVIRTUAL_CALL(_get_window_layout, p_layout);
|
||||
}
|
||||
|
||||
bool EditorPlugin::build() {
|
||||
bool success = true;
|
||||
GDVIRTUAL_CALL(_build, success);
|
||||
return success;
|
||||
}
|
||||
|
||||
void EditorPlugin::queue_save_layout() {
|
||||
EditorNode::get_singleton()->save_editor_layout_delayed();
|
||||
}
|
||||
|
||||
void EditorPlugin::make_bottom_panel_item_visible(Control *p_item) {
|
||||
EditorNode::get_bottom_panel()->make_item_visible(p_item);
|
||||
}
|
||||
|
||||
void EditorPlugin::hide_bottom_panel() {
|
||||
EditorNode::get_bottom_panel()->hide_bottom_panel();
|
||||
}
|
||||
|
||||
EditorInterface *EditorPlugin::get_editor_interface() {
|
||||
return EditorInterface::get_singleton();
|
||||
}
|
||||
|
||||
ScriptCreateDialog *EditorPlugin::get_script_create_dialog() {
|
||||
return SceneTreeDock::get_singleton()->get_script_create_dialog();
|
||||
}
|
||||
|
||||
void EditorPlugin::add_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin) {
|
||||
EditorDebuggerNode::get_singleton()->add_debugger_plugin(p_plugin);
|
||||
}
|
||||
|
||||
void EditorPlugin::remove_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin) {
|
||||
EditorDebuggerNode::get_singleton()->remove_debugger_plugin(p_plugin);
|
||||
}
|
||||
|
||||
void EditorPlugin::add_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin) {
|
||||
EditorNode::get_singleton()->add_resource_conversion_plugin(p_plugin);
|
||||
}
|
||||
|
||||
void EditorPlugin::remove_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin) {
|
||||
EditorNode::get_singleton()->remove_resource_conversion_plugin(p_plugin);
|
||||
}
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void EditorPlugin::_editor_project_settings_changed() {
|
||||
emit_signal(SNAME("project_settings_changed"));
|
||||
}
|
||||
#endif
|
||||
|
||||
void EditorPlugin::_notification(int p_what) {
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &EditorPlugin::_editor_project_settings_changed));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
ProjectSettings::get_singleton()->disconnect("settings_changed", callable_mp(this, &EditorPlugin::_editor_project_settings_changed));
|
||||
} break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void EditorPlugin::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("add_control_to_container", "container", "control"), &EditorPlugin::add_control_to_container);
|
||||
ClassDB::bind_method(D_METHOD("add_control_to_bottom_panel", "control", "title", "shortcut"), &EditorPlugin::add_control_to_bottom_panel, DEFVAL(Ref<Shortcut>()));
|
||||
ClassDB::bind_method(D_METHOD("add_control_to_dock", "slot", "control", "shortcut"), &EditorPlugin::add_control_to_dock, DEFVAL(Ref<Shortcut>()));
|
||||
ClassDB::bind_method(D_METHOD("remove_control_from_docks", "control"), &EditorPlugin::remove_control_from_docks);
|
||||
ClassDB::bind_method(D_METHOD("remove_control_from_bottom_panel", "control"), &EditorPlugin::remove_control_from_bottom_panel);
|
||||
ClassDB::bind_method(D_METHOD("remove_control_from_container", "container", "control"), &EditorPlugin::remove_control_from_container);
|
||||
ClassDB::bind_method(D_METHOD("set_dock_tab_icon", "control", "icon"), &EditorPlugin::set_dock_tab_icon);
|
||||
ClassDB::bind_method(D_METHOD("add_tool_menu_item", "name", "callable"), &EditorPlugin::add_tool_menu_item);
|
||||
ClassDB::bind_method(D_METHOD("add_tool_submenu_item", "name", "submenu"), &EditorPlugin::add_tool_submenu_item);
|
||||
ClassDB::bind_method(D_METHOD("remove_tool_menu_item", "name"), &EditorPlugin::remove_tool_menu_item);
|
||||
ClassDB::bind_method(D_METHOD("get_export_as_menu"), &EditorPlugin::get_export_as_menu);
|
||||
ClassDB::bind_method(D_METHOD("add_custom_type", "type", "base", "script", "icon"), &EditorPlugin::add_custom_type);
|
||||
ClassDB::bind_method(D_METHOD("remove_custom_type", "type"), &EditorPlugin::remove_custom_type);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("add_autoload_singleton", "name", "path"), &EditorPlugin::add_autoload_singleton);
|
||||
ClassDB::bind_method(D_METHOD("remove_autoload_singleton", "name"), &EditorPlugin::remove_autoload_singleton);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("update_overlays"), &EditorPlugin::update_overlays);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("make_bottom_panel_item_visible", "item"), &EditorPlugin::make_bottom_panel_item_visible);
|
||||
ClassDB::bind_method(D_METHOD("hide_bottom_panel"), &EditorPlugin::hide_bottom_panel);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_undo_redo"), &EditorPlugin::get_undo_redo);
|
||||
ClassDB::bind_method(D_METHOD("add_undo_redo_inspector_hook_callback", "callable"), &EditorPlugin::add_undo_redo_inspector_hook_callback);
|
||||
ClassDB::bind_method(D_METHOD("remove_undo_redo_inspector_hook_callback", "callable"), &EditorPlugin::remove_undo_redo_inspector_hook_callback);
|
||||
ClassDB::bind_method(D_METHOD("queue_save_layout"), &EditorPlugin::queue_save_layout);
|
||||
ClassDB::bind_method(D_METHOD("add_translation_parser_plugin", "parser"), &EditorPlugin::add_translation_parser_plugin);
|
||||
ClassDB::bind_method(D_METHOD("remove_translation_parser_plugin", "parser"), &EditorPlugin::remove_translation_parser_plugin);
|
||||
ClassDB::bind_method(D_METHOD("add_import_plugin", "importer", "first_priority"), &EditorPlugin::add_import_plugin, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("remove_import_plugin", "importer"), &EditorPlugin::remove_import_plugin);
|
||||
ClassDB::bind_method(D_METHOD("add_scene_format_importer_plugin", "scene_format_importer", "first_priority"), &EditorPlugin::add_scene_format_importer_plugin, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("remove_scene_format_importer_plugin", "scene_format_importer"), &EditorPlugin::remove_scene_format_importer_plugin);
|
||||
ClassDB::bind_method(D_METHOD("add_scene_post_import_plugin", "scene_import_plugin", "first_priority"), &EditorPlugin::add_scene_post_import_plugin, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("remove_scene_post_import_plugin", "scene_import_plugin"), &EditorPlugin::remove_scene_post_import_plugin);
|
||||
ClassDB::bind_method(D_METHOD("add_export_plugin", "plugin"), &EditorPlugin::add_export_plugin);
|
||||
ClassDB::bind_method(D_METHOD("remove_export_plugin", "plugin"), &EditorPlugin::remove_export_plugin);
|
||||
ClassDB::bind_method(D_METHOD("add_node_3d_gizmo_plugin", "plugin"), &EditorPlugin::add_node_3d_gizmo_plugin);
|
||||
ClassDB::bind_method(D_METHOD("remove_node_3d_gizmo_plugin", "plugin"), &EditorPlugin::remove_node_3d_gizmo_plugin);
|
||||
ClassDB::bind_method(D_METHOD("add_inspector_plugin", "plugin"), &EditorPlugin::add_inspector_plugin);
|
||||
ClassDB::bind_method(D_METHOD("remove_inspector_plugin", "plugin"), &EditorPlugin::remove_inspector_plugin);
|
||||
ClassDB::bind_method(D_METHOD("add_resource_conversion_plugin", "plugin"), &EditorPlugin::add_resource_conversion_plugin);
|
||||
ClassDB::bind_method(D_METHOD("remove_resource_conversion_plugin", "plugin"), &EditorPlugin::remove_resource_conversion_plugin);
|
||||
ClassDB::bind_method(D_METHOD("set_input_event_forwarding_always_enabled"), &EditorPlugin::set_input_event_forwarding_always_enabled);
|
||||
ClassDB::bind_method(D_METHOD("set_force_draw_over_forwarding_enabled"), &EditorPlugin::set_force_draw_over_forwarding_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_editor_interface"), &EditorPlugin::get_editor_interface);
|
||||
ClassDB::bind_method(D_METHOD("get_script_create_dialog"), &EditorPlugin::get_script_create_dialog);
|
||||
ClassDB::bind_method(D_METHOD("add_debugger_plugin", "script"), &EditorPlugin::add_debugger_plugin);
|
||||
ClassDB::bind_method(D_METHOD("remove_debugger_plugin", "script"), &EditorPlugin::remove_debugger_plugin);
|
||||
ClassDB::bind_method(D_METHOD("get_plugin_version"), &EditorPlugin::get_plugin_version);
|
||||
|
||||
GDVIRTUAL_BIND(_forward_canvas_gui_input, "event");
|
||||
GDVIRTUAL_BIND(_forward_canvas_draw_over_viewport, "viewport_control");
|
||||
GDVIRTUAL_BIND(_forward_canvas_force_draw_over_viewport, "viewport_control");
|
||||
GDVIRTUAL_BIND(_forward_3d_gui_input, "viewport_camera", "event");
|
||||
GDVIRTUAL_BIND(_forward_3d_draw_over_viewport, "viewport_control");
|
||||
GDVIRTUAL_BIND(_forward_3d_force_draw_over_viewport, "viewport_control");
|
||||
GDVIRTUAL_BIND(_get_plugin_name);
|
||||
GDVIRTUAL_BIND(_get_plugin_icon);
|
||||
GDVIRTUAL_BIND(_has_main_screen);
|
||||
GDVIRTUAL_BIND(_make_visible, "visible");
|
||||
GDVIRTUAL_BIND(_edit, "object");
|
||||
GDVIRTUAL_BIND(_handles, "object");
|
||||
GDVIRTUAL_BIND(_get_state);
|
||||
GDVIRTUAL_BIND(_set_state, "state");
|
||||
GDVIRTUAL_BIND(_clear);
|
||||
GDVIRTUAL_BIND(_get_unsaved_status, "for_scene");
|
||||
GDVIRTUAL_BIND(_save_external_data);
|
||||
GDVIRTUAL_BIND(_apply_changes);
|
||||
GDVIRTUAL_BIND(_get_breakpoints);
|
||||
GDVIRTUAL_BIND(_set_window_layout, "configuration");
|
||||
GDVIRTUAL_BIND(_get_window_layout, "configuration");
|
||||
GDVIRTUAL_BIND(_build);
|
||||
GDVIRTUAL_BIND(_enable_plugin);
|
||||
GDVIRTUAL_BIND(_disable_plugin);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("scene_changed", PropertyInfo(Variant::OBJECT, "scene_root", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
|
||||
ADD_SIGNAL(MethodInfo("scene_closed", PropertyInfo(Variant::STRING, "filepath")));
|
||||
ADD_SIGNAL(MethodInfo("main_screen_changed", PropertyInfo(Variant::STRING, "screen_name")));
|
||||
ADD_SIGNAL(MethodInfo("resource_saved", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource")));
|
||||
ADD_SIGNAL(MethodInfo("scene_saved", PropertyInfo(Variant::STRING, "filepath")));
|
||||
ADD_SIGNAL(MethodInfo("project_settings_changed"));
|
||||
|
||||
BIND_ENUM_CONSTANT(CONTAINER_TOOLBAR);
|
||||
BIND_ENUM_CONSTANT(CONTAINER_SPATIAL_EDITOR_MENU);
|
||||
BIND_ENUM_CONSTANT(CONTAINER_SPATIAL_EDITOR_SIDE_LEFT);
|
||||
BIND_ENUM_CONSTANT(CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT);
|
||||
BIND_ENUM_CONSTANT(CONTAINER_SPATIAL_EDITOR_BOTTOM);
|
||||
BIND_ENUM_CONSTANT(CONTAINER_CANVAS_EDITOR_MENU);
|
||||
BIND_ENUM_CONSTANT(CONTAINER_CANVAS_EDITOR_SIDE_LEFT);
|
||||
BIND_ENUM_CONSTANT(CONTAINER_CANVAS_EDITOR_SIDE_RIGHT);
|
||||
BIND_ENUM_CONSTANT(CONTAINER_CANVAS_EDITOR_BOTTOM);
|
||||
BIND_ENUM_CONSTANT(CONTAINER_INSPECTOR_BOTTOM);
|
||||
BIND_ENUM_CONSTANT(CONTAINER_PROJECT_SETTING_TAB_LEFT);
|
||||
BIND_ENUM_CONSTANT(CONTAINER_PROJECT_SETTING_TAB_RIGHT);
|
||||
|
||||
BIND_ENUM_CONSTANT(DOCK_SLOT_LEFT_UL);
|
||||
BIND_ENUM_CONSTANT(DOCK_SLOT_LEFT_BL);
|
||||
BIND_ENUM_CONSTANT(DOCK_SLOT_LEFT_UR);
|
||||
BIND_ENUM_CONSTANT(DOCK_SLOT_LEFT_BR);
|
||||
BIND_ENUM_CONSTANT(DOCK_SLOT_RIGHT_UL);
|
||||
BIND_ENUM_CONSTANT(DOCK_SLOT_RIGHT_BL);
|
||||
BIND_ENUM_CONSTANT(DOCK_SLOT_RIGHT_UR);
|
||||
BIND_ENUM_CONSTANT(DOCK_SLOT_RIGHT_BR);
|
||||
BIND_ENUM_CONSTANT(DOCK_SLOT_MAX);
|
||||
|
||||
BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_PASS);
|
||||
BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_STOP);
|
||||
BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_CUSTOM);
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *EditorPlugin::get_undo_redo() {
|
||||
return EditorUndoRedoManager::get_singleton();
|
||||
}
|
||||
|
||||
EditorPluginCreateFunc EditorPlugins::creation_funcs[MAX_CREATE_FUNCS];
|
||||
|
||||
int EditorPlugins::creation_func_count = 0;
|
||||
292
engine/editor/plugins/editor_plugin.h
Normal file
292
engine/editor/plugins/editor_plugin.h
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
/**************************************************************************/
|
||||
/* editor_plugin.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef EDITOR_PLUGIN_H
|
||||
#define EDITOR_PLUGIN_H
|
||||
|
||||
#include "core/io/config_file.h"
|
||||
#include "scene/3d/camera_3d.h"
|
||||
#include "scene/gui/control.h"
|
||||
|
||||
class Node3D;
|
||||
class Button;
|
||||
class PopupMenu;
|
||||
class EditorDebuggerPlugin;
|
||||
class EditorExport;
|
||||
class EditorExportPlugin;
|
||||
class EditorImportPlugin;
|
||||
class EditorInspectorPlugin;
|
||||
class EditorInterface;
|
||||
class EditorNode3DGizmoPlugin;
|
||||
class EditorResourceConversionPlugin;
|
||||
class EditorSceneFormatImporter;
|
||||
class EditorScenePostImportPlugin;
|
||||
class EditorToolAddons;
|
||||
class EditorTranslationParserPlugin;
|
||||
class EditorUndoRedoManager;
|
||||
class ScriptCreateDialog;
|
||||
|
||||
class EditorPlugin : public Node {
|
||||
GDCLASS(EditorPlugin, Node);
|
||||
friend class EditorData;
|
||||
|
||||
bool input_event_forwarding_always_enabled = false;
|
||||
bool force_draw_over_forwarding_enabled = false;
|
||||
|
||||
String last_main_screen_name;
|
||||
String plugin_version;
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void _editor_project_settings_changed();
|
||||
#endif
|
||||
|
||||
public:
|
||||
enum CustomControlContainer {
|
||||
CONTAINER_TOOLBAR,
|
||||
CONTAINER_SPATIAL_EDITOR_MENU,
|
||||
CONTAINER_SPATIAL_EDITOR_SIDE_LEFT,
|
||||
CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT,
|
||||
CONTAINER_SPATIAL_EDITOR_BOTTOM,
|
||||
CONTAINER_CANVAS_EDITOR_MENU,
|
||||
CONTAINER_CANVAS_EDITOR_SIDE_LEFT,
|
||||
CONTAINER_CANVAS_EDITOR_SIDE_RIGHT,
|
||||
CONTAINER_CANVAS_EDITOR_BOTTOM,
|
||||
CONTAINER_INSPECTOR_BOTTOM,
|
||||
CONTAINER_PROJECT_SETTING_TAB_LEFT,
|
||||
CONTAINER_PROJECT_SETTING_TAB_RIGHT,
|
||||
};
|
||||
|
||||
enum DockSlot {
|
||||
DOCK_SLOT_LEFT_UL,
|
||||
DOCK_SLOT_LEFT_BL,
|
||||
DOCK_SLOT_LEFT_UR,
|
||||
DOCK_SLOT_LEFT_BR,
|
||||
DOCK_SLOT_RIGHT_UL,
|
||||
DOCK_SLOT_RIGHT_BL,
|
||||
DOCK_SLOT_RIGHT_UR,
|
||||
DOCK_SLOT_RIGHT_BR,
|
||||
DOCK_SLOT_MAX
|
||||
};
|
||||
|
||||
enum AfterGUIInput {
|
||||
AFTER_GUI_INPUT_PASS,
|
||||
AFTER_GUI_INPUT_STOP,
|
||||
AFTER_GUI_INPUT_CUSTOM,
|
||||
};
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
static void _bind_methods();
|
||||
EditorUndoRedoManager *get_undo_redo();
|
||||
|
||||
void add_custom_type(const String &p_type, const String &p_base, const Ref<Script> &p_script, const Ref<Texture2D> &p_icon);
|
||||
void remove_custom_type(const String &p_type);
|
||||
|
||||
GDVIRTUAL1R(bool, _forward_canvas_gui_input, Ref<InputEvent>)
|
||||
GDVIRTUAL1(_forward_canvas_draw_over_viewport, Control *)
|
||||
GDVIRTUAL1(_forward_canvas_force_draw_over_viewport, Control *)
|
||||
GDVIRTUAL2R(int, _forward_3d_gui_input, Camera3D *, Ref<InputEvent>)
|
||||
GDVIRTUAL1(_forward_3d_draw_over_viewport, Control *)
|
||||
GDVIRTUAL1(_forward_3d_force_draw_over_viewport, Control *)
|
||||
GDVIRTUAL0RC(String, _get_plugin_name)
|
||||
GDVIRTUAL0RC(Ref<Texture2D>, _get_plugin_icon)
|
||||
GDVIRTUAL0RC(bool, _has_main_screen)
|
||||
GDVIRTUAL1(_make_visible, bool)
|
||||
GDVIRTUAL1(_edit, Object *)
|
||||
GDVIRTUAL1RC(bool, _handles, Object *)
|
||||
GDVIRTUAL0RC(Dictionary, _get_state)
|
||||
GDVIRTUAL1(_set_state, Dictionary)
|
||||
GDVIRTUAL0(_clear)
|
||||
GDVIRTUAL1RC(String, _get_unsaved_status, String)
|
||||
GDVIRTUAL0(_save_external_data)
|
||||
GDVIRTUAL0(_apply_changes)
|
||||
GDVIRTUAL0RC(Vector<String>, _get_breakpoints)
|
||||
GDVIRTUAL1(_set_window_layout, Ref<ConfigFile>)
|
||||
GDVIRTUAL1(_get_window_layout, Ref<ConfigFile>)
|
||||
GDVIRTUAL0R(bool, _build)
|
||||
GDVIRTUAL0(_enable_plugin)
|
||||
GDVIRTUAL0(_disable_plugin)
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
Button *_add_control_to_bottom_panel_compat_88081(Control *p_control, const String &p_title);
|
||||
void _add_control_to_dock_compat_88081(DockSlot p_slot, Control *p_control);
|
||||
static void _bind_compatibility_methods();
|
||||
#endif
|
||||
|
||||
public:
|
||||
//TODO: send a resource for editing to the editor node?
|
||||
|
||||
void add_control_to_container(CustomControlContainer p_location, Control *p_control);
|
||||
void remove_control_from_container(CustomControlContainer p_location, Control *p_control);
|
||||
Button *add_control_to_bottom_panel(Control *p_control, const String &p_title, const Ref<Shortcut> &p_shortcut = nullptr);
|
||||
void add_control_to_dock(DockSlot p_slot, Control *p_control, const Ref<Shortcut> &p_shortcut = nullptr);
|
||||
void remove_control_from_docks(Control *p_control);
|
||||
void remove_control_from_bottom_panel(Control *p_control);
|
||||
|
||||
void set_dock_tab_icon(Control *p_control, const Ref<Texture2D> &p_icon);
|
||||
|
||||
void add_tool_menu_item(const String &p_name, const Callable &p_callable);
|
||||
void add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu);
|
||||
void remove_tool_menu_item(const String &p_name);
|
||||
|
||||
PopupMenu *get_export_as_menu();
|
||||
|
||||
void set_input_event_forwarding_always_enabled();
|
||||
bool is_input_event_forwarding_always_enabled() { return input_event_forwarding_always_enabled; }
|
||||
|
||||
void set_force_draw_over_forwarding_enabled();
|
||||
bool is_force_draw_over_forwarding_enabled() { return force_draw_over_forwarding_enabled; }
|
||||
|
||||
void notify_main_screen_changed(const String &screen_name);
|
||||
void notify_scene_changed(const Node *scn_root);
|
||||
void notify_scene_closed(const String &scene_filepath);
|
||||
void notify_resource_saved(const Ref<Resource> &p_resource);
|
||||
void notify_scene_saved(const String &p_scene_filepath);
|
||||
|
||||
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event);
|
||||
virtual void forward_canvas_draw_over_viewport(Control *p_overlay);
|
||||
virtual void forward_canvas_force_draw_over_viewport(Control *p_overlay);
|
||||
|
||||
virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event);
|
||||
virtual void forward_3d_draw_over_viewport(Control *p_overlay);
|
||||
virtual void forward_3d_force_draw_over_viewport(Control *p_overlay);
|
||||
|
||||
virtual String get_name() const;
|
||||
virtual const Ref<Texture2D> get_icon() const;
|
||||
virtual String get_plugin_version() const;
|
||||
virtual void set_plugin_version(const String &p_version);
|
||||
virtual bool has_main_screen() const;
|
||||
virtual void make_visible(bool p_visible);
|
||||
virtual void selected_notify() {} //notify that it was raised by the user, not the editor
|
||||
virtual void edit(Object *p_object);
|
||||
virtual bool handles(Object *p_object) const;
|
||||
virtual bool can_auto_hide() const;
|
||||
virtual Dictionary get_state() const; //save editor state so it can't be reloaded when reloading scene
|
||||
virtual void set_state(const Dictionary &p_state); //restore editor state (likely was saved with the scene)
|
||||
virtual void clear(); // clear any temporary data in the editor, reset it (likely new scene or load another scene)
|
||||
virtual String get_unsaved_status(const String &p_for_scene = "") const;
|
||||
virtual void save_external_data(); // if editor references external resources/scenes, save them
|
||||
virtual void apply_changes(); // if changes are pending in editor, apply them
|
||||
virtual void get_breakpoints(List<String> *p_breakpoints);
|
||||
virtual bool get_remove_list(List<Node *> *p_list);
|
||||
virtual void set_window_layout(Ref<ConfigFile> p_layout);
|
||||
virtual void get_window_layout(Ref<ConfigFile> p_layout);
|
||||
virtual void edited_scene_changed() {} // if changes are pending in editor, apply them
|
||||
virtual bool build(); // builds with external tools. Returns true if safe to continue running scene.
|
||||
|
||||
EditorInterface *get_editor_interface();
|
||||
ScriptCreateDialog *get_script_create_dialog();
|
||||
|
||||
void add_undo_redo_inspector_hook_callback(Callable p_callable);
|
||||
void remove_undo_redo_inspector_hook_callback(Callable p_callable);
|
||||
|
||||
int update_overlays() const;
|
||||
|
||||
void queue_save_layout();
|
||||
|
||||
void make_bottom_panel_item_visible(Control *p_item);
|
||||
void hide_bottom_panel();
|
||||
|
||||
void add_translation_parser_plugin(const Ref<EditorTranslationParserPlugin> &p_parser);
|
||||
void remove_translation_parser_plugin(const Ref<EditorTranslationParserPlugin> &p_parser);
|
||||
|
||||
void add_import_plugin(const Ref<EditorImportPlugin> &p_importer, bool p_first_priority = false);
|
||||
void remove_import_plugin(const Ref<EditorImportPlugin> &p_importer);
|
||||
|
||||
void add_export_plugin(const Ref<EditorExportPlugin> &p_exporter);
|
||||
void remove_export_plugin(const Ref<EditorExportPlugin> &p_exporter);
|
||||
|
||||
void add_node_3d_gizmo_plugin(const Ref<EditorNode3DGizmoPlugin> &p_gizmo_plugin);
|
||||
void remove_node_3d_gizmo_plugin(const Ref<EditorNode3DGizmoPlugin> &p_gizmo_plugin);
|
||||
|
||||
void add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin);
|
||||
void remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin);
|
||||
|
||||
void add_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer, bool p_first_priority = false);
|
||||
void remove_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer);
|
||||
|
||||
void add_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_importer, bool p_first_priority = false);
|
||||
void remove_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_importer);
|
||||
|
||||
void add_autoload_singleton(const String &p_name, const String &p_path);
|
||||
void remove_autoload_singleton(const String &p_name);
|
||||
|
||||
void add_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin);
|
||||
void remove_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin);
|
||||
|
||||
void add_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin);
|
||||
void remove_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin);
|
||||
|
||||
void enable_plugin();
|
||||
void disable_plugin();
|
||||
|
||||
EditorPlugin() {}
|
||||
virtual ~EditorPlugin() {}
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(EditorPlugin::CustomControlContainer);
|
||||
VARIANT_ENUM_CAST(EditorPlugin::DockSlot);
|
||||
VARIANT_ENUM_CAST(EditorPlugin::AfterGUIInput);
|
||||
|
||||
typedef EditorPlugin *(*EditorPluginCreateFunc)();
|
||||
|
||||
class EditorPlugins {
|
||||
enum {
|
||||
MAX_CREATE_FUNCS = 128
|
||||
};
|
||||
|
||||
static EditorPluginCreateFunc creation_funcs[MAX_CREATE_FUNCS];
|
||||
static int creation_func_count;
|
||||
|
||||
template <typename T>
|
||||
static EditorPlugin *creator() {
|
||||
return memnew(T);
|
||||
}
|
||||
|
||||
public:
|
||||
static int get_plugin_count() { return creation_func_count; }
|
||||
static EditorPlugin *create(int p_idx) {
|
||||
ERR_FAIL_INDEX_V(p_idx, creation_func_count, nullptr);
|
||||
return creation_funcs[p_idx]();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void add_by_type() {
|
||||
add_create_func(creator<T>);
|
||||
}
|
||||
|
||||
static void add_create_func(EditorPluginCreateFunc p_func) {
|
||||
ERR_FAIL_COND(creation_func_count >= MAX_CREATE_FUNCS);
|
||||
creation_funcs[creation_func_count++] = p_func;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // EDITOR_PLUGIN_H
|
||||
263
engine/editor/plugins/editor_plugin_settings.cpp
Normal file
263
engine/editor/plugins/editor_plugin_settings.cpp
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
/**************************************************************************/
|
||||
/* editor_plugin_settings.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_plugin_settings.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/config_file.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/os/main_loop.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/margin_container.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
void EditorPluginSettings::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
|
||||
update_plugins();
|
||||
} break;
|
||||
|
||||
case Node::NOTIFICATION_READY: {
|
||||
plugin_config_dialog->connect("plugin_ready", callable_mp(EditorNode::get_singleton(), &EditorNode::_on_plugin_ready));
|
||||
plugin_list->connect("button_clicked", callable_mp(this, &EditorPluginSettings::_cell_button_pressed));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPluginSettings::update_plugins() {
|
||||
plugin_list->clear();
|
||||
updating = true;
|
||||
TreeItem *root = plugin_list->create_item();
|
||||
|
||||
Vector<String> plugins = _get_plugins("res://addons");
|
||||
plugins.sort();
|
||||
|
||||
for (int i = 0; i < plugins.size(); i++) {
|
||||
Ref<ConfigFile> cfg;
|
||||
cfg.instantiate();
|
||||
const String &path = plugins[i];
|
||||
|
||||
Error err = cfg->load(path);
|
||||
|
||||
if (err != OK) {
|
||||
WARN_PRINT("Can't load plugin config at: " + path);
|
||||
} else {
|
||||
Vector<String> missing_keys;
|
||||
for (const String required_key : { "name", "author", "version", "description", "script" }) {
|
||||
if (!cfg->has_section_key("plugin", required_key)) {
|
||||
missing_keys.append("\"plugin/" + required_key + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
if (!missing_keys.is_empty()) {
|
||||
WARN_PRINT(vformat("Plugin config at \"%s\" is missing the following keys: %s", path, String(",").join(missing_keys)));
|
||||
} else {
|
||||
String name = cfg->get_value("plugin", "name");
|
||||
String author = cfg->get_value("plugin", "author");
|
||||
String version = cfg->get_value("plugin", "version");
|
||||
String description = cfg->get_value("plugin", "description");
|
||||
String scr = cfg->get_value("plugin", "script");
|
||||
|
||||
bool is_enabled = EditorNode::get_singleton()->is_addon_plugin_enabled(path);
|
||||
Color disabled_color = get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor));
|
||||
|
||||
const PackedInt32Array boundaries = TS->string_get_word_breaks(description, "", 80);
|
||||
String wrapped_description;
|
||||
|
||||
for (int j = 0; j < boundaries.size(); j += 2) {
|
||||
const int start = boundaries[j];
|
||||
const int end = boundaries[j + 1];
|
||||
wrapped_description += "\n" + description.substr(start, end - start + 1).rstrip("\n");
|
||||
}
|
||||
|
||||
TreeItem *item = plugin_list->create_item(root);
|
||||
item->set_text(COLUMN_NAME, name);
|
||||
if (!is_enabled) {
|
||||
item->set_custom_color(COLUMN_NAME, disabled_color);
|
||||
}
|
||||
item->set_tooltip_text(COLUMN_NAME, vformat(TTR("Name: %s\nPath: %s\nMain Script: %s\n\n%s"), name, path, scr, wrapped_description));
|
||||
item->set_metadata(COLUMN_NAME, path);
|
||||
item->set_text(COLUMN_VERSION, version);
|
||||
item->set_custom_font(COLUMN_VERSION, get_theme_font("source", EditorStringName(EditorFonts)));
|
||||
item->set_metadata(COLUMN_VERSION, scr);
|
||||
item->set_text(COLUMN_AUTHOR, author);
|
||||
item->set_metadata(COLUMN_AUTHOR, description);
|
||||
item->set_cell_mode(COLUMN_STATUS, TreeItem::CELL_MODE_CHECK);
|
||||
item->set_text(COLUMN_STATUS, TTR("On"));
|
||||
item->set_checked(COLUMN_STATUS, is_enabled);
|
||||
item->set_editable(COLUMN_STATUS, true);
|
||||
item->add_button(COLUMN_EDIT, get_editor_theme_icon(SNAME("Edit")), BUTTON_PLUGIN_EDIT, false, TTR("Edit Plugin"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updating = false;
|
||||
}
|
||||
|
||||
void EditorPluginSettings::_plugin_activity_changed() {
|
||||
if (updating) {
|
||||
return;
|
||||
}
|
||||
|
||||
TreeItem *ti = plugin_list->get_edited();
|
||||
ERR_FAIL_NULL(ti);
|
||||
bool checked = ti->is_checked(COLUMN_STATUS);
|
||||
String name = ti->get_metadata(COLUMN_NAME);
|
||||
|
||||
EditorNode::get_singleton()->set_addon_plugin_enabled(name, checked, true);
|
||||
|
||||
bool is_enabled = EditorNode::get_singleton()->is_addon_plugin_enabled(name);
|
||||
|
||||
if (is_enabled != checked) {
|
||||
updating = true;
|
||||
ti->set_checked(COLUMN_STATUS, is_enabled);
|
||||
updating = false;
|
||||
}
|
||||
if (is_enabled) {
|
||||
ti->clear_custom_color(COLUMN_NAME);
|
||||
} else {
|
||||
ti->set_custom_color(COLUMN_NAME, get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor)));
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPluginSettings::_create_clicked() {
|
||||
plugin_config_dialog->config("");
|
||||
plugin_config_dialog->popup_centered();
|
||||
}
|
||||
|
||||
void EditorPluginSettings::_cell_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) {
|
||||
if (p_button != MouseButton::LEFT) {
|
||||
return;
|
||||
}
|
||||
TreeItem *item = Object::cast_to<TreeItem>(p_item);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
if (p_id == BUTTON_PLUGIN_EDIT) {
|
||||
if (p_column == COLUMN_EDIT) {
|
||||
String dir = item->get_metadata(COLUMN_NAME);
|
||||
plugin_config_dialog->config(dir);
|
||||
plugin_config_dialog->popup_centered();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector<String> EditorPluginSettings::_get_plugins(const String &p_dir) {
|
||||
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
|
||||
Error err = da->change_dir(p_dir);
|
||||
if (err != OK) {
|
||||
return Vector<String>();
|
||||
}
|
||||
|
||||
Vector<String> plugins;
|
||||
da->list_dir_begin();
|
||||
for (String path = da->get_next(); !path.is_empty(); path = da->get_next()) {
|
||||
if (path[0] == '.' || !da->current_is_dir()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const String full_path = p_dir.path_join(path);
|
||||
const String plugin_config = full_path.path_join("plugin.cfg");
|
||||
if (FileAccess::exists(plugin_config)) {
|
||||
plugins.push_back(plugin_config);
|
||||
} else {
|
||||
plugins.append_array(_get_plugins(full_path));
|
||||
}
|
||||
}
|
||||
|
||||
da->list_dir_end();
|
||||
return plugins;
|
||||
}
|
||||
|
||||
void EditorPluginSettings::_bind_methods() {
|
||||
}
|
||||
|
||||
EditorPluginSettings::EditorPluginSettings() {
|
||||
ProjectSettings::get_singleton()->add_hidden_prefix("editor_plugins/");
|
||||
|
||||
plugin_config_dialog = memnew(PluginConfigDialog);
|
||||
plugin_config_dialog->config("");
|
||||
add_child(plugin_config_dialog);
|
||||
|
||||
HBoxContainer *title_hb = memnew(HBoxContainer);
|
||||
Label *label = memnew(Label(TTR("Installed Plugins:")));
|
||||
label->set_theme_type_variation("HeaderSmall");
|
||||
title_hb->add_child(label);
|
||||
title_hb->add_spacer();
|
||||
Button *create_plugin_button = memnew(Button(TTR("Create New Plugin")));
|
||||
create_plugin_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPluginSettings::_create_clicked));
|
||||
title_hb->add_child(create_plugin_button);
|
||||
add_child(title_hb);
|
||||
|
||||
plugin_list = memnew(Tree);
|
||||
plugin_list->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
plugin_list->set_columns(COLUMN_MAX);
|
||||
plugin_list->set_column_titles_visible(true);
|
||||
plugin_list->set_column_title(COLUMN_STATUS, TTR("Enabled"));
|
||||
plugin_list->set_column_title(COLUMN_NAME, TTR("Name"));
|
||||
plugin_list->set_column_title(COLUMN_VERSION, TTR("Version"));
|
||||
plugin_list->set_column_title(COLUMN_AUTHOR, TTR("Author"));
|
||||
plugin_list->set_column_title(COLUMN_EDIT, TTR("Edit"));
|
||||
plugin_list->set_column_title_alignment(COLUMN_STATUS, HORIZONTAL_ALIGNMENT_LEFT);
|
||||
plugin_list->set_column_title_alignment(COLUMN_NAME, HORIZONTAL_ALIGNMENT_LEFT);
|
||||
plugin_list->set_column_title_alignment(COLUMN_VERSION, HORIZONTAL_ALIGNMENT_LEFT);
|
||||
plugin_list->set_column_title_alignment(COLUMN_AUTHOR, HORIZONTAL_ALIGNMENT_LEFT);
|
||||
plugin_list->set_column_title_alignment(COLUMN_EDIT, HORIZONTAL_ALIGNMENT_LEFT);
|
||||
plugin_list->set_column_expand(COLUMN_PADDING_LEFT, false);
|
||||
plugin_list->set_column_expand(COLUMN_STATUS, false);
|
||||
plugin_list->set_column_expand(COLUMN_NAME, true);
|
||||
plugin_list->set_column_expand(COLUMN_VERSION, false);
|
||||
plugin_list->set_column_expand(COLUMN_AUTHOR, false);
|
||||
plugin_list->set_column_expand(COLUMN_EDIT, false);
|
||||
plugin_list->set_column_expand(COLUMN_PADDING_RIGHT, false);
|
||||
plugin_list->set_column_clip_content(COLUMN_STATUS, true);
|
||||
plugin_list->set_column_clip_content(COLUMN_NAME, true);
|
||||
plugin_list->set_column_clip_content(COLUMN_VERSION, true);
|
||||
plugin_list->set_column_clip_content(COLUMN_AUTHOR, true);
|
||||
plugin_list->set_column_clip_content(COLUMN_EDIT, true);
|
||||
plugin_list->set_column_custom_minimum_width(COLUMN_PADDING_LEFT, 10 * EDSCALE);
|
||||
plugin_list->set_column_custom_minimum_width(COLUMN_STATUS, 80 * EDSCALE);
|
||||
plugin_list->set_column_custom_minimum_width(COLUMN_VERSION, 100 * EDSCALE);
|
||||
plugin_list->set_column_custom_minimum_width(COLUMN_AUTHOR, 250 * EDSCALE);
|
||||
plugin_list->set_column_custom_minimum_width(COLUMN_EDIT, 40 * EDSCALE);
|
||||
plugin_list->set_column_custom_minimum_width(COLUMN_PADDING_RIGHT, 10 * EDSCALE);
|
||||
plugin_list->set_hide_root(true);
|
||||
plugin_list->connect("item_edited", callable_mp(this, &EditorPluginSettings::_plugin_activity_changed), CONNECT_DEFERRED);
|
||||
|
||||
VBoxContainer *mc = memnew(VBoxContainer);
|
||||
mc->add_child(plugin_list);
|
||||
mc->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
mc->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
|
||||
add_child(mc);
|
||||
}
|
||||
78
engine/editor/plugins/editor_plugin_settings.h
Normal file
78
engine/editor/plugins/editor_plugin_settings.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/**************************************************************************/
|
||||
/* editor_plugin_settings.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef EDITOR_PLUGIN_SETTINGS_H
|
||||
#define EDITOR_PLUGIN_SETTINGS_H
|
||||
|
||||
#include "editor/editor_data.h"
|
||||
#include "editor/plugins/plugin_config_dialog.h"
|
||||
|
||||
class Tree;
|
||||
|
||||
class EditorPluginSettings : public VBoxContainer {
|
||||
GDCLASS(EditorPluginSettings, VBoxContainer);
|
||||
|
||||
enum {
|
||||
BUTTON_PLUGIN_EDIT
|
||||
};
|
||||
|
||||
enum {
|
||||
COLUMN_PADDING_LEFT,
|
||||
COLUMN_STATUS,
|
||||
COLUMN_NAME,
|
||||
COLUMN_VERSION,
|
||||
COLUMN_AUTHOR,
|
||||
COLUMN_EDIT,
|
||||
COLUMN_PADDING_RIGHT,
|
||||
COLUMN_MAX,
|
||||
};
|
||||
|
||||
PluginConfigDialog *plugin_config_dialog = nullptr;
|
||||
Tree *plugin_list = nullptr;
|
||||
bool updating = false;
|
||||
|
||||
void _plugin_activity_changed();
|
||||
void _create_clicked();
|
||||
void _cell_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
|
||||
|
||||
static Vector<String> _get_plugins(const String &p_dir);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void update_plugins();
|
||||
|
||||
EditorPluginSettings();
|
||||
};
|
||||
|
||||
#endif // EDITOR_PLUGIN_SETTINGS_H
|
||||
916
engine/editor/plugins/editor_preview_plugins.cpp
Normal file
916
engine/editor/plugins/editor_preview_plugins.cpp
Normal file
|
|
@ -0,0 +1,916 @@
|
|||
/**************************************************************************/
|
||||
/* 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/file_access_memory.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/object/script_language.h"
|
||||
#include "core/os/os.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, "Texture2D");
|
||||
}
|
||||
|
||||
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> atex = p_from;
|
||||
if (atex.is_valid()) {
|
||||
Ref<Texture2D> tex = atex->get_atlas();
|
||||
if (!tex.is_valid()) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
||||
Ref<Image> atlas = tex->get_image();
|
||||
if (!atlas.is_valid()) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
||||
if (!atex->get_region().has_area()) {
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
||||
img = atlas->get_region(atex->get_region());
|
||||
} 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);
|
||||
}
|
||||
|
||||
EditorTexturePreviewPlugin::EditorTexturePreviewPlugin() {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
EditorImagePreviewPlugin::EditorImagePreviewPlugin() {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
EditorBitmapPreviewPlugin::EditorBitmapPreviewPlugin() {
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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>();
|
||||
}
|
||||
}
|
||||
|
||||
EditorPackedScenePreviewPlugin::EditorPackedScenePreviewPlugin() {
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
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_valid(), 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>();
|
||||
}
|
||||
|
||||
List<String> kwors;
|
||||
if (p_language) {
|
||||
p_language->get_reserved_words(&kwors);
|
||||
}
|
||||
|
||||
HashSet<String> control_flow_keywords;
|
||||
HashSet<String> keywords;
|
||||
|
||||
for (const String &E : kwors) {
|
||||
if (p_language && p_language->is_control_flow_keyword(E)) {
|
||||
control_flow_keywords.insert(E);
|
||||
} else {
|
||||
keywords.insert(E);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
EditorScriptPreviewPlugin::EditorScriptPreviewPlugin() {
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
EditorAudioStreamPreviewPlugin::EditorAudioStreamPreviewPlugin() {
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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>();
|
||||
}
|
||||
|
||||
EditorGradientPreviewPlugin::EditorGradientPreviewPlugin() {
|
||||
}
|
||||
188
engine/editor/plugins/editor_preview_plugins.h
Normal file
188
engine/editor/plugins/editor_preview_plugins.h
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef EDITOR_PREVIEW_PLUGINS_H
|
||||
#define EDITOR_PREVIEW_PLUGINS_H
|
||||
|
||||
#include "core/templates/safe_refcount.h"
|
||||
#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;
|
||||
|
||||
EditorTexturePreviewPlugin();
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
EditorImagePreviewPlugin();
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
EditorBitmapPreviewPlugin();
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
EditorPackedScenePreviewPlugin();
|
||||
};
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
EditorScriptPreviewPlugin();
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
EditorAudioStreamPreviewPlugin();
|
||||
};
|
||||
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
|
||||
EditorGradientPreviewPlugin();
|
||||
};
|
||||
|
||||
#endif // EDITOR_PREVIEW_PLUGINS_H
|
||||
55
engine/editor/plugins/editor_resource_conversion_plugin.cpp
Normal file
55
engine/editor/plugins/editor_resource_conversion_plugin.cpp
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/**************************************************************************/
|
||||
/* editor_resource_conversion_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_resource_conversion_plugin.h"
|
||||
|
||||
void EditorResourceConversionPlugin::_bind_methods() {
|
||||
GDVIRTUAL_BIND(_converts_to);
|
||||
GDVIRTUAL_BIND(_handles, "resource");
|
||||
GDVIRTUAL_BIND(_convert, "resource");
|
||||
}
|
||||
|
||||
String EditorResourceConversionPlugin::converts_to() const {
|
||||
String ret;
|
||||
GDVIRTUAL_CALL(_converts_to, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool EditorResourceConversionPlugin::handles(const Ref<Resource> &p_resource) const {
|
||||
bool ret = false;
|
||||
GDVIRTUAL_CALL(_handles, p_resource, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Ref<Resource> EditorResourceConversionPlugin::convert(const Ref<Resource> &p_resource) const {
|
||||
Ref<Resource> ret;
|
||||
GDVIRTUAL_CALL(_convert, p_resource, ret);
|
||||
return ret;
|
||||
}
|
||||
53
engine/editor/plugins/editor_resource_conversion_plugin.h
Normal file
53
engine/editor/plugins/editor_resource_conversion_plugin.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/**************************************************************************/
|
||||
/* editor_resource_conversion_plugin.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef EDITOR_RESOURCE_CONVERSION_PLUGIN_H
|
||||
#define EDITOR_RESOURCE_CONVERSION_PLUGIN_H
|
||||
|
||||
#include "core/io/resource.h"
|
||||
#include "core/object/gdvirtual.gen.inc"
|
||||
|
||||
class EditorResourceConversionPlugin : public RefCounted {
|
||||
GDCLASS(EditorResourceConversionPlugin, RefCounted);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
GDVIRTUAL0RC(String, _converts_to)
|
||||
GDVIRTUAL1RC(bool, _handles, Ref<Resource>)
|
||||
GDVIRTUAL1RC(Ref<Resource>, _convert, Ref<Resource>)
|
||||
|
||||
public:
|
||||
virtual String converts_to() const;
|
||||
virtual bool handles(const Ref<Resource> &p_resource) const;
|
||||
virtual Ref<Resource> convert(const Ref<Resource> &p_resource) const;
|
||||
};
|
||||
|
||||
#endif // EDITOR_RESOURCE_CONVERSION_PLUGIN_H
|
||||
146
engine/editor/plugins/editor_resource_tooltip_plugins.cpp
Normal file
146
engine/editor/plugins/editor_resource_tooltip_plugins.cpp
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/**************************************************************************/
|
||||
/* 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_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 = Object::cast_to<TextureRect>(ObjectDB::get_instance(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);
|
||||
}
|
||||
|
||||
{
|
||||
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(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;
|
||||
}
|
||||
77
engine/editor/plugins/editor_resource_tooltip_plugins.h
Normal file
77
engine/editor/plugins/editor_resource_tooltip_plugins.h
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef EDITOR_RESOURCE_TOOLTIP_PLUGINS_H
|
||||
#define EDITOR_RESOURCE_TOOLTIP_PLUGINS_H
|
||||
|
||||
#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;
|
||||
};
|
||||
|
||||
#endif // EDITOR_RESOURCE_TOOLTIP_PLUGINS_H
|
||||
1089
engine/editor/plugins/font_config_plugin.cpp
Normal file
1089
engine/editor/plugins/font_config_plugin.cpp
Normal file
File diff suppressed because it is too large
Load diff
287
engine/editor/plugins/font_config_plugin.h
Normal file
287
engine/editor/plugins/font_config_plugin.h
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef FONT_CONFIG_PLUGIN_H
|
||||
#define FONT_CONFIG_PLUGIN_H
|
||||
|
||||
#include "core/io/marshalls.h"
|
||||
#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;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_dict(const Dictionary &p_dict);
|
||||
Dictionary get_dict();
|
||||
|
||||
EditorPropertyFontMetaObject(){};
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
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();
|
||||
|
||||
EditorPropertyFontOTObject(){};
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
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);
|
||||
|
||||
FontPreview();
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
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_name() const override { return "Font"; }
|
||||
};
|
||||
|
||||
#endif // FONT_CONFIG_PLUGIN_H
|
||||
139
engine/editor/plugins/gdextension_export_plugin.h
Normal file
139
engine/editor/plugins/gdextension_export_plugin.h
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef GDEXTENSION_EXPORT_PLUGIN_H
|
||||
#define GDEXTENSION_EXPORT_PLUGIN_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("ppc32");
|
||||
all_archs.insert("ppc64");
|
||||
all_archs.insert("wasm32");
|
||||
all_archs.insert("universal");
|
||||
|
||||
HashSet<String> archs;
|
||||
HashSet<String> features_wo_arch;
|
||||
for (const String &tag : p_features) {
|
||||
if (all_archs.has(tag)) {
|
||||
archs.insert(tag);
|
||||
} else {
|
||||
features_wo_arch.insert(tag);
|
||||
}
|
||||
}
|
||||
|
||||
if (archs.is_empty()) {
|
||||
archs.insert("unknown_arch"); // Not archs specified, still try to match.
|
||||
}
|
||||
|
||||
HashSet<String> libs_added;
|
||||
|
||||
for (const String &arch_tag : archs) {
|
||||
PackedStringArray tags;
|
||||
String library_path = GDExtension::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("ios") && (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_ios_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_ios_init_callback($ENTRY_init);\n"
|
||||
" }\n"
|
||||
"};\n"
|
||||
"$ENTRY_struct $ENTRY_struct_instance;\n\n";
|
||||
additional_code = additional_code.replace("$ENTRY", entry_symbol);
|
||||
add_ios_cpp_code(additional_code);
|
||||
|
||||
String linker_flags = "-Wl,-U,_" + entry_symbol;
|
||||
add_ios_linker_flags(linker_flags);
|
||||
}
|
||||
} else {
|
||||
Vector<String> features_vector;
|
||||
for (const String &E : p_features) {
|
||||
features_vector.append(E);
|
||||
}
|
||||
ERR_FAIL_MSG(vformat("No suitable library found for GDExtension: %s. Possible feature flags for your platform: %s", p_path, String(", ").join(features_vector)));
|
||||
}
|
||||
|
||||
Vector<SharedObject> dependencies_shared_objects = GDExtension::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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // GDEXTENSION_EXPORT_PLUGIN_H
|
||||
5
engine/editor/plugins/gizmos/SCsub
Normal file
5
engine/editor/plugins/gizmos/SCsub
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.editor_sources, "*.cpp")
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "editor/plugins/node_3d_editor_plugin.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);
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef AUDIO_LISTENER_3D_GIZMO_PLUGIN_H
|
||||
#define AUDIO_LISTENER_3D_GIZMO_PLUGIN_H
|
||||
|
||||
#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();
|
||||
};
|
||||
|
||||
#endif // AUDIO_LISTENER_3D_GIZMO_PLUGIN_H
|
||||
|
|
@ -0,0 +1,259 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "editor/plugins/node_3d_editor_plugin.h"
|
||||
#include "scene/3d/audio_stream_player_3d.h"
|
||||
|
||||
AudioStreamPlayer3DGizmoPlugin::AudioStreamPlayer3DGizmoPlugin() {
|
||||
Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/stream_player_3d", Color(0.4, 0.8, 1));
|
||||
|
||||
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 r;
|
||||
if (player->get_max_distance() > CMP_EPSILON) {
|
||||
r = MIN(player->get_unit_size() * soft_multiplier, player->get_max_distance());
|
||||
} else {
|
||||
r = player->get_unit_size() * soft_multiplier;
|
||||
}
|
||||
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 a billboarded circle.
|
||||
points_billboard.push_back(Vector3(a.x, a.y, 0));
|
||||
points_billboard.push_back(Vector3(b.x, b.y, 0));
|
||||
}
|
||||
|
||||
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 pc = player->get_emission_angle();
|
||||
const float ofs = -Math::cos(Math::deg_to_rad(pc));
|
||||
const float radius = Math::sin(Math::deg_to_rad(pc));
|
||||
|
||||
Vector<Vector3> points_primary;
|
||||
points_primary.resize(200);
|
||||
|
||||
real_t step = Math_TAU / 100.0;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
const float a = i * step;
|
||||
const float an = (i + 1) * step;
|
||||
|
||||
const Vector3 from(Math::sin(a) * radius, Math::cos(a) * radius, ofs);
|
||||
const Vector3 to(Math::sin(an) * radius, Math::cos(an) * radius, ofs);
|
||||
|
||||
points_primary.write[i * 2 + 0] = from;
|
||||
points_primary.write[i * 2 + 1] = to;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
const float a = i * (Math_TAU / 8.0);
|
||||
const Vector3 from(Math::sin(a) * radius, Math::cos(a) * radius, ofs);
|
||||
|
||||
points_secondary.write[i * 2 + 0] = from;
|
||||
points_secondary.write[i * 2 + 1] = Vector3();
|
||||
}
|
||||
|
||||
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;
|
||||
const float ha = Math::deg_to_rad(player->get_emission_angle());
|
||||
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);
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef AUDIO_STREAM_PLAYER_3D_GIZMO_PLUGIN_H
|
||||
#define AUDIO_STREAM_PLAYER_3D_GIZMO_PLUGIN_H
|
||||
|
||||
#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();
|
||||
};
|
||||
|
||||
#endif // AUDIO_STREAM_PLAYER_3D_GIZMO_PLUGIN_H
|
||||
295
engine/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp
Normal file
295
engine/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "core/config/project_settings.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_DEF_RST("editors/3d_gizmos/gizmo_colors/camera", Color(0.8, 0.4, 0.8));
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
Size2i Camera3DGizmoPlugin::_get_viewport_size(Camera3D *p_camera) {
|
||||
Viewport *viewport = p_camera->get_viewport();
|
||||
|
||||
Window *window = Object::cast_to<Window>(viewport);
|
||||
if (window) {
|
||||
return window->get_size();
|
||||
}
|
||||
|
||||
SubViewport *sub_viewport = Object::cast_to<SubViewport>(viewport);
|
||||
ERR_FAIL_NULL_V(sub_viewport, Size2i());
|
||||
|
||||
if (sub_viewport == EditorNode::get_singleton()->get_scene_root()) {
|
||||
return Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height"));
|
||||
}
|
||||
|
||||
return sub_viewport->get_size();
|
||||
}
|
||||
|
||||
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 {
|
||||
Vector3 ra, rb;
|
||||
Geometry3D::get_closest_points_between_segments(Vector3(0, 0, -1), Vector3(4096, 0, -1), s[0], s[1], ra, rb);
|
||||
float d = ra.x * 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 = _get_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 = Vector3(hsize * size_factor.x, 0, depth);
|
||||
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: {
|
||||
float size = camera->get_size();
|
||||
|
||||
float hsize = size * 0.5;
|
||||
Vector3 right(hsize * size_factor.x, 0, 0);
|
||||
Vector3 up(0, hsize * size_factor.y, 0);
|
||||
Vector3 back(0, 0, -1.0);
|
||||
Vector3 front(0, 0, 0);
|
||||
|
||||
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);
|
||||
|
||||
handles.push_back(right + back);
|
||||
|
||||
right.x = MIN(right.x, hsize * 0.25);
|
||||
Vector3 tup(0, up.y + hsize / 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);
|
||||
}
|
||||
57
engine/editor/plugins/gizmos/camera_3d_gizmo_plugin.h
Normal file
57
engine/editor/plugins/gizmos/camera_3d_gizmo_plugin.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CAMERA_3D_GIZMO_PLUGIN_H
|
||||
#define CAMERA_3D_GIZMO_PLUGIN_H
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class Camera3DGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(Camera3DGizmoPlugin, EditorNode3DGizmoPlugin);
|
||||
|
||||
private:
|
||||
static Size2i _get_viewport_size(Camera3D *p_camera);
|
||||
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();
|
||||
};
|
||||
|
||||
#endif // CAMERA_3D_GIZMO_PLUGIN_H
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "editor/editor_settings.h"
|
||||
#include "editor/plugins/node_3d_editor_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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef COLLISION_OBJECT_3D_GIZMO_PLUGIN_H
|
||||
#define COLLISION_OBJECT_3D_GIZMO_PLUGIN_H
|
||||
|
||||
#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();
|
||||
};
|
||||
|
||||
#endif // COLLISION_OBJECT_3D_GIZMO_PLUGIN_H
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "editor/editor_settings.h"
|
||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||
#include "scene/3d/physics/collision_polygon_3d.h"
|
||||
|
||||
CollisionPolygon3DGizmoPlugin::CollisionPolygon3DGizmoPlugin() {
|
||||
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 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();
|
||||
|
||||
Vector<Vector2> points = polygon->get_polygon();
|
||||
float depth = polygon->get_depth() * 0.5;
|
||||
|
||||
Vector<Vector3> lines;
|
||||
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));
|
||||
}
|
||||
|
||||
const Ref<Material> material =
|
||||
get_material(!polygon->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo);
|
||||
|
||||
p_gizmo->add_lines(lines, material);
|
||||
p_gizmo->add_collision_segments(lines);
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef COLLISION_POLYGON_3D_GIZMO_PLUGIN_H
|
||||
#define COLLISION_POLYGON_3D_GIZMO_PLUGIN_H
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class CollisionPolygon3DGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(CollisionPolygon3DGizmoPlugin, 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;
|
||||
CollisionPolygon3DGizmoPlugin();
|
||||
};
|
||||
|
||||
#endif // COLLISION_POLYGON_3D_GIZMO_PLUGIN_H
|
||||
623
engine/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp
Normal file
623
engine/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp
Normal file
|
|
@ -0,0 +1,623 @@
|
|||
/**************************************************************************/
|
||||
/* 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_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/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();
|
||||
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);
|
||||
create_handle_material("handles");
|
||||
}
|
||||
|
||||
CollisionShape3DGizmoPlugin::~CollisionShape3DGizmoPlugin() {
|
||||
}
|
||||
|
||||
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 p_id == 0 ? "Radius" : "Height";
|
||||
}
|
||||
|
||||
if (Object::cast_to<CylinderShape3D>(*s)) {
|
||||
return p_id == 0 ? "Radius" : "Height";
|
||||
}
|
||||
|
||||
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 p_id == 0 ? 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)) {
|
||||
Vector3 axis;
|
||||
axis[p_id == 0 ? 0 : 1] = 1.0;
|
||||
Ref<CapsuleShape3D> cs2 = s;
|
||||
Vector3 ra, rb;
|
||||
Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
|
||||
float d = axis.dot(ra);
|
||||
|
||||
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
|
||||
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
|
||||
}
|
||||
|
||||
if (d < 0.001) {
|
||||
d = 0.001;
|
||||
}
|
||||
|
||||
if (p_id == 0) {
|
||||
cs2->set_radius(d);
|
||||
} else if (p_id == 1) {
|
||||
cs2->set_height(d * 2.0);
|
||||
}
|
||||
}
|
||||
|
||||
if (Object::cast_to<CylinderShape3D>(*s)) {
|
||||
Vector3 axis;
|
||||
axis[p_id == 0 ? 0 : 1] = 1.0;
|
||||
Ref<CylinderShape3D> cs2 = s;
|
||||
Vector3 ra, rb;
|
||||
Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
|
||||
float d = axis.dot(ra);
|
||||
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
|
||||
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
|
||||
}
|
||||
|
||||
if (d < 0.001) {
|
||||
d = 0.001;
|
||||
}
|
||||
|
||||
if (p_id == 0) {
|
||||
cs2->set_radius(d);
|
||||
} else if (p_id == 1) {
|
||||
cs2->set_height(d * 2.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
Vector2 values = p_restore;
|
||||
|
||||
if (p_cancel) {
|
||||
ss->set_radius(values[0]);
|
||||
ss->set_height(values[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
if (p_id == 0) {
|
||||
ur->create_action(TTR("Change Capsule Shape Radius"));
|
||||
ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
|
||||
} else {
|
||||
ur->create_action(TTR("Change Capsule Shape Height"));
|
||||
ur->add_do_method(ss.ptr(), "set_height", ss->get_height());
|
||||
}
|
||||
ur->add_undo_method(ss.ptr(), "set_radius", values[0]);
|
||||
ur->add_undo_method(ss.ptr(), "set_height", values[1]);
|
||||
|
||||
ur->commit_action();
|
||||
}
|
||||
|
||||
if (Object::cast_to<CylinderShape3D>(*s)) {
|
||||
Ref<CylinderShape3D> ss = s;
|
||||
if (p_cancel) {
|
||||
if (p_id == 0) {
|
||||
ss->set_radius(p_restore);
|
||||
} else {
|
||||
ss->set_height(p_restore);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
if (p_id == 0) {
|
||||
ur->create_action(TTR("Change Cylinder Shape Radius"));
|
||||
ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
|
||||
ur->add_undo_method(ss.ptr(), "set_radius", p_restore);
|
||||
} else {
|
||||
ur->create_action(
|
||||
///
|
||||
|
||||
////////
|
||||
TTR("Change Cylinder Shape Height"));
|
||||
ur->add_do_method(ss.ptr(), "set_height", ss->get_height());
|
||||
ur->add_undo_method(ss.ptr(), "set_height", p_restore);
|
||||
}
|
||||
|
||||
ur->commit_action();
|
||||
}
|
||||
|
||||
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<Material> material =
|
||||
get_material(!cs->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo);
|
||||
Ref<Material> handles_material = get_material("handles");
|
||||
|
||||
if (Object::cast_to<SphereShape3D>(*s)) {
|
||||
Ref<SphereShape3D> sp = s;
|
||||
float r = sp->get_radius();
|
||||
|
||||
Vector<Vector3> points;
|
||||
|
||||
for (int i = 0; i <= 360; i++) {
|
||||
float ra = Math::deg_to_rad((float)i);
|
||||
float rb = Math::deg_to_rad((float)i + 1);
|
||||
Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r;
|
||||
Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
Vector<Vector3> collision_segments;
|
||||
|
||||
for (int i = 0; i < 64; i++) {
|
||||
float ra = i * (Math_TAU / 64.0);
|
||||
float rb = (i + 1) * (Math_TAU / 64.0);
|
||||
Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r;
|
||||
Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r;
|
||||
|
||||
collision_segments.push_back(Vector3(a.x, 0, a.y));
|
||||
collision_segments.push_back(Vector3(b.x, 0, b.y));
|
||||
collision_segments.push_back(Vector3(0, a.x, a.y));
|
||||
collision_segments.push_back(Vector3(0, b.x, b.y));
|
||||
collision_segments.push_back(Vector3(a.x, a.y, 0));
|
||||
collision_segments.push_back(Vector3(b.x, b.y, 0));
|
||||
}
|
||||
|
||||
p_gizmo->add_lines(points, material);
|
||||
p_gizmo->add_collision_segments(collision_segments);
|
||||
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);
|
||||
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();
|
||||
|
||||
Vector<Vector3> points;
|
||||
|
||||
Vector3 d(0, height * 0.5 - radius, 0);
|
||||
for (int i = 0; i < 360; i++) {
|
||||
float ra = Math::deg_to_rad((float)i);
|
||||
float rb = Math::deg_to_rad((float)i + 1);
|
||||
Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius;
|
||||
Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius;
|
||||
|
||||
points.push_back(Vector3(a.x, 0, a.y) + d);
|
||||
points.push_back(Vector3(b.x, 0, b.y) + d);
|
||||
|
||||
points.push_back(Vector3(a.x, 0, a.y) - d);
|
||||
points.push_back(Vector3(b.x, 0, b.y) - d);
|
||||
|
||||
if (i % 90 == 0) {
|
||||
points.push_back(Vector3(a.x, 0, a.y) + d);
|
||||
points.push_back(Vector3(a.x, 0, a.y) - d);
|
||||
}
|
||||
|
||||
Vector3 dud = i < 180 ? d : -d;
|
||||
|
||||
points.push_back(Vector3(0, a.x, a.y) + dud);
|
||||
points.push_back(Vector3(0, b.x, b.y) + dud);
|
||||
points.push_back(Vector3(a.y, a.x, 0) + dud);
|
||||
points.push_back(Vector3(b.y, b.x, 0) + dud);
|
||||
}
|
||||
|
||||
p_gizmo->add_lines(points, material);
|
||||
|
||||
Vector<Vector3> collision_segments;
|
||||
|
||||
for (int i = 0; i < 64; i++) {
|
||||
float ra = i * (Math_TAU / 64.0);
|
||||
float rb = (i + 1) * (Math_TAU / 64.0);
|
||||
Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius;
|
||||
Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius;
|
||||
|
||||
collision_segments.push_back(Vector3(a.x, 0, a.y) + d);
|
||||
collision_segments.push_back(Vector3(b.x, 0, b.y) + d);
|
||||
|
||||
collision_segments.push_back(Vector3(a.x, 0, a.y) - d);
|
||||
collision_segments.push_back(Vector3(b.x, 0, b.y) - d);
|
||||
|
||||
if (i % 16 == 0) {
|
||||
collision_segments.push_back(Vector3(a.x, 0, a.y) + d);
|
||||
collision_segments.push_back(Vector3(a.x, 0, a.y) - d);
|
||||
}
|
||||
|
||||
Vector3 dud = i < 32 ? d : -d;
|
||||
|
||||
collision_segments.push_back(Vector3(0, a.x, a.y) + dud);
|
||||
collision_segments.push_back(Vector3(0, b.x, b.y) + dud);
|
||||
collision_segments.push_back(Vector3(a.y, a.x, 0) + dud);
|
||||
collision_segments.push_back(Vector3(b.y, b.x, 0) + dud);
|
||||
}
|
||||
|
||||
p_gizmo->add_collision_segments(collision_segments);
|
||||
|
||||
Vector<Vector3> handles = {
|
||||
Vector3(cs2->get_radius(), 0, 0),
|
||||
Vector3(0, cs2->get_height() * 0.5, 0)
|
||||
};
|
||||
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();
|
||||
|
||||
Vector<Vector3> points;
|
||||
|
||||
Vector3 d(0, height * 0.5, 0);
|
||||
for (int i = 0; i < 360; i++) {
|
||||
float ra = Math::deg_to_rad((float)i);
|
||||
float rb = Math::deg_to_rad((float)i + 1);
|
||||
Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius;
|
||||
Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius;
|
||||
|
||||
points.push_back(Vector3(a.x, 0, a.y) + d);
|
||||
points.push_back(Vector3(b.x, 0, b.y) + d);
|
||||
|
||||
points.push_back(Vector3(a.x, 0, a.y) - d);
|
||||
points.push_back(Vector3(b.x, 0, b.y) - d);
|
||||
|
||||
if (i % 90 == 0) {
|
||||
points.push_back(Vector3(a.x, 0, a.y) + d);
|
||||
points.push_back(Vector3(a.x, 0, a.y) - d);
|
||||
}
|
||||
}
|
||||
|
||||
p_gizmo->add_lines(points, material);
|
||||
|
||||
Vector<Vector3> collision_segments;
|
||||
|
||||
for (int i = 0; i < 64; i++) {
|
||||
float ra = i * (Math_TAU / 64.0);
|
||||
float rb = (i + 1) * (Math_TAU / 64.0);
|
||||
Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius;
|
||||
Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius;
|
||||
|
||||
collision_segments.push_back(Vector3(a.x, 0, a.y) + d);
|
||||
collision_segments.push_back(Vector3(b.x, 0, b.y) + d);
|
||||
|
||||
collision_segments.push_back(Vector3(a.x, 0, a.y) - d);
|
||||
collision_segments.push_back(Vector3(b.x, 0, b.y) - d);
|
||||
|
||||
if (i % 16 == 0) {
|
||||
collision_segments.push_back(Vector3(a.x, 0, a.y) + d);
|
||||
collision_segments.push_back(Vector3(a.x, 0, a.y) - d);
|
||||
}
|
||||
}
|
||||
|
||||
p_gizmo->add_collision_segments(collision_segments);
|
||||
|
||||
Vector<Vector3> handles = {
|
||||
Vector3(cs2->get_radius(), 0, 0),
|
||||
Vector3(0, cs2->get_height() * 0.5, 0)
|
||||
};
|
||||
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);
|
||||
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() > 3) {
|
||||
Vector<Vector3> varr = Variant(points);
|
||||
Geometry3D::MeshData md;
|
||||
Error err = ConvexHullComputer::convex_hull(varr, md);
|
||||
if (err == OK) {
|
||||
Vector<Vector3> points2;
|
||||
points2.resize(md.edges.size() * 2);
|
||||
for (uint32_t i = 0; i < md.edges.size(); i++) {
|
||||
points2.write[i * 2 + 0] = md.vertices[md.edges[i].vertex_a];
|
||||
points2.write[i * 2 + 1] = md.vertices[md.edges[i].vertex_b];
|
||||
}
|
||||
|
||||
p_gizmo->add_lines(points2, material);
|
||||
p_gizmo->add_collision_segments(points2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Object::cast_to<ConcavePolygonShape3D>(*s)) {
|
||||
Ref<ConcavePolygonShape3D> cs2 = s;
|
||||
Ref<ArrayMesh> mesh = cs2->get_debug_mesh();
|
||||
p_gizmo->add_mesh(mesh, material);
|
||||
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);
|
||||
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;
|
||||
|
||||
Ref<ArrayMesh> mesh = hms->get_debug_mesh();
|
||||
p_gizmo->add_mesh(mesh, material);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef COLLISION_SHAPE_3D_GIZMO_PLUGIN_H
|
||||
#define COLLISION_SHAPE_3D_GIZMO_PLUGIN_H
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class Gizmo3DHelper;
|
||||
|
||||
class CollisionShape3DGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(CollisionShape3DGizmoPlugin, 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;
|
||||
|
||||
CollisionShape3DGizmoPlugin();
|
||||
~CollisionShape3DGizmoPlugin();
|
||||
};
|
||||
|
||||
#endif // COLLISION_SHAPE_3D_GIZMO_PLUGIN_H
|
||||
108
engine/editor/plugins/gizmos/cpu_particles_3d_gizmo_plugin.cpp
Normal file
108
engine/editor/plugins/gizmos/cpu_particles_3d_gizmo_plugin.cpp
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "editor/plugins/node_3d_editor_plugin.h"
|
||||
#include "scene/3d/cpu_particles_3d.h"
|
||||
|
||||
CPUParticles3DGizmoPlugin::CPUParticles3DGizmoPlugin() {
|
||||
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4));
|
||||
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);
|
||||
}
|
||||
48
engine/editor/plugins/gizmos/cpu_particles_3d_gizmo_plugin.h
Normal file
48
engine/editor/plugins/gizmos/cpu_particles_3d_gizmo_plugin.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CPU_PARTICLES_3D_GIZMO_PLUGIN_H
|
||||
#define CPU_PARTICLES_3D_GIZMO_PLUGIN_H
|
||||
|
||||
#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();
|
||||
};
|
||||
|
||||
#endif // CPU_PARTICLES_3D_GIZMO_PLUGIN_H
|
||||
136
engine/editor/plugins/gizmos/decal_gizmo_plugin.cpp
Normal file
136
engine/editor/plugins/gizmos/decal_gizmo_plugin.cpp
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
/**************************************************************************/
|
||||
/* 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/editor_undo_redo_manager.h"
|
||||
#include "editor/plugins/gizmos/gizmo_3d_helper.h"
|
||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||
#include "scene/3d/decal.h"
|
||||
|
||||
DecalGizmoPlugin::DecalGizmoPlugin() {
|
||||
helper.instantiate();
|
||||
Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/decal", Color(0.6, 0.5, 1.0));
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
DecalGizmoPlugin::~DecalGizmoPlugin() {
|
||||
}
|
||||
|
||||
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"));
|
||||
}
|
||||
59
engine/editor/plugins/gizmos/decal_gizmo_plugin.h
Normal file
59
engine/editor/plugins/gizmos/decal_gizmo_plugin.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DECAL_GIZMO_PLUGIN_H
|
||||
#define DECAL_GIZMO_PLUGIN_H
|
||||
|
||||
#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();
|
||||
~DecalGizmoPlugin();
|
||||
};
|
||||
|
||||
#endif // DECAL_GIZMO_PLUGIN_H
|
||||
130
engine/editor/plugins/gizmos/fog_volume_gizmo_plugin.cpp
Normal file
130
engine/editor/plugins/gizmos/fog_volume_gizmo_plugin.cpp
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
/**************************************************************************/
|
||||
/* 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/editor_undo_redo_manager.h"
|
||||
#include "editor/plugins/gizmos/gizmo_3d_helper.h"
|
||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||
#include "scene/3d/fog_volume.h"
|
||||
|
||||
FogVolumeGizmoPlugin::FogVolumeGizmoPlugin() {
|
||||
helper.instantiate();
|
||||
Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/fog_volume", Color(0.5, 0.7, 1));
|
||||
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");
|
||||
}
|
||||
|
||||
FogVolumeGizmoPlugin::~FogVolumeGizmoPlugin() {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
59
engine/editor/plugins/gizmos/fog_volume_gizmo_plugin.h
Normal file
59
engine/editor/plugins/gizmos/fog_volume_gizmo_plugin.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef FOG_VOLUME_GIZMO_PLUGIN_H
|
||||
#define FOG_VOLUME_GIZMO_PLUGIN_H
|
||||
|
||||
#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();
|
||||
~FogVolumeGizmoPlugin();
|
||||
};
|
||||
|
||||
#endif // FOG_VOLUME_GIZMO_PLUGIN_H
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/**************************************************************************/
|
||||
/* 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_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||
#include "scene/3d/visual_instance_3d.h"
|
||||
|
||||
GeometryInstance3DGizmoPlugin::GeometryInstance3DGizmoPlugin() {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef GEOMETRY_INSTANCE_3D_GIZMO_PLUGIN_H
|
||||
#define GEOMETRY_INSTANCE_3D_GIZMO_PLUGIN_H
|
||||
|
||||
#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;
|
||||
|
||||
GeometryInstance3DGizmoPlugin();
|
||||
};
|
||||
|
||||
#endif // GEOMETRY_INSTANCE_3D_GIZMO_PLUGIN_H
|
||||
141
engine/editor/plugins/gizmos/gizmo_3d_helper.cpp
Normal file
141
engine/editor/plugins/gizmos/gizmo_3d_helper.cpp
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/**************************************************************************/
|
||||
/* 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();
|
||||
}
|
||||
55
engine/editor/plugins/gizmos/gizmo_3d_helper.h
Normal file
55
engine/editor/plugins/gizmos/gizmo_3d_helper.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef GIZMO_3D_HELPER_H
|
||||
#define GIZMO_3D_HELPER_H
|
||||
|
||||
#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;
|
||||
|
||||
public:
|
||||
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);
|
||||
|
||||
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");
|
||||
};
|
||||
|
||||
#endif // GIZMO_3D_HELPER_H
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||
#include "scene/3d/gpu_particles_3d.h"
|
||||
|
||||
GPUParticles3DGizmoPlugin::GPUParticles3DGizmoPlugin() {
|
||||
Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4));
|
||||
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);
|
||||
}
|
||||
49
engine/editor/plugins/gizmos/gpu_particles_3d_gizmo_plugin.h
Normal file
49
engine/editor/plugins/gizmos/gpu_particles_3d_gizmo_plugin.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef GPU_PARTICLES_3D_GIZMO_PLUGIN_H
|
||||
#define GPU_PARTICLES_3D_GIZMO_PLUGIN_H
|
||||
|
||||
#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();
|
||||
};
|
||||
|
||||
#endif // GPU_PARTICLES_3D_GIZMO_PLUGIN_H
|
||||
|
|
@ -0,0 +1,273 @@
|
|||
/**************************************************************************/
|
||||
/* 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_DEF_RST("editors/3d_gizmos/gizmo_colors/particle_attractor", Color(1, 0.7, 0.5));
|
||||
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_DEF_RST("editors/3d_gizmos/gizmo_colors/particle_collision", Color(0.5, 0.7, 1));
|
||||
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");
|
||||
}
|
||||
|
||||
GPUParticlesCollision3DGizmoPlugin::~GPUParticlesCollision3DGizmoPlugin() {
|
||||
}
|
||||
|
||||
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 r = cs->call("get_radius");
|
||||
|
||||
Vector<Vector3> points;
|
||||
|
||||
for (int i = 0; i <= 360; i++) {
|
||||
float ra = Math::deg_to_rad((float)i);
|
||||
float rb = Math::deg_to_rad((float)i + 1);
|
||||
Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r;
|
||||
Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
Vector<Vector3> collision_segments;
|
||||
|
||||
for (int i = 0; i < 64; i++) {
|
||||
float ra = i * (Math_TAU / 64.0);
|
||||
float rb = (i + 1) * (Math_TAU / 64.0);
|
||||
Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r;
|
||||
Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r;
|
||||
|
||||
collision_segments.push_back(Vector3(a.x, 0, a.y));
|
||||
collision_segments.push_back(Vector3(b.x, 0, b.y));
|
||||
collision_segments.push_back(Vector3(0, a.x, a.y));
|
||||
collision_segments.push_back(Vector3(0, b.x, b.y));
|
||||
collision_segments.push_back(Vector3(a.x, a.y, 0));
|
||||
collision_segments.push_back(Vector3(b.x, b.y, 0));
|
||||
}
|
||||
|
||||
p_gizmo->add_lines(points, material);
|
||||
p_gizmo->add_collision_segments(collision_segments);
|
||||
Vector<Vector3> handles;
|
||||
handles.push_back(Vector3(r, 0, 0));
|
||||
p_gizmo->add_handles(handles, handles_material);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef GPU_PARTICLES_COLLISION_3D_GIZMO_PLUGIN_H
|
||||
#define GPU_PARTICLES_COLLISION_3D_GIZMO_PLUGIN_H
|
||||
|
||||
#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();
|
||||
~GPUParticlesCollision3DGizmoPlugin();
|
||||
};
|
||||
|
||||
#endif // GPU_PARTICLES_COLLISION_3D_GIZMO_PLUGIN_H
|
||||
718
engine/editor/plugins/gizmos/joint_3d_gizmo_plugin.cpp
Normal file
718
engine/editor/plugins/gizmos/joint_3d_gizmo_plugin.cpp
Normal file
|
|
@ -0,0 +1,718 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "editor/plugins/node_3d_editor_plugin.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_DEF_RST("editors/3d_gizmos/gizmo_colors/joint_body_a", Color(0.6, 0.8, 1)));
|
||||
create_material("joint_body_b_material", EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/joint_body_b", Color(0.6, 0.9, 1)));
|
||||
|
||||
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()) {
|
||||
update_idx++;
|
||||
update_idx = update_idx % current_gizmos.size();
|
||||
redraw(current_gizmos.get(update_idx));
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
99
engine/editor/plugins/gizmos/joint_3d_gizmo_plugin.h
Normal file
99
engine/editor/plugins/gizmos/joint_3d_gizmo_plugin.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef JOINT_3D_GIZMO_PLUGIN_H
|
||||
#define JOINT_3D_GIZMO_PLUGIN_H
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class Joint3DGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(Joint3DGizmoPlugin, EditorNode3DGizmoPlugin);
|
||||
|
||||
Timer *update_timer = nullptr;
|
||||
uint64_t update_idx = 0;
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
#endif // JOINT_3D_GIZMO_PLUGIN_H
|
||||
64
engine/editor/plugins/gizmos/label_3d_gizmo_plugin.cpp
Normal file
64
engine/editor/plugins/gizmos/label_3d_gizmo_plugin.cpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "editor/plugins/node_3d_editor_plugin.h"
|
||||
#include "scene/3d/label_3d.h"
|
||||
|
||||
Label3DGizmoPlugin::Label3DGizmoPlugin() {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
49
engine/editor/plugins/gizmos/label_3d_gizmo_plugin.h
Normal file
49
engine/editor/plugins/gizmos/label_3d_gizmo_plugin.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef LABEL_3D_GIZMO_PLUGIN_H
|
||||
#define LABEL_3D_GIZMO_PLUGIN_H
|
||||
|
||||
#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;
|
||||
|
||||
Label3DGizmoPlugin();
|
||||
};
|
||||
|
||||
#endif // LABEL_3D_GIZMO_PLUGIN_H
|
||||
318
engine/editor/plugins/gizmos/light_3d_gizmo_plugin.cpp
Normal file
318
engine/editor/plugins/gizmos/light_3d_gizmo_plugin.cpp
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "core/config/project_settings.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/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);
|
||||
}
|
||||
56
engine/editor/plugins/gizmos/light_3d_gizmo_plugin.h
Normal file
56
engine/editor/plugins/gizmos/light_3d_gizmo_plugin.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/**************************************************************************/
|
||||
/* light_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef LIGHT_3D_GIZMO_PLUGIN_H
|
||||
#define LIGHT_3D_GIZMO_PLUGIN_H
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class Light3DGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(Light3DGizmoPlugin, 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;
|
||||
|
||||
Light3DGizmoPlugin();
|
||||
};
|
||||
|
||||
#endif // LIGHT_3D_GIZMO_PLUGIN_H
|
||||
214
engine/editor/plugins/gizmos/lightmap_gi_gizmo_plugin.cpp
Normal file
214
engine/editor/plugins/gizmos/lightmap_gi_gizmo_plugin.cpp
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
/**************************************************************************/
|
||||
/* lightmap_gi_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 "lightmap_gi_gizmo_plugin.h"
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||
#include "scene/3d/lightmap_gi.h"
|
||||
|
||||
LightmapGIGizmoPlugin::LightmapGIGizmoPlugin() {
|
||||
Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/lightmap_lines", Color(0.5, 0.6, 1));
|
||||
|
||||
gizmo_color.a = 0.1;
|
||||
create_material("lightmap_lines", gizmo_color);
|
||||
|
||||
Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D);
|
||||
mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
|
||||
mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
|
||||
mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
|
||||
mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, false);
|
||||
mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
|
||||
|
||||
add_material("lightmap_probe_material", mat);
|
||||
|
||||
create_icon_material("baked_indirect_light_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoLightmapGI"), EditorStringName(EditorIcons)));
|
||||
}
|
||||
|
||||
bool LightmapGIGizmoPlugin::has_gizmo(Node3D *p_spatial) {
|
||||
return Object::cast_to<LightmapGI>(p_spatial) != nullptr;
|
||||
}
|
||||
|
||||
String LightmapGIGizmoPlugin::get_gizmo_name() const {
|
||||
return "LightmapGI";
|
||||
}
|
||||
|
||||
int LightmapGIGizmoPlugin::get_priority() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void LightmapGIGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
Ref<Material> icon = get_material("baked_indirect_light_icon", p_gizmo);
|
||||
LightmapGI *baker = Object::cast_to<LightmapGI>(p_gizmo->get_node_3d());
|
||||
Ref<LightmapGIData> data = baker->get_light_data();
|
||||
|
||||
p_gizmo->clear();
|
||||
|
||||
p_gizmo->add_unscaled_billboard(icon, 0.05);
|
||||
|
||||
if (data.is_null() || !p_gizmo->is_selected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<Material> material_lines = get_material("lightmap_lines", p_gizmo);
|
||||
Ref<Material> material_probes = get_material("lightmap_probe_material", p_gizmo);
|
||||
|
||||
Vector<Vector3> lines;
|
||||
HashSet<Vector2i> lines_found;
|
||||
|
||||
Vector<Vector3> points = data->get_capture_points();
|
||||
if (points.size() == 0) {
|
||||
return;
|
||||
}
|
||||
Vector<Color> sh = data->get_capture_sh();
|
||||
if (sh.size() != points.size() * 9) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector<int> tetrahedrons = data->get_capture_tetrahedra();
|
||||
|
||||
for (int i = 0; i < tetrahedrons.size(); i += 4) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int k = j + 1; k < 4; k++) {
|
||||
Vector2i pair;
|
||||
pair.x = tetrahedrons[i + j];
|
||||
pair.y = tetrahedrons[i + k];
|
||||
|
||||
if (pair.y < pair.x) {
|
||||
SWAP(pair.x, pair.y);
|
||||
}
|
||||
if (lines_found.has(pair)) {
|
||||
continue;
|
||||
}
|
||||
lines_found.insert(pair);
|
||||
lines.push_back(points[pair.x]);
|
||||
lines.push_back(points[pair.y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p_gizmo->add_lines(lines, material_lines);
|
||||
|
||||
int stack_count = 8;
|
||||
int sector_count = 16;
|
||||
|
||||
float sector_step = (Math_PI * 2.0) / sector_count;
|
||||
float stack_step = Math_PI / stack_count;
|
||||
|
||||
Vector<Vector3> vertices;
|
||||
Vector<Color> colors;
|
||||
Vector<int> indices;
|
||||
float radius = 0.3;
|
||||
|
||||
for (int p = 0; p < points.size(); p++) {
|
||||
int vertex_base = vertices.size();
|
||||
Vector3 sh_col[9];
|
||||
for (int i = 0; i < 9; i++) {
|
||||
sh_col[i].x = sh[p * 9 + i].r;
|
||||
sh_col[i].y = sh[p * 9 + i].g;
|
||||
sh_col[i].z = sh[p * 9 + i].b;
|
||||
}
|
||||
|
||||
for (int i = 0; i <= stack_count; ++i) {
|
||||
float stack_angle = Math_PI / 2 - i * stack_step; // starting from pi/2 to -pi/2
|
||||
float xy = radius * Math::cos(stack_angle); // r * cos(u)
|
||||
float z = radius * Math::sin(stack_angle); // r * sin(u)
|
||||
|
||||
// add (sector_count+1) vertices per stack
|
||||
// the first and last vertices have same position and normal, but different tex coords
|
||||
for (int j = 0; j <= sector_count; ++j) {
|
||||
float sector_angle = j * sector_step; // starting from 0 to 2pi
|
||||
|
||||
// vertex position (x, y, z)
|
||||
float x = xy * Math::cos(sector_angle); // r * cos(u) * cos(v)
|
||||
float y = xy * Math::sin(sector_angle); // r * cos(u) * sin(v)
|
||||
|
||||
Vector3 n = Vector3(x, z, y);
|
||||
vertices.push_back(points[p] + n);
|
||||
n.normalize();
|
||||
|
||||
const float c1 = 0.429043;
|
||||
const float c2 = 0.511664;
|
||||
const float c3 = 0.743125;
|
||||
const float c4 = 0.886227;
|
||||
const float c5 = 0.247708;
|
||||
Vector3 light = (c1 * sh_col[8] * (n.x * n.x - n.y * n.y) +
|
||||
c3 * sh_col[6] * n.z * n.z +
|
||||
c4 * sh_col[0] -
|
||||
c5 * sh_col[6] +
|
||||
2.0 * c1 * sh_col[4] * n.x * n.y +
|
||||
2.0 * c1 * sh_col[7] * n.x * n.z +
|
||||
2.0 * c1 * sh_col[5] * n.y * n.z +
|
||||
2.0 * c2 * sh_col[3] * n.x +
|
||||
2.0 * c2 * sh_col[1] * n.y +
|
||||
2.0 * c2 * sh_col[2] * n.z);
|
||||
|
||||
colors.push_back(Color(light.x, light.y, light.z, 1));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < stack_count; ++i) {
|
||||
int k1 = i * (sector_count + 1); // beginning of current stack
|
||||
int k2 = k1 + sector_count + 1; // beginning of next stack
|
||||
|
||||
for (int j = 0; j < sector_count; ++j, ++k1, ++k2) {
|
||||
// 2 triangles per sector excluding first and last stacks
|
||||
// k1 => k2 => k1+1
|
||||
if (i != 0) {
|
||||
indices.push_back(vertex_base + k1);
|
||||
indices.push_back(vertex_base + k2);
|
||||
indices.push_back(vertex_base + k1 + 1);
|
||||
}
|
||||
|
||||
// k1+1 => k2 => k2+1
|
||||
if (i != (stack_count - 1)) {
|
||||
indices.push_back(vertex_base + k1 + 1);
|
||||
indices.push_back(vertex_base + k2);
|
||||
indices.push_back(vertex_base + k2 + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Array array;
|
||||
array.resize(RS::ARRAY_MAX);
|
||||
array[RS::ARRAY_VERTEX] = vertices;
|
||||
array[RS::ARRAY_INDEX] = indices;
|
||||
array[RS::ARRAY_COLOR] = colors;
|
||||
|
||||
Ref<ArrayMesh> mesh;
|
||||
mesh.instantiate();
|
||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array, Array(), Dictionary(), 0); //no compression
|
||||
mesh->surface_set_material(0, material_probes);
|
||||
|
||||
p_gizmo->add_mesh(mesh);
|
||||
}
|
||||
48
engine/editor/plugins/gizmos/lightmap_gi_gizmo_plugin.h
Normal file
48
engine/editor/plugins/gizmos/lightmap_gi_gizmo_plugin.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/**************************************************************************/
|
||||
/* lightmap_gi_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef LIGHTMAP_GI_GIZMO_PLUGIN_H
|
||||
#define LIGHTMAP_GI_GIZMO_PLUGIN_H
|
||||
|
||||
#include "editor/plugins/node_3d_editor_gizmos.h"
|
||||
|
||||
class LightmapGIGizmoPlugin : public EditorNode3DGizmoPlugin {
|
||||
GDCLASS(LightmapGIGizmoPlugin, 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;
|
||||
|
||||
LightmapGIGizmoPlugin();
|
||||
};
|
||||
|
||||
#endif // LIGHTMAP_GI_GIZMO_PLUGIN_H
|
||||
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