initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

9
editor/scene/3d/SCsub Normal file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env.add_source_files(env.editor_sources, "*.cpp")
SConscript("gizmos/SCsub")
SConscript("physics/SCsub")

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,223 @@
/**************************************************************************/
/* bone_map_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/editor_node.h"
#include "editor/inspector/editor_properties.h"
#include "editor/plugins/editor_plugin.h"
#include "scene/3d/skeleton_3d.h"
#include "scene/gui/box_container.h"
#include "scene/gui/color_rect.h"
#include "scene/gui/dialogs.h"
#include "scene/resources/bone_map.h"
#include "scene/resources/texture.h"
class AspectRatioContainer;
class BoneMapperButton : public TextureButton {
GDCLASS(BoneMapperButton, TextureButton);
public:
enum BoneMapState {
BONE_MAP_STATE_UNSET,
BONE_MAP_STATE_SET,
BONE_MAP_STATE_MISSING,
BONE_MAP_STATE_ERROR
};
private:
StringName profile_bone_name;
bool selected = false;
bool require = false;
TextureRect *circle = nullptr;
void fetch_textures();
protected:
void _notification(int p_what);
public:
StringName get_profile_bone_name() const;
void set_state(BoneMapState p_state);
bool is_require() const;
BoneMapperButton(const StringName &p_profile_bone_name, bool p_require, bool p_selected);
};
class BoneMapperItem : public VBoxContainer {
GDCLASS(BoneMapperItem, VBoxContainer);
int button_id = -1;
StringName profile_bone_name;
Ref<BoneMap> bone_map;
EditorPropertyText *skeleton_bone_selector = nullptr;
Button *picker_button = nullptr;
void _update_property();
void _open_picker();
protected:
void _notification(int p_what);
static void _bind_methods();
virtual void _value_changed(const String &p_property, const Variant &p_value, const String &p_name, bool p_changing);
virtual void create_editor();
public:
void assign_button_id(int p_button_id);
BoneMapperItem(Ref<BoneMap> &p_bone_map, const StringName &p_profile_bone_name = StringName());
};
class BonePicker : public AcceptDialog {
GDCLASS(BonePicker, AcceptDialog);
Skeleton3D *skeleton = nullptr;
Tree *bones = nullptr;
public:
void popup_bones_tree(const Size2i &p_minsize = Size2i());
bool has_selected_bone();
StringName get_selected_bone();
protected:
void _notification(int p_what);
void _confirm();
private:
void create_editors();
void create_bones_tree(Skeleton3D *p_skeleton);
public:
BonePicker(Skeleton3D *p_skeleton);
};
class BoneMapper : public VBoxContainer {
GDCLASS(BoneMapper, VBoxContainer);
Skeleton3D *skeleton = nullptr;
Ref<BoneMap> bone_map;
EditorPropertyResource *profile_selector = nullptr;
Vector<BoneMapperItem *> bone_mapper_items;
Button *clear_mapping_button = nullptr;
VBoxContainer *mapper_item_vbox = nullptr;
int current_group_idx = 0;
int current_bone_idx = -1;
AspectRatioContainer *bone_mapper_field = nullptr;
EditorPropertyEnum *profile_group_selector = nullptr;
ColorRect *profile_bg = nullptr;
TextureRect *profile_texture = nullptr;
Vector<BoneMapperButton *> bone_mapper_buttons;
void create_editor();
void recreate_editor();
void clear_items();
void recreate_items();
void update_group_idx();
void _update_state();
/* Bone picker */
BonePicker *picker = nullptr;
StringName picker_key_name;
void _pick_bone(const StringName &p_bone_name);
void _apply_picker_selection();
void _clear_mapping_current_group();
/* For auto mapping */
enum BoneSegregation {
BONE_SEGREGATION_NONE,
BONE_SEGREGATION_LEFT,
BONE_SEGREGATION_RIGHT
};
bool is_match_with_bone_name(const String &p_bone_name, const String &p_word);
int search_bone_by_name(Skeleton3D *p_skeleton, const Vector<String> &p_picklist, BoneSegregation p_segregation = BONE_SEGREGATION_NONE, int p_parent = -1, int p_child = -1, int p_children_count = -1);
BoneSegregation guess_bone_segregation(const String &p_bone_name);
void auto_mapping_process(Ref<BoneMap> &p_bone_map);
void _run_auto_mapping();
protected:
void _notification(int p_what);
static void _bind_methods();
virtual void _value_changed(const String &p_property, const Variant &p_value, const String &p_name, bool p_changing);
virtual void _profile_changed(const String &p_property, const Variant &p_value, const String &p_name, bool p_changing);
public:
void set_current_group_idx(int p_group_idx);
int get_current_group_idx() const;
void set_current_bone_idx(int p_bone_idx);
int get_current_bone_idx() const;
BoneMapper(Skeleton3D *p_skeleton, Ref<BoneMap> &p_bone_map);
};
class BoneMapEditor : public VBoxContainer {
GDCLASS(BoneMapEditor, VBoxContainer);
Skeleton3D *skeleton = nullptr;
Ref<BoneMap> bone_map;
BoneMapper *bone_mapper = nullptr;
void fetch_objects();
void create_editors();
protected:
void _notification(int p_what);
public:
BoneMapEditor(Ref<BoneMap> &p_bone_map);
};
class EditorInspectorPluginBoneMap : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginBoneMap, EditorInspectorPlugin);
BoneMapEditor *editor = nullptr;
public:
virtual bool can_handle(Object *p_object) override;
virtual void parse_begin(Object *p_object) override;
};
class BoneMapEditorPlugin : public EditorPlugin {
GDCLASS(BoneMapEditorPlugin, EditorPlugin);
public:
virtual String get_plugin_name() const override { return "BoneMap"; }
BoneMapEditorPlugin();
};

View File

@@ -0,0 +1,128 @@
/**************************************************************************/
/* camera_3d_editor_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "camera_3d_editor_plugin.h"
#include "core/config/project_settings.h"
#include "editor/editor_node.h"
#include "node_3d_editor_plugin.h"
#include "scene/gui/texture_rect.h"
#include "scene/main/viewport.h"
void Camera3DEditor::_node_removed(Node *p_node) {
if (p_node == node) {
node = nullptr;
Node3DEditor::get_singleton()->set_custom_camera(nullptr);
hide();
}
}
void Camera3DEditor::_pressed() {
Node *sn = (node && preview->is_pressed()) ? node : nullptr;
Node3DEditor::get_singleton()->set_custom_camera(sn);
}
void Camera3DEditor::edit(Node *p_camera) {
node = p_camera;
if (!node) {
preview->set_pressed(false);
Node3DEditor::get_singleton()->set_custom_camera(nullptr);
} else {
if (preview->is_pressed()) {
Node3DEditor::get_singleton()->set_custom_camera(p_camera);
} else {
Node3DEditor::get_singleton()->set_custom_camera(nullptr);
}
}
}
Camera3DEditor::Camera3DEditor() {
preview = memnew(Button);
add_child(preview);
preview->set_text(TTR("Preview"));
preview->set_toggle_mode(true);
preview->set_anchor(SIDE_LEFT, Control::ANCHOR_END);
preview->set_anchor(SIDE_RIGHT, Control::ANCHOR_END);
preview->set_offset(SIDE_LEFT, -60);
preview->set_offset(SIDE_RIGHT, 0);
preview->set_offset(SIDE_TOP, 0);
preview->set_offset(SIDE_BOTTOM, 10);
preview->connect(SceneStringName(pressed), callable_mp(this, &Camera3DEditor::_pressed));
}
void Camera3DPreview::_update_sub_viewport_size() {
sub_viewport->set_size(Node3DEditor::get_camera_viewport_size(camera));
}
Camera3DPreview::Camera3DPreview(Camera3D *p_camera) :
TexturePreview(nullptr, false), camera(p_camera), sub_viewport(memnew(SubViewport)) {
RenderingServer::get_singleton()->viewport_attach_camera(sub_viewport->get_viewport_rid(), camera->get_camera());
add_child(sub_viewport);
TextureRect *display = get_texture_display();
display->set_texture(sub_viewport->get_texture());
sub_viewport->connect("size_changed", callable_mp((CanvasItem *)display, &CanvasItem::queue_redraw));
sub_viewport->get_texture()->connect_changed(callable_mp((TexturePreview *)this, &Camera3DPreview::_update_texture_display_ratio));
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &Camera3DPreview::_update_sub_viewport_size));
_update_sub_viewport_size();
}
bool EditorInspectorPluginCamera3DPreview::can_handle(Object *p_object) {
return Object::cast_to<Camera3D>(p_object) != nullptr;
}
void EditorInspectorPluginCamera3DPreview::parse_begin(Object *p_object) {
Camera3D *camera = Object::cast_to<Camera3D>(p_object);
Camera3DPreview *preview = memnew(Camera3DPreview(camera));
add_custom_control(preview);
}
void Camera3DEditorPlugin::edit(Object *p_object) {
Node3DEditor::get_singleton()->set_can_preview(Object::cast_to<Camera3D>(p_object));
}
bool Camera3DEditorPlugin::handles(Object *p_object) const {
return p_object->is_class("Camera3D");
}
void Camera3DEditorPlugin::make_visible(bool p_visible) {
if (!p_visible) {
Node3DEditor::get_singleton()->set_can_preview(nullptr);
}
}
Camera3DEditorPlugin::Camera3DEditorPlugin() {
Ref<EditorInspectorPluginCamera3DPreview> plugin;
plugin.instantiate();
add_inspector_plugin(plugin);
}

View File

@@ -0,0 +1,87 @@
/**************************************************************************/
/* camera_3d_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/plugins/editor_plugin.h"
#include "editor/scene/texture/texture_editor_plugin.h"
class Camera3D;
class SubViewport;
class Camera3DEditor : public Control {
GDCLASS(Camera3DEditor, Control);
Panel *panel = nullptr;
Button *preview = nullptr;
Node *node = nullptr;
void _pressed();
protected:
void _node_removed(Node *p_node);
public:
void edit(Node *p_camera);
Camera3DEditor();
};
class Camera3DPreview : public TexturePreview {
GDCLASS(Camera3DPreview, TexturePreview);
Camera3D *camera = nullptr;
SubViewport *sub_viewport = nullptr;
void _update_sub_viewport_size();
public:
Camera3DPreview(Camera3D *p_camera);
};
class EditorInspectorPluginCamera3DPreview : public EditorInspectorPluginTexture {
GDCLASS(EditorInspectorPluginCamera3DPreview, EditorInspectorPluginTexture);
public:
virtual bool can_handle(Object *p_object) override;
virtual void parse_begin(Object *p_object) override;
};
class Camera3DEditorPlugin : public EditorPlugin {
GDCLASS(Camera3DEditorPlugin, EditorPlugin);
public:
virtual String get_plugin_name() const override { return "Camera3D"; }
bool has_main_screen() const override { return false; }
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
Camera3DEditorPlugin();
};

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env.add_source_files(env.editor_sources, "*.cpp")
SConscript("physics/SCsub")

View File

@@ -0,0 +1,56 @@
/**************************************************************************/
/* audio_listener_3d_gizmo_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "audio_listener_3d_gizmo_plugin.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "scene/3d/audio_listener_3d.h"
AudioListener3DGizmoPlugin::AudioListener3DGizmoPlugin() {
create_icon_material("audio_listener_3d_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoAudioListener3D"), EditorStringName(EditorIcons)));
}
bool AudioListener3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<AudioListener3D>(p_spatial) != nullptr;
}
String AudioListener3DGizmoPlugin::get_gizmo_name() const {
return "AudioListener3D";
}
int AudioListener3DGizmoPlugin::get_priority() const {
return -1;
}
void AudioListener3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
const Ref<Material> icon = get_material("audio_listener_3d_icon", p_gizmo);
p_gizmo->add_unscaled_billboard(icon, 0.05);
}

View File

@@ -0,0 +1,46 @@
/**************************************************************************/
/* audio_listener_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/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();
};

View File

@@ -0,0 +1,314 @@
/**************************************************************************/
/* 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_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/audio_stream_player_3d.h"
AudioStreamPlayer3DGizmoPlugin::AudioStreamPlayer3DGizmoPlugin() {
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/stream_player_3d");
create_icon_material("stream_player_3d_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Gizmo3DSamplePlayer"), EditorStringName(EditorIcons)));
create_material("stream_player_3d_material_primary", gizmo_color);
create_material("stream_player_3d_material_secondary", gizmo_color * Color(1, 1, 1, 0.35));
// Enable vertex colors for the billboard material as the gizmo color depends on the
// AudioStreamPlayer3D attenuation type and source (Unit Size or Max Distance).
create_material("stream_player_3d_material_billboard", Color(1, 1, 1), true, false, true);
create_handle_material("handles");
}
bool AudioStreamPlayer3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<AudioStreamPlayer3D>(p_spatial) != nullptr;
}
String AudioStreamPlayer3DGizmoPlugin::get_gizmo_name() const {
return "AudioStreamPlayer3D";
}
int AudioStreamPlayer3DGizmoPlugin::get_priority() const {
return -1;
}
String AudioStreamPlayer3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
return "Emission Radius";
}
Variant AudioStreamPlayer3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
AudioStreamPlayer3D *player = Object::cast_to<AudioStreamPlayer3D>(p_gizmo->get_node_3d());
return player->get_emission_angle();
}
void AudioStreamPlayer3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
AudioStreamPlayer3D *player = Object::cast_to<AudioStreamPlayer3D>(p_gizmo->get_node_3d());
Transform3D gt = player->get_global_transform();
Transform3D gi = gt.affine_inverse();
Vector3 ray_from = p_camera->project_ray_origin(p_point);
Vector3 ray_dir = p_camera->project_ray_normal(p_point);
Vector3 ray_to = ray_from + ray_dir * 4096;
ray_from = gi.xform(ray_from);
ray_to = gi.xform(ray_to);
float closest_dist = 1e20;
float closest_angle = 1e20;
for (int i = 0; i < 180; i++) {
float a = Math::deg_to_rad((float)i);
float an = Math::deg_to_rad((float)(i + 1));
Vector3 from(Math::sin(a), 0, -Math::cos(a));
Vector3 to(Math::sin(an), 0, -Math::cos(an));
Vector3 r1, r2;
Geometry3D::get_closest_points_between_segments(from, to, ray_from, ray_to, r1, r2);
float d = r1.distance_to(r2);
if (d < closest_dist) {
closest_dist = d;
closest_angle = i;
}
}
if (closest_angle < 91) {
player->set_emission_angle(closest_angle);
}
}
void AudioStreamPlayer3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
AudioStreamPlayer3D *player = Object::cast_to<AudioStreamPlayer3D>(p_gizmo->get_node_3d());
if (p_cancel) {
player->set_emission_angle(p_restore);
} else {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change AudioStreamPlayer3D Emission Angle"));
ur->add_do_method(player, "set_emission_angle", player->get_emission_angle());
ur->add_undo_method(player, "set_emission_angle", p_restore);
ur->commit_action();
}
}
void AudioStreamPlayer3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->clear();
if (p_gizmo->is_selected()) {
const AudioStreamPlayer3D *player = Object::cast_to<AudioStreamPlayer3D>(p_gizmo->get_node_3d());
if (player->get_attenuation_model() != AudioStreamPlayer3D::ATTENUATION_DISABLED || player->get_max_distance() > CMP_EPSILON) {
// Draw a circle to represent sound volume attenuation.
// Use only a billboard circle to represent radius.
// This helps distinguish AudioStreamPlayer3D gizmos from OmniLight3D gizmos.
const Ref<Material> lines_billboard_material = get_material("stream_player_3d_material_billboard", p_gizmo);
// Soft distance cap varies depending on attenuation model, as some will fade out more aggressively than others.
// Multipliers were empirically determined through testing.
float soft_multiplier;
switch (player->get_attenuation_model()) {
case AudioStreamPlayer3D::ATTENUATION_INVERSE_DISTANCE:
soft_multiplier = 12.0;
break;
case AudioStreamPlayer3D::ATTENUATION_INVERSE_SQUARE_DISTANCE:
soft_multiplier = 4.0;
break;
case AudioStreamPlayer3D::ATTENUATION_LOGARITHMIC:
soft_multiplier = 3.25;
break;
default:
// Ensures Max Distance's radius visualization is not capped by Unit Size
// (when the attenuation mode is Disabled).
soft_multiplier = 10000.0;
break;
}
// Draw the distance at which the sound can be reasonably heard.
// This can be either a hard distance cap with the Max Distance property (if set above 0.0),
// or a soft distance cap with the Unit Size property (sound never reaches true zero).
// When Max Distance is 0.0, `r` represents the distance above which the
// sound can't be heard in *most* (but not all) scenarios.
float radius = player->get_unit_size() * soft_multiplier;
if (player->get_max_distance() > CMP_EPSILON) {
radius = MIN(radius, player->get_max_distance());
}
#define PUSH_QUARTER_XY(m_from_x, m_from_y, m_to_x, m_to_y, m_y) \
points_ptrw[index++] = Vector3(m_from_x, -m_from_y - m_y, 0); \
points_ptrw[index++] = Vector3(m_to_x, -m_to_y - m_y, 0); \
points_ptrw[index++] = Vector3(m_from_x, m_from_y + m_y, 0); \
points_ptrw[index++] = Vector3(m_to_x, m_to_y + m_y, 0); \
points_ptrw[index++] = Vector3(-m_from_x, -m_from_y - m_y, 0); \
points_ptrw[index++] = Vector3(-m_to_x, -m_to_y - m_y, 0); \
points_ptrw[index++] = Vector3(-m_from_x, m_from_y + m_y, 0); \
points_ptrw[index++] = Vector3(-m_to_x, m_to_y + m_y, 0);
// Number of points in an octant. So there will be 8 * points_in_octant points in total.
// This corresponds to the smoothness of the circle.
const uint32_t points_in_octant = 15;
const real_t octant_angle = Math::PI / 4;
const real_t inc = (Math::PI / (4 * points_in_octant));
const real_t radius_squared = radius * radius;
real_t r = 0;
Vector<Vector3> points_billboard;
points_billboard.resize(8 * points_in_octant * 2);
Vector3 *points_ptrw = points_billboard.ptrw();
uint32_t index = 0;
float previous_x = radius;
float previous_y = 0.f;
for (uint32_t i = 0; i < points_in_octant; i++) {
r += inc;
real_t x = Math::cos((i == points_in_octant - 1) ? octant_angle : r) * radius;
real_t y = Math::sqrt(radius_squared - (x * x));
PUSH_QUARTER_XY(previous_x, previous_y, x, y, 0);
PUSH_QUARTER_XY(previous_y, previous_x, y, x, 0);
previous_x = x;
previous_y = y;
}
#undef PUSH_QUARTER_XY
Color color;
switch (player->get_attenuation_model()) {
// Pick cold colors for all attenuation models (except Disabled),
// so that soft caps can be easily distinguished from hard caps
// (which use warm colors).
case AudioStreamPlayer3D::ATTENUATION_INVERSE_DISTANCE:
color = Color(0.4, 0.8, 1);
break;
case AudioStreamPlayer3D::ATTENUATION_INVERSE_SQUARE_DISTANCE:
color = Color(0.4, 0.5, 1);
break;
case AudioStreamPlayer3D::ATTENUATION_LOGARITHMIC:
color = Color(0.4, 0.2, 1);
break;
default:
// Disabled attenuation mode.
// This is never reached when Max Distance is 0, but the
// hue-inverted form of this color will be used if Max Distance is greater than 0.
color = Color(1, 1, 1);
break;
}
if (player->get_max_distance() > CMP_EPSILON) {
// Sound is hard-capped by max distance. The attenuation model still matters,
// so invert the hue of the color that was chosen above.
color.set_h(color.get_h() + 0.5);
}
p_gizmo->add_lines(points_billboard, lines_billboard_material, true, color);
}
if (player->is_emission_angle_enabled()) {
const float ha = Math::deg_to_rad(player->get_emission_angle());
const float ofs = -Math::cos(ha);
const float radius = Math::sin(ha);
const uint32_t points_in_octant = 7;
const real_t octant_angle = Math::PI / 4;
const real_t inc = (Math::PI / (4 * points_in_octant));
const real_t radius_squared = radius * radius;
real_t r = 0;
Vector<Vector3> points_primary;
points_primary.resize(8 * points_in_octant * 2);
Vector3 *points_ptrw = points_primary.ptrw();
uint32_t index = 0;
float previous_x = radius;
float previous_y = 0.f;
#define PUSH_QUARTER(m_from_x, m_from_y, m_to_x, m_to_y, m_y) \
points_ptrw[index++] = Vector3(m_from_x, -m_from_y, m_y); \
points_ptrw[index++] = Vector3(m_to_x, -m_to_y, m_y); \
points_ptrw[index++] = Vector3(m_from_x, m_from_y, m_y); \
points_ptrw[index++] = Vector3(m_to_x, m_to_y, m_y); \
points_ptrw[index++] = Vector3(-m_from_x, -m_from_y, m_y); \
points_ptrw[index++] = Vector3(-m_to_x, -m_to_y, m_y); \
points_ptrw[index++] = Vector3(-m_from_x, m_from_y, m_y); \
points_ptrw[index++] = Vector3(-m_to_x, m_to_y, m_y);
for (uint32_t i = 0; i < points_in_octant; i++) {
r += inc;
real_t x = Math::cos((i == points_in_octant - 1) ? octant_angle : r) * radius;
real_t y = Math::sqrt(radius_squared - (x * x));
PUSH_QUARTER(previous_x, previous_y, x, y, ofs);
PUSH_QUARTER(previous_y, previous_x, y, x, ofs);
previous_x = x;
previous_y = y;
}
#undef PUSH_QUARTER
const Ref<Material> material_primary = get_material("stream_player_3d_material_primary", p_gizmo);
p_gizmo->add_lines(points_primary, material_primary);
Vector<Vector3> points_secondary;
points_secondary.resize(16);
Vector3 *points_second_ptrw = points_secondary.ptrw();
uint32_t index2 = 0;
// Lines to the circle.
points_second_ptrw[index2++] = Vector3();
points_second_ptrw[index2++] = Vector3(radius, 0, ofs);
points_second_ptrw[index2++] = Vector3();
points_second_ptrw[index2++] = Vector3(-radius, 0, ofs);
points_second_ptrw[index2++] = Vector3();
points_second_ptrw[index2++] = Vector3(0, radius, ofs);
points_second_ptrw[index2++] = Vector3();
points_second_ptrw[index2++] = Vector3(0, -radius, ofs);
real_t octant_value = Math::cos(octant_angle) * radius;
points_second_ptrw[index2++] = Vector3();
points_second_ptrw[index2++] = Vector3(octant_value, octant_value, ofs);
points_second_ptrw[index2++] = Vector3();
points_second_ptrw[index2++] = Vector3(-octant_value, octant_value, ofs);
points_second_ptrw[index2++] = Vector3();
points_second_ptrw[index2++] = Vector3(-octant_value, -octant_value, ofs);
points_second_ptrw[index2++] = Vector3();
points_second_ptrw[index2++] = Vector3(octant_value, -octant_value, ofs);
const Ref<Material> material_secondary = get_material("stream_player_3d_material_secondary", p_gizmo);
p_gizmo->add_lines(points_secondary, material_secondary);
Vector<Vector3> handles;
handles.push_back(Vector3(Math::sin(ha), 0, -Math::cos(ha)));
p_gizmo->add_handles(handles, get_material("handles"));
}
}
const Ref<Material> icon = get_material("stream_player_3d_icon", p_gizmo);
p_gizmo->add_unscaled_billboard(icon, 0.05);
}

View File

@@ -0,0 +1,50 @@
/**************************************************************************/
/* audio_stream_player_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/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();
};

View File

@@ -0,0 +1,292 @@
/**************************************************************************/
/* camera_3d_gizmo_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "camera_3d_gizmo_plugin.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/scene/3d/node_3d_editor_plugin.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/camera_3d.h"
Camera3DGizmoPlugin::Camera3DGizmoPlugin() {
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/camera");
create_material("camera_material", gizmo_color);
create_icon_material("camera_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoCamera3D"), EditorStringName(EditorIcons)));
create_handle_material("handles");
}
bool Camera3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<Camera3D>(p_spatial) != nullptr;
}
String Camera3DGizmoPlugin::get_gizmo_name() const {
return "Camera3D";
}
int Camera3DGizmoPlugin::get_priority() const {
return -1;
}
String Camera3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
Camera3D *camera = Object::cast_to<Camera3D>(p_gizmo->get_node_3d());
if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) {
return "FOV";
} else {
return "Size";
}
}
Variant Camera3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
Camera3D *camera = Object::cast_to<Camera3D>(p_gizmo->get_node_3d());
if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) {
return camera->get_fov();
} else {
return camera->get_size();
}
}
void Camera3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
Camera3D *camera = Object::cast_to<Camera3D>(p_gizmo->get_node_3d());
Transform3D gt = camera->get_global_transform();
Transform3D gi = gt.affine_inverse();
Vector3 ray_from = p_camera->project_ray_origin(p_point);
Vector3 ray_dir = p_camera->project_ray_normal(p_point);
Vector3 s[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) };
if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) {
Transform3D gt2 = camera->get_global_transform();
float a = _find_closest_angle_to_half_pi_arc(s[0], s[1], 1.0, gt2);
camera->set("fov", CLAMP(a * 2.0, 1, 179));
} else {
Camera3D::KeepAspect aspect = camera->get_keep_aspect_mode();
Vector3 camera_far = aspect == Camera3D::KeepAspect::KEEP_WIDTH ? Vector3(4096, 0, -1) : Vector3(0, 4096, -1);
Vector3 ra, rb;
Geometry3D::get_closest_points_between_segments(Vector3(0, 0, -1), camera_far, s[0], s[1], ra, rb);
float d = aspect == Camera3D::KeepAspect::KEEP_WIDTH ? ra.x * 2 : ra.y * 2;
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
}
d = CLAMP(d, 0.1, 16384);
camera->set("size", d);
}
}
void Camera3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
Camera3D *camera = Object::cast_to<Camera3D>(p_gizmo->get_node_3d());
if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) {
if (p_cancel) {
camera->set("fov", p_restore);
} else {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Camera FOV"));
ur->add_do_property(camera, "fov", camera->get_fov());
ur->add_undo_property(camera, "fov", p_restore);
ur->commit_action();
}
} else {
if (p_cancel) {
camera->set("size", p_restore);
} else {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Camera Size"));
ur->add_do_property(camera, "size", camera->get_size());
ur->add_undo_property(camera, "size", p_restore);
ur->commit_action();
}
}
}
void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Camera3D *camera = Object::cast_to<Camera3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
Vector<Vector3> lines;
Vector<Vector3> handles;
Ref<Material> material = get_material("camera_material", p_gizmo);
Ref<Material> icon = get_material("camera_icon", p_gizmo);
const Size2i viewport_size = Node3DEditor::get_camera_viewport_size(camera);
const real_t viewport_aspect = viewport_size.x > 0 && viewport_size.y > 0 ? viewport_size.aspect() : 1.0;
const Size2 size_factor = viewport_aspect > 1.0 ? Size2(1.0, 1.0 / viewport_aspect) : Size2(viewport_aspect, 1.0);
#define ADD_TRIANGLE(m_a, m_b, m_c) \
{ \
lines.push_back(m_a); \
lines.push_back(m_b); \
lines.push_back(m_b); \
lines.push_back(m_c); \
lines.push_back(m_c); \
lines.push_back(m_a); \
}
#define ADD_QUAD(m_a, m_b, m_c, m_d) \
{ \
lines.push_back(m_a); \
lines.push_back(m_b); \
lines.push_back(m_b); \
lines.push_back(m_c); \
lines.push_back(m_c); \
lines.push_back(m_d); \
lines.push_back(m_d); \
lines.push_back(m_a); \
}
switch (camera->get_projection()) {
case Camera3D::PROJECTION_PERSPECTIVE: {
// The real FOV is halved for accurate representation
float fov = camera->get_fov() / 2.0;
const float hsize = Math::sin(Math::deg_to_rad(fov));
const float depth = -Math::cos(Math::deg_to_rad(fov));
Vector3 side;
if (camera->get_keep_aspect_mode() == Camera3D::KEEP_WIDTH) {
side = Vector3(hsize * size_factor.x, 0, depth * size_factor.x);
} else {
side = Vector3(hsize * size_factor.x, 0, depth * size_factor.y);
}
Vector3 nside = Vector3(-side.x, side.y, side.z);
Vector3 up = Vector3(0, hsize * size_factor.y, 0);
ADD_TRIANGLE(Vector3(), side + up, side - up);
ADD_TRIANGLE(Vector3(), nside + up, nside - up);
ADD_TRIANGLE(Vector3(), side + up, nside + up);
ADD_TRIANGLE(Vector3(), side - up, nside - up);
handles.push_back(side);
side.x = MIN(side.x, hsize * 0.25);
nside.x = -side.x;
Vector3 tup(0, up.y + hsize / 2, side.z);
ADD_TRIANGLE(tup, side + up, nside + up);
} break;
case Camera3D::PROJECTION_ORTHOGONAL: {
Camera3D::KeepAspect aspect = camera->get_keep_aspect_mode();
float size = camera->get_size();
float keep_size = size * 0.5;
Vector3 right, up;
Vector3 back(0, 0, -1.0);
if (aspect == Camera3D::KeepAspect::KEEP_WIDTH) {
right = Vector3(keep_size, 0, 0);
up = Vector3(0, keep_size / viewport_aspect, 0);
handles.push_back(right + back);
} else {
right = Vector3(keep_size * viewport_aspect, 0, 0);
up = Vector3(0, keep_size, 0);
handles.push_back(up + back);
}
ADD_QUAD(-up - right, -up + right, up + right, up - right);
ADD_QUAD(-up - right + back, -up + right + back, up + right + back, up - right + back);
ADD_QUAD(up + right, up + right + back, up - right + back, up - right);
ADD_QUAD(-up + right, -up + right + back, -up - right + back, -up - right);
right.x = MIN(right.x, keep_size * 0.25);
Vector3 tup(0, up.y + keep_size / 2, back.z);
ADD_TRIANGLE(tup, right + up + back, -right + up + back);
} break;
case Camera3D::PROJECTION_FRUSTUM: {
float hsize = camera->get_size() / 2.0;
Vector3 side = Vector3(hsize, 0, -camera->get_near()).normalized();
side.x *= size_factor.x;
Vector3 nside = Vector3(-side.x, side.y, side.z);
Vector3 up = Vector3(0, hsize * size_factor.y, 0);
Vector3 offset = Vector3(camera->get_frustum_offset().x, camera->get_frustum_offset().y, 0.0);
ADD_TRIANGLE(Vector3(), side + up + offset, side - up + offset);
ADD_TRIANGLE(Vector3(), nside + up + offset, nside - up + offset);
ADD_TRIANGLE(Vector3(), side + up + offset, nside + up + offset);
ADD_TRIANGLE(Vector3(), side - up + offset, nside - up + offset);
side.x = MIN(side.x, hsize * 0.25);
nside.x = -side.x;
Vector3 tup(0, up.y + hsize / 2, side.z);
ADD_TRIANGLE(tup + offset, side + up + offset, nside + up + offset);
} break;
}
#undef ADD_TRIANGLE
#undef ADD_QUAD
p_gizmo->add_lines(lines, material);
p_gizmo->add_unscaled_billboard(icon, 0.05);
p_gizmo->add_collision_segments(lines);
if (!handles.is_empty()) {
p_gizmo->add_handles(handles, get_material("handles"));
}
}
float Camera3DGizmoPlugin::_find_closest_angle_to_half_pi_arc(const Vector3 &p_from, const Vector3 &p_to, float p_arc_radius, const Transform3D &p_arc_xform) {
//bleh, discrete is simpler
static const int arc_test_points = 64;
float min_d = 1e20;
Vector3 min_p;
for (int i = 0; i < arc_test_points; i++) {
float a = i * Math::PI * 0.5 / arc_test_points;
float an = (i + 1) * Math::PI * 0.5 / arc_test_points;
Vector3 p = Vector3(Math::cos(a), 0, -Math::sin(a)) * p_arc_radius;
Vector3 n = Vector3(Math::cos(an), 0, -Math::sin(an)) * p_arc_radius;
Vector3 ra, rb;
Geometry3D::get_closest_points_between_segments(p, n, p_from, p_to, ra, rb);
float d = ra.distance_to(rb);
if (d < min_d) {
min_d = d;
min_p = ra;
}
}
//min_p = p_arc_xform.affine_inverse().xform(min_p);
float a = (Math::PI * 0.5) - Vector2(min_p.x, -min_p.z).angle();
return Math::rad_to_deg(a);
}

View File

@@ -0,0 +1,53 @@
/**************************************************************************/
/* camera_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class Camera3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(Camera3DGizmoPlugin, EditorNode3DGizmoPlugin);
private:
static float _find_closest_angle_to_half_pi_arc(const Vector3 &p_from, const Vector3 &p_to, float p_arc_radius, const Transform3D &p_arc_xform);
public:
bool has_gizmo(Node3D *p_spatial) override;
String get_gizmo_name() const override;
int get_priority() const override;
String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) override;
void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false) override;
void redraw(EditorNode3DGizmo *p_gizmo) override;
Camera3DGizmoPlugin();
};

View File

@@ -0,0 +1,107 @@
/**************************************************************************/
/* 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_string_names.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/cpu_particles_3d.h"
CPUParticles3DGizmoPlugin::CPUParticles3DGizmoPlugin() {
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/particles");
create_material("particles_material", gizmo_color);
gizmo_color.a = MAX((gizmo_color.a - 0.2) * 0.02, 0.0);
create_material("particles_solid_material", gizmo_color);
create_icon_material("particles_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoCPUParticles3D"), EditorStringName(EditorIcons)));
}
bool CPUParticles3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<CPUParticles3D>(p_spatial) != nullptr;
}
String CPUParticles3DGizmoPlugin::get_gizmo_name() const {
return "CPUParticles3D";
}
int CPUParticles3DGizmoPlugin::get_priority() const {
return -1;
}
bool CPUParticles3DGizmoPlugin::is_selectable_when_hidden() const {
return true;
}
void CPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
CPUParticles3D *particles = Object::cast_to<CPUParticles3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
Vector<Vector3> lines;
AABB aabb = particles->get_visibility_aabb();
for (int i = 0; i < 12; i++) {
Vector3 a, b;
aabb.get_edge(i, a, b);
lines.push_back(a);
lines.push_back(b);
}
Vector<Vector3> handles;
for (int i = 0; i < 3; i++) {
Vector3 ax;
ax[i] = aabb.position[i] + aabb.size[i];
ax[(i + 1) % 3] = aabb.position[(i + 1) % 3] + aabb.size[(i + 1) % 3] * 0.5;
ax[(i + 2) % 3] = aabb.position[(i + 2) % 3] + aabb.size[(i + 2) % 3] * 0.5;
handles.push_back(ax);
}
Vector3 center = aabb.get_center();
for (int i = 0; i < 3; i++) {
Vector3 ax;
ax[i] = 1.0;
handles.push_back(center + ax);
lines.push_back(center);
lines.push_back(center + ax);
}
Ref<Material> material = get_material("particles_material", p_gizmo);
p_gizmo->add_lines(lines, material);
if (p_gizmo->is_selected()) {
Ref<Material> solid_material = get_material("particles_solid_material", p_gizmo);
p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_center());
}
Ref<Material> icon = get_material("particles_icon", p_gizmo);
p_gizmo->add_unscaled_billboard(icon, 0.05);
}

View File

@@ -0,0 +1,45 @@
/**************************************************************************/
/* cpu_particles_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/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();
};

View File

@@ -0,0 +1,131 @@
/**************************************************************************/
/* 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_string_names.h"
#include "editor/scene/3d/gizmos/gizmo_3d_helper.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/decal.h"
DecalGizmoPlugin::DecalGizmoPlugin() {
helper.instantiate();
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/decal");
create_icon_material("decal_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoDecal"), EditorStringName(EditorIcons)));
create_material("decal_material", gizmo_color);
create_handle_material("handles");
}
bool DecalGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<Decal>(p_spatial) != nullptr;
}
String DecalGizmoPlugin::get_gizmo_name() const {
return "Decal";
}
int DecalGizmoPlugin::get_priority() const {
return -1;
}
String DecalGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
return helper->box_get_handle_name(p_id);
}
Variant DecalGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
Decal *decal = Object::cast_to<Decal>(p_gizmo->get_node_3d());
return decal->get_size();
}
void DecalGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) {
helper->initialize_handle_action(get_handle_value(p_gizmo, p_id, p_secondary), p_gizmo->get_node_3d()->get_global_transform());
}
void DecalGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
Decal *decal = Object::cast_to<Decal>(p_gizmo->get_node_3d());
Vector3 size = decal->get_size();
Vector3 sg[2];
helper->get_segment(p_camera, p_point, sg);
Vector3 position;
helper->box_set_handle(sg, p_id, size, position);
decal->set_size(size);
decal->set_global_position(position);
}
void DecalGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
helper->box_commit_handle(TTR("Change Decal Size"), p_cancel, p_gizmo->get_node_3d());
}
void DecalGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Decal *decal = Object::cast_to<Decal>(p_gizmo->get_node_3d());
p_gizmo->clear();
Vector<Vector3> lines;
Vector3 size = decal->get_size();
AABB aabb;
aabb.position = -size / 2;
aabb.size = size;
for (int i = 0; i < 12; i++) {
Vector3 a, b;
aabb.get_edge(i, a, b);
if (a.y == b.y) {
lines.push_back(a);
lines.push_back(b);
} else {
Vector3 ah = a.lerp(b, 0.2);
lines.push_back(a);
lines.push_back(ah);
Vector3 bh = b.lerp(a, 0.2);
lines.push_back(b);
lines.push_back(bh);
}
}
float half_size_y = size.y / 2;
lines.push_back(Vector3(0, half_size_y, 0));
lines.push_back(Vector3(0, half_size_y * 1.2, 0));
Vector<Vector3> handles = helper->box_get_handles(decal->get_size());
Ref<Material> material = get_material("decal_material", p_gizmo);
const Ref<Material> icon = get_material("decal_icon", p_gizmo);
p_gizmo->add_lines(lines, material);
p_gizmo->add_unscaled_billboard(icon, 0.05);
p_gizmo->add_handles(handles, get_material("handles"));
}

View File

@@ -0,0 +1,55 @@
/**************************************************************************/
/* decal_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/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();
};

View File

@@ -0,0 +1,125 @@
/**************************************************************************/
/* 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_string_names.h"
#include "editor/scene/3d/gizmos/gizmo_3d_helper.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/fog_volume.h"
FogVolumeGizmoPlugin::FogVolumeGizmoPlugin() {
helper.instantiate();
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/fog_volume");
create_material("shape_material", gizmo_color);
gizmo_color.a = 0.15;
create_material("shape_material_internal", gizmo_color);
create_icon_material("fog_volume_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoFogVolume"), EditorStringName(EditorIcons)));
create_handle_material("handles");
}
bool FogVolumeGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return (Object::cast_to<FogVolume>(p_spatial) != nullptr);
}
String FogVolumeGizmoPlugin::get_gizmo_name() const {
return "FogVolume";
}
int FogVolumeGizmoPlugin::get_priority() const {
return -1;
}
String FogVolumeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
return helper->box_get_handle_name(p_id);
}
Variant FogVolumeGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
return Vector3(p_gizmo->get_node_3d()->call("get_size"));
}
void FogVolumeGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) {
helper->initialize_handle_action(get_handle_value(p_gizmo, p_id, p_secondary), p_gizmo->get_node_3d()->get_global_transform());
}
void FogVolumeGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
FogVolume *fog_volume = Object::cast_to<FogVolume>(p_gizmo->get_node_3d());
Vector3 size = fog_volume->get_size();
Vector3 sg[2];
helper->get_segment(p_camera, p_point, sg);
Vector3 position;
helper->box_set_handle(sg, p_id, size, position);
fog_volume->set_size(size);
fog_volume->set_global_position(position);
}
void FogVolumeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
helper->box_commit_handle(TTR("Change FogVolume Size"), p_cancel, p_gizmo->get_node_3d());
}
void FogVolumeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
FogVolume *fog_volume = Object::cast_to<FogVolume>(p_gizmo->get_node_3d());
p_gizmo->clear();
if (fog_volume->get_shape() != RS::FOG_VOLUME_SHAPE_WORLD) {
const Ref<Material> material =
get_material("shape_material", p_gizmo);
const Ref<Material> material_internal =
get_material("shape_material_internal", p_gizmo);
Ref<Material> handles_material = get_material("handles");
Vector<Vector3> lines;
AABB aabb;
aabb.size = fog_volume->get_size();
aabb.position = aabb.size / -2;
for (int i = 0; i < 12; i++) {
Vector3 a, b;
aabb.get_edge(i, a, b);
lines.push_back(a);
lines.push_back(b);
}
Vector<Vector3> handles = helper->box_get_handles(fog_volume->get_size());
p_gizmo->add_lines(lines, material);
p_gizmo->add_collision_segments(lines);
const Ref<Material> icon = get_material("fog_volume_icon", p_gizmo);
p_gizmo->add_unscaled_billboard(icon, 0.05);
p_gizmo->add_handles(handles, handles_material);
}
}

View File

@@ -0,0 +1,55 @@
/**************************************************************************/
/* fog_volume_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/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();
};

View File

@@ -0,0 +1,74 @@
/**************************************************************************/
/* 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/settings/editor_settings.h"
#include "scene/3d/visual_instance_3d.h"
bool GeometryInstance3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<GeometryInstance3D>(p_spatial) != nullptr;
}
String GeometryInstance3DGizmoPlugin::get_gizmo_name() const {
return "MeshInstance3DCustomAABB";
}
int GeometryInstance3DGizmoPlugin::get_priority() const {
return -1;
}
void GeometryInstance3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
GeometryInstance3D *geometry = Object::cast_to<GeometryInstance3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
if (p_gizmo->is_selected()) {
AABB aabb = geometry->get_custom_aabb();
Vector<Vector3> lines;
for (int i = 0; i < 12; i++) {
Vector3 a;
Vector3 b;
aabb.get_edge(i, a, b);
lines.push_back(a);
lines.push_back(b);
}
Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D);
mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
const Color selection_box_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/aabb");
mat->set_albedo(selection_box_color);
mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
p_gizmo->add_lines(lines, mat);
}
}

View File

@@ -0,0 +1,44 @@
/**************************************************************************/
/* geometry_instance_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/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;
};

View File

@@ -0,0 +1,243 @@
/**************************************************************************/
/* 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/scene/3d/node_3d_editor_plugin.h"
#include "scene/3d/camera_3d.h"
void Gizmo3DHelper::initialize_handle_action(const Variant &p_initial_value, const Transform3D &p_initial_transform) {
initial_value = p_initial_value;
initial_transform = p_initial_transform;
}
void Gizmo3DHelper::get_segment(Camera3D *p_camera, const Point2 &p_point, Vector3 *r_segment) {
Transform3D gt = initial_transform;
Transform3D gi = gt.affine_inverse();
Vector3 ray_from = p_camera->project_ray_origin(p_point);
Vector3 ray_dir = p_camera->project_ray_normal(p_point);
r_segment[0] = gi.xform(ray_from);
r_segment[1] = gi.xform(ray_from + ray_dir * 4096);
}
Vector<Vector3> Gizmo3DHelper::box_get_handles(const Vector3 &p_box_size) {
Vector<Vector3> handles;
for (int i = 0; i < 3; i++) {
Vector3 ax;
ax[i] = p_box_size[i] / 2;
handles.push_back(ax);
handles.push_back(-ax);
}
return handles;
}
String Gizmo3DHelper::box_get_handle_name(int p_id) const {
switch (p_id) {
case 0:
case 1:
return "Size X";
case 2:
case 3:
return "Size Y";
case 4:
case 5:
return "Size Z";
}
return "";
}
void Gizmo3DHelper::box_set_handle(const Vector3 p_segment[2], int p_id, Vector3 &r_box_size, Vector3 &r_box_position) {
int axis = p_id / 2;
int sign = p_id % 2 * -2 + 1;
Vector3 initial_size = initial_value;
float neg_end = initial_size[axis] * -0.5;
float pos_end = initial_size[axis] * 0.5;
Vector3 axis_segment[2] = { Vector3(), Vector3() };
axis_segment[0][axis] = 4096.0;
axis_segment[1][axis] = -4096.0;
Vector3 ra, rb;
Geometry3D::get_closest_points_between_segments(axis_segment[0], axis_segment[1], p_segment[0], p_segment[1], ra, rb);
// Calculate new size.
r_box_size = initial_size;
if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
r_box_size[axis] = ra[axis] * sign * 2;
} else {
r_box_size[axis] = sign > 0 ? ra[axis] - neg_end : pos_end - ra[axis];
}
// Snap to grid.
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
r_box_size[axis] = Math::snapped(r_box_size[axis], Node3DEditor::get_singleton()->get_translate_snap());
}
r_box_size[axis] = MAX(r_box_size[axis], 0.001);
// Adjust position.
if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
r_box_position = initial_transform.get_origin();
} else {
if (sign > 0) {
pos_end = neg_end + r_box_size[axis];
} else {
neg_end = pos_end - r_box_size[axis];
}
Vector3 offset;
offset[axis] = (pos_end + neg_end) * 0.5;
r_box_position = initial_transform.xform(offset);
}
}
void Gizmo3DHelper::box_commit_handle(const String &p_action_name, bool p_cancel, Object *p_position_object, Object *p_size_object, const StringName &p_position_property, const StringName &p_size_property) {
if (!p_size_object) {
p_size_object = p_position_object;
}
if (p_cancel) {
p_size_object->set(p_size_property, initial_value);
p_position_object->set(p_position_property, initial_transform.get_origin());
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(p_action_name);
ur->add_do_property(p_size_object, p_size_property, p_size_object->get(p_size_property));
ur->add_do_property(p_position_object, p_position_property, p_position_object->get(p_position_property));
ur->add_undo_property(p_size_object, p_size_property, initial_value);
ur->add_undo_property(p_position_object, p_position_property, initial_transform.get_origin());
ur->commit_action();
}
Vector<Vector3> Gizmo3DHelper::cylinder_get_handles(real_t p_height, real_t p_radius) {
Vector<Vector3> handles;
handles.push_back(Vector3(p_radius, 0, 0));
handles.push_back(Vector3(0, p_height * 0.5, 0));
handles.push_back(Vector3(0, p_height * -0.5, 0));
return handles;
}
String Gizmo3DHelper::cylinder_get_handle_name(int p_id) const {
if (p_id == 0) {
return "Radius";
} else {
return "Height";
}
}
void Gizmo3DHelper::_cylinder_or_capsule_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_cylinder_position, bool p_is_capsule) {
real_t initial_radius = initial_value.operator Vector2().x;
real_t initial_height = initial_value.operator Vector2().y;
int sign = p_id == 2 ? -1 : 1;
int axis = p_id == 0 ? 0 : 1;
Vector3 axis_vector;
axis_vector[axis] = sign;
Vector3 ra, rb;
Geometry3D::get_closest_points_between_segments(axis_vector * -4096, axis_vector * 4096, p_segment[0], p_segment[1], ra, rb);
float d = axis_vector.dot(ra);
// Snap to grid.
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
}
if (p_id == 0) {
// Adjust radius.
if (d < 0.001) {
d = 0.001;
}
r_radius = d;
r_cylinder_position = initial_transform.get_origin();
if (p_is_capsule) {
r_height = MAX(initial_height, r_radius * 2.0);
} else {
r_height = initial_height;
}
} else if (p_id == 1 || p_id == 2) {
// Adjust height.
if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
r_height = d * 2.0;
} else {
r_height = (initial_height * 0.5) + d;
}
if (r_height < 0.001) {
r_height = 0.001;
}
// Adjust position.
if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
r_cylinder_position = initial_transform.get_origin();
} else {
Vector3 offset;
offset[axis] = (r_height - initial_height) * 0.5 * sign;
r_cylinder_position = initial_transform.xform(offset);
}
if (p_is_capsule) {
r_radius = MIN(initial_radius, r_height / 2.0);
} else {
r_radius = initial_radius;
}
}
}
void Gizmo3DHelper::cylinder_commit_handle(int p_id, const String &p_radius_action_name, const String &p_height_action_name, bool p_cancel, Object *p_position_object, Object *p_height_object, Object *p_radius_object, const StringName &p_position_property, const StringName &p_height_property, const StringName &p_radius_property) {
if (!p_height_object) {
p_height_object = p_position_object;
}
if (!p_radius_object) {
p_radius_object = p_position_object;
}
if (p_cancel) {
p_radius_object->set(p_radius_property, initial_value.operator Vector2().x);
p_height_object->set(p_height_property, initial_value.operator Vector2().y);
p_position_object->set(p_position_property, initial_transform.get_origin());
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(p_id == 0 ? p_radius_action_name : p_height_action_name);
ur->add_do_property(p_radius_object, p_radius_property, p_radius_object->get(p_radius_property));
ur->add_undo_property(p_radius_object, p_radius_property, initial_value.operator Vector2().x);
ur->add_do_property(p_height_object, p_height_property, p_height_object->get(p_height_property));
ur->add_undo_property(p_height_object, p_height_property, initial_value.operator Vector2().y);
ur->add_do_property(p_position_object, p_position_property, p_position_object->get(p_position_property));
ur->add_undo_property(p_position_object, p_position_property, initial_transform.get_origin());
ur->commit_action();
}

View File

@@ -0,0 +1,84 @@
/**************************************************************************/
/* gizmo_3d_helper.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "core/object/ref_counted.h"
class Camera3D;
class Gizmo3DHelper : public RefCounted {
GDCLASS(Gizmo3DHelper, RefCounted);
int current_handle_id;
Variant initial_value;
Transform3D initial_transform;
private:
void _cylinder_or_capsule_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_cylinder_position, bool p_is_capsule);
public:
/**
* Initializes a new action involving a handle.
*
* Depending on the type of gizmo that will be used, different formats for the `p_initial_value` are required:
* Box: The size of the box as `Vector3`
* Cylinder or Capsule: A `Vector2` of the form `Vector2(radius, height)`
*/
void initialize_handle_action(const Variant &p_initial_value, const Transform3D &p_initial_transform);
void get_segment(Camera3D *p_camera, const Point2 &p_point, Vector3 *r_segment);
// Box
Vector<Vector3> box_get_handles(const Vector3 &p_box_size);
String box_get_handle_name(int p_id) const;
void box_set_handle(const Vector3 p_segment[2], int p_id, Vector3 &r_box_size, Vector3 &r_box_position);
void box_commit_handle(const String &p_action_name, bool p_cancel, Object *p_position_object, Object *p_size_object = nullptr, const StringName &p_position_property = "global_position", const StringName &p_size_property = "size");
// Cylinder
Vector<Vector3> cylinder_get_handles(real_t p_height, real_t p_radius);
String cylinder_get_handle_name(int p_id) const;
_FORCE_INLINE_ void cylinder_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_cylinder_position) {
_cylinder_or_capsule_set_handle(p_segment, p_id, r_height, r_radius, r_cylinder_position, false);
}
void cylinder_commit_handle(int p_id, const String &p_radius_action_name, const String &p_height_action_name, bool p_cancel, Object *p_position_object, Object *p_height_object = nullptr, Object *p_radius_object = nullptr, const StringName &p_position_property = "global_position", const StringName &p_height_property = "height", const StringName &p_radius_property = "radius");
// Capsule
_FORCE_INLINE_ Vector<Vector3> capsule_get_handles(real_t p_height, real_t p_radius) { return cylinder_get_handles(p_height, p_radius); }
_FORCE_INLINE_ String capsule_get_handle_name(int p_id) { return cylinder_get_handle_name(p_id); }
_FORCE_INLINE_ void capsule_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_capsule_position) {
_cylinder_or_capsule_set_handle(p_segment, p_id, r_height, r_radius, r_capsule_position, true);
}
_FORCE_INLINE_ void capsule_commit_handle(int p_id, const String &p_radius_action_name, const String &p_height_action_name, bool p_cancel, Object *p_position_object, Object *p_height_object = nullptr, Object *p_radius_object = nullptr, const StringName &p_position_property = "global_position", const StringName &p_height_property = "height", const StringName &p_radius_property = "radius") {
cylinder_commit_handle(p_id, p_radius_action_name, p_height_action_name, p_cancel, p_position_object, p_height_object, p_radius_object, p_position_property, p_height_property, p_radius_property);
}
};

View File

@@ -0,0 +1,84 @@
/**************************************************************************/
/* 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_string_names.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/gpu_particles_3d.h"
GPUParticles3DGizmoPlugin::GPUParticles3DGizmoPlugin() {
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/particles");
create_material("particles_material", gizmo_color);
gizmo_color.a = MAX((gizmo_color.a - 0.2) * 0.02, 0.0);
create_icon_material("particles_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoGPUParticles3D"), EditorStringName(EditorIcons)));
}
bool GPUParticles3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<GPUParticles3D>(p_spatial) != nullptr;
}
String GPUParticles3DGizmoPlugin::get_gizmo_name() const {
return "GPUParticles3D";
}
int GPUParticles3DGizmoPlugin::get_priority() const {
return -1;
}
bool GPUParticles3DGizmoPlugin::is_selectable_when_hidden() const {
return true;
}
void GPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->clear();
if (p_gizmo->is_selected()) {
GPUParticles3D *particles = Object::cast_to<GPUParticles3D>(p_gizmo->get_node_3d());
Vector<Vector3> lines;
AABB aabb = particles->get_visibility_aabb();
for (int i = 0; i < 12; i++) {
Vector3 a, b;
aabb.get_edge(i, a, b);
lines.push_back(a);
lines.push_back(b);
}
Ref<Material> material = get_material("particles_material", p_gizmo);
p_gizmo->add_lines(lines, material);
}
Ref<Material> icon = get_material("particles_icon", p_gizmo);
p_gizmo->add_unscaled_billboard(icon, 0.05);
}

View File

@@ -0,0 +1,46 @@
/**************************************************************************/
/* gpu_particles_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/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();
};

View File

@@ -0,0 +1,306 @@
/**************************************************************************/
/* 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_undo_redo_manager.h"
#include "editor/scene/3d/gizmos/gizmo_3d_helper.h"
#include "editor/scene/3d/node_3d_editor_plugin.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/gpu_particles_collision_3d.h"
GPUParticlesCollision3DGizmoPlugin::GPUParticlesCollision3DGizmoPlugin() {
helper.instantiate();
Color gizmo_color_attractor = EDITOR_GET("editors/3d_gizmos/gizmo_colors/particle_attractor");
create_material("shape_material_attractor", gizmo_color_attractor);
gizmo_color_attractor.a = 0.15;
create_material("shape_material_attractor_internal", gizmo_color_attractor);
Color gizmo_color_collision = EDITOR_GET("editors/3d_gizmos/gizmo_colors/particle_collision");
create_material("shape_material_collision", gizmo_color_collision);
gizmo_color_collision.a = 0.15;
create_material("shape_material_collision_internal", gizmo_color_collision);
create_handle_material("handles");
}
bool GPUParticlesCollision3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return (Object::cast_to<GPUParticlesCollision3D>(p_spatial) != nullptr) || (Object::cast_to<GPUParticlesAttractor3D>(p_spatial) != nullptr);
}
String GPUParticlesCollision3DGizmoPlugin::get_gizmo_name() const {
return "GPUParticlesCollision3D";
}
int GPUParticlesCollision3DGizmoPlugin::get_priority() const {
return -1;
}
String GPUParticlesCollision3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
const Node3D *cs = p_gizmo->get_node_3d();
if (Object::cast_to<GPUParticlesCollisionSphere3D>(cs) || Object::cast_to<GPUParticlesAttractorSphere3D>(cs)) {
return "Radius";
}
if (Object::cast_to<GPUParticlesCollisionBox3D>(cs) || Object::cast_to<GPUParticlesAttractorBox3D>(cs) || Object::cast_to<GPUParticlesAttractorVectorField3D>(cs) || Object::cast_to<GPUParticlesCollisionSDF3D>(cs) || Object::cast_to<GPUParticlesCollisionHeightField3D>(cs)) {
return helper->box_get_handle_name(p_id);
}
return "";
}
Variant GPUParticlesCollision3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
const Node3D *cs = p_gizmo->get_node_3d();
if (Object::cast_to<GPUParticlesCollisionSphere3D>(cs) || Object::cast_to<GPUParticlesAttractorSphere3D>(cs)) {
return p_gizmo->get_node_3d()->call("get_radius");
}
if (Object::cast_to<GPUParticlesCollisionBox3D>(cs) || Object::cast_to<GPUParticlesAttractorBox3D>(cs) || Object::cast_to<GPUParticlesAttractorVectorField3D>(cs) || Object::cast_to<GPUParticlesCollisionSDF3D>(cs) || Object::cast_to<GPUParticlesCollisionHeightField3D>(cs)) {
return Vector3(p_gizmo->get_node_3d()->call("get_size"));
}
return Variant();
}
void GPUParticlesCollision3DGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) {
helper->initialize_handle_action(get_handle_value(p_gizmo, p_id, p_secondary), p_gizmo->get_node_3d()->get_global_transform());
}
void GPUParticlesCollision3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
Node3D *sn = p_gizmo->get_node_3d();
Vector3 sg[2];
helper->get_segment(p_camera, p_point, sg);
if (Object::cast_to<GPUParticlesCollisionSphere3D>(sn) || Object::cast_to<GPUParticlesAttractorSphere3D>(sn)) {
Vector3 ra, rb;
Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb);
float d = ra.x;
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
}
if (d < 0.001) {
d = 0.001;
}
sn->call("set_radius", d);
}
if (Object::cast_to<GPUParticlesCollisionBox3D>(sn) || Object::cast_to<GPUParticlesAttractorBox3D>(sn) || Object::cast_to<GPUParticlesAttractorVectorField3D>(sn) || Object::cast_to<GPUParticlesCollisionSDF3D>(sn) || Object::cast_to<GPUParticlesCollisionHeightField3D>(sn)) {
Vector3 size = sn->call("get_size");
Vector3 position;
helper->box_set_handle(sg, p_id, size, position);
sn->call("set_size", size);
sn->set_global_position(position);
}
}
void GPUParticlesCollision3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
Node3D *sn = p_gizmo->get_node_3d();
if (Object::cast_to<GPUParticlesCollisionSphere3D>(sn) || Object::cast_to<GPUParticlesAttractorSphere3D>(sn)) {
if (p_cancel) {
sn->call("set_radius", p_restore);
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Radius"));
ur->add_do_method(sn, "set_radius", sn->call("get_radius"));
ur->add_undo_method(sn, "set_radius", p_restore);
ur->commit_action();
}
if (Object::cast_to<GPUParticlesCollisionBox3D>(sn) || Object::cast_to<GPUParticlesAttractorBox3D>(sn) || Object::cast_to<GPUParticlesAttractorVectorField3D>(sn) || Object::cast_to<GPUParticlesCollisionSDF3D>(sn) || Object::cast_to<GPUParticlesCollisionHeightField3D>(sn)) {
helper->box_commit_handle("Change Box Shape Size", p_cancel, sn);
}
}
void GPUParticlesCollision3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Node3D *cs = p_gizmo->get_node_3d();
p_gizmo->clear();
Ref<Material> material;
Ref<Material> material_internal;
if (Object::cast_to<GPUParticlesAttractor3D>(cs)) {
material = get_material("shape_material_attractor", p_gizmo);
material_internal = get_material("shape_material_attractor_internal", p_gizmo);
} else {
material = get_material("shape_material_collision", p_gizmo);
material_internal = get_material("shape_material_collision_internal", p_gizmo);
}
const Ref<Material> handles_material = get_material("handles");
if (Object::cast_to<GPUParticlesCollisionSphere3D>(cs) || Object::cast_to<GPUParticlesAttractorSphere3D>(cs)) {
float radius = cs->call("get_radius");
#define PUSH_QUARTER(m_from_x, m_from_y, m_to_x, m_to_y, m_y) \
points_ptrw[index++] = Vector3(m_from_x, m_y, m_from_y); \
points_ptrw[index++] = Vector3(m_to_x, m_y, m_to_y); \
points_ptrw[index++] = Vector3(m_from_x, m_y, -m_from_y); \
points_ptrw[index++] = Vector3(m_to_x, m_y, -m_to_y); \
points_ptrw[index++] = Vector3(-m_from_x, m_y, m_from_y); \
points_ptrw[index++] = Vector3(-m_to_x, m_y, m_to_y); \
points_ptrw[index++] = Vector3(-m_from_x, m_y, -m_from_y); \
points_ptrw[index++] = Vector3(-m_to_x, m_y, -m_to_y);
#define PUSH_QUARTER_XY(m_from_x, m_from_y, m_to_x, m_to_y) \
points_ptrw[index++] = Vector3(m_from_x, -m_from_y, 0); \
points_ptrw[index++] = Vector3(m_to_x, -m_to_y, 0); \
points_ptrw[index++] = Vector3(m_from_x, m_from_y, 0); \
points_ptrw[index++] = Vector3(m_to_x, m_to_y, 0); \
points_ptrw[index++] = Vector3(-m_from_x, -m_from_y, 0); \
points_ptrw[index++] = Vector3(-m_to_x, -m_to_y, 0); \
points_ptrw[index++] = Vector3(-m_from_x, m_from_y, 0); \
points_ptrw[index++] = Vector3(-m_to_x, m_to_y, 0);
#define PUSH_QUARTER_YZ(m_from_x, m_from_y, m_to_x, m_to_y) \
points_ptrw[index++] = Vector3(0, -m_from_y, m_from_x); \
points_ptrw[index++] = Vector3(0, -m_to_y, m_to_x); \
points_ptrw[index++] = Vector3(0, m_from_y, m_from_x); \
points_ptrw[index++] = Vector3(0, m_to_y, m_to_x); \
points_ptrw[index++] = Vector3(0, -m_from_y, -m_from_x); \
points_ptrw[index++] = Vector3(0, -m_to_y, -m_to_x); \
points_ptrw[index++] = Vector3(0, m_from_y, -m_from_x); \
points_ptrw[index++] = Vector3(0, m_to_y, -m_to_x);
// Number of points in an octant. So there will be 8 * points_in_octant points in total.
// This corresponds to the smoothness of the circle.
const uint32_t points_in_octant = 16;
const real_t octant_angle = Math::PI / 4;
const real_t inc = (Math::PI / (4 * points_in_octant));
const real_t radius_squared = radius * radius;
real_t r = 0;
Vector<Vector3> points;
points.resize(3 * 8 * points_in_octant * 2);
Vector3 *points_ptrw = points.ptrw();
uint32_t index = 0;
float previous_x = radius;
float previous_y = 0.f;
for (uint32_t i = 0; i < points_in_octant; ++i) {
r += inc;
real_t x = Math::cos((i == points_in_octant - 1) ? octant_angle : r) * radius;
real_t y = Math::sqrt(radius_squared - (x * x));
PUSH_QUARTER(previous_x, previous_y, x, y, 0);
PUSH_QUARTER(previous_y, previous_x, y, x, 0);
PUSH_QUARTER_XY(previous_x, previous_y, x, y);
PUSH_QUARTER_XY(previous_y, previous_x, y, x);
PUSH_QUARTER_YZ(previous_x, previous_y, x, y);
PUSH_QUARTER_YZ(previous_y, previous_x, y, x);
previous_x = x;
previous_y = y;
}
p_gizmo->add_lines(points, material);
p_gizmo->add_collision_segments(points);
Vector<Vector3> handles;
handles.push_back(Vector3(r, 0, 0));
p_gizmo->add_handles(handles, handles_material);
#undef PUSH_QUARTER
#undef PUSH_QUARTER_XY
#undef PUSH_QUARTER_YZ
}
if (Object::cast_to<GPUParticlesCollisionBox3D>(cs) || Object::cast_to<GPUParticlesAttractorBox3D>(cs) || Object::cast_to<GPUParticlesAttractorVectorField3D>(cs) || Object::cast_to<GPUParticlesCollisionSDF3D>(cs) || Object::cast_to<GPUParticlesCollisionHeightField3D>(cs)) {
Vector<Vector3> lines;
AABB aabb;
aabb.size = cs->call("get_size").operator Vector3();
aabb.position = aabb.size / -2;
for (int i = 0; i < 12; i++) {
Vector3 a, b;
aabb.get_edge(i, a, b);
lines.push_back(a);
lines.push_back(b);
}
Vector<Vector3> handles = helper->box_get_handles(aabb.size);
p_gizmo->add_lines(lines, material);
p_gizmo->add_collision_segments(lines);
p_gizmo->add_handles(handles, handles_material);
GPUParticlesCollisionSDF3D *col_sdf = Object::cast_to<GPUParticlesCollisionSDF3D>(cs);
if (col_sdf) {
static const int subdivs[GPUParticlesCollisionSDF3D::RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 };
int subdiv = subdivs[col_sdf->get_resolution()];
float cell_size = aabb.get_longest_axis_size() / subdiv;
lines.clear();
for (int i = 1; i < subdiv; i++) {
for (int j = 0; j < 3; j++) {
if (cell_size * i > aabb.size[j]) {
continue;
}
int j_n1 = (j + 1) % 3;
int j_n2 = (j + 2) % 3;
for (int k = 0; k < 4; k++) {
Vector3 from = aabb.position, to = aabb.position;
from[j] += cell_size * i;
to[j] += cell_size * i;
if (k & 1) {
to[j_n1] += aabb.size[j_n1];
} else {
to[j_n2] += aabb.size[j_n2];
}
if (k & 2) {
from[j_n1] += aabb.size[j_n1];
from[j_n2] += aabb.size[j_n2];
}
lines.push_back(from);
lines.push_back(to);
}
}
}
p_gizmo->add_lines(lines, material_internal);
}
}
}

View File

@@ -0,0 +1,55 @@
/**************************************************************************/
/* gpu_particles_collision_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/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();
};

View File

@@ -0,0 +1,60 @@
/**************************************************************************/
/* label_3d_gizmo_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "label_3d_gizmo_plugin.h"
#include "scene/3d/label_3d.h"
bool Label3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<Label3D>(p_spatial) != nullptr;
}
String Label3DGizmoPlugin::get_gizmo_name() const {
return "Label3D";
}
int Label3DGizmoPlugin::get_priority() const {
return -1;
}
bool Label3DGizmoPlugin::can_be_hidden() const {
return false;
}
void Label3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Label3D *label = Object::cast_to<Label3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
Ref<TriangleMesh> tm = label->generate_triangle_mesh();
if (tm.is_valid()) {
p_gizmo->add_collision_triangles(tm);
}
}

View File

@@ -0,0 +1,44 @@
/**************************************************************************/
/* label_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/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;
};

View File

@@ -0,0 +1,316 @@
/**************************************************************************/
/* light_3d_gizmo_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "light_3d_gizmo_plugin.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/scene/3d/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);
}

View File

@@ -0,0 +1,53 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/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();
};

View File

@@ -0,0 +1,224 @@
/**************************************************************************/
/* 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_string_names.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/lightmap_gi.h"
LightmapGIGizmoPlugin::LightmapGIGizmoPlugin() {
// NOTE: This gizmo only renders solid spheres for previewing indirect lighting on dynamic objects.
// The wireframe representation for LightmapProbe nodes is handled in LightmapProbeGizmoPlugin.
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/lightmap_lines");
probe_size = EDITOR_GET("editors/3d_gizmos/gizmo_settings/lightmap_gi_probe_size");
gizmo_color.a = 0.1;
create_material("lightmap_lines", gizmo_color);
Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D);
mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
// Fade out probes when camera gets too close to them.
mat->set_distance_fade(StandardMaterial3D::DISTANCE_FADE_PIXEL_DITHER);
mat->set_distance_fade_min_distance(probe_size * 0.5);
mat->set_distance_fade_max_distance(probe_size * 1.5);
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.is_empty()) {
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;
LocalVector<Vector3> vertices;
LocalVector<Color> colors;
LocalVector<int> indices;
float radius = probe_size * 0.5f;
if (!Math::is_zero_approx(radius)) {
// L2 Spherical Harmonics evaluation and diffuse convolution coefficients.
const float sh_coeffs[5] = {
static_cast<float>(sqrt(1.0 / (4.0 * Math::PI)) * Math::PI),
static_cast<float>(sqrt(3.0 / (4.0 * Math::PI)) * Math::PI * 2.0 / 3.0),
static_cast<float>(sqrt(15.0 / (4.0 * Math::PI)) * Math::PI * 1.0 / 4.0),
static_cast<float>(sqrt(5.0 / (16.0 * Math::PI)) * Math::PI * 1.0 / 4.0),
static_cast<float>(sqrt(15.0 / (16.0 * Math::PI)) * Math::PI * 1.0 / 4.0)
};
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 Vector3 light = (sh_coeffs[0] * sh_col[0] +
sh_coeffs[1] * sh_col[1] * n.y +
sh_coeffs[1] * sh_col[2] * n.z +
sh_coeffs[1] * sh_col[3] * n.x +
sh_coeffs[2] * sh_col[4] * n.x * n.y +
sh_coeffs[2] * sh_col[5] * n.y * n.z +
sh_coeffs[3] * sh_col[6] * (3.0 * n.z * n.z - 1.0) +
sh_coeffs[2] * sh_col[7] * n.x * n.z +
sh_coeffs[4] * sh_col[8] * (n.x * n.x - n.y * n.y));
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] = Vector<Vector3>(vertices);
array[RS::ARRAY_INDEX] = Vector<int>(indices);
array[RS::ARRAY_COLOR] = Vector<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);
}
}

View File

@@ -0,0 +1,47 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class LightmapGIGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(LightmapGIGizmoPlugin, EditorNode3DGizmoPlugin);
float probe_size = 0.4f;
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();
};

View File

@@ -0,0 +1,126 @@
/**************************************************************************/
/* lightmap_probe_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_probe_gizmo_plugin.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/lightmap_probe.h"
LightmapProbeGizmoPlugin::LightmapProbeGizmoPlugin() {
// NOTE: This gizmo only renders LightmapProbe nodes as wireframes.
// The solid sphere representation is handled in LightmapGIGizmoPlugin.
create_icon_material("lightmap_probe_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoLightmapProbe"), EditorStringName(EditorIcons)));
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/lightprobe_lines");
probe_size = EDITOR_GET("editors/3d_gizmos/gizmo_settings/lightmap_gi_probe_size");
gizmo_color.a = 0.3;
create_material("lightprobe_lines", gizmo_color);
}
bool LightmapProbeGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<LightmapProbe>(p_spatial) != nullptr;
}
String LightmapProbeGizmoPlugin::get_gizmo_name() const {
return "LightmapProbe";
}
int LightmapProbeGizmoPlugin::get_priority() const {
return -1;
}
void LightmapProbeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Ref<Material> material_lines = get_material("lightprobe_lines", p_gizmo);
p_gizmo->clear();
Vector<Vector3> 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;
// Make the lines' radius slightly smaller than its mesh representation to avoid Z-fighting.
float radius = probe_size * 0.495f;
if (!Math::is_zero_approx(radius)) {
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(n);
}
}
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) {
lines.push_back(vertices[k1]);
lines.push_back(vertices[k2]);
lines.push_back(vertices[k1]);
lines.push_back(vertices[k1 + 1]);
}
if (i != (stack_count - 1)) {
lines.push_back(vertices[k1 + 1]);
lines.push_back(vertices[k2]);
lines.push_back(vertices[k2]);
lines.push_back(vertices[k2 + 1]);
}
}
}
p_gizmo->add_lines(lines, material_lines);
}
const Ref<Material> icon = get_material("lightmap_probe_icon", p_gizmo);
p_gizmo->add_unscaled_billboard(icon, 0.05);
}

View File

@@ -0,0 +1,47 @@
/**************************************************************************/
/* lightmap_probe_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class LightmapProbeGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(LightmapProbeGizmoPlugin, EditorNode3DGizmoPlugin);
float probe_size = 0.4f;
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;
LightmapProbeGizmoPlugin();
};

View File

@@ -0,0 +1,127 @@
/**************************************************************************/
/* marker_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 "marker_3d_gizmo_plugin.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "scene/3d/marker_3d.h"
Marker3DGizmoPlugin::Marker3DGizmoPlugin() {
pos3d_mesh.instantiate();
Vector<Vector3> cursor_points;
Vector<Color> cursor_colors;
const float cs = 1.0;
// Add more points to create a "hard stop" in the color gradient.
cursor_points.push_back(Vector3(+cs, 0, 0));
cursor_points.push_back(Vector3());
cursor_points.push_back(Vector3());
cursor_points.push_back(Vector3(-cs, 0, 0));
cursor_points.push_back(Vector3(0, +cs, 0));
cursor_points.push_back(Vector3());
cursor_points.push_back(Vector3());
cursor_points.push_back(Vector3(0, -cs, 0));
cursor_points.push_back(Vector3(0, 0, +cs));
cursor_points.push_back(Vector3());
cursor_points.push_back(Vector3());
cursor_points.push_back(Vector3(0, 0, -cs));
// Use the axis color which is brighter for the positive axis.
// Use a darkened axis color for the negative axis.
// This makes it possible to see in which direction the Marker3D node is rotated
// (which can be important depending on how it's used).
const Color color_x = EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME("axis_x_color"), EditorStringName(Editor));
cursor_colors.push_back(color_x);
cursor_colors.push_back(color_x);
// FIXME: Use less strong darkening factor once GH-48573 is fixed.
// The current darkening factor compensates for lines being too bright in the 3D editor.
cursor_colors.push_back(color_x.lerp(Color(0, 0, 0), 0.75));
cursor_colors.push_back(color_x.lerp(Color(0, 0, 0), 0.75));
const Color color_y = EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME("axis_y_color"), EditorStringName(Editor));
cursor_colors.push_back(color_y);
cursor_colors.push_back(color_y);
cursor_colors.push_back(color_y.lerp(Color(0, 0, 0), 0.75));
cursor_colors.push_back(color_y.lerp(Color(0, 0, 0), 0.75));
const Color color_z = EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME("axis_z_color"), EditorStringName(Editor));
cursor_colors.push_back(color_z);
cursor_colors.push_back(color_z);
cursor_colors.push_back(color_z.lerp(Color(0, 0, 0), 0.75));
cursor_colors.push_back(color_z.lerp(Color(0, 0, 0), 0.75));
Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D);
mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
Array d;
d.resize(RS::ARRAY_MAX);
d[Mesh::ARRAY_VERTEX] = cursor_points;
d[Mesh::ARRAY_COLOR] = cursor_colors;
pos3d_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, d);
pos3d_mesh->surface_set_material(0, mat);
}
bool Marker3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<Marker3D>(p_spatial) != nullptr;
}
String Marker3DGizmoPlugin::get_gizmo_name() const {
return "Marker3D";
}
int Marker3DGizmoPlugin::get_priority() const {
return -1;
}
void Marker3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
const Marker3D *marker = Object::cast_to<Marker3D>(p_gizmo->get_node_3d());
const real_t extents = marker->get_gizmo_extents();
const Transform3D xform(Basis::from_scale(Vector3(extents, extents, extents)));
p_gizmo->clear();
p_gizmo->add_mesh(pos3d_mesh, Ref<Material>(), xform);
const Vector<Vector3> points = {
Vector3(-extents, 0, 0),
Vector3(+extents, 0, 0),
Vector3(0, -extents, 0),
Vector3(0, +extents, 0),
Vector3(0, 0, -extents),
Vector3(0, 0, +extents),
};
p_gizmo->add_collision_segments(points);
}

View File

@@ -0,0 +1,47 @@
/**************************************************************************/
/* marker_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class Marker3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(Marker3DGizmoPlugin, EditorNode3DGizmoPlugin);
Ref<ArrayMesh> pos3d_mesh;
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;
Marker3DGizmoPlugin();
};

View File

@@ -0,0 +1,84 @@
/**************************************************************************/
/* mesh_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 "mesh_instance_3d_gizmo_plugin.h"
#include "editor/scene/3d/node_3d_editor_plugin.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/physics/soft_body_3d.h"
#include "scene/resources/3d/primitive_meshes.h"
bool MeshInstance3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<MeshInstance3D>(p_spatial) != nullptr && Object::cast_to<SoftBody3D>(p_spatial) == nullptr;
}
String MeshInstance3DGizmoPlugin::get_gizmo_name() const {
return "MeshInstance3D";
}
int MeshInstance3DGizmoPlugin::get_priority() const {
return -1;
}
bool MeshInstance3DGizmoPlugin::can_be_hidden() const {
return false;
}
void MeshInstance3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
MeshInstance3D *mesh = Object::cast_to<MeshInstance3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
Ref<Mesh> m = mesh->get_mesh();
if (m.is_null()) {
return; //none
}
Ref<TriangleMesh> tm;
Ref<PlaneMesh> plane_mesh = mesh->get_mesh();
if (plane_mesh.is_valid() && (plane_mesh->get_subdivide_depth() > 0 || plane_mesh->get_subdivide_width() > 0)) {
// PlaneMesh subdiv makes gizmo redraw very slow due to TriangleMesh BVH calculation for every face.
// For gizmo collision this is very much unnecessary since a PlaneMesh is always flat, 2 faces is enough.
Ref<PlaneMesh> simple_plane_mesh;
simple_plane_mesh.instantiate();
simple_plane_mesh->set_orientation(plane_mesh->get_orientation());
simple_plane_mesh->set_size(plane_mesh->get_size());
simple_plane_mesh->set_center_offset(plane_mesh->get_center_offset());
tm = simple_plane_mesh->generate_triangle_mesh();
} else {
tm = m->generate_triangle_mesh();
}
if (tm.is_valid()) {
p_gizmo->add_collision_triangles(tm);
}
}

View File

@@ -0,0 +1,44 @@
/**************************************************************************/
/* mesh_instance_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class MeshInstance3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(MeshInstance3DGizmoPlugin, 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;
};

View File

@@ -0,0 +1,284 @@
/**************************************************************************/
/* occluder_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 "occluder_instance_3d_gizmo_plugin.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/scene/3d/node_3d_editor_plugin.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/occluder_instance_3d.h"
OccluderInstance3DGizmoPlugin::OccluderInstance3DGizmoPlugin() {
create_material("line_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/occluder"));
create_handle_material("handles");
}
bool OccluderInstance3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<OccluderInstance3D>(p_spatial) != nullptr;
}
String OccluderInstance3DGizmoPlugin::get_gizmo_name() const {
return "OccluderInstance3D";
}
int OccluderInstance3DGizmoPlugin::get_priority() const {
return -1;
}
String OccluderInstance3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
const OccluderInstance3D *cs = Object::cast_to<OccluderInstance3D>(p_gizmo->get_node_3d());
Ref<Occluder3D> o = cs->get_occluder();
if (o.is_null()) {
return "";
}
if (Object::cast_to<SphereOccluder3D>(*o)) {
return "Radius";
}
if (Object::cast_to<BoxOccluder3D>(*o) || Object::cast_to<QuadOccluder3D>(*o)) {
return "Size";
}
return "";
}
Variant OccluderInstance3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
OccluderInstance3D *oi = Object::cast_to<OccluderInstance3D>(p_gizmo->get_node_3d());
Ref<Occluder3D> o = oi->get_occluder();
if (o.is_null()) {
return Variant();
}
if (Object::cast_to<SphereOccluder3D>(*o)) {
Ref<SphereOccluder3D> so = o;
return so->get_radius();
}
if (Object::cast_to<BoxOccluder3D>(*o)) {
Ref<BoxOccluder3D> bo = o;
return bo->get_size();
}
if (Object::cast_to<QuadOccluder3D>(*o)) {
Ref<QuadOccluder3D> qo = o;
return qo->get_size();
}
return Variant();
}
void OccluderInstance3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
OccluderInstance3D *oi = Object::cast_to<OccluderInstance3D>(p_gizmo->get_node_3d());
Ref<Occluder3D> o = oi->get_occluder();
if (o.is_null()) {
return;
}
Transform3D gt = oi->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 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) };
bool snap_enabled = Node3DEditor::get_singleton()->is_snap_enabled();
float snap = Node3DEditor::get_singleton()->get_translate_snap();
if (Object::cast_to<SphereOccluder3D>(*o)) {
Ref<SphereOccluder3D> so = o;
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 (snap_enabled) {
d = Math::snapped(d, snap);
}
if (d < 0.001) {
d = 0.001;
}
so->set_radius(d);
}
if (Object::cast_to<BoxOccluder3D>(*o)) {
Vector3 axis;
axis[p_id] = 1.0;
Ref<BoxOccluder3D> bo = o;
Vector3 ra, rb;
Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
float d = ra[p_id] * 2;
if (snap_enabled) {
d = Math::snapped(d, snap);
}
if (d < 0.001) {
d = 0.001;
}
Vector3 he = bo->get_size();
he[p_id] = d;
bo->set_size(he);
}
if (Object::cast_to<QuadOccluder3D>(*o)) {
Ref<QuadOccluder3D> qo = o;
Plane p = Plane(Vector3(0.0f, 0.0f, 1.0f), 0.0f);
Vector3 intersection;
if (!p.intersects_segment(sg[0], sg[1], &intersection)) {
return;
}
if (p_id == 2) {
Vector2 s = Vector2(intersection.x, intersection.y) * 2.0f;
if (snap_enabled) {
s = s.snappedf(snap);
}
s = s.maxf(0.001);
qo->set_size(s);
} else {
float d = intersection[p_id];
if (snap_enabled) {
d = Math::snapped(d, snap);
}
if (d < 0.001) {
d = 0.001;
}
Vector2 he = qo->get_size();
he[p_id] = d * 2.0f;
qo->set_size(he);
}
}
}
void OccluderInstance3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
OccluderInstance3D *oi = Object::cast_to<OccluderInstance3D>(p_gizmo->get_node_3d());
Ref<Occluder3D> o = oi->get_occluder();
if (o.is_null()) {
return;
}
if (Object::cast_to<SphereOccluder3D>(*o)) {
Ref<SphereOccluder3D> so = o;
if (p_cancel) {
so->set_radius(p_restore);
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Sphere Shape Radius"));
ur->add_do_method(so.ptr(), "set_radius", so->get_radius());
ur->add_undo_method(so.ptr(), "set_radius", p_restore);
ur->commit_action();
}
if (Object::cast_to<BoxOccluder3D>(*o)) {
Ref<BoxOccluder3D> bo = o;
if (p_cancel) {
bo->set_size(p_restore);
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Box Shape Size"));
ur->add_do_method(bo.ptr(), "set_size", bo->get_size());
ur->add_undo_method(bo.ptr(), "set_size", p_restore);
ur->commit_action();
}
if (Object::cast_to<QuadOccluder3D>(*o)) {
Ref<QuadOccluder3D> qo = o;
if (p_cancel) {
qo->set_size(p_restore);
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Box Shape Size"));
ur->add_do_method(qo.ptr(), "set_size", qo->get_size());
ur->add_undo_method(qo.ptr(), "set_size", p_restore);
ur->commit_action();
}
}
void OccluderInstance3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
OccluderInstance3D *occluder_instance = Object::cast_to<OccluderInstance3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
Ref<Occluder3D> o = occluder_instance->get_occluder();
if (o.is_null()) {
return;
}
Vector<Vector3> lines = o->get_debug_lines();
if (!lines.is_empty()) {
Ref<Material> material = get_material("line_material", p_gizmo);
p_gizmo->add_lines(lines, material);
p_gizmo->add_collision_segments(lines);
}
Ref<Material> handles_material = get_material("handles");
if (Object::cast_to<SphereOccluder3D>(*o)) {
Ref<SphereOccluder3D> so = o;
float r = so->get_radius();
Vector<Vector3> handles = { Vector3(r, 0, 0) };
p_gizmo->add_handles(handles, handles_material);
}
if (Object::cast_to<BoxOccluder3D>(*o)) {
Ref<BoxOccluder3D> bo = o;
Vector<Vector3> handles;
for (int i = 0; i < 3; i++) {
Vector3 ax;
ax[i] = bo->get_size()[i] / 2;
handles.push_back(ax);
}
p_gizmo->add_handles(handles, handles_material);
}
if (Object::cast_to<QuadOccluder3D>(*o)) {
Ref<QuadOccluder3D> qo = o;
Vector2 size = qo->get_size();
Vector3 s = Vector3(size.x, size.y, 0.0f) / 2.0f;
Vector<Vector3> handles = { Vector3(s.x, 0.0f, 0.0f), Vector3(0.0f, s.y, 0.0f), Vector3(s.x, s.y, 0.0f) };
p_gizmo->add_handles(handles, handles_material);
}
}

View File

@@ -0,0 +1,50 @@
/**************************************************************************/
/* occluder_instance_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class OccluderInstance3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(OccluderInstance3DGizmoPlugin, 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;
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;
OccluderInstance3DGizmoPlugin();
};

View File

@@ -0,0 +1,339 @@
/**************************************************************************/
/* particles_3d_emission_shape_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 "particles_3d_emission_shape_gizmo_plugin.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/scene/3d/node_3d_editor_plugin.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/cpu_particles_3d.h"
#include "scene/3d/gpu_particles_3d.h"
#include "scene/resources/3d/primitive_meshes.h"
#include "scene/resources/particle_process_material.h"
Particles3DEmissionShapeGizmoPlugin::Particles3DEmissionShapeGizmoPlugin() {
helper.instantiate();
Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/particles_emission_shape", Color(0.5, 0.7, 1));
create_material("particles_emission_shape_material", gizmo_color);
create_handle_material("handles");
}
bool Particles3DEmissionShapeGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<GPUParticles3D>(p_spatial) || Object::cast_to<CPUParticles3D>(p_spatial) != nullptr;
}
String Particles3DEmissionShapeGizmoPlugin::get_gizmo_name() const {
return "Particles3DEmissionShape";
}
int Particles3DEmissionShapeGizmoPlugin::get_priority() const {
return -1;
}
bool Particles3DEmissionShapeGizmoPlugin::is_selectable_when_hidden() const {
return true;
}
String Particles3DEmissionShapeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
return "";
}
Variant Particles3DEmissionShapeGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
return Variant();
}
void Particles3DEmissionShapeGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
}
void Particles3DEmissionShapeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
}
void Particles3DEmissionShapeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->clear();
if (Object::cast_to<GPUParticles3D>(p_gizmo->get_node_3d())) {
const GPUParticles3D *particles = Object::cast_to<GPUParticles3D>(p_gizmo->get_node_3d());
const Ref<ParticleProcessMaterial> mat = particles->get_process_material();
if (mat.is_valid()) {
const ParticleProcessMaterial::EmissionShape shape = mat->get_emission_shape();
const Ref<Material> material = get_material("particles_emission_shape_material", p_gizmo);
const Ref<Material> handles_material = get_material("handles");
if (shape == ParticleProcessMaterial::EMISSION_SHAPE_SPHERE || shape == ParticleProcessMaterial::EMISSION_SHAPE_SPHERE_SURFACE) {
const Vector3 offset = mat->get_emission_shape_offset();
const Vector3 scale = mat->get_emission_shape_scale();
const float r = mat->get_emission_sphere_radius();
Vector<Vector3> points;
for (int i = 0; i <= 120; i++) {
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;
points.push_back(Vector3(a.x * scale.x + offset.x, offset.y, a.y * scale.z + offset.z));
points.push_back(Vector3(b.x * scale.x + offset.x, offset.y, b.y * scale.z + offset.z));
points.push_back(Vector3(offset.x, a.x * scale.y + offset.y, a.y * scale.z + offset.z));
points.push_back(Vector3(offset.x, b.x * scale.y + offset.y, b.y * scale.z + offset.z));
points.push_back(Vector3(a.x * scale.x + offset.x, a.y * scale.y + offset.y, offset.z));
points.push_back(Vector3(b.x * scale.x + offset.x, b.y * scale.y + offset.y, offset.z));
}
if (p_gizmo->is_selected()) {
p_gizmo->add_lines(points, material);
}
} else if (shape == ParticleProcessMaterial::EMISSION_SHAPE_BOX) {
const Vector3 offset = mat->get_emission_shape_offset();
const Vector3 scale = mat->get_emission_shape_scale();
const Vector3 box_extents = mat->get_emission_box_extents();
Ref<BoxMesh> box;
box.instantiate();
const AABB box_aabb = box->get_aabb();
Vector<Vector3> lines;
for (int i = 0; i < 12; i++) {
Vector3 a;
Vector3 b;
box_aabb.get_edge(i, a, b);
// Multiplication by 2 due to the extents being only half of the box size.
lines.push_back(a * 2.0 * scale * box_extents + offset);
lines.push_back(b * 2.0 * scale * box_extents + offset);
}
if (p_gizmo->is_selected()) {
p_gizmo->add_lines(lines, material);
}
} else if (shape == ParticleProcessMaterial::EMISSION_SHAPE_RING) {
const Vector3 offset = mat->get_emission_shape_offset();
const Vector3 scale = mat->get_emission_shape_scale();
const float ring_height = mat->get_emission_ring_height();
const float half_ring_height = ring_height / 2;
const float ring_radius = mat->get_emission_ring_radius();
const float ring_inner_radius = mat->get_emission_ring_inner_radius();
const Vector3 ring_axis = mat->get_emission_ring_axis();
const float ring_cone_angle = mat->get_emission_ring_cone_angle();
const float ring_radius_top = MAX(ring_radius - Math::tan(Math::deg_to_rad(90.0 - ring_cone_angle)) * ring_height, 0.0);
const float ring_inner_radius_top = (ring_inner_radius / ring_radius) * ring_radius_top;
Vector<Vector3> points;
Basis basis;
basis.rows[1] = ring_axis.normalized();
basis.rows[0] = Vector3(basis[1][1], -basis[1][2], -basis[1][0]).normalized();
basis.rows[0] = (basis[0] - basis[0].dot(basis[1]) * basis[1]).normalized();
basis[2] = basis[0].cross(basis[1]).normalized();
basis.invert();
for (int i = 0; i <= 120; i++) {
const float ra = Math::deg_to_rad((float)(i * 3));
const float ra_sin = Math::sin(ra);
const float ra_cos = Math::cos(ra);
const float rb = Math::deg_to_rad((float)((i + 1) * 3));
const float rb_sin = Math::sin(rb);
const float rb_cos = Math::cos(rb);
const Point2 a = Vector2(ra_sin, ra_cos) * ring_radius;
const Point2 b = Vector2(rb_sin, rb_cos) * ring_radius;
const Point2 a2 = Vector2(ra_sin, ra_cos) * ring_radius_top;
const Point2 b2 = Vector2(rb_sin, rb_cos) * ring_radius_top;
const Point2 inner_a = Vector2(ra_sin, ra_cos) * ring_inner_radius;
const Point2 inner_b = Vector2(rb_sin, rb_cos) * ring_inner_radius;
const Point2 inner_a2 = Vector2(ra_sin, ra_cos) * ring_inner_radius_top;
const Point2 inner_b2 = Vector2(rb_sin, rb_cos) * ring_inner_radius_top;
// Outer top ring cap.
points.push_back(basis.xform(Vector3(a2.x, half_ring_height, a2.y)) * scale + offset);
points.push_back(basis.xform(Vector3(b2.x, half_ring_height, b2.y)) * scale + offset);
// Outer bottom ring cap.
points.push_back(basis.xform(Vector3(a.x, -half_ring_height, a.y)) * scale + offset);
points.push_back(basis.xform(Vector3(b.x, -half_ring_height, b.y)) * scale + offset);
// Inner top ring cap.
points.push_back(basis.xform(Vector3(inner_a2.x, half_ring_height, inner_a2.y)) * scale + offset);
points.push_back(basis.xform(Vector3(inner_b2.x, half_ring_height, inner_b2.y)) * scale + offset);
// Inner bottom ring cap.
points.push_back(basis.xform(Vector3(inner_a.x, -half_ring_height, inner_a.y)) * scale + offset);
points.push_back(basis.xform(Vector3(inner_b.x, -half_ring_height, inner_b.y)) * scale + offset);
}
for (int i = 0; i <= 120; i = i + 30) {
const float ra = Math::deg_to_rad((float)(i * 3));
const float ra_sin = Math::sin(ra);
const float ra_cos = Math::cos(ra);
const Point2 a = Vector2(ra_sin, ra_cos) * ring_radius;
const Point2 a2 = Vector2(ra_sin, ra_cos) * ring_radius_top;
const Point2 inner_a = Vector2(ra_sin, ra_cos) * ring_inner_radius;
const Point2 inner_a2 = Vector2(ra_sin, ra_cos) * ring_inner_radius_top;
// Outer 90 degrees vertical lines.
points.push_back(basis.xform(Vector3(a2.x, half_ring_height, a2.y)) * scale + offset);
points.push_back(basis.xform(Vector3(a.x, -half_ring_height, a.y)) * scale + offset);
// Inner 90 degrees vertical lines.
points.push_back(basis.xform(Vector3(inner_a2.x, half_ring_height, inner_a2.y)) * scale + offset);
points.push_back(basis.xform(Vector3(inner_a.x, -half_ring_height, inner_a.y)) * scale + offset);
}
if (p_gizmo->is_selected()) {
p_gizmo->add_lines(points, material);
}
}
}
} else if (Object::cast_to<CPUParticles3D>(p_gizmo->get_node_3d())) {
const CPUParticles3D *particles = Object::cast_to<CPUParticles3D>(p_gizmo->get_node_3d());
const CPUParticles3D::EmissionShape shape = particles->get_emission_shape();
const Ref<Material> material = get_material("particles_emission_shape_material", p_gizmo);
const Ref<Material> handles_material = get_material("handles");
if (shape == CPUParticles3D::EMISSION_SHAPE_SPHERE || shape == CPUParticles3D::EMISSION_SHAPE_SPHERE_SURFACE) {
const float r = particles->get_emission_sphere_radius();
Vector<Vector3> points;
for (int i = 0; i <= 120; i++) {
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;
points.push_back(Vector3(a.x, 0.0, a.y));
points.push_back(Vector3(b.x, 0.0, b.y));
points.push_back(Vector3(0.0, a.x, a.y));
points.push_back(Vector3(0.0, b.x, b.y));
points.push_back(Vector3(a.x, a.y, 0.0));
points.push_back(Vector3(b.x, b.y, 0.0));
}
if (p_gizmo->is_selected()) {
p_gizmo->add_lines(points, material);
}
} else if (shape == CPUParticles3D::EMISSION_SHAPE_BOX) {
const Vector3 box_extents = particles->get_emission_box_extents();
Ref<BoxMesh> box;
box.instantiate();
const AABB box_aabb = box->get_aabb();
Vector<Vector3> lines;
for (int i = 0; i < 12; i++) {
Vector3 a;
Vector3 b;
box_aabb.get_edge(i, a, b);
// Multiplication by 2 due to the extents being only half of the box size.
lines.push_back(a * 2.0 * box_extents);
lines.push_back(b * 2.0 * box_extents);
}
if (p_gizmo->is_selected()) {
p_gizmo->add_lines(lines, material);
}
} else if (shape == CPUParticles3D::EMISSION_SHAPE_RING) {
const float ring_height = particles->get_emission_ring_height();
const float half_ring_height = ring_height / 2;
const float ring_radius = particles->get_emission_ring_radius();
const float ring_inner_radius = particles->get_emission_ring_inner_radius();
const Vector3 ring_axis = particles->get_emission_ring_axis();
const float ring_cone_angle = particles->get_emission_ring_cone_angle();
const float ring_radius_top = MAX(ring_radius - Math::tan(Math::deg_to_rad(90.0 - ring_cone_angle)) * ring_height, 0.0);
const float ring_inner_radius_top = (ring_inner_radius / ring_radius) * ring_radius_top;
Vector<Vector3> points;
Basis basis;
basis.rows[1] = ring_axis.normalized();
basis.rows[0] = Vector3(basis[1][1], -basis[1][2], -basis[1][0]).normalized();
basis.rows[0] = (basis[0] - basis[0].dot(basis[1]) * basis[1]).normalized();
basis[2] = basis[0].cross(basis[1]).normalized();
basis.invert();
for (int i = 0; i <= 120; i++) {
const float ra = Math::deg_to_rad((float)(i * 3));
const float ra_sin = Math::sin(ra);
const float ra_cos = Math::cos(ra);
const float rb = Math::deg_to_rad((float)((i + 1) * 3));
const float rb_sin = Math::sin(rb);
const float rb_cos = Math::cos(rb);
const Point2 a = Vector2(ra_sin, ra_cos) * ring_radius;
const Point2 b = Vector2(rb_sin, rb_cos) * ring_radius;
const Point2 a2 = Vector2(ra_sin, ra_cos) * ring_radius_top;
const Point2 b2 = Vector2(rb_sin, rb_cos) * ring_radius_top;
const Point2 inner_a = Vector2(ra_sin, ra_cos) * ring_inner_radius;
const Point2 inner_b = Vector2(rb_sin, rb_cos) * ring_inner_radius;
const Point2 inner_a2 = Vector2(ra_sin, ra_cos) * ring_inner_radius_top;
const Point2 inner_b2 = Vector2(rb_sin, rb_cos) * ring_inner_radius_top;
// Outer top ring cap.
points.push_back(basis.xform(Vector3(a2.x, half_ring_height, a2.y)));
points.push_back(basis.xform(Vector3(b2.x, half_ring_height, b2.y)));
// Outer bottom ring cap.
points.push_back(basis.xform(Vector3(a.x, -half_ring_height, a.y)));
points.push_back(basis.xform(Vector3(b.x, -half_ring_height, b.y)));
// Inner top ring cap.
points.push_back(basis.xform(Vector3(inner_a2.x, half_ring_height, inner_a2.y)));
points.push_back(basis.xform(Vector3(inner_b2.x, half_ring_height, inner_b2.y)));
// Inner bottom ring cap.
points.push_back(basis.xform(Vector3(inner_a.x, -half_ring_height, inner_a.y)));
points.push_back(basis.xform(Vector3(inner_b.x, -half_ring_height, inner_b.y)));
}
for (int i = 0; i <= 120; i = i + 30) {
const float ra = Math::deg_to_rad((float)(i * 3));
const float ra_sin = Math::sin(ra);
const float ra_cos = Math::cos(ra);
const Point2 a = Vector2(ra_sin, ra_cos) * ring_radius;
const Point2 a2 = Vector2(ra_sin, ra_cos) * ring_radius_top;
const Point2 inner_a = Vector2(ra_sin, ra_cos) * ring_inner_radius;
const Point2 inner_a2 = Vector2(ra_sin, ra_cos) * ring_inner_radius_top;
// Outer 90 degrees vertical lines.
points.push_back(basis.xform(Vector3(a2.x, half_ring_height, a2.y)));
points.push_back(basis.xform(Vector3(a.x, -half_ring_height, a.y)));
// Inner 90 degrees vertical lines.
points.push_back(basis.xform(Vector3(inner_a2.x, half_ring_height, inner_a2.y)));
points.push_back(basis.xform(Vector3(inner_a.x, -half_ring_height, inner_a.y)));
}
if (p_gizmo->is_selected()) {
p_gizmo->add_lines(points, material);
}
}
}
}

View File

@@ -0,0 +1,54 @@
/**************************************************************************/
/* particles_3d_emission_shape_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/gizmos/gizmo_3d_helper.h"
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class Particles3DEmissionShapeGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(Particles3DEmissionShapeGizmoPlugin, EditorNode3DGizmoPlugin);
Ref<Gizmo3DHelper> helper;
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;
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;
Particles3DEmissionShapeGizmoPlugin();
};

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env.add_source_files(env.editor_sources, "*.cpp")

View File

@@ -0,0 +1,84 @@
/**************************************************************************/
/* collision_object_3d_gizmo_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "collision_object_3d_gizmo_plugin.h"
#include "scene/3d/physics/collision_object_3d.h"
#include "scene/3d/physics/collision_polygon_3d.h"
#include "scene/3d/physics/collision_shape_3d.h"
#include "scene/resources/surface_tool.h"
CollisionObject3DGizmoPlugin::CollisionObject3DGizmoPlugin() {
const Color gizmo_color = SceneTree::get_singleton()->get_debug_collisions_color();
create_material("shape_material", gizmo_color);
const float gizmo_value = gizmo_color.get_v();
const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65);
create_material("shape_material_disabled", gizmo_color_disabled);
}
bool CollisionObject3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<CollisionObject3D>(p_spatial) != nullptr;
}
String CollisionObject3DGizmoPlugin::get_gizmo_name() const {
return "CollisionObject3D";
}
int CollisionObject3DGizmoPlugin::get_priority() const {
return -2;
}
void CollisionObject3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
List<uint32_t> owner_ids;
co->get_shape_owners(&owner_ids);
for (uint32_t &owner_id : owner_ids) {
Transform3D xform = co->shape_owner_get_transform(owner_id);
Object *owner = co->shape_owner_get_owner(owner_id);
// Exclude CollisionShape3D and CollisionPolygon3D as they have their gizmo.
if (!Object::cast_to<CollisionShape3D>(owner) && !Object::cast_to<CollisionPolygon3D>(owner)) {
Ref<Material> material = get_material(!co->is_shape_owner_disabled(owner_id) ? "shape_material" : "shape_material_disabled", p_gizmo);
for (int shape_id = 0; shape_id < co->shape_owner_get_shape_count(owner_id); shape_id++) {
Ref<Shape3D> s = co->shape_owner_get_shape(owner_id, shape_id);
if (s.is_null()) {
continue;
}
SurfaceTool st;
st.append_from(s->get_debug_mesh(), 0, xform);
p_gizmo->add_mesh(st.commit(), material);
p_gizmo->add_collision_segments(s->get_debug_mesh_lines());
}
}
}
}

View File

@@ -0,0 +1,45 @@
/**************************************************************************/
/* collision_object_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/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();
};

View File

@@ -0,0 +1,237 @@
/**************************************************************************/
/* collision_polygon_3d_gizmo_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "collision_polygon_3d_gizmo_plugin.h"
#include "core/math/geometry_2d.h"
#include "scene/3d/physics/collision_polygon_3d.h"
CollisionPolygon3DGizmoPlugin::CollisionPolygon3DGizmoPlugin() {
create_collision_material("shape_material", 2.0);
create_collision_material("shape_material_arraymesh", 0.0625);
create_collision_material("shape_material_disabled", 0.0625);
create_collision_material("shape_material_arraymesh_disabled", 0.015625);
}
void CollisionPolygon3DGizmoPlugin::create_collision_material(const String &p_name, float p_alpha) {
Vector<Ref<StandardMaterial3D>> mats;
const Color collision_color(1.0, 1.0, 1.0, p_alpha);
for (int i = 0; i < 4; i++) {
bool instantiated = i < 2;
Ref<StandardMaterial3D> material;
material.instantiate();
Color color = collision_color;
color.a *= instantiated ? 0.25 : 1.0;
material->set_albedo(color);
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1);
material->set_cull_mode(StandardMaterial3D::CULL_BACK);
material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
mats.push_back(material);
}
materials[p_name] = mats;
}
bool CollisionPolygon3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<CollisionPolygon3D>(p_spatial) != nullptr;
}
String CollisionPolygon3DGizmoPlugin::get_gizmo_name() const {
return "CollisionPolygon3D";
}
int CollisionPolygon3DGizmoPlugin::get_priority() const {
return -1;
}
void CollisionPolygon3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
CollisionPolygon3D *polygon = Object::cast_to<CollisionPolygon3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
const Ref<StandardMaterial3D> material =
get_material(!polygon->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo);
const Ref<StandardMaterial3D> material_arraymesh =
get_material(!polygon->is_disabled() ? "shape_material_arraymesh" : "shape_material_arraymesh_disabled", p_gizmo);
const Color collision_color = polygon->is_disabled() ? Color(1.0, 1.0, 1.0, 0.75) : polygon->get_debug_color();
Vector<Vector2> points = polygon->get_polygon();
float depth = polygon->get_depth() * 0.5;
Vector<Vector3> lines;
const int points_size = points.size();
for (int i = 0; i < points_size; i++) {
int n = (i + 1) % points_size;
lines.push_back(Vector3(points[i].x, points[i].y, depth));
lines.push_back(Vector3(points[n].x, points[n].y, depth));
lines.push_back(Vector3(points[i].x, points[i].y, -depth));
lines.push_back(Vector3(points[n].x, points[n].y, -depth));
lines.push_back(Vector3(points[i].x, points[i].y, depth));
lines.push_back(Vector3(points[i].x, points[i].y, -depth));
}
if (polygon->get_debug_fill_enabled()) {
Ref<ArrayMesh> array_mesh;
array_mesh.instantiate();
Vector<Vector3> verts;
Vector<Color> colors;
Vector<int> indices;
// Determine orientation of the 2D polygon's vertices to determine
// which direction to draw outer polygons.
float signed_area = 0.0f;
for (int i = 0; i < points_size; i++) {
const int j = (i + 1) % points_size;
signed_area += points[i].x * points[j].y - points[j].x * points[i].y;
}
// Generate triangles for the sides of the extruded polygon.
for (int i = 0; i < points_size; i++) {
verts.push_back(Vector3(points[i].x, points[i].y, depth));
verts.push_back(Vector3(points[i].x, points[i].y, -depth));
colors.push_back(collision_color);
colors.push_back(collision_color);
}
const int verts_size = verts.size();
for (int i = 0; i < verts_size; i += 2) {
const int j = (i + 1) % verts_size;
const int k = (i + 2) % verts_size;
const int l = (i + 3) % verts_size;
indices.push_back(i);
if (signed_area < 0) {
indices.push_back(j);
indices.push_back(k);
} else {
indices.push_back(k);
indices.push_back(j);
}
indices.push_back(j);
if (signed_area < 0) {
indices.push_back(l);
indices.push_back(k);
} else {
indices.push_back(k);
indices.push_back(l);
}
}
Vector<Vector<Vector2>> decomp = Geometry2D::decompose_polygon_in_convex(polygon->get_polygon());
// Generate triangles for the bottom cap of the extruded polygon.
for (int i = 0; i < decomp.size(); i++) {
Vector<Vector3> cap_verts_bottom;
Vector<Color> cap_colours_bottom;
Vector<int> cap_indices_bottom;
const int index_offset = verts_size;
const Vector<Vector2> &convex = decomp[i];
const int convex_size = convex.size();
for (int j = 0; j < convex_size; j++) {
cap_verts_bottom.push_back(Vector3(convex[j].x, convex[j].y, -depth));
cap_colours_bottom.push_back(collision_color);
}
if (convex_size >= 3) {
for (int j = 1; j < convex_size; j++) {
const int k = (j + 1) % convex_size;
cap_indices_bottom.push_back(index_offset + 0);
cap_indices_bottom.push_back(index_offset + j);
cap_indices_bottom.push_back(index_offset + k);
}
}
verts.append_array(cap_verts_bottom);
colors.append_array(cap_colours_bottom);
indices.append_array(cap_indices_bottom);
}
// Generate triangles for the top cap of the extruded polygon.
for (int i = 0; i < decomp.size(); i++) {
Vector<Vector3> cap_verts_top;
Vector<Color> cap_colours_top;
Vector<int> cap_indices_top;
const int index_offset = verts_size;
const Vector<Vector2> &convex = decomp[i];
const int convex_size = convex.size();
for (int j = 0; j < convex_size; j++) {
cap_verts_top.push_back(Vector3(convex[j].x, convex[j].y, depth));
cap_colours_top.push_back(collision_color);
}
if (convex_size >= 3) {
for (int j = 1; j < convex_size; j++) {
const int k = (j + 1) % convex_size;
cap_indices_top.push_back(index_offset + k);
cap_indices_top.push_back(index_offset + j);
cap_indices_top.push_back(index_offset + 0);
}
}
verts.append_array(cap_verts_top);
colors.append_array(cap_colours_top);
indices.append_array(cap_indices_top);
}
Array a;
a.resize(Mesh::ARRAY_MAX);
a[RS::ARRAY_VERTEX] = verts;
a[RS::ARRAY_COLOR] = colors;
a[RS::ARRAY_INDEX] = indices;
array_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a);
p_gizmo->add_mesh(array_mesh, material_arraymesh);
}
p_gizmo->add_lines(lines, material, false, collision_color);
p_gizmo->add_collision_segments(lines);
}

View File

@@ -0,0 +1,46 @@
/**************************************************************************/
/* collision_polygon_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class CollisionPolygon3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(CollisionPolygon3DGizmoPlugin, EditorNode3DGizmoPlugin);
void create_collision_material(const String &p_name, float p_alpha);
public:
bool has_gizmo(Node3D *p_spatial) override;
String get_gizmo_name() const override;
int get_priority() const override;
void redraw(EditorNode3DGizmo *p_gizmo) override;
CollisionPolygon3DGizmoPlugin();
};

View File

@@ -0,0 +1,669 @@
/**************************************************************************/
/* collision_shape_3d_gizmo_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "collision_shape_3d_gizmo_plugin.h"
#include "core/math/convex_hull.h"
#include "core/math/geometry_3d.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/scene/3d/gizmos/gizmo_3d_helper.h"
#include "editor/scene/3d/node_3d_editor_plugin.h"
#include "scene/3d/physics/collision_shape_3d.h"
#include "scene/resources/3d/box_shape_3d.h"
#include "scene/resources/3d/capsule_shape_3d.h"
#include "scene/resources/3d/concave_polygon_shape_3d.h"
#include "scene/resources/3d/convex_polygon_shape_3d.h"
#include "scene/resources/3d/cylinder_shape_3d.h"
#include "scene/resources/3d/height_map_shape_3d.h"
#include "scene/resources/3d/separation_ray_shape_3d.h"
#include "scene/resources/3d/sphere_shape_3d.h"
#include "scene/resources/3d/world_boundary_shape_3d.h"
CollisionShape3DGizmoPlugin::CollisionShape3DGizmoPlugin() {
helper.instantiate();
create_collision_material("shape_material", 2.0);
create_collision_material("shape_material_arraymesh", 0.0625);
create_collision_material("shape_material_disabled", 0.0625);
create_collision_material("shape_material_arraymesh_disabled", 0.015625);
create_handle_material("handles");
}
void CollisionShape3DGizmoPlugin::create_collision_material(const String &p_name, float p_alpha) {
Vector<Ref<StandardMaterial3D>> mats;
const Color collision_color(1.0, 1.0, 1.0, p_alpha);
for (int i = 0; i < 4; i++) {
bool instantiated = i < 2;
Ref<StandardMaterial3D> material = memnew(StandardMaterial3D);
Color color = collision_color;
color.a *= instantiated ? 0.25 : 1.0;
material->set_albedo(color);
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1);
material->set_cull_mode(StandardMaterial3D::CULL_BACK);
material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
mats.push_back(material);
}
materials[p_name] = mats;
}
bool CollisionShape3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<CollisionShape3D>(p_spatial) != nullptr;
}
String CollisionShape3DGizmoPlugin::get_gizmo_name() const {
return "CollisionShape3D";
}
int CollisionShape3DGizmoPlugin::get_priority() const {
return -1;
}
String CollisionShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
const CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d());
Ref<Shape3D> s = cs->get_shape();
if (s.is_null()) {
return "";
}
if (Object::cast_to<SphereShape3D>(*s)) {
return "Radius";
}
if (Object::cast_to<BoxShape3D>(*s)) {
return helper->box_get_handle_name(p_id);
}
if (Object::cast_to<CapsuleShape3D>(*s)) {
return helper->capsule_get_handle_name(p_id);
}
if (Object::cast_to<CylinderShape3D>(*s)) {
return helper->cylinder_get_handle_name(p_id);
}
if (Object::cast_to<SeparationRayShape3D>(*s)) {
return "Length";
}
return "";
}
Variant CollisionShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d());
Ref<Shape3D> s = cs->get_shape();
if (s.is_null()) {
return Variant();
}
if (Object::cast_to<SphereShape3D>(*s)) {
Ref<SphereShape3D> ss = s;
return ss->get_radius();
}
if (Object::cast_to<BoxShape3D>(*s)) {
Ref<BoxShape3D> bs = s;
return bs->get_size();
}
if (Object::cast_to<CapsuleShape3D>(*s)) {
Ref<CapsuleShape3D> cs2 = s;
return Vector2(cs2->get_radius(), cs2->get_height());
}
if (Object::cast_to<CylinderShape3D>(*s)) {
Ref<CylinderShape3D> cs2 = s;
return Vector2(cs2->get_radius(), cs2->get_height());
}
if (Object::cast_to<SeparationRayShape3D>(*s)) {
Ref<SeparationRayShape3D> cs2 = s;
return cs2->get_length();
}
return Variant();
}
void CollisionShape3DGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) {
helper->initialize_handle_action(get_handle_value(p_gizmo, p_id, p_secondary), p_gizmo->get_node_3d()->get_global_transform());
}
void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d());
Ref<Shape3D> s = cs->get_shape();
if (s.is_null()) {
return;
}
Vector3 sg[2];
helper->get_segment(p_camera, p_point, sg);
if (Object::cast_to<SphereShape3D>(*s)) {
Ref<SphereShape3D> ss = s;
Vector3 ra, rb;
Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb);
float d = ra.x;
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
}
if (d < 0.001) {
d = 0.001;
}
ss->set_radius(d);
}
if (Object::cast_to<SeparationRayShape3D>(*s)) {
Ref<SeparationRayShape3D> rs = s;
Vector3 ra, rb;
Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(0, 0, 4096), sg[0], sg[1], ra, rb);
float d = ra.z;
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
}
if (d < 0.001) {
d = 0.001;
}
rs->set_length(d);
}
if (Object::cast_to<BoxShape3D>(*s)) {
Ref<BoxShape3D> bs = s;
Vector3 size = bs->get_size();
Vector3 position;
helper->box_set_handle(sg, p_id, size, position);
bs->set_size(size);
cs->set_global_position(position);
}
if (Object::cast_to<CapsuleShape3D>(*s)) {
Ref<CapsuleShape3D> cs2 = s;
real_t height = cs2->get_height();
real_t radius = cs2->get_radius();
Vector3 position;
helper->capsule_set_handle(sg, p_id, height, radius, position);
cs2->set_height(height);
cs2->set_radius(radius);
cs->set_global_position(position);
}
if (Object::cast_to<CylinderShape3D>(*s)) {
Ref<CylinderShape3D> cs2 = s;
real_t height = cs2->get_height();
real_t radius = cs2->get_radius();
Vector3 position;
helper->cylinder_set_handle(sg, p_id, height, radius, position);
cs2->set_height(height);
cs2->set_radius(radius);
cs->set_global_position(position);
}
}
void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d());
Ref<Shape3D> s = cs->get_shape();
if (s.is_null()) {
return;
}
if (Object::cast_to<SphereShape3D>(*s)) {
Ref<SphereShape3D> ss = s;
if (p_cancel) {
ss->set_radius(p_restore);
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Sphere Shape Radius"));
ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
ur->add_undo_method(ss.ptr(), "set_radius", p_restore);
ur->commit_action();
}
if (Object::cast_to<BoxShape3D>(*s)) {
helper->box_commit_handle(TTR("Change Box Shape Size"), p_cancel, cs, s.ptr());
}
if (Object::cast_to<CapsuleShape3D>(*s)) {
Ref<CapsuleShape3D> ss = s;
helper->cylinder_commit_handle(p_id, TTR("Change Capsule Shape Radius"), TTR("Change Capsule Shape Height"), p_cancel, cs, *ss, *ss);
}
if (Object::cast_to<CylinderShape3D>(*s)) {
Ref<CylinderShape3D> ss = s;
helper->cylinder_commit_handle(p_id, TTR("Change Cylinder Shape Radius"), TTR("Change Cylinder Shape Height"), p_cancel, cs, *ss, *ss);
}
if (Object::cast_to<SeparationRayShape3D>(*s)) {
Ref<SeparationRayShape3D> ss = s;
if (p_cancel) {
ss->set_length(p_restore);
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Separation Ray Shape Length"));
ur->add_do_method(ss.ptr(), "set_length", ss->get_length());
ur->add_undo_method(ss.ptr(), "set_length", p_restore);
ur->commit_action();
}
}
void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
Ref<Shape3D> s = cs->get_shape();
if (s.is_null()) {
return;
}
const Ref<StandardMaterial3D> material =
get_material(!cs->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo);
const Ref<StandardMaterial3D> material_arraymesh =
get_material(!cs->is_disabled() ? "shape_material_arraymesh" : "shape_material_arraymesh_disabled", p_gizmo);
const Ref<Material> handles_material = get_material("handles");
const Color collision_color = cs->is_disabled() ? Color(1.0, 1.0, 1.0, 0.75) : cs->get_debug_color();
if (cs->get_debug_fill_enabled()) {
Ref<ArrayMesh> array_mesh = s->get_debug_arraymesh_faces(collision_color);
if (array_mesh.is_valid() && array_mesh->get_surface_count() > 0) {
p_gizmo->add_mesh(array_mesh, material_arraymesh);
}
}
if (Object::cast_to<SphereShape3D>(*s)) {
Ref<SphereShape3D> sp = s;
float radius = sp->get_radius();
#define PUSH_QUARTER(from_x, from_y, to_x, to_y, y) \
points_ptrw[index++] = Vector3(from_x, y, from_y); \
points_ptrw[index++] = Vector3(to_x, y, to_y); \
points_ptrw[index++] = Vector3(from_x, y, -from_y); \
points_ptrw[index++] = Vector3(to_x, y, -to_y); \
points_ptrw[index++] = Vector3(-from_x, y, from_y); \
points_ptrw[index++] = Vector3(-to_x, y, to_y); \
points_ptrw[index++] = Vector3(-from_x, y, -from_y); \
points_ptrw[index++] = Vector3(-to_x, y, -to_y);
#define PUSH_QUARTER_XY(from_x, from_y, to_x, to_y, y) \
points_ptrw[index++] = Vector3(from_x, -from_y - y, 0); \
points_ptrw[index++] = Vector3(to_x, -to_y - y, 0); \
points_ptrw[index++] = Vector3(from_x, from_y + y, 0); \
points_ptrw[index++] = Vector3(to_x, to_y + y, 0); \
points_ptrw[index++] = Vector3(-from_x, -from_y - y, 0); \
points_ptrw[index++] = Vector3(-to_x, -to_y - y, 0); \
points_ptrw[index++] = Vector3(-from_x, from_y + y, 0); \
points_ptrw[index++] = Vector3(-to_x, to_y + y, 0);
#define PUSH_QUARTER_YZ(from_x, from_y, to_x, to_y, y) \
points_ptrw[index++] = Vector3(0, -from_y - y, from_x); \
points_ptrw[index++] = Vector3(0, -to_y - y, to_x); \
points_ptrw[index++] = Vector3(0, from_y + y, from_x); \
points_ptrw[index++] = Vector3(0, to_y + y, to_x); \
points_ptrw[index++] = Vector3(0, -from_y - y, -from_x); \
points_ptrw[index++] = Vector3(0, -to_y - y, -to_x); \
points_ptrw[index++] = Vector3(0, from_y + y, -from_x); \
points_ptrw[index++] = Vector3(0, to_y + y, -to_x);
// Number of points in an octant. So there will be 8 * points_in_octant * 2 points in total for one circle.
// This Corresponds to the smoothness of the circle.
const uint32_t points_in_octant = 16;
const real_t inc = (Math::PI / (4 * points_in_octant));
const real_t radius_squared = radius * radius;
real_t r = 0;
Vector<Vector3> points;
uint32_t index = 0;
// 3 full circles.
points.resize(3 * 8 * points_in_octant * 2);
Vector3 *points_ptrw = points.ptrw();
float previous_x = radius;
float previous_y = 0.f;
for (uint32_t i = 0; i < points_in_octant; ++i) {
r += inc;
real_t x = Math::cos(r) * radius;
real_t y = Math::sqrt(radius_squared - (x * x));
PUSH_QUARTER(previous_x, previous_y, x, y, 0);
PUSH_QUARTER(previous_y, previous_x, y, x, 0);
PUSH_QUARTER_XY(previous_x, previous_y, x, y, 0);
PUSH_QUARTER_XY(previous_y, previous_x, y, x, 0);
PUSH_QUARTER_YZ(previous_x, previous_y, x, y, 0);
PUSH_QUARTER_YZ(previous_y, previous_x, y, x, 0)
previous_x = x;
previous_y = y;
}
#undef PUSH_QUARTER
#undef PUSH_QUARTER_XY
#undef PUSH_QUARTER_YZ
p_gizmo->add_lines(points, material, false, collision_color);
p_gizmo->add_collision_segments(points);
Vector<Vector3> handles;
handles.push_back(Vector3(radius, 0, 0));
p_gizmo->add_handles(handles, handles_material);
}
if (Object::cast_to<BoxShape3D>(*s)) {
Ref<BoxShape3D> bs = s;
Vector<Vector3> lines;
AABB aabb;
aabb.position = -bs->get_size() / 2;
aabb.size = bs->get_size();
for (int i = 0; i < 12; i++) {
Vector3 a, b;
aabb.get_edge(i, a, b);
lines.push_back(a);
lines.push_back(b);
}
const Vector<Vector3> handles = helper->box_get_handles(bs->get_size());
p_gizmo->add_lines(lines, material, false, collision_color);
p_gizmo->add_collision_segments(lines);
p_gizmo->add_handles(handles, handles_material);
}
if (Object::cast_to<CapsuleShape3D>(*s)) {
Ref<CapsuleShape3D> cs2 = s;
float radius = cs2->get_radius();
float height = cs2->get_height();
// Number of points in an octant. So there will be 8 * points_in_octant points in total.
// This corresponds to the smoothness of the circle.
const uint32_t points_in_octant = 16;
const real_t octant_angle = Math::PI / 4;
const real_t inc = (Math::PI / (4 * points_in_octant));
const real_t radius_squared = radius * radius;
real_t r = 0;
Vector<Vector3> points;
// 4 vertical lines and 4 full circles.
points.resize(4 * 2 + 4 * 8 * points_in_octant * 2);
Vector3 *points_ptrw = points.ptrw();
uint32_t index = 0;
float y_value = height * 0.5 - radius;
// Vertical Lines.
points_ptrw[index++] = Vector3(0.f, y_value, radius);
points_ptrw[index++] = Vector3(0.f, -y_value, radius);
points_ptrw[index++] = Vector3(0.f, y_value, -radius);
points_ptrw[index++] = Vector3(0.f, -y_value, -radius);
points_ptrw[index++] = Vector3(radius, y_value, 0.f);
points_ptrw[index++] = Vector3(radius, -y_value, 0.f);
points_ptrw[index++] = Vector3(-radius, y_value, 0.f);
points_ptrw[index++] = Vector3(-radius, -y_value, 0.f);
#define PUSH_QUARTER(from_x, from_y, to_x, to_y, y) \
points_ptrw[index++] = Vector3(from_x, y, from_y); \
points_ptrw[index++] = Vector3(to_x, y, to_y); \
points_ptrw[index++] = Vector3(from_x, y, -from_y); \
points_ptrw[index++] = Vector3(to_x, y, -to_y); \
points_ptrw[index++] = Vector3(-from_x, y, from_y); \
points_ptrw[index++] = Vector3(-to_x, y, to_y); \
points_ptrw[index++] = Vector3(-from_x, y, -from_y); \
points_ptrw[index++] = Vector3(-to_x, y, -to_y);
#define PUSH_QUARTER_XY(from_x, from_y, to_x, to_y, y) \
points_ptrw[index++] = Vector3(from_x, -from_y - y, 0); \
points_ptrw[index++] = Vector3(to_x, -to_y - y, 0); \
points_ptrw[index++] = Vector3(from_x, from_y + y, 0); \
points_ptrw[index++] = Vector3(to_x, to_y + y, 0); \
points_ptrw[index++] = Vector3(-from_x, -from_y - y, 0); \
points_ptrw[index++] = Vector3(-to_x, -to_y - y, 0); \
points_ptrw[index++] = Vector3(-from_x, from_y + y, 0); \
points_ptrw[index++] = Vector3(-to_x, to_y + y, 0);
#define PUSH_QUARTER_YZ(from_x, from_y, to_x, to_y, y) \
points_ptrw[index++] = Vector3(0, -from_y - y, from_x); \
points_ptrw[index++] = Vector3(0, -to_y - y, to_x); \
points_ptrw[index++] = Vector3(0, from_y + y, from_x); \
points_ptrw[index++] = Vector3(0, to_y + y, to_x); \
points_ptrw[index++] = Vector3(0, -from_y - y, -from_x); \
points_ptrw[index++] = Vector3(0, -to_y - y, -to_x); \
points_ptrw[index++] = Vector3(0, from_y + y, -from_x); \
points_ptrw[index++] = Vector3(0, to_y + y, -to_x);
float previous_x = radius;
float previous_y = 0.f;
for (uint32_t i = 0; i < points_in_octant; ++i) {
r += inc;
real_t x = Math::cos((i == points_in_octant - 1) ? octant_angle : r) * radius;
real_t y = Math::sqrt(radius_squared - (x * x));
// High circle ring.
PUSH_QUARTER(previous_x, previous_y, x, y, y_value);
PUSH_QUARTER(previous_y, previous_x, y, x, y_value);
// Low circle ring.
PUSH_QUARTER(previous_x, previous_y, x, y, -y_value);
PUSH_QUARTER(previous_y, previous_x, y, x, -y_value);
// Up and Low circle in X-Y plane.
PUSH_QUARTER_XY(previous_x, previous_y, x, y, y_value);
PUSH_QUARTER_XY(previous_y, previous_x, y, x, y_value);
// Up and Low circle in Y-Z plane.
PUSH_QUARTER_YZ(previous_x, previous_y, x, y, y_value);
PUSH_QUARTER_YZ(previous_y, previous_x, y, x, y_value)
previous_x = x;
previous_y = y;
}
#undef PUSH_QUARTER
#undef PUSH_QUARTER_XY
#undef PUSH_QUARTER_YZ
p_gizmo->add_lines(points, material, false, collision_color);
p_gizmo->add_collision_segments(points);
Vector<Vector3> handles = helper->capsule_get_handles(cs2->get_height(), cs2->get_radius());
p_gizmo->add_handles(handles, handles_material);
}
if (Object::cast_to<CylinderShape3D>(*s)) {
Ref<CylinderShape3D> cs2 = s;
float radius = cs2->get_radius();
float height = cs2->get_height();
#define PUSH_QUARTER(from_x, from_y, to_x, to_y, y) \
points_ptrw[index++] = Vector3(from_x, y, from_y); \
points_ptrw[index++] = Vector3(to_x, y, to_y); \
points_ptrw[index++] = Vector3(from_x, y, -from_y); \
points_ptrw[index++] = Vector3(to_x, y, -to_y); \
points_ptrw[index++] = Vector3(-from_x, y, from_y); \
points_ptrw[index++] = Vector3(-to_x, y, to_y); \
points_ptrw[index++] = Vector3(-from_x, y, -from_y); \
points_ptrw[index++] = Vector3(-to_x, y, -to_y);
// Number of points in an octant. So there will be 8 * points_in_octant * 2 points in total for one circle.
// This corresponds to the smoothness of the circle.
const uint32_t points_in_octant = 16;
const real_t inc = (Math::PI / (4 * points_in_octant));
const real_t radius_squared = radius * radius;
real_t r = 0;
Vector<Vector3> points;
uint32_t index = 0;
// 4 vertical lines and 2 full circles.
points.resize(4 * 2 + 2 * 8 * points_in_octant * 2);
Vector3 *points_ptrw = points.ptrw();
float y_value = height * 0.5;
// Vertical lines.
points_ptrw[index++] = Vector3(0.f, y_value, radius);
points_ptrw[index++] = Vector3(0.f, -y_value, radius);
points_ptrw[index++] = Vector3(0.f, y_value, -radius);
points_ptrw[index++] = Vector3(0.f, -y_value, -radius);
points_ptrw[index++] = Vector3(radius, y_value, 0.f);
points_ptrw[index++] = Vector3(radius, -y_value, 0.f);
points_ptrw[index++] = Vector3(-radius, y_value, 0.f);
points_ptrw[index++] = Vector3(-radius, -y_value, 0.f);
float previous_x = radius;
float previous_y = 0.f;
for (uint32_t i = 0; i < points_in_octant; ++i) {
r += inc;
real_t x = Math::cos(r) * radius;
real_t y = Math::sqrt(radius_squared - (x * x));
// High circle ring.
PUSH_QUARTER(previous_x, previous_y, x, y, y_value);
PUSH_QUARTER(previous_y, previous_x, y, x, y_value);
// Low circle ring.
PUSH_QUARTER(previous_x, previous_y, x, y, -y_value);
PUSH_QUARTER(previous_y, previous_x, y, x, -y_value);
previous_x = x;
previous_y = y;
}
#undef PUSH_QUARTER
p_gizmo->add_lines(points, material, false, collision_color);
p_gizmo->add_collision_segments(points);
Vector<Vector3> handles = helper->cylinder_get_handles(cs2->get_height(), cs2->get_radius());
p_gizmo->add_handles(handles, handles_material);
}
if (Object::cast_to<WorldBoundaryShape3D>(*s)) {
Ref<WorldBoundaryShape3D> wbs = s;
const Plane &p = wbs->get_plane();
Vector3 n1 = p.get_any_perpendicular_normal();
Vector3 n2 = p.normal.cross(n1).normalized();
Vector3 pface[4] = {
p.normal * p.d + n1 * 10.0 + n2 * 10.0,
p.normal * p.d + n1 * 10.0 + n2 * -10.0,
p.normal * p.d + n1 * -10.0 + n2 * -10.0,
p.normal * p.d + n1 * -10.0 + n2 * 10.0,
};
Vector<Vector3> points = {
pface[0],
pface[1],
pface[1],
pface[2],
pface[2],
pface[3],
pface[3],
pface[0],
p.normal * p.d,
p.normal * p.d + p.normal * 3
};
p_gizmo->add_lines(points, material, false, collision_color);
p_gizmo->add_collision_segments(points);
}
if (Object::cast_to<ConvexPolygonShape3D>(*s)) {
Vector<Vector3> points = Object::cast_to<ConvexPolygonShape3D>(*s)->get_points();
if (points.size() > 1) { // Need at least 2 points for a line.
Vector<Vector3> varr = Variant(points);
Geometry3D::MeshData md;
Error err = ConvexHullComputer::convex_hull(varr, md);
if (err == OK) {
Vector<Vector3> lines;
lines.resize(md.edges.size() * 2);
for (uint32_t i = 0; i < md.edges.size(); i++) {
lines.write[i * 2 + 0] = md.vertices[md.edges[i].vertex_a];
lines.write[i * 2 + 1] = md.vertices[md.edges[i].vertex_b];
}
p_gizmo->add_lines(lines, material, false, collision_color);
p_gizmo->add_collision_segments(lines);
}
}
}
if (Object::cast_to<ConcavePolygonShape3D>(*s)) {
Ref<ConcavePolygonShape3D> cs2 = s;
Ref<ArrayMesh> mesh = cs2->get_debug_mesh();
p_gizmo->add_lines(cs2->get_debug_mesh_lines(), material, false, collision_color);
p_gizmo->add_collision_segments(cs2->get_debug_mesh_lines());
}
if (Object::cast_to<SeparationRayShape3D>(*s)) {
Ref<SeparationRayShape3D> rs = s;
Vector<Vector3> points = {
Vector3(),
Vector3(0, 0, rs->get_length())
};
p_gizmo->add_lines(points, material, false, collision_color);
p_gizmo->add_collision_segments(points);
Vector<Vector3> handles;
handles.push_back(Vector3(0, 0, rs->get_length()));
p_gizmo->add_handles(handles, handles_material);
}
if (Object::cast_to<HeightMapShape3D>(*s)) {
Ref<HeightMapShape3D> hms = s;
Vector<Vector3> lines = hms->get_debug_mesh_lines();
p_gizmo->add_lines(lines, material, false, collision_color);
}
}

View File

@@ -0,0 +1,57 @@
/**************************************************************************/
/* collision_shape_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class Gizmo3DHelper;
class CollisionShape3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(CollisionShape3DGizmoPlugin, EditorNode3DGizmoPlugin);
void create_collision_material(const String &p_name, float p_alpha);
Ref<Gizmo3DHelper> helper;
public:
bool has_gizmo(Node3D *p_spatial) override;
String get_gizmo_name() const override;
int get_priority() const override;
void redraw(EditorNode3DGizmo *p_gizmo) override;
String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
void begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) override;
void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) override;
void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false) override;
CollisionShape3DGizmoPlugin();
};

View File

@@ -0,0 +1,723 @@
/**************************************************************************/
/* 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/settings/editor_settings.h"
#include "scene/3d/physics/joints/cone_twist_joint_3d.h"
#include "scene/3d/physics/joints/generic_6dof_joint_3d.h"
#include "scene/3d/physics/joints/hinge_joint_3d.h"
#include "scene/3d/physics/joints/pin_joint_3d.h"
#include "scene/3d/physics/joints/slider_joint_3d.h"
#define BODY_A_RADIUS 0.25
#define BODY_B_RADIUS 0.27
Basis JointGizmosDrawer::look_body(const Transform3D &p_joint_transform, const Transform3D &p_body_transform) {
const Vector3 &p_eye(p_joint_transform.origin);
const Vector3 &p_target(p_body_transform.origin);
Vector3 v_x, v_y, v_z;
// Look the body with X
v_x = p_target - p_eye;
v_x.normalize();
v_z = v_x.cross(Vector3(0, 1, 0));
v_z.normalize();
v_y = v_z.cross(v_x);
v_y.normalize();
Basis base;
base.set_columns(v_x, v_y, v_z);
// Absorb current joint transform
base = p_joint_transform.basis.inverse() * base;
return base;
}
Basis JointGizmosDrawer::look_body_toward(Vector3::Axis p_axis, const Transform3D &joint_transform, const Transform3D &body_transform) {
switch (p_axis) {
case Vector3::AXIS_X:
return look_body_toward_x(joint_transform, body_transform);
case Vector3::AXIS_Y:
return look_body_toward_y(joint_transform, body_transform);
case Vector3::AXIS_Z:
return look_body_toward_z(joint_transform, body_transform);
default:
return Basis();
}
}
Basis JointGizmosDrawer::look_body_toward_x(const Transform3D &p_joint_transform, const Transform3D &p_body_transform) {
const Vector3 &p_eye(p_joint_transform.origin);
const Vector3 &p_target(p_body_transform.origin);
const Vector3 p_front(p_joint_transform.basis.get_column(0));
Vector3 v_x, v_y, v_z;
// Look the body with X
v_x = p_target - p_eye;
v_x.normalize();
v_y = p_front.cross(v_x);
v_y.normalize();
v_z = v_y.cross(p_front);
v_z.normalize();
// Clamp X to FRONT axis
v_x = p_front;
v_x.normalize();
Basis base;
base.set_columns(v_x, v_y, v_z);
// Absorb current joint transform
base = p_joint_transform.basis.inverse() * base;
return base;
}
Basis JointGizmosDrawer::look_body_toward_y(const Transform3D &p_joint_transform, const Transform3D &p_body_transform) {
const Vector3 &p_eye(p_joint_transform.origin);
const Vector3 &p_target(p_body_transform.origin);
const Vector3 p_up(p_joint_transform.basis.get_column(1));
Vector3 v_x, v_y, v_z;
// Look the body with X
v_x = p_target - p_eye;
v_x.normalize();
v_z = v_x.cross(p_up);
v_z.normalize();
v_x = p_up.cross(v_z);
v_x.normalize();
// Clamp Y to UP axis
v_y = p_up;
v_y.normalize();
Basis base;
base.set_columns(v_x, v_y, v_z);
// Absorb current joint transform
base = p_joint_transform.basis.inverse() * base;
return base;
}
Basis JointGizmosDrawer::look_body_toward_z(const Transform3D &p_joint_transform, const Transform3D &p_body_transform) {
const Vector3 &p_eye(p_joint_transform.origin);
const Vector3 &p_target(p_body_transform.origin);
const Vector3 p_lateral(p_joint_transform.basis.get_column(2));
Vector3 v_x, v_y, v_z;
// Look the body with X
v_x = p_target - p_eye;
v_x.normalize();
v_z = p_lateral;
v_z.normalize();
v_y = v_z.cross(v_x);
v_y.normalize();
// Clamp X to Z axis
v_x = v_y.cross(v_z);
v_x.normalize();
Basis base;
base.set_columns(v_x, v_y, v_z);
// Absorb current joint transform
base = p_joint_transform.basis.inverse() * base;
return base;
}
void JointGizmosDrawer::draw_circle(Vector3::Axis p_axis, real_t p_radius, const Transform3D &p_offset, const Basis &p_base, real_t p_limit_lower, real_t p_limit_upper, Vector<Vector3> &r_points, bool p_inverse) {
if (p_limit_lower == p_limit_upper) {
r_points.push_back(p_offset.translated_local(Vector3()).origin);
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3(0.5, 0, 0))).origin);
} else {
if (p_limit_lower > p_limit_upper) {
p_limit_lower = -Math::PI;
p_limit_upper = Math::PI;
}
const int points = 32;
for (int i = 0; i < points; i++) {
real_t s = p_limit_lower + i * (p_limit_upper - p_limit_lower) / points;
real_t n = p_limit_lower + (i + 1) * (p_limit_upper - p_limit_lower) / points;
Vector3 from;
Vector3 to;
switch (p_axis) {
case Vector3::AXIS_X:
if (p_inverse) {
from = p_base.xform(Vector3(0, Math::sin(s), Math::cos(s))) * p_radius;
to = p_base.xform(Vector3(0, Math::sin(n), Math::cos(n))) * p_radius;
} else {
from = p_base.xform(Vector3(0, -Math::sin(s), Math::cos(s))) * p_radius;
to = p_base.xform(Vector3(0, -Math::sin(n), Math::cos(n))) * p_radius;
}
break;
case Vector3::AXIS_Y:
if (p_inverse) {
from = p_base.xform(Vector3(Math::cos(s), 0, -Math::sin(s))) * p_radius;
to = p_base.xform(Vector3(Math::cos(n), 0, -Math::sin(n))) * p_radius;
} else {
from = p_base.xform(Vector3(Math::cos(s), 0, Math::sin(s))) * p_radius;
to = p_base.xform(Vector3(Math::cos(n), 0, Math::sin(n))) * p_radius;
}
break;
case Vector3::AXIS_Z:
from = p_base.xform(Vector3(Math::cos(s), Math::sin(s), 0)) * p_radius;
to = p_base.xform(Vector3(Math::cos(n), Math::sin(n), 0)) * p_radius;
break;
}
if (i == points - 1) {
r_points.push_back(p_offset.translated_local(to).origin);
r_points.push_back(p_offset.translated_local(Vector3()).origin);
}
if (i == 0) {
r_points.push_back(p_offset.translated_local(from).origin);
r_points.push_back(p_offset.translated_local(Vector3()).origin);
}
r_points.push_back(p_offset.translated_local(from).origin);
r_points.push_back(p_offset.translated_local(to).origin);
}
r_points.push_back(p_offset.translated_local(Vector3(0, p_radius * 1.5, 0)).origin);
r_points.push_back(p_offset.translated_local(Vector3()).origin);
}
}
void JointGizmosDrawer::draw_cone(const Transform3D &p_offset, const Basis &p_base, real_t p_swing, real_t p_twist, Vector<Vector3> &r_points) {
float r = 1.0;
float w = r * Math::sin(p_swing);
float d = r * Math::cos(p_swing);
//swing
for (int i = 0; i < 360; i += 10) {
float ra = Math::deg_to_rad((float)i);
float rb = Math::deg_to_rad((float)i + 10);
Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w;
Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w;
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3(d, a.x, a.y))).origin);
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3(d, b.x, b.y))).origin);
if (i % 90 == 0) {
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3(d, a.x, a.y))).origin);
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3())).origin);
}
}
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3())).origin);
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3(1, 0, 0))).origin);
/// Twist
float ts = Math::rad_to_deg(p_twist);
ts = MIN(ts, 720);
for (int i = 0; i < int(ts); i += 5) {
float ra = Math::deg_to_rad((float)i);
float rb = Math::deg_to_rad((float)i + 5);
float c = i / 720.0;
float cn = (i + 5) / 720.0;
Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w * c;
Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w * cn;
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3(c, a.x, a.y))).origin);
r_points.push_back(p_offset.translated_local(p_base.xform(Vector3(cn, b.x, b.y))).origin);
}
}
////
Joint3DGizmoPlugin::Joint3DGizmoPlugin() {
create_material("joint_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint"));
create_material("joint_body_a_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint_body_a"));
create_material("joint_body_b_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint_body_b"));
update_timer = memnew(Timer);
update_timer->set_name("JointGizmoUpdateTimer");
update_timer->set_wait_time(1.0 / 120.0);
update_timer->connect("timeout", callable_mp(this, &Joint3DGizmoPlugin::incremental_update_gizmos));
update_timer->set_autostart(true);
callable_mp((Node *)EditorNode::get_singleton(), &Node::add_child).call_deferred(update_timer, false, Node::INTERNAL_MODE_DISABLED);
}
void Joint3DGizmoPlugin::incremental_update_gizmos() {
if (!current_gizmos.is_empty()) {
HashSet<EditorNode3DGizmo *>::Iterator E = current_gizmos.find(last_drawn);
if (E) {
++E;
}
if (!E) {
E = current_gizmos.begin();
}
redraw(*E);
last_drawn = *E;
}
}
bool Joint3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<Joint3D>(p_spatial) != nullptr;
}
String Joint3DGizmoPlugin::get_gizmo_name() const {
return "Joint3D";
}
int Joint3DGizmoPlugin::get_priority() const {
return -1;
}
void Joint3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Joint3D *joint = Object::cast_to<Joint3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
Node3D *node_body_a = nullptr;
if (!joint->get_node_a().is_empty()) {
node_body_a = Object::cast_to<Node3D>(joint->get_node(joint->get_node_a()));
}
Node3D *node_body_b = nullptr;
if (!joint->get_node_b().is_empty()) {
node_body_b = Object::cast_to<Node3D>(joint->get_node(joint->get_node_b()));
}
if (!node_body_a && !node_body_b) {
return;
}
Ref<Material> common_material = get_material("joint_material", p_gizmo);
Ref<Material> body_a_material = get_material("joint_body_a_material", p_gizmo);
Ref<Material> body_b_material = get_material("joint_body_b_material", p_gizmo);
Vector<Vector3> points;
Vector<Vector3> body_a_points;
Vector<Vector3> body_b_points;
if (Object::cast_to<PinJoint3D>(joint)) {
CreatePinJointGizmo(Transform3D(), points);
p_gizmo->add_collision_segments(points);
p_gizmo->add_lines(points, common_material);
}
HingeJoint3D *hinge = Object::cast_to<HingeJoint3D>(joint);
if (hinge) {
CreateHingeJointGizmo(
Transform3D(),
hinge->get_global_transform(),
node_body_a ? node_body_a->get_global_transform() : Transform3D(),
node_body_b ? node_body_b->get_global_transform() : Transform3D(),
hinge->get_param(HingeJoint3D::PARAM_LIMIT_LOWER),
hinge->get_param(HingeJoint3D::PARAM_LIMIT_UPPER),
hinge->get_flag(HingeJoint3D::FLAG_USE_LIMIT),
points,
node_body_a ? &body_a_points : nullptr,
node_body_b ? &body_b_points : nullptr);
p_gizmo->add_collision_segments(points);
p_gizmo->add_collision_segments(body_a_points);
p_gizmo->add_collision_segments(body_b_points);
p_gizmo->add_lines(points, common_material);
p_gizmo->add_lines(body_a_points, body_a_material);
p_gizmo->add_lines(body_b_points, body_b_material);
}
SliderJoint3D *slider = Object::cast_to<SliderJoint3D>(joint);
if (slider) {
CreateSliderJointGizmo(
Transform3D(),
slider->get_global_transform(),
node_body_a ? node_body_a->get_global_transform() : Transform3D(),
node_body_b ? node_body_b->get_global_transform() : Transform3D(),
slider->get_param(SliderJoint3D::PARAM_ANGULAR_LIMIT_LOWER),
slider->get_param(SliderJoint3D::PARAM_ANGULAR_LIMIT_UPPER),
slider->get_param(SliderJoint3D::PARAM_LINEAR_LIMIT_LOWER),
slider->get_param(SliderJoint3D::PARAM_LINEAR_LIMIT_UPPER),
points,
node_body_a ? &body_a_points : nullptr,
node_body_b ? &body_b_points : nullptr);
p_gizmo->add_collision_segments(points);
p_gizmo->add_collision_segments(body_a_points);
p_gizmo->add_collision_segments(body_b_points);
p_gizmo->add_lines(points, common_material);
p_gizmo->add_lines(body_a_points, body_a_material);
p_gizmo->add_lines(body_b_points, body_b_material);
}
ConeTwistJoint3D *cone = Object::cast_to<ConeTwistJoint3D>(joint);
if (cone) {
CreateConeTwistJointGizmo(
Transform3D(),
cone->get_global_transform(),
node_body_a ? node_body_a->get_global_transform() : Transform3D(),
node_body_b ? node_body_b->get_global_transform() : Transform3D(),
cone->get_param(ConeTwistJoint3D::PARAM_SWING_SPAN),
cone->get_param(ConeTwistJoint3D::PARAM_TWIST_SPAN),
node_body_a ? &body_a_points : nullptr,
node_body_b ? &body_b_points : nullptr);
p_gizmo->add_collision_segments(body_a_points);
p_gizmo->add_collision_segments(body_b_points);
p_gizmo->add_lines(body_a_points, body_a_material);
p_gizmo->add_lines(body_b_points, body_b_material);
}
Generic6DOFJoint3D *gen = Object::cast_to<Generic6DOFJoint3D>(joint);
if (gen) {
CreateGeneric6DOFJointGizmo(
Transform3D(),
gen->get_global_transform(),
node_body_a ? node_body_a->get_global_transform() : Transform3D(),
node_body_b ? node_body_b->get_global_transform() : Transform3D(),
gen->get_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT),
gen->get_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT),
gen->get_param_x(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT),
gen->get_param_x(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT),
gen->get_flag_x(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT),
gen->get_flag_x(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT),
gen->get_param_y(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT),
gen->get_param_y(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT),
gen->get_param_y(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT),
gen->get_param_y(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT),
gen->get_flag_y(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT),
gen->get_flag_y(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT),
gen->get_param_z(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT),
gen->get_param_z(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT),
gen->get_param_z(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT),
gen->get_param_z(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT),
gen->get_flag_z(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT),
gen->get_flag_z(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT),
points,
node_body_a ? &body_a_points : nullptr,
node_body_a ? &body_b_points : nullptr);
p_gizmo->add_collision_segments(points);
p_gizmo->add_collision_segments(body_a_points);
p_gizmo->add_collision_segments(body_b_points);
p_gizmo->add_lines(points, common_material);
p_gizmo->add_lines(body_a_points, body_a_material);
p_gizmo->add_lines(body_b_points, body_b_material);
}
}
void Joint3DGizmoPlugin::CreatePinJointGizmo(const Transform3D &p_offset, Vector<Vector3> &r_cursor_points) {
float cs = 0.25;
r_cursor_points.push_back(p_offset.translated_local(Vector3(+cs, 0, 0)).origin);
r_cursor_points.push_back(p_offset.translated_local(Vector3(-cs, 0, 0)).origin);
r_cursor_points.push_back(p_offset.translated_local(Vector3(0, +cs, 0)).origin);
r_cursor_points.push_back(p_offset.translated_local(Vector3(0, -cs, 0)).origin);
r_cursor_points.push_back(p_offset.translated_local(Vector3(0, 0, +cs)).origin);
r_cursor_points.push_back(p_offset.translated_local(Vector3(0, 0, -cs)).origin);
}
void Joint3DGizmoPlugin::CreateHingeJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_limit_lower, real_t p_limit_upper, bool p_use_limit, Vector<Vector3> &r_common_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points) {
r_common_points.push_back(p_offset.translated_local(Vector3(0, 0, 0.5)).origin);
r_common_points.push_back(p_offset.translated_local(Vector3(0, 0, -0.5)).origin);
if (!p_use_limit) {
p_limit_upper = -1;
p_limit_lower = 0;
}
if (r_body_a_points) {
JointGizmosDrawer::draw_circle(Vector3::AXIS_Z,
BODY_A_RADIUS,
p_offset,
JointGizmosDrawer::look_body_toward_z(p_trs_joint, p_trs_body_a),
p_limit_lower,
p_limit_upper,
*r_body_a_points);
}
if (r_body_b_points) {
JointGizmosDrawer::draw_circle(Vector3::AXIS_Z,
BODY_B_RADIUS,
p_offset,
JointGizmosDrawer::look_body_toward_z(p_trs_joint, p_trs_body_b),
p_limit_lower,
p_limit_upper,
*r_body_b_points);
}
}
void Joint3DGizmoPlugin::CreateSliderJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_angular_limit_lower, real_t p_angular_limit_upper, real_t p_linear_limit_lower, real_t p_linear_limit_upper, Vector<Vector3> &r_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points) {
p_linear_limit_lower = -p_linear_limit_lower;
p_linear_limit_upper = -p_linear_limit_upper;
float cs = 0.25;
r_points.push_back(p_offset.translated_local(Vector3(0, 0, 0.5)).origin);
r_points.push_back(p_offset.translated_local(Vector3(0, 0, -0.5)).origin);
if (p_linear_limit_lower >= p_linear_limit_upper) {
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, 0, 0)).origin);
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, 0, 0)).origin);
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, -cs, -cs)).origin);
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, -cs, cs)).origin);
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, -cs, cs)).origin);
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, cs, cs)).origin);
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, cs, cs)).origin);
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, cs, -cs)).origin);
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, cs, -cs)).origin);
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_upper, -cs, -cs)).origin);
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, -cs, -cs)).origin);
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, -cs, cs)).origin);
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, -cs, cs)).origin);
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, cs, cs)).origin);
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, cs, cs)).origin);
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, cs, -cs)).origin);
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, cs, -cs)).origin);
r_points.push_back(p_offset.translated_local(Vector3(p_linear_limit_lower, -cs, -cs)).origin);
} else {
r_points.push_back(p_offset.translated_local(Vector3(+cs * 2, 0, 0)).origin);
r_points.push_back(p_offset.translated_local(Vector3(-cs * 2, 0, 0)).origin);
}
if (r_body_a_points) {
JointGizmosDrawer::draw_circle(
Vector3::AXIS_X,
BODY_A_RADIUS,
p_offset,
JointGizmosDrawer::look_body_toward(Vector3::AXIS_X, p_trs_joint, p_trs_body_a),
p_angular_limit_lower,
p_angular_limit_upper,
*r_body_a_points);
}
if (r_body_b_points) {
JointGizmosDrawer::draw_circle(
Vector3::AXIS_X,
BODY_B_RADIUS,
p_offset,
JointGizmosDrawer::look_body_toward(Vector3::AXIS_X, p_trs_joint, p_trs_body_b),
p_angular_limit_lower,
p_angular_limit_upper,
*r_body_b_points,
true);
}
}
void Joint3DGizmoPlugin::CreateConeTwistJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_swing, real_t p_twist, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points) {
if (r_body_a_points) {
JointGizmosDrawer::draw_cone(
p_offset,
JointGizmosDrawer::look_body(p_trs_joint, p_trs_body_a),
p_swing,
p_twist,
*r_body_a_points);
}
if (r_body_b_points) {
JointGizmosDrawer::draw_cone(
p_offset,
JointGizmosDrawer::look_body(p_trs_joint, p_trs_body_b),
p_swing,
p_twist,
*r_body_b_points);
}
}
void Joint3DGizmoPlugin::CreateGeneric6DOFJointGizmo(
const Transform3D &p_offset,
const Transform3D &p_trs_joint,
const Transform3D &p_trs_body_a,
const Transform3D &p_trs_body_b,
real_t p_angular_limit_lower_x,
real_t p_angular_limit_upper_x,
real_t p_linear_limit_lower_x,
real_t p_linear_limit_upper_x,
bool p_enable_angular_limit_x,
bool p_enable_linear_limit_x,
real_t p_angular_limit_lower_y,
real_t p_angular_limit_upper_y,
real_t p_linear_limit_lower_y,
real_t p_linear_limit_upper_y,
bool p_enable_angular_limit_y,
bool p_enable_linear_limit_y,
real_t p_angular_limit_lower_z,
real_t p_angular_limit_upper_z,
real_t p_linear_limit_lower_z,
real_t p_linear_limit_upper_z,
bool p_enable_angular_limit_z,
bool p_enable_linear_limit_z,
Vector<Vector3> &r_points,
Vector<Vector3> *r_body_a_points,
Vector<Vector3> *r_body_b_points) {
float cs = 0.25;
for (int ax = 0; ax < 3; ax++) {
float ll = 0;
float ul = 0;
float lll = 0;
float lul = 0;
int a1 = 0;
int a2 = 0;
int a3 = 0;
bool enable_ang = false;
bool enable_lin = false;
switch (ax) {
case 0:
ll = p_angular_limit_lower_x;
ul = p_angular_limit_upper_x;
lll = -p_linear_limit_lower_x;
lul = -p_linear_limit_upper_x;
enable_ang = p_enable_angular_limit_x;
enable_lin = p_enable_linear_limit_x;
a1 = 0;
a2 = 1;
a3 = 2;
break;
case 1:
ll = p_angular_limit_lower_y;
ul = p_angular_limit_upper_y;
lll = -p_linear_limit_lower_y;
lul = -p_linear_limit_upper_y;
enable_ang = p_enable_angular_limit_y;
enable_lin = p_enable_linear_limit_y;
a1 = 1;
a2 = 2;
a3 = 0;
break;
case 2:
ll = p_angular_limit_lower_z;
ul = p_angular_limit_upper_z;
lll = -p_linear_limit_lower_z;
lul = -p_linear_limit_upper_z;
enable_ang = p_enable_angular_limit_z;
enable_lin = p_enable_linear_limit_z;
a1 = 2;
a2 = 0;
a3 = 1;
break;
}
#define ADD_VTX(x, y, z) \
{ \
Vector3 v; \
v[a1] = (x); \
v[a2] = (y); \
v[a3] = (z); \
r_points.push_back(p_offset.translated_local(v).origin); \
}
if (enable_lin && lll >= lul) {
ADD_VTX(lul, 0, 0);
ADD_VTX(lll, 0, 0);
ADD_VTX(lul, -cs, -cs);
ADD_VTX(lul, -cs, cs);
ADD_VTX(lul, -cs, cs);
ADD_VTX(lul, cs, cs);
ADD_VTX(lul, cs, cs);
ADD_VTX(lul, cs, -cs);
ADD_VTX(lul, cs, -cs);
ADD_VTX(lul, -cs, -cs);
ADD_VTX(lll, -cs, -cs);
ADD_VTX(lll, -cs, cs);
ADD_VTX(lll, -cs, cs);
ADD_VTX(lll, cs, cs);
ADD_VTX(lll, cs, cs);
ADD_VTX(lll, cs, -cs);
ADD_VTX(lll, cs, -cs);
ADD_VTX(lll, -cs, -cs);
} else {
ADD_VTX(+cs * 2, 0, 0);
ADD_VTX(-cs * 2, 0, 0);
}
if (!enable_ang) {
ll = 0;
ul = -1;
}
if (r_body_a_points) {
JointGizmosDrawer::draw_circle(
static_cast<Vector3::Axis>(ax),
BODY_A_RADIUS,
p_offset,
JointGizmosDrawer::look_body_toward(static_cast<Vector3::Axis>(ax), p_trs_joint, p_trs_body_a),
ll,
ul,
*r_body_a_points,
true);
}
if (r_body_b_points) {
JointGizmosDrawer::draw_circle(
static_cast<Vector3::Axis>(ax),
BODY_B_RADIUS,
p_offset,
JointGizmosDrawer::look_body_toward(static_cast<Vector3::Axis>(ax), p_trs_joint, p_trs_body_b),
ll,
ul,
*r_body_b_points);
}
}
#undef ADD_VTX
}

View File

@@ -0,0 +1,96 @@
/**************************************************************************/
/* joint_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class Joint3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(Joint3DGizmoPlugin, EditorNode3DGizmoPlugin);
Timer *update_timer = nullptr;
EditorNode3DGizmo *last_drawn = nullptr;
void incremental_update_gizmos();
public:
bool has_gizmo(Node3D *p_spatial) override;
String get_gizmo_name() const override;
int get_priority() const override;
void redraw(EditorNode3DGizmo *p_gizmo) override;
static void CreatePinJointGizmo(const Transform3D &p_offset, Vector<Vector3> &r_cursor_points);
static void CreateHingeJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_limit_lower, real_t p_limit_upper, bool p_use_limit, Vector<Vector3> &r_common_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points);
static void CreateSliderJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_angular_limit_lower, real_t p_angular_limit_upper, real_t p_linear_limit_lower, real_t p_linear_limit_upper, Vector<Vector3> &r_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points);
static void CreateConeTwistJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_swing, real_t p_twist, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points);
static void CreateGeneric6DOFJointGizmo(
const Transform3D &p_offset,
const Transform3D &p_trs_joint,
const Transform3D &p_trs_body_a,
const Transform3D &p_trs_body_b,
real_t p_angular_limit_lower_x,
real_t p_angular_limit_upper_x,
real_t p_linear_limit_lower_x,
real_t p_linear_limit_upper_x,
bool p_enable_angular_limit_x,
bool p_enable_linear_limit_x,
real_t p_angular_limit_lower_y,
real_t p_angular_limit_upper_y,
real_t p_linear_limit_lower_y,
real_t p_linear_limit_upper_y,
bool p_enable_angular_limit_y,
bool p_enable_linear_limit_y,
real_t p_angular_limit_lower_z,
real_t p_angular_limit_upper_z,
real_t p_linear_limit_lower_z,
real_t p_linear_limit_upper_z,
bool p_enable_angular_limit_z,
bool p_enable_linear_limit_z,
Vector<Vector3> &r_points,
Vector<Vector3> *r_body_a_points,
Vector<Vector3> *r_body_b_points);
Joint3DGizmoPlugin();
};
class JointGizmosDrawer {
public:
static Basis look_body(const Transform3D &p_joint_transform, const Transform3D &p_body_transform);
static Basis look_body_toward(Vector3::Axis p_axis, const Transform3D &joint_transform, const Transform3D &body_transform);
static Basis look_body_toward_x(const Transform3D &p_joint_transform, const Transform3D &p_body_transform);
static Basis look_body_toward_y(const Transform3D &p_joint_transform, const Transform3D &p_body_transform);
/// Special function just used for physics joints, it returns a basis constrained toward Joint Z axis
/// with axis X and Y that are looking toward the body and oriented toward up
static Basis look_body_toward_z(const Transform3D &p_joint_transform, const Transform3D &p_body_transform);
// Draw circle around p_axis
static void draw_circle(Vector3::Axis p_axis, real_t p_radius, const Transform3D &p_offset, const Basis &p_base, real_t p_limit_lower, real_t p_limit_upper, Vector<Vector3> &r_points, bool p_inverse = false);
static void draw_cone(const Transform3D &p_offset, const Basis &p_base, real_t p_swing, real_t p_twist, Vector<Vector3> &r_points);
};

View File

@@ -0,0 +1,167 @@
/**************************************************************************/
/* physics_bone_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 "physics_bone_3d_gizmo_plugin.h"
#include "editor/scene/3d/gizmos/physics/joint_3d_gizmo_plugin.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/physics/physical_bone_3d.h"
#include "scene/3d/physics/physical_bone_simulator_3d.h"
PhysicalBone3DGizmoPlugin::PhysicalBone3DGizmoPlugin() {
create_material("joint_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint"));
}
bool PhysicalBone3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<PhysicalBone3D>(p_spatial) != nullptr;
}
String PhysicalBone3DGizmoPlugin::get_gizmo_name() const {
return "PhysicalBone3D";
}
int PhysicalBone3DGizmoPlugin::get_priority() const {
return -1;
}
void PhysicalBone3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->clear();
PhysicalBone3D *physical_bone = Object::cast_to<PhysicalBone3D>(p_gizmo->get_node_3d());
if (!physical_bone) {
return;
}
PhysicalBoneSimulator3D *sm(physical_bone->get_simulator());
if (!sm) {
return;
}
PhysicalBone3D *pb(sm->get_physical_bone(physical_bone->get_bone_id()));
if (!pb) {
return;
}
PhysicalBone3D *pbp(sm->get_physical_bone_parent(physical_bone->get_bone_id()));
if (!pbp) {
return;
}
Vector<Vector3> points;
switch (physical_bone->get_joint_type()) {
case PhysicalBone3D::JOINT_TYPE_PIN: {
Joint3DGizmoPlugin::CreatePinJointGizmo(physical_bone->get_joint_offset(), points);
} break;
case PhysicalBone3D::JOINT_TYPE_CONE: {
const PhysicalBone3D::ConeJointData *cjd(static_cast<const PhysicalBone3D::ConeJointData *>(physical_bone->get_joint_data()));
Joint3DGizmoPlugin::CreateConeTwistJointGizmo(
physical_bone->get_joint_offset(),
physical_bone->get_global_transform() * physical_bone->get_joint_offset(),
pb->get_global_transform(),
pbp->get_global_transform(),
cjd->swing_span,
cjd->twist_span,
&points,
&points);
} break;
case PhysicalBone3D::JOINT_TYPE_HINGE: {
const PhysicalBone3D::HingeJointData *hjd(static_cast<const PhysicalBone3D::HingeJointData *>(physical_bone->get_joint_data()));
Joint3DGizmoPlugin::CreateHingeJointGizmo(
physical_bone->get_joint_offset(),
physical_bone->get_global_transform() * physical_bone->get_joint_offset(),
pb->get_global_transform(),
pbp->get_global_transform(),
hjd->angular_limit_lower,
hjd->angular_limit_upper,
hjd->angular_limit_enabled,
points,
&points,
&points);
} break;
case PhysicalBone3D::JOINT_TYPE_SLIDER: {
const PhysicalBone3D::SliderJointData *sjd(static_cast<const PhysicalBone3D::SliderJointData *>(physical_bone->get_joint_data()));
Joint3DGizmoPlugin::CreateSliderJointGizmo(
physical_bone->get_joint_offset(),
physical_bone->get_global_transform() * physical_bone->get_joint_offset(),
pb->get_global_transform(),
pbp->get_global_transform(),
sjd->angular_limit_lower,
sjd->angular_limit_upper,
sjd->linear_limit_lower,
sjd->linear_limit_upper,
points,
&points,
&points);
} break;
case PhysicalBone3D::JOINT_TYPE_6DOF: {
const PhysicalBone3D::SixDOFJointData *sdofjd(static_cast<const PhysicalBone3D::SixDOFJointData *>(physical_bone->get_joint_data()));
Joint3DGizmoPlugin::CreateGeneric6DOFJointGizmo(
physical_bone->get_joint_offset(),
physical_bone->get_global_transform() * physical_bone->get_joint_offset(),
pb->get_global_transform(),
pbp->get_global_transform(),
sdofjd->axis_data[0].angular_limit_lower,
sdofjd->axis_data[0].angular_limit_upper,
sdofjd->axis_data[0].linear_limit_lower,
sdofjd->axis_data[0].linear_limit_upper,
sdofjd->axis_data[0].angular_limit_enabled,
sdofjd->axis_data[0].linear_limit_enabled,
sdofjd->axis_data[1].angular_limit_lower,
sdofjd->axis_data[1].angular_limit_upper,
sdofjd->axis_data[1].linear_limit_lower,
sdofjd->axis_data[1].linear_limit_upper,
sdofjd->axis_data[1].angular_limit_enabled,
sdofjd->axis_data[1].linear_limit_enabled,
sdofjd->axis_data[2].angular_limit_lower,
sdofjd->axis_data[2].angular_limit_upper,
sdofjd->axis_data[2].linear_limit_lower,
sdofjd->axis_data[2].linear_limit_upper,
sdofjd->axis_data[2].angular_limit_enabled,
sdofjd->axis_data[2].linear_limit_enabled,
points,
&points,
&points);
} break;
default:
return;
}
Ref<Material> material = get_material("joint_material", p_gizmo);
p_gizmo->add_collision_segments(points);
p_gizmo->add_lines(points, material);
}

View File

@@ -0,0 +1,45 @@
/**************************************************************************/
/* physics_bone_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class PhysicalBone3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(PhysicalBone3DGizmoPlugin, 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;
PhysicalBone3DGizmoPlugin();
};

View File

@@ -0,0 +1,69 @@
/**************************************************************************/
/* ray_cast_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 "ray_cast_3d_gizmo_plugin.h"
#include "scene/3d/physics/ray_cast_3d.h"
RayCast3DGizmoPlugin::RayCast3DGizmoPlugin() {
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 RayCast3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<RayCast3D>(p_spatial) != nullptr;
}
String RayCast3DGizmoPlugin::get_gizmo_name() const {
return "RayCast3D";
}
int RayCast3DGizmoPlugin::get_priority() const {
return -1;
}
void RayCast3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
RayCast3D *raycast = Object::cast_to<RayCast3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
const Ref<StandardMaterial3D> material = raycast->is_enabled() ? raycast->get_debug_material() : get_material("shape_material_disabled");
p_gizmo->add_lines(raycast->get_debug_line_vertices(), material);
if (raycast->get_debug_shape_thickness() > 1) {
p_gizmo->add_vertices(raycast->get_debug_shape_vertices(), material, Mesh::PRIMITIVE_TRIANGLE_STRIP);
}
p_gizmo->add_collision_segments(raycast->get_debug_line_vertices());
}

View File

@@ -0,0 +1,45 @@
/**************************************************************************/
/* ray_cast_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class RayCast3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(RayCast3DGizmoPlugin, 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;
RayCast3DGizmoPlugin();
};

View File

@@ -0,0 +1,69 @@
/**************************************************************************/
/* shape_cast_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 "shape_cast_3d_gizmo_plugin.h"
#include "scene/3d/physics/shape_cast_3d.h"
ShapeCast3DGizmoPlugin::ShapeCast3DGizmoPlugin() {
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 ShapeCast3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<ShapeCast3D>(p_spatial) != nullptr;
}
String ShapeCast3DGizmoPlugin::get_gizmo_name() const {
return "ShapeCast3D";
}
int ShapeCast3DGizmoPlugin::get_priority() const {
return -1;
}
void ShapeCast3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
ShapeCast3D *shapecast = Object::cast_to<ShapeCast3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
const Ref<StandardMaterial3D> material = shapecast->is_enabled() ? shapecast->get_debug_material() : get_material("shape_material_disabled");
p_gizmo->add_lines(shapecast->get_debug_line_vertices(), material);
if (shapecast->get_shape().is_valid()) {
p_gizmo->add_lines(shapecast->get_debug_shape_vertices(), material);
}
p_gizmo->add_collision_segments(shapecast->get_debug_line_vertices());
}

View File

@@ -0,0 +1,45 @@
/**************************************************************************/
/* shape_cast_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class ShapeCast3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(ShapeCast3DGizmoPlugin, 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;
ShapeCast3DGizmoPlugin();
};

View File

@@ -0,0 +1,124 @@
/**************************************************************************/
/* soft_body_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 "soft_body_3d_gizmo_plugin.h"
#include "editor/editor_undo_redo_manager.h"
#include "scene/3d/physics/soft_body_3d.h"
SoftBody3DGizmoPlugin::SoftBody3DGizmoPlugin() {
Color gizmo_color = SceneTree::get_singleton()->get_debug_collisions_color();
create_material("shape_material", gizmo_color);
create_handle_material("handles");
}
bool SoftBody3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<SoftBody3D>(p_spatial) != nullptr;
}
String SoftBody3DGizmoPlugin::get_gizmo_name() const {
return "SoftBody3D";
}
int SoftBody3DGizmoPlugin::get_priority() const {
return -1;
}
bool SoftBody3DGizmoPlugin::is_selectable_when_hidden() const {
return true;
}
bool SoftBody3DGizmoPlugin::can_commit_handle_on_click() const {
return true;
}
void SoftBody3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
if (!soft_body || soft_body->get_mesh().is_null()) {
return;
}
// find mesh
Vector<Vector3> lines;
soft_body->get_mesh()->generate_debug_mesh_lines(lines);
if (!lines.size()) {
return;
}
Ref<TriangleMesh> tm = soft_body->get_mesh()->generate_triangle_mesh();
Vector<Vector3> points;
for (int i = 0; i < soft_body->get_mesh()->get_surface_count(); i++) {
Array arrays = soft_body->get_mesh()->surface_get_arrays(i);
ERR_CONTINUE(arrays.is_empty());
const Vector<Vector3> &vertices = arrays[Mesh::ARRAY_VERTEX];
points.append_array(vertices);
}
Ref<Material> material = get_material("shape_material", p_gizmo);
p_gizmo->add_lines(lines, material);
p_gizmo->add_handles(points, get_material("handles"));
p_gizmo->add_collision_triangles(tm);
}
String SoftBody3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
return "SoftBody3D pin point";
}
Variant SoftBody3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_node_3d());
return Variant(soft_body->is_point_pinned(p_id));
}
void SoftBody3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_node_3d());
const bool is_pinned = soft_body->is_point_pinned(p_id);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(vformat(is_pinned ? TTR("Remove SoftBody3D pinned point %d") : TTR("Add SoftBody3D pinned point %d"), p_id));
undo_redo->add_do_method(soft_body, "set_point_pinned", p_id, !is_pinned);
undo_redo->add_do_method(soft_body, "update_gizmos");
undo_redo->add_undo_method(soft_body, "set_point_pinned", p_id, is_pinned);
undo_redo->add_undo_method(soft_body, "update_gizmos");
undo_redo->commit_action();
}
bool SoftBody3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_node_3d());
return soft_body->is_point_pinned(p_id);
}

View File

@@ -0,0 +1,52 @@
/**************************************************************************/
/* soft_body_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class SoftBody3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(SoftBody3DGizmoPlugin, 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;
bool can_commit_handle_on_click() 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 commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false) override;
bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
SoftBody3DGizmoPlugin();
};

View File

@@ -0,0 +1,66 @@
/**************************************************************************/
/* spring_arm_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 "spring_arm_3d_gizmo_plugin.h"
#include "scene/3d/physics/spring_arm_3d.h"
void SpringArm3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
SpringArm3D *spring_arm = Object::cast_to<SpringArm3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
Vector<Vector3> lines = {
Vector3(),
Vector3(0, 0, 1.0) * spring_arm->get_length()
};
Ref<StandardMaterial3D> material = get_material("shape_material", p_gizmo);
p_gizmo->add_lines(lines, material);
p_gizmo->add_collision_segments(lines);
}
SpringArm3DGizmoPlugin::SpringArm3DGizmoPlugin() {
Color gizmo_color = SceneTree::get_singleton()->get_debug_collisions_color();
create_material("shape_material", gizmo_color);
}
bool SpringArm3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<SpringArm3D>(p_spatial) != nullptr;
}
String SpringArm3DGizmoPlugin::get_gizmo_name() const {
return "SpringArm3D";
}
int SpringArm3DGizmoPlugin::get_priority() const {
return -1;
}

View File

@@ -0,0 +1,45 @@
/**************************************************************************/
/* spring_arm_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class SpringArm3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(SpringArm3DGizmoPlugin, 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;
SpringArm3DGizmoPlugin();
};

View File

@@ -0,0 +1,102 @@
/**************************************************************************/
/* vehicle_body_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 "vehicle_body_3d_gizmo_plugin.h"
#include "scene/3d/physics/vehicle_body_3d.h"
VehicleWheel3DGizmoPlugin::VehicleWheel3DGizmoPlugin() {
Color gizmo_color = SceneTree::get_singleton()->get_debug_collisions_color();
create_material("shape_material", gizmo_color);
}
bool VehicleWheel3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<VehicleWheel3D>(p_spatial) != nullptr;
}
String VehicleWheel3DGizmoPlugin::get_gizmo_name() const {
return "VehicleWheel3D";
}
int VehicleWheel3DGizmoPlugin::get_priority() const {
return -1;
}
void VehicleWheel3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
VehicleWheel3D *car_wheel = Object::cast_to<VehicleWheel3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
Vector<Vector3> points;
float r = car_wheel->get_radius();
const int skip = 10;
for (int i = 0; i <= 360; i += skip) {
float ra = Math::deg_to_rad((float)i);
float rb = Math::deg_to_rad((float)i + skip);
Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r;
Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r;
points.push_back(Vector3(0, a.x, a.y));
points.push_back(Vector3(0, b.x, b.y));
const int springsec = 4;
for (int j = 0; j < springsec; j++) {
float t = car_wheel->get_suspension_rest_length() * 5;
points.push_back(Vector3(a.x, i / 360.0 * t / springsec + j * (t / springsec), a.y) * 0.2);
points.push_back(Vector3(b.x, (i + skip) / 360.0 * t / springsec + j * (t / springsec), b.y) * 0.2);
}
}
//travel
points.push_back(Vector3(0, 0, 0));
points.push_back(Vector3(0, car_wheel->get_suspension_rest_length(), 0));
//axis
points.push_back(Vector3(r * 0.2, car_wheel->get_suspension_rest_length(), 0));
points.push_back(Vector3(-r * 0.2, car_wheel->get_suspension_rest_length(), 0));
//axis
points.push_back(Vector3(r * 0.2, 0, 0));
points.push_back(Vector3(-r * 0.2, 0, 0));
//forward line
points.push_back(Vector3(0, -r, 0));
points.push_back(Vector3(0, -r, r * 2));
points.push_back(Vector3(0, -r, r * 2));
points.push_back(Vector3(r * 2 * 0.2, -r, r * 2 * 0.8));
points.push_back(Vector3(0, -r, r * 2));
points.push_back(Vector3(-r * 2 * 0.2, -r, r * 2 * 0.8));
Ref<Material> material = get_material("shape_material", p_gizmo);
p_gizmo->add_lines(points, material);
p_gizmo->add_collision_segments(points);
}

View File

@@ -0,0 +1,45 @@
/**************************************************************************/
/* vehicle_body_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class VehicleWheel3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(VehicleWheel3DGizmoPlugin, 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;
VehicleWheel3DGizmoPlugin();
};

View File

@@ -0,0 +1,212 @@
/**************************************************************************/
/* reflection_probe_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 "reflection_probe_gizmo_plugin.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/scene/3d/gizmos/gizmo_3d_helper.h"
#include "editor/scene/3d/node_3d_editor_plugin.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/reflection_probe.h"
ReflectionProbeGizmoPlugin::ReflectionProbeGizmoPlugin() {
helper.instantiate();
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/reflection_probe");
create_material("reflection_probe_material", gizmo_color);
gizmo_color.a = 0.5;
create_material("reflection_internal_material", gizmo_color);
create_icon_material("reflection_probe_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoReflectionProbe"), EditorStringName(EditorIcons)));
create_handle_material("handles");
}
bool ReflectionProbeGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<ReflectionProbe>(p_spatial) != nullptr;
}
String ReflectionProbeGizmoPlugin::get_gizmo_name() const {
return "ReflectionProbe";
}
int ReflectionProbeGizmoPlugin::get_priority() const {
return -1;
}
String ReflectionProbeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
if (p_id < 6) {
return helper->box_get_handle_name(p_id);
}
switch (p_id) {
case 6:
return "Origin X";
case 7:
return "Origin Y";
case 8:
return "Origin Z";
}
return "";
}
Variant ReflectionProbeGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
ReflectionProbe *probe = Object::cast_to<ReflectionProbe>(p_gizmo->get_node_3d());
return AABB(probe->get_origin_offset(), probe->get_size());
}
void ReflectionProbeGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) {
// The initial value is only used for resizing the box, so we only need AABB size.
AABB aabb = get_handle_value(p_gizmo, p_id, p_secondary);
helper->initialize_handle_action(aabb.size, p_gizmo->get_node_3d()->get_global_transform());
}
void ReflectionProbeGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
ReflectionProbe *probe = Object::cast_to<ReflectionProbe>(p_gizmo->get_node_3d());
Vector3 sg[2];
helper->get_segment(p_camera, p_point, sg);
if (p_id < 6) {
Vector3 size = probe->get_size();
Vector3 position;
helper->box_set_handle(sg, p_id, size, position);
probe->set_size(size);
probe->set_global_position(position);
} else {
p_id -= 6;
Vector3 origin = probe->get_origin_offset();
origin[p_id] = 0;
Vector3 axis;
axis[p_id] = 1.0;
Vector3 ra, rb;
Geometry3D::get_closest_points_between_segments(origin - axis * 16384, origin + axis * 16384, sg[0], sg[1], ra, rb);
// Adjust the actual position to account for the gizmo handle position
float d = ra[p_id] + 0.25;
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
}
origin[p_id] = d;
probe->set_origin_offset(origin);
}
}
void ReflectionProbeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
ReflectionProbe *probe = Object::cast_to<ReflectionProbe>(p_gizmo->get_node_3d());
if (p_id < 6) {
helper->box_commit_handle(TTR("Change Probe Size"), p_cancel, probe);
return;
}
AABB restore = p_restore;
if (p_cancel) {
probe->set_origin_offset(restore.position);
probe->set_size(restore.size);
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Probe Origin Offset"));
ur->add_do_method(probe, "set_origin_offset", probe->get_origin_offset());
ur->add_undo_method(probe, "set_origin_offset", restore.position);
ur->commit_action();
}
void ReflectionProbeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->clear();
if (p_gizmo->is_selected()) {
ReflectionProbe *probe = Object::cast_to<ReflectionProbe>(p_gizmo->get_node_3d());
Vector<Vector3> lines;
Vector<Vector3> internal_lines;
Vector3 size = probe->get_size();
AABB aabb;
aabb.position = -size / 2;
aabb.size = size;
AABB blend_aabb;
for (int i = 0; i < 3; i++) {
blend_aabb.position[i] = aabb.position[i] + probe->get_blend_distance();
blend_aabb.size[i] = aabb.size[i] - probe->get_blend_distance() * 2.0;
if (blend_aabb.size[i] < blend_aabb.position[i]) {
blend_aabb.position[i] = aabb.position[i] + aabb.size[i] / 2.0;
blend_aabb.size[i] = 0.0;
}
}
if (probe->get_blend_distance() != 0.0) {
for (int i = 0; i < 12; i++) {
Vector3 a;
Vector3 b;
blend_aabb.get_edge(i, a, b);
lines.push_back(a);
lines.push_back(b);
}
for (int i = 0; i < 8; i++) {
Vector3 ep = aabb.get_endpoint(i);
internal_lines.push_back(blend_aabb.get_endpoint(i));
internal_lines.push_back(ep);
}
}
Vector<Vector3> handles = helper->box_get_handles(probe->get_size());
if (probe->get_origin_offset() != Vector3(0.0, 0.0, 0.0)) {
for (int i = 0; i < 3; i++) {
Vector3 orig_handle = probe->get_origin_offset();
orig_handle[i] -= 0.25;
lines.push_back(orig_handle);
orig_handle[i] += 0.5;
lines.push_back(orig_handle);
}
}
Ref<Material> material = get_material("reflection_probe_material", p_gizmo);
Ref<Material> material_internal = get_material("reflection_internal_material", p_gizmo);
p_gizmo->add_lines(lines, material);
p_gizmo->add_lines(internal_lines, material_internal);
p_gizmo->add_handles(handles, get_material("handles"));
}
Ref<Material> icon = get_material("reflection_probe_icon", p_gizmo);
p_gizmo->add_unscaled_billboard(icon, 0.05);
}

View File

@@ -0,0 +1,55 @@
/**************************************************************************/
/* reflection_probe_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class Gizmo3DHelper;
class ReflectionProbeGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(ReflectionProbeGizmoPlugin, 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;
ReflectionProbeGizmoPlugin();
};

View File

@@ -0,0 +1,449 @@
/**************************************************************************/
/* spring_bone_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 "spring_bone_3d_gizmo_plugin.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/spring_bone_collision_capsule_3d.h"
#include "scene/3d/spring_bone_collision_plane_3d.h"
#include "scene/3d/spring_bone_collision_sphere_3d.h"
// SpringBoneSimulator3D
SpringBoneSimulator3DGizmoPlugin::SelectionMaterials SpringBoneSimulator3DGizmoPlugin::selection_materials;
SpringBoneSimulator3DGizmoPlugin::SpringBoneSimulator3DGizmoPlugin() {
selection_materials.unselected_mat.instantiate();
selection_materials.unselected_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
selection_materials.unselected_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
selection_materials.unselected_mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
selection_materials.unselected_mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
selection_materials.unselected_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
selection_materials.selected_mat.instantiate();
Ref<Shader> sh;
sh.instantiate();
sh->set_code(R"(
// Skeleton 3D gizmo bones shader.
shader_type spatial;
render_mode unshaded, shadows_disabled;
void vertex() {
if (!OUTPUT_IS_SRGB) {
COLOR.rgb = mix( pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb* (1.0 / 12.92), lessThan(COLOR.rgb,vec3(0.04045)) );
}
VERTEX = VERTEX;
POSITION = PROJECTION_MATRIX * VIEW_MATRIX * MODEL_MATRIX * vec4(VERTEX.xyz, 1.0);
POSITION.z = mix(POSITION.z, POSITION.w, 0.998);
}
void fragment() {
ALBEDO = COLOR.rgb;
ALPHA = COLOR.a;
}
)");
selection_materials.selected_mat->set_shader(sh);
}
SpringBoneSimulator3DGizmoPlugin::~SpringBoneSimulator3DGizmoPlugin() {
selection_materials.unselected_mat.unref();
selection_materials.selected_mat.unref();
}
bool SpringBoneSimulator3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<SpringBoneSimulator3D>(p_spatial) != nullptr;
}
String SpringBoneSimulator3DGizmoPlugin::get_gizmo_name() const {
return "SpringBoneSimulator3D";
}
int SpringBoneSimulator3DGizmoPlugin::get_priority() const {
return -1;
}
void SpringBoneSimulator3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
SpringBoneSimulator3D *simulator = Object::cast_to<SpringBoneSimulator3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
if (!simulator->get_setting_count()) {
return;
}
Skeleton3D *skeleton = simulator->get_skeleton();
if (!skeleton) {
return;
}
Ref<ArrayMesh> mesh = get_joints_mesh(skeleton, simulator, p_gizmo->is_selected());
Transform3D skel_tr = simulator->get_global_transform().inverse() * skeleton->get_global_transform();
p_gizmo->add_mesh(mesh, Ref<Material>(), skel_tr, skeleton->register_skin(skeleton->create_skin_from_rest_transforms()));
}
Ref<ArrayMesh> SpringBoneSimulator3DGizmoPlugin::get_joints_mesh(Skeleton3D *p_skeleton, SpringBoneSimulator3D *p_simulator, bool p_is_selected) {
Color bone_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/spring_bone_joint");
Ref<SurfaceTool> surface_tool;
surface_tool.instantiate();
surface_tool->begin(Mesh::PRIMITIVE_LINES);
if (p_is_selected) {
surface_tool->set_material(selection_materials.selected_mat);
} else {
selection_materials.unselected_mat->set_albedo(bone_color);
surface_tool->set_material(selection_materials.unselected_mat);
}
LocalVector<int> bones;
LocalVector<float> weights;
bones.resize(4);
weights.resize(4);
for (int i = 0; i < 4; i++) {
bones[i] = 0;
weights[i] = 0;
}
weights[0] = 1;
for (int i = 0; i < p_simulator->get_setting_count(); i++) {
int current_bone = -1;
int prev_bone = -1;
int joint_end = p_simulator->get_joint_count(i) - 1;
bool is_extended = p_simulator->is_end_bone_extended(i) && p_simulator->get_end_bone_length(i) > 0;
for (int j = 0; j <= joint_end; j++) {
current_bone = p_simulator->get_joint_bone(i, j);
Transform3D global_pose = p_skeleton->get_bone_global_rest(current_bone);
if (j > 0) {
Transform3D parent_global_pose = p_skeleton->get_bone_global_rest(prev_bone);
draw_line(surface_tool, parent_global_pose.origin, global_pose.origin, bone_color);
draw_sphere(surface_tool, global_pose.basis, global_pose.origin, p_simulator->get_joint_radius(i, j - 1), bone_color);
// Draw rotation axis vector if not ROTATION_AXIS_ALL.
if (j != joint_end || (j == joint_end && is_extended)) {
SpringBoneSimulator3D::RotationAxis rotation_axis = p_simulator->get_joint_rotation_axis(i, j);
if (rotation_axis != SpringBoneSimulator3D::ROTATION_AXIS_ALL) {
Vector3 axis_vector = p_simulator->get_joint_rotation_axis_vector(i, j);
if (!axis_vector.is_zero_approx()) {
float line_length = p_simulator->get_joint_radius(i, j - 1) * 2.0;
Vector3 axis = global_pose.basis.xform(axis_vector.normalized()) * line_length;
draw_line(surface_tool, global_pose.origin - axis, global_pose.origin + axis, bone_color);
}
}
}
}
if (j == joint_end && is_extended) {
Vector3 axis = p_simulator->get_end_bone_axis(current_bone, p_simulator->get_end_bone_direction(i));
if (axis.is_zero_approx()) {
continue;
}
bones[0] = current_bone;
surface_tool->set_bones(bones);
surface_tool->set_weights(weights);
axis = global_pose.xform(axis * p_simulator->get_end_bone_length(i));
draw_line(surface_tool, global_pose.origin, axis, bone_color);
draw_sphere(surface_tool, global_pose.basis, axis, p_simulator->get_joint_radius(i, j), bone_color);
} else {
bones[0] = current_bone;
surface_tool->set_bones(bones);
surface_tool->set_weights(weights);
if (j == 0) {
// Draw rotation axis vector if not ROTATION_AXIS_ALL.
SpringBoneSimulator3D::RotationAxis rotation_axis = p_simulator->get_joint_rotation_axis(i, j);
if (rotation_axis != SpringBoneSimulator3D::ROTATION_AXIS_ALL) {
Vector3 axis_vector = p_simulator->get_joint_rotation_axis_vector(i, j);
if (!axis_vector.is_zero_approx()) {
float line_length = p_simulator->get_joint_radius(i, j) * 2.0;
Vector3 axis = global_pose.basis.xform(axis_vector.normalized()) * line_length;
draw_line(surface_tool, global_pose.origin - axis, global_pose.origin + axis, bone_color);
}
}
}
}
prev_bone = current_bone;
}
}
return surface_tool->commit();
}
void SpringBoneSimulator3DGizmoPlugin::draw_sphere(Ref<SurfaceTool> &p_surface_tool, const Basis &p_basis, const Vector3 &p_center, float p_radius, const Color &p_color) {
static const Vector3 VECTOR3_RIGHT = Vector3(1, 0, 0);
static const Vector3 VECTOR3_UP = Vector3(0, 1, 0);
static const Vector3 VECTOR3_FORWARD = Vector3(0, 0, 1);
static const int STEP = 16;
static const float SPPI = Math::TAU / (float)STEP;
for (int i = 1; i <= STEP; i++) {
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex(p_center + ((p_basis.xform(VECTOR3_UP * p_radius)).rotated(p_basis.xform(VECTOR3_RIGHT), SPPI * ((i - 1) % STEP))));
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex(p_center + ((p_basis.xform(VECTOR3_UP * p_radius)).rotated(p_basis.xform(VECTOR3_RIGHT), SPPI * (i % STEP))));
}
for (int i = 1; i <= STEP; i++) {
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex(p_center + ((p_basis.xform(VECTOR3_RIGHT * p_radius)).rotated(p_basis.xform(VECTOR3_FORWARD), SPPI * ((i - 1) % STEP))));
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex(p_center + ((p_basis.xform(VECTOR3_RIGHT * p_radius)).rotated(p_basis.xform(VECTOR3_FORWARD), SPPI * (i % STEP))));
}
for (int i = 1; i <= STEP; i++) {
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex(p_center + ((p_basis.xform(VECTOR3_FORWARD * p_radius)).rotated(p_basis.xform(VECTOR3_UP), SPPI * ((i - 1) % STEP))));
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex(p_center + ((p_basis.xform(VECTOR3_FORWARD * p_radius)).rotated(p_basis.xform(VECTOR3_UP), SPPI * (i % STEP))));
}
}
void SpringBoneSimulator3DGizmoPlugin::draw_line(Ref<SurfaceTool> &p_surface_tool, const Vector3 &p_begin_pos, const Vector3 &p_end_pos, const Color &p_color) {
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex(p_begin_pos);
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex(p_end_pos);
}
// SpringBoneCollision3D
SpringBoneCollision3DGizmoPlugin::SelectionMaterials SpringBoneCollision3DGizmoPlugin::selection_materials;
SpringBoneCollision3DGizmoPlugin::SpringBoneCollision3DGizmoPlugin() {
selection_materials.unselected_mat.instantiate();
selection_materials.unselected_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
selection_materials.unselected_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
selection_materials.unselected_mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
selection_materials.unselected_mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
selection_materials.unselected_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
selection_materials.selected_mat.instantiate();
Ref<Shader> sh;
sh.instantiate();
sh->set_code(R"(
// Skeleton 3D gizmo bones shader.
shader_type spatial;
render_mode unshaded, shadows_disabled;
void vertex() {
if (!OUTPUT_IS_SRGB) {
COLOR.rgb = mix( pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb* (1.0 / 12.92), lessThan(COLOR.rgb,vec3(0.04045)) );
}
VERTEX = VERTEX;
POSITION = PROJECTION_MATRIX * VIEW_MATRIX * MODEL_MATRIX * vec4(VERTEX.xyz, 1.0);
POSITION.z = mix(POSITION.z, POSITION.w, 0.998);
}
void fragment() {
ALBEDO = COLOR.rgb;
ALPHA = COLOR.a;
}
)");
selection_materials.selected_mat->set_shader(sh);
}
SpringBoneCollision3DGizmoPlugin::~SpringBoneCollision3DGizmoPlugin() {
selection_materials.unselected_mat.unref();
selection_materials.selected_mat.unref();
}
bool SpringBoneCollision3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<SpringBoneCollision3D>(p_spatial) != nullptr;
}
String SpringBoneCollision3DGizmoPlugin::get_gizmo_name() const {
return "SpringBoneCollision3D";
}
int SpringBoneCollision3DGizmoPlugin::get_priority() const {
return -1;
}
void SpringBoneCollision3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
SpringBoneCollision3D *collision = Object::cast_to<SpringBoneCollision3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
Ref<ArrayMesh> mesh = get_collision_mesh(collision, p_gizmo->is_selected());
p_gizmo->add_mesh(mesh);
}
Ref<ArrayMesh> SpringBoneCollision3DGizmoPlugin::get_collision_mesh(SpringBoneCollision3D *p_collision, bool p_is_selected) {
Color collision_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/spring_bone_collision");
Color inside_collision_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/spring_bone_inside_collision");
Ref<SurfaceTool> surface_tool;
surface_tool.instantiate();
surface_tool->begin(Mesh::PRIMITIVE_LINES);
if (p_is_selected) {
surface_tool->set_material(selection_materials.selected_mat);
} else {
selection_materials.unselected_mat->set_albedo(collision_color);
surface_tool->set_material(selection_materials.unselected_mat);
}
SpringBoneCollisionSphere3D *sphere = Object::cast_to<SpringBoneCollisionSphere3D>(p_collision);
if (sphere) {
draw_sphere(surface_tool, sphere->get_radius(), sphere->is_inside() ? inside_collision_color : collision_color);
return surface_tool->commit();
}
SpringBoneCollisionCapsule3D *capsule = Object::cast_to<SpringBoneCollisionCapsule3D>(p_collision);
if (capsule) {
draw_capsule(surface_tool, capsule->get_radius(), capsule->get_height(), capsule->is_inside() ? inside_collision_color : collision_color);
return surface_tool->commit();
}
SpringBoneCollisionPlane3D *plane = Object::cast_to<SpringBoneCollisionPlane3D>(p_collision);
if (plane) {
draw_plane(surface_tool, collision_color);
return surface_tool->commit();
}
return surface_tool->commit();
}
void SpringBoneCollision3DGizmoPlugin::draw_sphere(Ref<SurfaceTool> &p_surface_tool, float p_radius, const Color &p_color) {
static const Vector3 VECTOR3_RIGHT = Vector3(1, 0, 0);
static const Vector3 VECTOR3_UP = Vector3(0, 1, 0);
static const Vector3 VECTOR3_FORWARD = Vector3(0, 0, 1);
static const int STEP = 16;
static const float SPPI = Math::TAU / (float)STEP;
for (int i = 1; i <= STEP; i++) {
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex((VECTOR3_UP * p_radius).rotated(VECTOR3_RIGHT, SPPI * ((i - 1) % STEP)));
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex((VECTOR3_UP * p_radius).rotated(VECTOR3_RIGHT, SPPI * (i % STEP)));
}
for (int i = 1; i <= STEP; i++) {
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex((VECTOR3_RIGHT * p_radius).rotated(VECTOR3_FORWARD, SPPI * ((i - 1) % STEP)));
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex((VECTOR3_RIGHT * p_radius).rotated(VECTOR3_FORWARD, SPPI * (i % STEP)));
}
for (int i = 1; i <= STEP; i++) {
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex((VECTOR3_FORWARD * p_radius).rotated(VECTOR3_UP, SPPI * ((i - 1) % STEP)));
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex((VECTOR3_FORWARD * p_radius).rotated(VECTOR3_UP, SPPI * (i % STEP)));
}
}
void SpringBoneCollision3DGizmoPlugin::draw_capsule(Ref<SurfaceTool> &p_surface_tool, float p_radius, float p_height, const Color &p_color) {
static const Vector3 VECTOR3_RIGHT = Vector3(1, 0, 0);
static const Vector3 VECTOR3_UP = Vector3(0, 1, 0);
static const Vector3 VECTOR3_FORWARD = Vector3(0, 0, 1);
static const int STEP = 16;
static const int HALF_STEP = 8;
static const float SPPI = Math::TAU / (float)STEP;
static const float HALF_PI = Math::PI * 0.5;
Vector3 top = VECTOR3_UP * (p_height * 0.5 - p_radius);
Vector3 bottom = -top;
for (int i = 1; i <= STEP; i++) {
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex((i - 1 < HALF_STEP ? top : bottom) + (VECTOR3_UP * p_radius).rotated(VECTOR3_RIGHT, -HALF_PI + SPPI * ((i - 1) % STEP)));
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex((i - 1 < HALF_STEP ? top : bottom) + (VECTOR3_UP * p_radius).rotated(VECTOR3_RIGHT, -HALF_PI + SPPI * (i % STEP)));
}
for (int i = 1; i <= STEP; i++) {
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex((i - 1 < HALF_STEP ? top : bottom) + (VECTOR3_RIGHT * p_radius).rotated(VECTOR3_FORWARD, SPPI * ((i - 1) % STEP)));
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex((i - 1 < HALF_STEP ? top : bottom) + (VECTOR3_RIGHT * p_radius).rotated(VECTOR3_FORWARD, SPPI * (i % STEP)));
}
for (int i = 1; i <= STEP; i++) {
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex(top + (VECTOR3_FORWARD * p_radius).rotated(VECTOR3_UP, SPPI * ((i - 1) % STEP)));
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex(top + (VECTOR3_FORWARD * p_radius).rotated(VECTOR3_UP, SPPI * (i % STEP)));
}
for (int i = 1; i <= STEP; i++) {
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex(bottom + (VECTOR3_FORWARD * p_radius).rotated(VECTOR3_UP, SPPI * ((i - 1) % STEP)));
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex(bottom + (VECTOR3_FORWARD * p_radius).rotated(VECTOR3_UP, SPPI * (i % STEP)));
}
LocalVector<Vector3> directions;
directions.resize(4);
directions[0] = VECTOR3_RIGHT;
directions[1] = -VECTOR3_RIGHT;
directions[2] = VECTOR3_FORWARD;
directions[3] = -VECTOR3_FORWARD;
for (int i = 0; i < 4; i++) {
Vector3 dir = directions[i] * p_radius;
p_surface_tool->set_color(p_color);
p_surface_tool->add_vertex(top + dir);
p_surface_tool->add_vertex(bottom + dir);
}
}
void SpringBoneCollision3DGizmoPlugin::draw_plane(Ref<SurfaceTool> &p_surface_tool, const Color &p_color) {
static const Vector3 VECTOR3_UP = Vector3(0, 1, 0);
static const float HALF_PI = Math::PI * 0.5;
static const float ARROW_LENGTH = 0.3;
static const float ARROW_HALF_WIDTH = 0.05;
static const float ARROW_TOP_HALF_WIDTH = 0.1;
static const float ARROW_TOP = 0.5;
static const float RECT_SIZE = 1.0;
static const int RECT_STEP_COUNT = 9;
static const float RECT_HALF_SIZE = RECT_SIZE * 0.5;
static const float RECT_STEP = RECT_SIZE / (float)RECT_STEP_COUNT;
p_surface_tool->set_color(p_color);
// Draw arrow of the normal.
LocalVector<Vector3> arrow;
arrow.resize(7);
arrow[0] = Vector3(0, ARROW_TOP, 0);
arrow[1] = Vector3(-ARROW_TOP_HALF_WIDTH, ARROW_LENGTH, 0);
arrow[2] = Vector3(-ARROW_HALF_WIDTH, ARROW_LENGTH, 0);
arrow[3] = Vector3(-ARROW_HALF_WIDTH, 0, 0);
arrow[4] = Vector3(ARROW_HALF_WIDTH, 0, 0);
arrow[5] = Vector3(ARROW_HALF_WIDTH, ARROW_LENGTH, 0);
arrow[6] = Vector3(ARROW_TOP_HALF_WIDTH, ARROW_LENGTH, 0);
for (int i = 0; i < 2; i++) {
Basis ma(VECTOR3_UP, HALF_PI * i);
for (uint32_t j = 0; j < arrow.size(); j++) {
Vector3 v1 = arrow[j];
Vector3 v2 = arrow[(j + 1) % arrow.size()];
p_surface_tool->add_vertex(ma.xform(v1));
p_surface_tool->add_vertex(ma.xform(v2));
}
}
// Draw dashed line of the rect.
for (int i = 0; i < 4; i++) {
Basis ma(VECTOR3_UP, HALF_PI * i);
for (int j = 0; j < RECT_STEP_COUNT; j++) {
if (j % 2 == 1) {
continue;
}
Vector3 v1 = Vector3(RECT_HALF_SIZE, 0, RECT_HALF_SIZE - RECT_STEP * j);
Vector3 v2 = Vector3(RECT_HALF_SIZE, 0, RECT_HALF_SIZE - RECT_STEP * (j + 1));
p_surface_tool->add_vertex(ma.xform(v1));
p_surface_tool->add_vertex(ma.xform(v2));
}
}
}

View File

@@ -0,0 +1,86 @@
/**************************************************************************/
/* spring_bone_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/plugins/editor_plugin.h"
#include "editor/scene/3d/node_3d_editor_plugin.h"
#include "scene/3d/spring_bone_collision_3d.h"
#include "scene/3d/spring_bone_simulator_3d.h"
#include "scene/resources/surface_tool.h"
class SpringBoneSimulator3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(SpringBoneSimulator3DGizmoPlugin, EditorNode3DGizmoPlugin);
struct SelectionMaterials {
Ref<StandardMaterial3D> unselected_mat;
Ref<ShaderMaterial> selected_mat;
};
static SelectionMaterials selection_materials;
public:
static Ref<ArrayMesh> get_joints_mesh(Skeleton3D *p_skeleton, SpringBoneSimulator3D *p_simulator, bool p_is_selected);
static void draw_sphere(Ref<SurfaceTool> &p_surface_tool, const Basis &p_basis, const Vector3 &p_center, float p_radius, const Color &p_color);
static void draw_line(Ref<SurfaceTool> &p_surface_tool, const Vector3 &p_begin_pos, const Vector3 &p_end_pos, const Color &p_color);
bool has_gizmo(Node3D *p_spatial) override;
String get_gizmo_name() const override;
int get_priority() const override;
void redraw(EditorNode3DGizmo *p_gizmo) override;
SpringBoneSimulator3DGizmoPlugin();
~SpringBoneSimulator3DGizmoPlugin();
};
class SpringBoneCollision3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(SpringBoneCollision3DGizmoPlugin, EditorNode3DGizmoPlugin);
struct SelectionMaterials {
Ref<StandardMaterial3D> unselected_mat;
Ref<ShaderMaterial> selected_mat;
};
static SelectionMaterials selection_materials;
public:
static Ref<ArrayMesh> get_collision_mesh(SpringBoneCollision3D *p_collision, bool p_is_selected);
static void draw_sphere(Ref<SurfaceTool> &p_surface_tool, float p_radius, const Color &p_color);
static void draw_capsule(Ref<SurfaceTool> &p_surface_tool, float p_radius, float p_height, const Color &p_color);
static void draw_plane(Ref<SurfaceTool> &p_surface_tool, const Color &p_color);
bool has_gizmo(Node3D *p_spatial) override;
String get_gizmo_name() const override;
int get_priority() const override;
void redraw(EditorNode3DGizmo *p_gizmo) override;
SpringBoneCollision3DGizmoPlugin();
~SpringBoneCollision3DGizmoPlugin();
};

View File

@@ -0,0 +1,60 @@
/**************************************************************************/
/* sprite_base_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 "sprite_base_3d_gizmo_plugin.h"
#include "scene/3d/sprite_3d.h"
bool SpriteBase3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<SpriteBase3D>(p_spatial) != nullptr;
}
String SpriteBase3DGizmoPlugin::get_gizmo_name() const {
return "SpriteBase3D";
}
int SpriteBase3DGizmoPlugin::get_priority() const {
return -1;
}
bool SpriteBase3DGizmoPlugin::can_be_hidden() const {
return false;
}
void SpriteBase3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
SpriteBase3D *sprite_base = Object::cast_to<SpriteBase3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
Ref<TriangleMesh> tm = sprite_base->generate_triangle_mesh();
if (tm.is_valid()) {
p_gizmo->add_collision_triangles(tm);
}
}

View File

@@ -0,0 +1,44 @@
/**************************************************************************/
/* sprite_base_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class SpriteBase3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(SpriteBase3DGizmoPlugin, 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;
};

View File

@@ -0,0 +1,194 @@
/**************************************************************************/
/* visible_on_screen_notifier_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 "visible_on_screen_notifier_3d_gizmo_plugin.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/scene/3d/node_3d_editor_plugin.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/visible_on_screen_notifier_3d.h"
VisibleOnScreenNotifier3DGizmoPlugin::VisibleOnScreenNotifier3DGizmoPlugin() {
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/visibility_notifier");
create_material("visibility_notifier_material", gizmo_color);
gizmo_color.a = 0.1;
create_material("visibility_notifier_solid_material", gizmo_color);
create_handle_material("handles");
}
bool VisibleOnScreenNotifier3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<VisibleOnScreenNotifier3D>(p_spatial) != nullptr;
}
String VisibleOnScreenNotifier3DGizmoPlugin::get_gizmo_name() const {
return "VisibleOnScreenNotifier3D";
}
int VisibleOnScreenNotifier3DGizmoPlugin::get_priority() const {
return -1;
}
String VisibleOnScreenNotifier3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
switch (p_id) {
case 0:
return "Size X";
case 1:
return "Size Y";
case 2:
return "Size Z";
case 3:
return "Pos X";
case 4:
return "Pos Y";
case 5:
return "Pos Z";
}
return "";
}
Variant VisibleOnScreenNotifier3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
VisibleOnScreenNotifier3D *notifier = Object::cast_to<VisibleOnScreenNotifier3D>(p_gizmo->get_node_3d());
return notifier->get_aabb();
}
void VisibleOnScreenNotifier3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
VisibleOnScreenNotifier3D *notifier = Object::cast_to<VisibleOnScreenNotifier3D>(p_gizmo->get_node_3d());
Transform3D gt = notifier->get_global_transform();
Transform3D gi = gt.affine_inverse();
bool move = p_id >= 3;
p_id = p_id % 3;
AABB aabb = notifier->get_aabb();
Vector3 ray_from = p_camera->project_ray_origin(p_point);
Vector3 ray_dir = p_camera->project_ray_normal(p_point);
Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) };
Vector3 ofs = aabb.get_center();
Vector3 axis;
axis[p_id] = 1.0;
if (move) {
Vector3 ra, rb;
Geometry3D::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb);
float d = ra[p_id];
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
}
aabb.position[p_id] = d - 1.0 - aabb.size[p_id] * 0.5;
notifier->set_aabb(aabb);
} else {
Vector3 ra, rb;
Geometry3D::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb);
float d = ra[p_id] - ofs[p_id];
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
}
if (d < 0.001) {
d = 0.001;
}
//resize
aabb.position[p_id] = (aabb.position[p_id] + aabb.size[p_id] * 0.5) - d;
aabb.size[p_id] = d * 2;
notifier->set_aabb(aabb);
}
}
void VisibleOnScreenNotifier3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
VisibleOnScreenNotifier3D *notifier = Object::cast_to<VisibleOnScreenNotifier3D>(p_gizmo->get_node_3d());
if (p_cancel) {
notifier->set_aabb(p_restore);
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Notifier AABB"));
ur->add_do_method(notifier, "set_aabb", notifier->get_aabb());
ur->add_undo_method(notifier, "set_aabb", p_restore);
ur->commit_action();
}
void VisibleOnScreenNotifier3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
VisibleOnScreenNotifier3D *notifier = Object::cast_to<VisibleOnScreenNotifier3D>(p_gizmo->get_node_3d());
p_gizmo->clear();
Vector<Vector3> lines;
AABB aabb = notifier->get_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("visibility_notifier_material", p_gizmo);
p_gizmo->add_lines(lines, material);
p_gizmo->add_collision_segments(lines);
if (p_gizmo->is_selected()) {
Ref<Material> solid_material = get_material("visibility_notifier_solid_material", p_gizmo);
p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_center());
}
p_gizmo->add_handles(handles, get_material("handles"));
}

View File

@@ -0,0 +1,50 @@
/**************************************************************************/
/* visible_on_screen_notifier_3d_gizmo_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class VisibleOnScreenNotifier3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(VisibleOnScreenNotifier3DGizmoPlugin, 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;
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;
VisibleOnScreenNotifier3DGizmoPlugin();
};

View File

@@ -0,0 +1,164 @@
/**************************************************************************/
/* voxel_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 "voxel_gi_gizmo_plugin.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/scene/3d/gizmos/gizmo_3d_helper.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/voxel_gi.h"
VoxelGIGizmoPlugin::VoxelGIGizmoPlugin() {
helper.instantiate();
Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/voxel_gi");
create_material("voxel_gi_material", gizmo_color);
// This gizmo draws a lot of lines. Use a low opacity to make it not too intrusive.
gizmo_color.a = 0.02;
create_material("voxel_gi_internal_material", gizmo_color);
create_icon_material("voxel_gi_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoVoxelGI"), EditorStringName(EditorIcons)));
create_handle_material("handles");
}
bool VoxelGIGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<VoxelGI>(p_spatial) != nullptr;
}
String VoxelGIGizmoPlugin::get_gizmo_name() const {
return "VoxelGI";
}
int VoxelGIGizmoPlugin::get_priority() const {
return -1;
}
String VoxelGIGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
return helper->box_get_handle_name(p_id);
}
Variant VoxelGIGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
VoxelGI *probe = Object::cast_to<VoxelGI>(p_gizmo->get_node_3d());
return probe->get_size();
}
void VoxelGIGizmoPlugin::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 VoxelGIGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
VoxelGI *probe = Object::cast_to<VoxelGI>(p_gizmo->get_node_3d());
Vector3 sg[2];
helper->get_segment(p_camera, p_point, sg);
Vector3 size = probe->get_size();
Vector3 position;
helper->box_set_handle(sg, p_id, size, position);
probe->set_size(size);
probe->set_global_position(position);
}
void VoxelGIGizmoPlugin::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 Probe Size"), p_cancel, p_gizmo->get_node_3d());
}
void VoxelGIGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->clear();
if (p_gizmo->is_selected()) {
VoxelGI *probe = Object::cast_to<VoxelGI>(p_gizmo->get_node_3d());
Ref<Material> material = get_material("voxel_gi_material", p_gizmo);
Ref<Material> material_internal = get_material("voxel_gi_internal_material", p_gizmo);
Vector<Vector3> lines;
Vector3 size = probe->get_size();
static const int subdivs[VoxelGI::SUBDIV_MAX] = { 64, 128, 256, 512 };
AABB aabb = AABB(-size / 2, size);
int subdiv = subdivs[probe->get_subdiv()];
float cell_size = aabb.get_longest_axis_size() / subdiv;
for (int i = 0; i < 12; i++) {
Vector3 a, b;
aabb.get_edge(i, a, b);
lines.push_back(a);
lines.push_back(b);
}
p_gizmo->add_lines(lines, material);
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);
Vector<Vector3> handles = helper->box_get_handles(probe->get_size());
p_gizmo->add_handles(handles, get_material("handles"));
}
Ref<Material> icon = get_material("voxel_gi_icon", p_gizmo);
p_gizmo->add_unscaled_billboard(icon, 0.05);
}

View File

@@ -0,0 +1,55 @@
/**************************************************************************/
/* voxel_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. */
/**************************************************************************/
#pragma once
#include "editor/scene/3d/node_3d_editor_gizmos.h"
class Gizmo3DHelper;
class VoxelGIGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(VoxelGIGizmoPlugin, 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;
VoxelGIGizmoPlugin();
};

View File

@@ -0,0 +1,201 @@
/**************************************************************************/
/* gpu_particles_collision_sdf_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 "gpu_particles_collision_sdf_editor_plugin.h"
#include "editor/editor_interface.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/gui/editor_file_dialog.h"
void GPUParticlesCollisionSDF3DEditorPlugin::_bake() {
if (col_sdf) {
if (col_sdf->get_texture().is_null() || !col_sdf->get_texture()->get_path().is_resource_file()) {
String path = get_tree()->get_edited_scene_root()->get_scene_file_path();
if (path.is_empty()) {
path = "res://" + col_sdf->get_name() + "_data.exr";
} else {
String ext = path.get_extension();
path = path.get_basename() + "." + col_sdf->get_name() + "_data.exr";
}
probe_file->set_current_path(path);
probe_file->popup_file_dialog();
return;
}
_sdf_save_path_and_bake(col_sdf->get_texture()->get_path());
}
}
void GPUParticlesCollisionSDF3DEditorPlugin::edit(Object *p_object) {
GPUParticlesCollisionSDF3D *s = Object::cast_to<GPUParticlesCollisionSDF3D>(p_object);
if (!s) {
return;
}
col_sdf = s;
}
bool GPUParticlesCollisionSDF3DEditorPlugin::handles(Object *p_object) const {
return p_object->is_class("GPUParticlesCollisionSDF3D");
}
void GPUParticlesCollisionSDF3DEditorPlugin::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_PROCESS: {
if (!col_sdf) {
return;
}
// Set information tooltip on the Bake button. This information is useful
// to optimize performance (video RAM size) and reduce collision tunneling (individual cell size).
const Vector3i size = col_sdf->get_estimated_cell_size();
const Vector3 extents = col_sdf->get_size() / 2;
int data_size = 2;
const double size_mb = size.x * size.y * size.z * data_size / (1024.0 * 1024.0);
// Add a qualitative measurement to help the user assess whether a GPUParticlesCollisionSDF3D node is using a lot of VRAM.
String size_quality;
if (size_mb < 8.0) {
size_quality = TTR("Low");
} else if (size_mb < 32.0) {
size_quality = TTR("Moderate");
} else {
size_quality = TTR("High");
}
String text;
text += vformat(TTR("Subdivisions: %s"), vformat(U"%d × %d × %d", size.x, size.y, size.z)) + "\n";
text += vformat(TTR("Cell size: %s"), vformat(U"%.3f × %.3f × %.3f", extents.x / size.x, extents.y / size.y, extents.z / size.z)) + "\n";
text += vformat(TTR("Video RAM size: %s MB (%s)"), String::num(size_mb, 2), size_quality);
// Only update the tooltip when needed to avoid constant redrawing.
if (bake->get_tooltip(Point2()) == text) {
return;
}
bake->set_tooltip_text(text);
} break;
}
}
void GPUParticlesCollisionSDF3DEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
bake_hb->show();
set_process(true);
} else {
bake_hb->hide();
set_process(false);
}
}
EditorProgress *GPUParticlesCollisionSDF3DEditorPlugin::tmp_progress = nullptr;
void GPUParticlesCollisionSDF3DEditorPlugin::bake_func_begin(int p_steps) {
ERR_FAIL_COND(tmp_progress != nullptr);
tmp_progress = memnew(EditorProgress("bake_sdf", TTR("Bake SDF"), p_steps));
}
void GPUParticlesCollisionSDF3DEditorPlugin::bake_func_step(int p_step, const String &p_description) {
ERR_FAIL_NULL(tmp_progress);
tmp_progress->step(p_description, p_step, false);
}
void GPUParticlesCollisionSDF3DEditorPlugin::bake_func_end() {
ERR_FAIL_NULL(tmp_progress);
memdelete(tmp_progress);
tmp_progress = nullptr;
}
void GPUParticlesCollisionSDF3DEditorPlugin::_sdf_save_path_and_bake(const String &p_path) {
probe_file->hide();
if (col_sdf) {
Ref<Image> bake_img = col_sdf->bake();
if (bake_img.is_null()) {
EditorNode::get_singleton()->show_warning(TTR("No faces detected during GPUParticlesCollisionSDF3D bake.\nCheck whether there are visible meshes matching the bake mask within its extents."));
return;
}
Ref<ConfigFile> config;
config.instantiate();
if (FileAccess::exists(p_path + ".import")) {
config->load(p_path + ".import");
}
config->set_value("remap", "importer", "3d_texture");
config->set_value("remap", "type", "CompressedTexture3D");
if (!config->has_section_key("params", "compress/mode")) {
config->set_value("params", "compress/mode", 3); //user may want another compression, so leave it be
}
config->set_value("params", "compress/channel_pack", 1);
config->set_value("params", "mipmaps/generate", false);
config->set_value("params", "slices/horizontal", 1);
config->set_value("params", "slices/vertical", bake_img->get_meta("depth"));
config->save(p_path + ".import");
Error err = bake_img->save_exr(p_path, false);
ERR_FAIL_COND(err);
ResourceLoader::import(p_path);
Ref<Texture> t = ResourceLoader::load(p_path); //if already loaded, it will be updated on refocus?
ERR_FAIL_COND(t.is_null());
col_sdf->set_texture(t);
}
}
GPUParticlesCollisionSDF3DEditorPlugin::GPUParticlesCollisionSDF3DEditorPlugin() {
bake_hb = memnew(HBoxContainer);
bake_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
bake_hb->hide();
bake = memnew(Button);
bake->set_theme_type_variation(SceneStringName(FlatButton));
bake->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Bake"), EditorStringName(EditorIcons)));
bake->set_text(TTR("Bake SDF"));
bake->connect(SceneStringName(pressed), callable_mp(this, &GPUParticlesCollisionSDF3DEditorPlugin::_bake));
bake_hb->add_child(bake);
add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, bake_hb);
col_sdf = nullptr;
probe_file = memnew(EditorFileDialog);
probe_file->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
probe_file->add_filter("*.exr");
probe_file->connect("file_selected", callable_mp(this, &GPUParticlesCollisionSDF3DEditorPlugin::_sdf_save_path_and_bake));
EditorInterface::get_singleton()->get_base_control()->add_child(probe_file);
probe_file->set_title(TTR("Select path for SDF Texture"));
GPUParticlesCollisionSDF3D::bake_begin_function = bake_func_begin;
GPUParticlesCollisionSDF3D::bake_step_function = bake_func_step;
GPUParticlesCollisionSDF3D::bake_end_function = bake_func_end;
}

View File

@@ -0,0 +1,70 @@
/**************************************************************************/
/* gpu_particles_collision_sdf_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/plugins/editor_plugin.h"
#include "scene/3d/gpu_particles_collision_3d.h"
#include "scene/resources/material.h"
struct EditorProgress;
class EditorFileDialog;
class HBoxContainer;
class GPUParticlesCollisionSDF3DEditorPlugin : public EditorPlugin {
GDCLASS(GPUParticlesCollisionSDF3DEditorPlugin, EditorPlugin);
GPUParticlesCollisionSDF3D *col_sdf = nullptr;
HBoxContainer *bake_hb = nullptr;
Button *bake = nullptr;
EditorFileDialog *probe_file = nullptr;
static EditorProgress *tmp_progress;
static void bake_func_begin(int p_steps);
static void bake_func_step(int p_step, const String &p_description);
static void bake_func_end();
void _bake();
void _sdf_save_path_and_bake(const String &p_path);
protected:
void _notification(int p_what);
public:
virtual String get_plugin_name() const override { return "GPUParticlesCollisionSDF3D"; }
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;
GPUParticlesCollisionSDF3DEditorPlugin();
};

View File

@@ -0,0 +1,216 @@
/**************************************************************************/
/* lightmap_gi_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 "lightmap_gi_editor_plugin.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/gui/editor_file_dialog.h"
#include "modules/modules_enabled.gen.h" // For lightmapper_rd.
void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) {
if (lightmap) {
LightmapGI::BakeError err = LightmapGI::BAKE_ERROR_OK;
const uint64_t time_started = OS::get_singleton()->get_ticks_msec();
if (get_tree()->get_edited_scene_root()) {
Ref<LightmapGIData> lightmapGIData = lightmap->get_light_data();
if (lightmapGIData.is_valid()) {
String path = lightmapGIData->get_path();
if (!path.is_resource_file()) {
int srpos = path.find("::");
if (srpos != -1) {
String base = 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) {
err = LightmapGI::BAKE_ERROR_FOREIGN_DATA;
}
} else {
if (FileAccess::exists(base + ".import")) {
err = LightmapGI::BAKE_ERROR_FOREIGN_DATA;
}
}
}
} else {
if (FileAccess::exists(path + ".import")) {
err = LightmapGI::BAKE_ERROR_FOREIGN_DATA;
}
}
}
if (err == LightmapGI::BAKE_ERROR_OK) {
if (get_tree()->get_edited_scene_root() == lightmap) {
err = lightmap->bake(lightmap, p_file, bake_func_step);
} else {
err = lightmap->bake(lightmap->get_parent(), p_file, bake_func_step);
}
}
} else {
err = LightmapGI::BAKE_ERROR_NO_SCENE_ROOT;
}
bake_func_end(time_started);
switch (err) {
case LightmapGI::BAKE_ERROR_NO_SAVE_PATH: {
String scene_path = lightmap->get_scene_file_path();
if (scene_path.is_empty() && lightmap->get_owner()) {
scene_path = lightmap->get_owner()->get_scene_file_path();
}
if (scene_path.is_empty()) {
EditorNode::get_singleton()->show_warning(TTR("Can't determine a save path for lightmap images.\nSave your scene and try again."));
break;
}
scene_path = scene_path.get_basename() + ".lmbake";
file_dialog->set_current_path(scene_path);
file_dialog->popup_file_dialog();
} break;
case LightmapGI::BAKE_ERROR_NO_MESHES: {
EditorNode::get_singleton()->show_warning(
TTR("No meshes with lightmapping support to bake. Make sure they contain UV2 data and their Global Illumination property is set to Static.") +
String::utf8("\n\n") + TTR("To import a scene with lightmapping support, set Meshes > Light Baking to Static Lightmaps in the Import dock.") +
String::utf8("\n") + TTR("To enable lightmapping support on a primitive mesh, edit the PrimitiveMesh resource in the inspector and check Add UV2.") +
String::utf8("\n") + TTR("To enable lightmapping support on a CSG mesh, select the root CSG node and choose CSG > Bake Mesh Instance at the top of the 3D editor viewport.\nSelect the generated MeshInstance3D node and choose Mesh > Unwrap UV2 for Lightmap/AO at the top of the 3D editor viewport."));
} break;
case LightmapGI::BAKE_ERROR_CANT_CREATE_IMAGE: {
EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images. Make sure the lightmap destination path is writable."));
} break;
case LightmapGI::BAKE_ERROR_NO_SCENE_ROOT: {
EditorNode::get_singleton()->show_warning(TTR("No editor scene root found."));
} break;
case LightmapGI::BAKE_ERROR_FOREIGN_DATA: {
EditorNode::get_singleton()->show_warning(TTR("Lightmap data is not local to the scene."));
} break;
case LightmapGI::BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL: {
EditorNode::get_singleton()->show_warning(TTR("Maximum texture size is too small for the lightmap images.\nWhile this can be fixed by increasing the maximum texture size, it is recommended you split the scene into more objects instead."));
} break;
case LightmapGI::BAKE_ERROR_LIGHTMAP_TOO_SMALL: {
EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images. Make sure all meshes to bake have the Lightmap Size Hint property set high enough, and the LightmapGI's Texel Scale value is not too low."));
} break;
case LightmapGI::BAKE_ERROR_ATLAS_TOO_SMALL: {
EditorNode::get_singleton()->show_warning(TTR("Failed fitting a lightmap image into an atlas. This should never happen and should be reported."));
} break;
default: {
} break;
}
}
}
void LightmapGIEditorPlugin::_bake() {
_bake_select_file("");
}
void LightmapGIEditorPlugin::edit(Object *p_object) {
LightmapGI *s = Object::cast_to<LightmapGI>(p_object);
if (!s) {
return;
}
lightmap = s;
}
bool LightmapGIEditorPlugin::handles(Object *p_object) const {
return p_object->is_class("LightmapGI");
}
void LightmapGIEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
bake->show();
} else {
bake->hide();
}
}
EditorProgress *LightmapGIEditorPlugin::tmp_progress = nullptr;
bool LightmapGIEditorPlugin::bake_func_step(float p_progress, const String &p_description, void *, bool p_refresh) {
if (!tmp_progress) {
tmp_progress = memnew(EditorProgress("bake_lightmaps", TTR("Bake Lightmaps"), 1000, true));
ERR_FAIL_NULL_V(tmp_progress, false);
}
return tmp_progress->step(p_description, p_progress * 1000, p_refresh);
}
void LightmapGIEditorPlugin::bake_func_end(uint64_t p_time_started) {
if (tmp_progress != nullptr) {
memdelete(tmp_progress);
tmp_progress = nullptr;
}
const int time_taken = (OS::get_singleton()->get_ticks_msec() - p_time_started) * 0.001;
print_line(vformat("Done baking lightmaps in %02d:%02d:%02d.", time_taken / 3600, (time_taken % 3600) / 60, time_taken % 60));
// Request attention in case the user was doing something else.
// Baking lightmaps is likely the editor task that can take the most time,
// so only request the attention for baking lightmaps.
DisplayServer::get_singleton()->window_request_attention();
}
void LightmapGIEditorPlugin::_bind_methods() {
ClassDB::bind_method("_bake", &LightmapGIEditorPlugin::_bake);
}
LightmapGIEditorPlugin::LightmapGIEditorPlugin() {
bake = memnew(Button);
bake->set_theme_type_variation(SceneStringName(FlatButton));
// TODO: Rework this as a dedicated toolbar control so we can hook into theme changes and update it
// when the editor theme updates.
bake->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Bake"), EditorStringName(EditorIcons)));
bake->set_text(TTR("Bake Lightmaps"));
#ifdef MODULE_LIGHTMAPPER_RD_ENABLED
// Disable lightmap baking if not supported on the current GPU.
if (!DisplayServer::get_singleton()->can_create_rendering_device()) {
bake->set_disabled(true);
bake->set_tooltip_text(vformat(TTR("Lightmap baking is not supported on this GPU (%s)."), RenderingServer::get_singleton()->get_video_adapter_name()));
}
#else
// Disable lightmap baking if the module is disabled at compile-time.
bake->set_disabled(true);
#if defined(ANDROID_ENABLED) || defined(APPLE_EMBEDDED_ENABLED)
bake->set_tooltip_text(vformat(TTR("Lightmaps cannot be baked on %s."), OS::get_singleton()->get_name()));
#else
bake->set_tooltip_text(TTR("Lightmaps cannot be baked, as the `lightmapper_rd` module was disabled at compile-time."));
#endif
#endif // MODULE_LIGHTMAPPER_RD_ENABLED
bake->hide();
bake->connect(SceneStringName(pressed), Callable(this, "_bake"));
add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, bake);
lightmap = nullptr;
file_dialog = memnew(EditorFileDialog);
file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
file_dialog->add_filter("*.lmbake", TTR("LightMap Bake"));
file_dialog->set_title(TTR("Select lightmap bake file:"));
file_dialog->connect("file_selected", callable_mp(this, &LightmapGIEditorPlugin::_bake_select_file));
bake->add_child(file_dialog);
}

View File

@@ -0,0 +1,66 @@
/**************************************************************************/
/* lightmap_gi_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/plugins/editor_plugin.h"
#include "scene/3d/lightmap_gi.h"
#include "scene/resources/material.h"
struct EditorProgress;
class EditorFileDialog;
class LightmapGIEditorPlugin : public EditorPlugin {
GDCLASS(LightmapGIEditorPlugin, EditorPlugin);
LightmapGI *lightmap = nullptr;
Button *bake = nullptr;
EditorFileDialog *file_dialog = nullptr;
static EditorProgress *tmp_progress;
static bool bake_func_step(float p_progress, const String &p_description, void *, bool p_refresh);
static void bake_func_end(uint64_t p_time_started);
void _bake_select_file(const String &p_file);
void _bake();
protected:
static void _bind_methods();
public:
virtual String get_plugin_name() const override { return "LightmapGI"; }
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;
LightmapGIEditorPlugin();
};

View File

@@ -0,0 +1,190 @@
/**************************************************************************/
/* mesh_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 "mesh_editor_plugin.h"
#include "core/config/project_settings.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/button.h"
#include "scene/main/viewport.h"
void MeshEditor::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
rot_x -= mm->get_relative().y * 0.01;
rot_y -= mm->get_relative().x * 0.01;
rot_x = CLAMP(rot_x, -Math::PI / 2, Math::PI / 2);
_update_rotation();
}
}
void MeshEditor::_update_theme_item_cache() {
SubViewportContainer::_update_theme_item_cache();
theme_cache.light_1_icon = get_editor_theme_icon(SNAME("MaterialPreviewLight1"));
theme_cache.light_2_icon = get_editor_theme_icon(SNAME("MaterialPreviewLight2"));
}
void MeshEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
light_1_switch->set_button_icon(theme_cache.light_1_icon);
light_2_switch->set_button_icon(theme_cache.light_2_icon);
} break;
}
}
void MeshEditor::_update_rotation() {
Transform3D t;
t.basis.rotate(Vector3(0, 1, 0), -rot_y);
t.basis.rotate(Vector3(1, 0, 0), -rot_x);
rotation->set_transform(t);
}
void MeshEditor::edit(Ref<Mesh> p_mesh) {
mesh = p_mesh;
mesh_instance->set_mesh(mesh);
rot_x = Math::deg_to_rad(-15.0);
rot_y = Math::deg_to_rad(30.0);
_update_rotation();
AABB aabb = mesh->get_aabb();
Vector3 ofs = aabb.get_center();
float m = aabb.get_longest_axis_size();
if (m != 0) {
m = 1.0 / m;
m *= 0.5;
Transform3D xform;
xform.basis.scale(Vector3(m, m, m));
xform.origin = -xform.basis.xform(ofs); //-ofs*m;
//xform.origin.z -= aabb.get_longest_axis_size() * 2;
mesh_instance->set_transform(xform);
}
}
void MeshEditor::_on_light_1_switch_pressed() {
light1->set_visible(light_1_switch->is_pressed());
}
void MeshEditor::_on_light_2_switch_pressed() {
light2->set_visible(light_2_switch->is_pressed());
}
MeshEditor::MeshEditor() {
viewport = memnew(SubViewport);
Ref<World3D> world_3d;
world_3d.instantiate();
viewport->set_world_3d(world_3d); // Use own world.
add_child(viewport);
viewport->set_disable_input(true);
viewport->set_msaa_3d(Viewport::MSAA_4X);
set_stretch(true);
camera = memnew(Camera3D);
camera->set_transform(Transform3D(Basis(), Vector3(0, 0, 1.1)));
camera->set_perspective(45, 0.1, 10);
viewport->add_child(camera);
if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) {
camera_attributes.instantiate();
camera->set_attributes(camera_attributes);
}
light1 = memnew(DirectionalLight3D);
light1->set_transform(Transform3D().looking_at(Vector3(-1, -1, -1), Vector3(0, 1, 0)));
viewport->add_child(light1);
light2 = memnew(DirectionalLight3D);
light2->set_transform(Transform3D().looking_at(Vector3(0, 1, 0), Vector3(0, 0, 1)));
light2->set_color(Color(0.7, 0.7, 0.7));
viewport->add_child(light2);
rotation = memnew(Node3D);
viewport->add_child(rotation);
mesh_instance = memnew(MeshInstance3D);
rotation->add_child(mesh_instance);
set_custom_minimum_size(Size2(1, 150) * EDSCALE);
HBoxContainer *hb = memnew(HBoxContainer);
add_child(hb);
hb->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 2);
hb->add_spacer();
VBoxContainer *vb_light = memnew(VBoxContainer);
hb->add_child(vb_light);
light_1_switch = memnew(Button);
light_1_switch->set_theme_type_variation("PreviewLightButton");
light_1_switch->set_toggle_mode(true);
light_1_switch->set_pressed(true);
light_1_switch->set_accessibility_name(TTRC("First Light"));
vb_light->add_child(light_1_switch);
light_1_switch->connect(SceneStringName(pressed), callable_mp(this, &MeshEditor::_on_light_1_switch_pressed));
light_2_switch = memnew(Button);
light_2_switch->set_theme_type_variation("PreviewLightButton");
light_2_switch->set_toggle_mode(true);
light_2_switch->set_pressed(true);
light_2_switch->set_accessibility_name(TTRC("Second Light"));
vb_light->add_child(light_2_switch);
light_2_switch->connect(SceneStringName(pressed), callable_mp(this, &MeshEditor::_on_light_2_switch_pressed));
rot_x = 0;
rot_y = 0;
}
///////////////////////
bool EditorInspectorPluginMesh::can_handle(Object *p_object) {
return Object::cast_to<Mesh>(p_object) != nullptr;
}
void EditorInspectorPluginMesh::parse_begin(Object *p_object) {
Mesh *mesh = Object::cast_to<Mesh>(p_object);
if (!mesh) {
return;
}
Ref<Mesh> m(mesh);
MeshEditor *editor = memnew(MeshEditor);
editor->edit(m);
add_custom_control(editor);
}
MeshEditorPlugin::MeshEditorPlugin() {
Ref<EditorInspectorPluginMesh> plugin;
plugin.instantiate();
add_inspector_plugin(plugin);
}

View File

@@ -0,0 +1,97 @@
/**************************************************************************/
/* mesh_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/inspector/editor_inspector.h"
#include "editor/plugins/editor_plugin.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/gui/subviewport_container.h"
#include "scene/resources/camera_attributes.h"
class SubViewport;
class Button;
class MeshEditor : public SubViewportContainer {
GDCLASS(MeshEditor, SubViewportContainer);
float rot_x;
float rot_y;
SubViewport *viewport = nullptr;
MeshInstance3D *mesh_instance = nullptr;
Node3D *rotation = nullptr;
DirectionalLight3D *light1 = nullptr;
DirectionalLight3D *light2 = nullptr;
Camera3D *camera = nullptr;
Ref<CameraAttributesPractical> camera_attributes;
Ref<Mesh> mesh;
Button *light_1_switch = nullptr;
Button *light_2_switch = nullptr;
struct ThemeCache {
Ref<Texture2D> light_1_icon;
Ref<Texture2D> light_2_icon;
} theme_cache;
void _on_light_1_switch_pressed();
void _on_light_2_switch_pressed();
void _update_rotation();
protected:
virtual void _update_theme_item_cache() override;
void _notification(int p_what);
void gui_input(const Ref<InputEvent> &p_event) override;
public:
void edit(Ref<Mesh> p_mesh);
MeshEditor();
};
class EditorInspectorPluginMesh : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginMesh, EditorInspectorPlugin);
public:
virtual bool can_handle(Object *p_object) override;
virtual void parse_begin(Object *p_object) override;
};
class MeshEditorPlugin : public EditorPlugin {
GDCLASS(MeshEditorPlugin, EditorPlugin);
public:
virtual String get_plugin_name() const override { return "Mesh"; }
MeshEditorPlugin();
};

View File

@@ -0,0 +1,745 @@
/**************************************************************************/
/* mesh_instance_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 "mesh_instance_3d_editor_plugin.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/inspector/multi_node_edit.h"
#include "editor/scene/3d/node_3d_editor_plugin.h"
#include "editor/themes/editor_scale.h"
#include "scene/3d/navigation/navigation_region_3d.h"
#include "scene/3d/physics/collision_shape_3d.h"
#include "scene/3d/physics/static_body_3d.h"
#include "scene/gui/aspect_ratio_container.h"
#include "scene/gui/box_container.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/spin_box.h"
#include "scene/resources/3d/concave_polygon_shape_3d.h"
#include "scene/resources/3d/convex_polygon_shape_3d.h"
#include "scene/resources/3d/primitive_meshes.h"
void MeshInstance3DEditor::_node_removed(Node *p_node) {
if (p_node == node) {
node = nullptr;
options->hide();
}
}
void MeshInstance3DEditor::edit(MeshInstance3D *p_mesh) {
node = p_mesh;
}
Vector<Ref<Shape3D>> MeshInstance3DEditor::create_shape_from_mesh(Ref<Mesh> p_mesh, int p_option, bool p_verbose) {
Vector<Ref<Shape3D>> shapes;
switch (p_option) {
case SHAPE_TYPE_TRIMESH: {
shapes.push_back(p_mesh->create_trimesh_shape());
if (p_verbose && shapes.is_empty()) {
err_dialog->set_text(TTR("Couldn't create a Trimesh collision shape."));
err_dialog->popup_centered();
}
} break;
case SHAPE_TYPE_SINGLE_CONVEX: {
shapes.push_back(p_mesh->create_convex_shape(true, false));
if (p_verbose && shapes.is_empty()) {
err_dialog->set_text(TTR("Couldn't create a single collision shape."));
err_dialog->popup_centered();
}
} break;
case SHAPE_TYPE_SIMPLIFIED_CONVEX: {
shapes.push_back(p_mesh->create_convex_shape(true, true));
if (p_verbose && shapes.is_empty()) {
err_dialog->set_text(TTR("Couldn't create a simplified collision shape."));
err_dialog->popup_centered();
}
} break;
case SHAPE_TYPE_MULTIPLE_CONVEX: {
Ref<MeshConvexDecompositionSettings> settings;
settings.instantiate();
settings->set_max_convex_hulls(32);
settings->set_max_concavity(0.001);
shapes = p_mesh->convex_decompose(settings);
if (p_verbose && shapes.is_empty()) {
err_dialog->set_text(TTR("Couldn't create any collision shapes."));
err_dialog->popup_centered();
}
} break;
default:
break;
}
return shapes;
}
void MeshInstance3DEditor::_create_collision_shape() {
int placement_option = shape_placement->get_selected();
int shape_type_option = shape_type->get_selected();
EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
switch (shape_type_option) {
case SHAPE_TYPE_TRIMESH: {
ur->create_action(TTR(placement_option == SHAPE_PLACEMENT_SIBLING ? "Create Trimesh Collision Shape Sibling" : "Create Trimesh Static Body"));
} break;
case SHAPE_TYPE_SINGLE_CONVEX: {
ur->create_action(TTR(placement_option == SHAPE_PLACEMENT_SIBLING ? "Create Single Convex Collision Shape Sibling" : "Create Single Convex Static Body"));
} break;
case SHAPE_TYPE_SIMPLIFIED_CONVEX: {
ur->create_action(TTR(placement_option == SHAPE_PLACEMENT_SIBLING ? "Create Simplified Convex Collision Shape Sibling" : "Create Simplified Convex Static Body"));
} break;
case SHAPE_TYPE_MULTIPLE_CONVEX: {
ur->create_action(TTR(placement_option == SHAPE_PLACEMENT_SIBLING ? "Create Multiple Convex Collision Shape Siblings" : "Create Multiple Convex Static Body"));
} break;
default:
break;
}
List<Node *> selection = editor_selection->get_top_selected_node_list();
bool verbose = false;
if (selection.is_empty()) {
selection.push_back(node);
verbose = true;
}
for (Node *E : selection) {
if (placement_option == SHAPE_PLACEMENT_SIBLING && E == get_tree()->get_edited_scene_root()) {
if (verbose) {
err_dialog->set_text(TTR("Can't create a collision shape as sibling for the scene root."));
err_dialog->popup_centered();
}
continue;
}
MeshInstance3D *instance = Object::cast_to<MeshInstance3D>(E);
if (!instance) {
continue;
}
Ref<Mesh> m = instance->get_mesh();
if (m.is_null()) {
continue;
}
Vector<Ref<Shape3D>> shapes = create_shape_from_mesh(m, shape_type_option, verbose);
if (shapes.is_empty()) {
return;
}
Node *owner = get_tree()->get_edited_scene_root();
if (placement_option == SHAPE_PLACEMENT_STATIC_BODY_CHILD) {
StaticBody3D *body = memnew(StaticBody3D);
ur->add_do_method(instance, "add_child", body, true);
ur->add_do_method(body, "set_owner", owner);
ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), body);
for (Ref<Shape3D> shape : shapes) {
CollisionShape3D *cshape = memnew(CollisionShape3D);
cshape->set_shape(shape);
body->add_child(cshape, true);
ur->add_do_method(cshape, "set_owner", owner);
ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), cshape);
}
ur->add_do_reference(body);
ur->add_undo_method(instance, "remove_child", body);
} else {
for (Ref<Shape3D> shape : shapes) {
CollisionShape3D *cshape = memnew(CollisionShape3D);
cshape->set_shape(shape);
cshape->set_name("CollisionShape3D");
cshape->set_transform(instance->get_transform());
ur->add_do_method(E, "add_sibling", cshape, true);
ur->add_do_method(cshape, "set_owner", owner);
ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), cshape);
ur->add_do_reference(cshape);
ur->add_undo_method(instance->get_parent(), "remove_child", cshape);
}
}
}
ur->commit_action();
}
void MeshInstance3DEditor::_menu_option(int p_option) {
Ref<Mesh> mesh = node->get_mesh();
if (mesh.is_null()) {
err_dialog->set_text(TTR("Mesh is empty!"));
err_dialog->popup_centered();
return;
}
switch (p_option) {
case MENU_OPTION_CREATE_COLLISION_SHAPE: {
shape_dialog->popup_centered();
} break;
case MENU_OPTION_CREATE_NAVMESH: {
navigation_mesh_dialog->popup_centered(Vector2(200, 90));
} break;
case MENU_OPTION_CREATE_OUTLINE_MESH: {
outline_dialog->popup_centered(Vector2(200, 90));
} break;
case MENU_OPTION_CREATE_DEBUG_TANGENTS: {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Create Debug Tangents"));
MeshInstance3D *tangents = node->create_debug_tangents_node();
if (tangents) {
Node *owner = get_tree()->get_edited_scene_root();
ur->add_do_reference(tangents);
ur->add_do_method(node, "add_child", tangents, true);
ur->add_do_method(tangents, "set_owner", owner);
ur->add_undo_method(node, "remove_child", tangents);
}
ur->commit_action();
} break;
case MENU_OPTION_CREATE_UV2: {
Ref<Mesh> mesh2 = node->get_mesh();
if (mesh.is_null()) {
err_dialog->set_text(TTR("No mesh to unwrap."));
err_dialog->popup_centered();
return;
}
// Test if we are allowed to unwrap this mesh resource.
String path = mesh2->get_path();
int srpos = path.find("::");
if (srpos != -1) {
String base = 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) {
err_dialog->set_text(TTR("Mesh cannot unwrap UVs because it does not belong to the edited scene. Make it unique first."));
err_dialog->popup_centered();
return;
}
} else {
if (FileAccess::exists(path + ".import")) {
err_dialog->set_text(TTR("Mesh cannot unwrap UVs because it belongs to another resource which was imported from another file type. Make it unique first."));
err_dialog->popup_centered();
return;
}
}
} else {
if (FileAccess::exists(path + ".import")) {
err_dialog->set_text(TTR("Mesh cannot unwrap UVs because it was imported from another file type. Make it unique first."));
err_dialog->popup_centered();
return;
}
}
Ref<PrimitiveMesh> primitive_mesh = mesh2;
if (primitive_mesh.is_valid()) {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Unwrap UV2"));
ur->add_do_method(*primitive_mesh, "set_add_uv2", true);
ur->add_undo_method(*primitive_mesh, "set_add_uv2", primitive_mesh->get_add_uv2());
ur->commit_action();
} else {
Ref<ArrayMesh> array_mesh = mesh2;
if (array_mesh.is_null()) {
err_dialog->set_text(TTR("Contained Mesh is not of type ArrayMesh."));
err_dialog->popup_centered();
return;
}
// Preemptively evaluate common fail cases for lightmap unwrapping.
{
if (array_mesh->get_blend_shape_count() > 0) {
err_dialog->set_text(TTR("Can't unwrap mesh with blend shapes."));
err_dialog->popup_centered();
return;
}
for (int i = 0; i < array_mesh->get_surface_count(); i++) {
Mesh::PrimitiveType primitive = array_mesh->surface_get_primitive_type(i);
if (primitive != Mesh::PRIMITIVE_TRIANGLES) {
err_dialog->set_text(TTR("Only triangles are supported for lightmap unwrap."));
err_dialog->popup_centered();
return;
}
uint64_t format = array_mesh->surface_get_format(i);
if (!(format & Mesh::ArrayFormat::ARRAY_FORMAT_NORMAL)) {
err_dialog->set_text(TTR("Normals are required for lightmap unwrap."));
err_dialog->popup_centered();
return;
}
}
}
Ref<ArrayMesh> unwrapped_mesh = array_mesh->duplicate(false);
Error err = unwrapped_mesh->lightmap_unwrap(node->get_global_transform());
if (err != OK) {
err_dialog->set_text(TTR("UV Unwrap failed, mesh may not be manifold?"));
err_dialog->popup_centered();
return;
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Unwrap UV2"));
ur->add_do_method(node, "set_mesh", unwrapped_mesh);
ur->add_do_reference(node);
ur->add_do_reference(array_mesh.ptr());
ur->add_undo_method(node, "set_mesh", array_mesh);
ur->add_undo_reference(unwrapped_mesh.ptr());
ur->commit_action();
}
} break;
case MENU_OPTION_DEBUG_UV1: {
Ref<Mesh> mesh2 = node->get_mesh();
if (mesh2.is_null()) {
err_dialog->set_text(TTR("No mesh to debug."));
err_dialog->popup_centered();
return;
}
_create_uv_lines(0);
} break;
case MENU_OPTION_DEBUG_UV2: {
Ref<Mesh> mesh2 = node->get_mesh();
if (mesh2.is_null()) {
err_dialog->set_text(TTR("No mesh to debug."));
err_dialog->popup_centered();
return;
}
_create_uv_lines(1);
} break;
}
}
struct MeshInstance3DEditorEdgeSort {
Vector2 a;
Vector2 b;
static uint32_t hash(const MeshInstance3DEditorEdgeSort &p_edge) {
uint32_t h = hash_murmur3_one_32(HashMapHasherDefault::hash(p_edge.a));
return hash_fmix32(hash_murmur3_one_32(HashMapHasherDefault::hash(p_edge.b), h));
}
bool operator==(const MeshInstance3DEditorEdgeSort &p_b) const {
return a == p_b.a && b == p_b.b;
}
MeshInstance3DEditorEdgeSort() {}
MeshInstance3DEditorEdgeSort(const Vector2 &p_a, const Vector2 &p_b) {
if (p_a < p_b) {
a = p_a;
b = p_b;
} else {
b = p_a;
a = p_b;
}
}
};
void MeshInstance3DEditor::_create_uv_lines(int p_layer) {
Ref<Mesh> mesh = node->get_mesh();
ERR_FAIL_COND(mesh.is_null());
HashSet<MeshInstance3DEditorEdgeSort, MeshInstance3DEditorEdgeSort> edges;
uv_lines.clear();
for (int i = 0; i < mesh->get_surface_count(); i++) {
if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
continue;
}
Array a = mesh->surface_get_arrays(i);
Vector<Vector2> uv = a[p_layer == 0 ? Mesh::ARRAY_TEX_UV : Mesh::ARRAY_TEX_UV2];
if (uv.is_empty()) {
err_dialog->set_text(vformat(TTR("Mesh has no UV in layer %d."), p_layer + 1));
err_dialog->popup_centered();
return;
}
const Vector2 *r = uv.ptr();
Vector<int> indices = a[Mesh::ARRAY_INDEX];
const int *ri = nullptr;
int ic;
if (indices.size()) {
ic = indices.size();
ri = indices.ptr();
} else {
ic = uv.size();
}
for (int j = 0; j < ic; j += 3) {
for (int k = 0; k < 3; k++) {
MeshInstance3DEditorEdgeSort edge;
if (ri) {
edge.a = r[ri[j + k]];
edge.b = r[ri[j + ((k + 1) % 3)]];
} else {
edge.a = r[j + k];
edge.b = r[j + ((k + 1) % 3)];
}
if (edges.has(edge)) {
continue;
}
uv_lines.push_back(edge.a);
uv_lines.push_back(edge.b);
edges.insert(edge);
}
}
}
debug_uv_dialog->popup_centered();
}
void MeshInstance3DEditor::_debug_uv_draw() {
if (uv_lines.is_empty()) {
return;
}
debug_uv->set_clip_contents(true);
debug_uv->draw_rect(
Rect2(Vector2(), debug_uv->get_size()),
get_theme_color(SNAME("dark_color_3"), EditorStringName(Editor)));
// Draw an outline to represent the UV2's beginning and end area (useful on Black OLED theme).
// Top-left coordinate needs to be `(1, 1)` to prevent `clip_contents` from clipping the top and left lines.
debug_uv->draw_rect(
Rect2(Vector2(1, 1), debug_uv->get_size() - Vector2(1, 1)),
get_theme_color(SNAME("mono_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.125),
false,
Math::round(EDSCALE));
for (int x = 1; x <= 7; x++) {
debug_uv->draw_line(
Vector2(debug_uv->get_size().x * 0.125 * x, 0),
Vector2(debug_uv->get_size().x * 0.125 * x, debug_uv->get_size().y),
get_theme_color(SNAME("mono_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.125),
Math::round(EDSCALE));
}
for (int y = 1; y <= 7; y++) {
debug_uv->draw_line(
Vector2(0, debug_uv->get_size().y * 0.125 * y),
Vector2(debug_uv->get_size().x, debug_uv->get_size().y * 0.125 * y),
get_theme_color(SNAME("mono_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.125),
Math::round(EDSCALE));
}
debug_uv->draw_set_transform(Vector2(), 0, debug_uv->get_size());
// Use a translucent color to allow overlapping triangles to be visible.
// Divide line width by the drawing scale set above, so that line width is consistent regardless of dialog size.
// Aspect ratio is preserved by the parent AspectRatioContainer, so we only need to check the X size which is always equal to Y.
debug_uv->draw_multiline(
uv_lines,
get_theme_color(SNAME("mono_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.5),
Math::round(EDSCALE) / debug_uv->get_size().x);
}
void MeshInstance3DEditor::_create_navigation_mesh() {
Ref<Mesh> mesh = node->get_mesh();
if (mesh.is_null()) {
return;
}
Ref<NavigationMesh> nmesh = memnew(NavigationMesh);
if (nmesh.is_null()) {
return;
}
nmesh->create_from_mesh(mesh);
NavigationRegion3D *nmi = memnew(NavigationRegion3D);
nmi->set_navigation_mesh(nmesh);
Node *owner = get_tree()->get_edited_scene_root();
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Create Navigation Mesh"));
ur->add_do_method(node, "add_child", nmi, true);
ur->add_do_method(nmi, "set_owner", owner);
ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), nmi);
ur->add_do_reference(nmi);
ur->add_undo_method(node, "remove_child", nmi);
ur->commit_action();
}
void MeshInstance3DEditor::_create_outline_mesh() {
Ref<Mesh> mesh = node->get_mesh();
if (mesh.is_null()) {
err_dialog->set_text(TTR("MeshInstance3D lacks a Mesh."));
err_dialog->popup_centered();
return;
}
if (mesh->get_surface_count() == 0) {
err_dialog->set_text(TTR("Mesh has no surface to create outlines from."));
err_dialog->popup_centered();
return;
} else if (mesh->get_surface_count() == 1 && mesh->surface_get_primitive_type(0) != Mesh::PRIMITIVE_TRIANGLES) {
err_dialog->set_text(TTR("Mesh primitive type is not PRIMITIVE_TRIANGLES."));
err_dialog->popup_centered();
return;
}
Ref<Mesh> mesho = mesh->create_outline(outline_size->get_value());
if (mesho.is_null()) {
err_dialog->set_text(TTR("Could not create outline."));
err_dialog->popup_centered();
return;
}
MeshInstance3D *mi = memnew(MeshInstance3D);
mi->set_mesh(mesho);
Node *skeleton = node->get_node_or_null(node->get_skeleton_path());
if (skeleton && node->get_skin().is_valid()) {
mi->set_skin(node->get_skin());
mi->set_skeleton_path("../" + String(node->get_path_to(skeleton)));
}
Node *owner = get_tree()->get_edited_scene_root();
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Create Outline"));
ur->add_do_method(node, "add_child", mi, true);
ur->add_do_method(mi, "set_owner", owner);
ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), mi);
ur->add_do_reference(mi);
ur->add_undo_method(node, "remove_child", mi);
ur->commit_action();
}
void MeshInstance3DEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
options->set_button_icon(get_editor_theme_icon(SNAME("MeshInstance3D")));
} break;
}
}
MeshInstance3DEditor::MeshInstance3DEditor() {
options = memnew(MenuButton);
options->set_text(TTR("Mesh"));
options->set_switch_on_hover(true);
options->set_flat(false);
options->set_theme_type_variation("FlatMenuButton");
Node3DEditor::get_singleton()->add_control_to_menu_panel(options);
options->get_popup()->add_item(TTR("Create Collision Shape..."), MENU_OPTION_CREATE_COLLISION_SHAPE);
options->get_popup()->add_item(TTR("Create Navigation Mesh"), MENU_OPTION_CREATE_NAVMESH);
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Create Outline Mesh..."), MENU_OPTION_CREATE_OUTLINE_MESH);
options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a static outline mesh. The outline mesh will have its normals flipped automatically.\nThis can be used instead of the StandardMaterial Grow property when using that property isn't possible."));
options->get_popup()->add_item(TTR("Create Debug Tangents"), MENU_OPTION_CREATE_DEBUG_TANGENTS);
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("View UV1"), MENU_OPTION_DEBUG_UV1);
options->get_popup()->add_item(TTR("View UV2"), MENU_OPTION_DEBUG_UV2);
options->get_popup()->add_item(TTR("Unwrap UV2 for Lightmap/AO"), MENU_OPTION_CREATE_UV2);
options->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &MeshInstance3DEditor::_menu_option));
outline_dialog = memnew(ConfirmationDialog);
outline_dialog->set_title(TTR("Create Outline Mesh"));
outline_dialog->set_ok_button_text(TTR("Create"));
VBoxContainer *outline_dialog_vbc = memnew(VBoxContainer);
outline_dialog->add_child(outline_dialog_vbc);
//outline_dialog->set_child_rect(outline_dialog_vbc);
outline_size = memnew(SpinBox);
outline_size->set_accessibility_name(TTRC("Outline Size:"));
outline_size->set_min(0.001);
outline_size->set_max(1024);
outline_size->set_step(0.001);
outline_size->set_value(0.05);
outline_dialog_vbc->add_margin_child(TTR("Outline Size:"), outline_size);
add_child(outline_dialog);
outline_dialog->connect(SceneStringName(confirmed), callable_mp(this, &MeshInstance3DEditor::_create_outline_mesh));
shape_dialog = memnew(ConfirmationDialog);
shape_dialog->set_title(TTR("Create Collision Shape"));
shape_dialog->set_ok_button_text(TTR("Create"));
VBoxContainer *shape_dialog_vbc = memnew(VBoxContainer);
shape_dialog->add_child(shape_dialog_vbc);
Label *l = memnew(Label);
l->set_text(TTR("Collision Shape Placement"));
shape_dialog_vbc->add_child(l);
shape_placement = memnew(OptionButton);
shape_placement->set_accessibility_name(TTRC("Collision Shape Placement"));
shape_placement->set_h_size_flags(SIZE_EXPAND_FILL);
shape_placement->add_item(TTR("Sibling"), SHAPE_PLACEMENT_SIBLING);
shape_placement->set_item_tooltip(-1, TTR("Creates collision shapes as Sibling."));
shape_placement->add_item(TTR("Static Body Child"), SHAPE_PLACEMENT_STATIC_BODY_CHILD);
shape_placement->set_item_tooltip(-1, TTR("Creates a StaticBody3D as child and assigns collision shapes to it."));
shape_dialog_vbc->add_child(shape_placement);
l = memnew(Label);
l->set_text(TTR("Collision Shape Type"));
shape_dialog_vbc->add_child(l);
shape_type = memnew(OptionButton);
shape_type->set_accessibility_name(TTRC("Collision Shape Type"));
shape_type->set_h_size_flags(SIZE_EXPAND_FILL);
shape_type->add_item(TTR("Trimesh"), SHAPE_TYPE_TRIMESH);
shape_type->set_item_tooltip(-1, TTR("Creates a polygon-based collision shape.\nThis is the most accurate (but slowest) option for collision detection."));
shape_type->add_item(TTR("Single Convex"), SHAPE_TYPE_SINGLE_CONVEX);
shape_type->set_item_tooltip(-1, TTR("Creates a single convex collision shape.\nThis is the fastest (but least accurate) option for collision detection."));
shape_type->add_item(TTR("Simplified Convex"), SHAPE_TYPE_SIMPLIFIED_CONVEX);
shape_type->set_item_tooltip(-1, TTR("Creates a simplified convex collision shape.\nThis is similar to single collision shape, but can result in a simpler geometry in some cases, at the cost of accuracy."));
shape_type->add_item(TTR("Multiple Convex"), SHAPE_TYPE_MULTIPLE_CONVEX);
shape_type->set_item_tooltip(-1, TTR("Creates a polygon-based collision shape.\nThis is a performance middle-ground between a single convex collision and a polygon-based collision."));
shape_dialog_vbc->add_child(shape_type);
add_child(shape_dialog);
shape_dialog->connect(SceneStringName(confirmed), callable_mp(this, &MeshInstance3DEditor::_create_collision_shape));
err_dialog = memnew(AcceptDialog);
add_child(err_dialog);
debug_uv_dialog = memnew(AcceptDialog);
debug_uv_dialog->set_title(TTR("UV Channel Debug"));
add_child(debug_uv_dialog);
debug_uv_arc = memnew(AspectRatioContainer);
debug_uv_dialog->add_child(debug_uv_arc);
debug_uv = memnew(Control);
debug_uv->set_custom_minimum_size(Size2(600, 600) * EDSCALE);
debug_uv->connect(SceneStringName(draw), callable_mp(this, &MeshInstance3DEditor::_debug_uv_draw));
debug_uv_arc->add_child(debug_uv);
navigation_mesh_dialog = memnew(ConfirmationDialog);
navigation_mesh_dialog->set_title(TTR("Create NavigationMesh"));
navigation_mesh_dialog->set_ok_button_text(TTR("Create"));
VBoxContainer *navigation_mesh_dialog_vbc = memnew(VBoxContainer);
navigation_mesh_dialog->add_child(navigation_mesh_dialog_vbc);
Label *navigation_mesh_l = memnew(Label);
navigation_mesh_l->set_focus_mode(FOCUS_ACCESSIBILITY);
navigation_mesh_l->set_text(TTR("Before converting a rendering mesh to a navigation mesh, please verify:\n\n- The mesh is two-dimensional.\n- The mesh has no surface overlap.\n- The mesh has no self-intersection.\n- The mesh surfaces have indices.\n\nIf the mesh does not fulfill these requirements, the pathfinding will be broken."));
navigation_mesh_dialog_vbc->add_child(navigation_mesh_l);
add_child(navigation_mesh_dialog);
navigation_mesh_dialog->connect(SceneStringName(confirmed), callable_mp(this, &MeshInstance3DEditor::_create_navigation_mesh));
}
void MeshInstance3DEditorPlugin::edit(Object *p_object) {
{
MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_object);
if (mi) {
mesh_editor->edit(mi);
return;
}
}
Ref<MultiNodeEdit> mne = Ref<MultiNodeEdit>(p_object);
Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
if (mne.is_valid() && edited_scene) {
for (int i = 0; i < mne->get_node_count(); i++) {
MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(edited_scene->get_node(mne->get_node(i)));
if (mi) {
mesh_editor->edit(mi);
return;
}
}
}
mesh_editor->edit(nullptr);
}
bool MeshInstance3DEditorPlugin::handles(Object *p_object) const {
if (Object::cast_to<MeshInstance3D>(p_object)) {
return true;
}
Ref<MultiNodeEdit> mne = Ref<MultiNodeEdit>(p_object);
Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
if (mne.is_valid() && edited_scene) {
bool has_mesh = false;
for (int i = 0; i < mne->get_node_count(); i++) {
if (Object::cast_to<MeshInstance3D>(edited_scene->get_node(mne->get_node(i)))) {
if (has_mesh) {
return true;
} else {
has_mesh = true;
}
}
}
}
return false;
}
void MeshInstance3DEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
mesh_editor->options->show();
} else {
mesh_editor->options->hide();
mesh_editor->edit(nullptr);
}
}
MeshInstance3DEditorPlugin::MeshInstance3DEditorPlugin() {
mesh_editor = memnew(MeshInstance3DEditor);
EditorNode::get_singleton()->get_gui_base()->add_child(mesh_editor);
mesh_editor->options->hide();
}

View File

@@ -0,0 +1,122 @@
/**************************************************************************/
/* mesh_instance_3d_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/plugins/editor_plugin.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/gui/option_button.h"
class AcceptDialog;
class AspectRatioContainer;
class ConfirmationDialog;
class MenuButton;
class SpinBox;
class MeshInstance3DEditor : public Control {
GDCLASS(MeshInstance3DEditor, Control);
enum Menu {
MENU_OPTION_CREATE_COLLISION_SHAPE,
MENU_OPTION_CREATE_NAVMESH,
MENU_OPTION_CREATE_OUTLINE_MESH,
MENU_OPTION_CREATE_DEBUG_TANGENTS,
MENU_OPTION_CREATE_UV2,
MENU_OPTION_DEBUG_UV1,
MENU_OPTION_DEBUG_UV2,
};
enum ShapePlacement {
SHAPE_PLACEMENT_SIBLING,
SHAPE_PLACEMENT_STATIC_BODY_CHILD,
};
enum ShapeType {
SHAPE_TYPE_TRIMESH,
SHAPE_TYPE_SINGLE_CONVEX,
SHAPE_TYPE_SIMPLIFIED_CONVEX,
SHAPE_TYPE_MULTIPLE_CONVEX,
};
MeshInstance3D *node = nullptr;
MenuButton *options = nullptr;
ConfirmationDialog *outline_dialog = nullptr;
SpinBox *outline_size = nullptr;
ConfirmationDialog *shape_dialog = nullptr;
OptionButton *shape_type = nullptr;
OptionButton *shape_placement = nullptr;
AcceptDialog *err_dialog = nullptr;
AcceptDialog *debug_uv_dialog = nullptr;
AspectRatioContainer *debug_uv_arc = nullptr;
Control *debug_uv = nullptr;
Vector<Vector2> uv_lines;
ConfirmationDialog *navigation_mesh_dialog = nullptr;
void _create_collision_shape();
Vector<Ref<Shape3D>> create_shape_from_mesh(Ref<Mesh> p_mesh, int p_option, bool p_verbose);
void _menu_option(int p_option);
void _create_outline_mesh();
void _create_navigation_mesh();
void _create_uv_lines(int p_layer);
friend class MeshInstance3DEditorPlugin;
void _debug_uv_draw();
protected:
void _node_removed(Node *p_node);
void _notification(int p_what);
public:
void edit(MeshInstance3D *p_mesh);
MeshInstance3DEditor();
};
class MeshInstance3DEditorPlugin : public EditorPlugin {
GDCLASS(MeshInstance3DEditorPlugin, EditorPlugin);
MeshInstance3DEditor *mesh_editor = nullptr;
public:
virtual String get_plugin_name() const override { return "MeshInstance3D"; }
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;
MeshInstance3DEditorPlugin();
};

View File

@@ -0,0 +1,334 @@
/**************************************************************************/
/* mesh_library_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 "mesh_library_editor_plugin.h"
#include "editor/docks/inspector_dock.h"
#include "editor/editor_interface.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/scene/3d/node_3d_editor_plugin.h"
#include "editor/settings/editor_settings.h"
#include "main/main.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/navigation/navigation_region_3d.h"
#include "scene/3d/physics/static_body_3d.h"
#include "scene/gui/menu_button.h"
#include "scene/resources/packed_scene.h"
void MeshLibraryEditor::edit(const Ref<MeshLibrary> &p_mesh_library) {
mesh_library = p_mesh_library;
if (mesh_library.is_valid()) {
menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), !mesh_library->has_meta("_editor_source_scene"));
}
}
void MeshLibraryEditor::_menu_remove_confirm() {
switch (option) {
case MENU_OPTION_REMOVE_ITEM: {
mesh_library->remove_item(to_erase);
} break;
default: {
};
}
}
void MeshLibraryEditor::_menu_update_confirm(bool p_apply_xforms) {
cd_update->hide();
apply_xforms = p_apply_xforms;
String existing = mesh_library->get_meta("_editor_source_scene");
ERR_FAIL_COND(existing.is_empty());
_import_scene_cbk(existing);
}
void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge, bool p_apply_xforms) {
if (!p_merge) {
p_library->clear();
}
HashMap<int, MeshInstance3D *> mesh_instances;
for (int i = 0; i < p_scene->get_child_count(); i++) {
_import_scene_parse_node(p_library, mesh_instances, p_scene->get_child(i), p_merge, p_apply_xforms);
}
//generate previews!
if (true) {
Vector<Ref<Mesh>> meshes;
Vector<Transform3D> transforms;
Vector<int> ids = p_library->get_item_list();
for (int i = 0; i < ids.size(); i++) {
if (mesh_instances.find(ids[i])) {
meshes.push_back(p_library->get_item_mesh(ids[i]));
transforms.push_back(mesh_instances[ids[i]]->get_transform());
}
}
Vector<Ref<Texture2D>> textures = EditorInterface::get_singleton()->make_mesh_previews(meshes, &transforms, EDITOR_GET("editors/grid_map/preview_size"));
int j = 0;
for (int i = 0; i < ids.size(); i++) {
if (mesh_instances.find(ids[i])) {
p_library->set_item_preview(ids[i], textures[j]);
j++;
}
}
}
}
void MeshLibraryEditor::_import_scene_cbk(const String &p_str) {
Ref<PackedScene> ps = ResourceLoader::load(p_str, "PackedScene");
ERR_FAIL_COND(ps.is_null());
Node *scene = ps->instantiate();
ERR_FAIL_NULL_MSG(scene, "Cannot create an instance from PackedScene '" + p_str + "'.");
_import_scene(scene, mesh_library, option == MENU_OPTION_UPDATE_FROM_SCENE, apply_xforms);
memdelete(scene);
mesh_library->set_meta("_editor_source_scene", p_str);
menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), false);
}
void MeshLibraryEditor::_import_scene_parse_node(Ref<MeshLibrary> p_library, HashMap<int, MeshInstance3D *> &p_mesh_instances, Node *p_node, bool p_merge, bool p_apply_xforms) {
MeshInstance3D *mesh_instance_node = Object::cast_to<MeshInstance3D>(p_node);
if (!mesh_instance_node) {
// No MeshInstance so search deeper ...
for (int i = 0; i < p_node->get_child_count(); i++) {
_import_scene_parse_node(p_library, p_mesh_instances, p_node->get_child(i), p_merge, p_apply_xforms);
}
return;
}
Ref<Mesh> source_mesh = mesh_instance_node->get_mesh();
if (source_mesh.is_null()) {
return;
}
int item_id = p_library->find_item_by_name(mesh_instance_node->get_name());
if (item_id < 0) {
item_id = p_library->get_last_unused_item_id();
p_library->create_item(item_id);
p_library->set_item_name(item_id, mesh_instance_node->get_name());
} else if (!p_merge) {
WARN_PRINT(vformat("MeshLibrary export found a MeshInstance3D with a duplicated name '%s' in the exported scene that overrides a previously parsed MeshInstance3D item with the same name.", mesh_instance_node->get_name()));
}
p_mesh_instances[item_id] = mesh_instance_node;
Ref<Mesh> item_mesh = source_mesh->duplicate();
for (int i = 0; i < item_mesh->get_surface_count(); i++) {
Ref<Material> surface_override_material = mesh_instance_node->get_surface_override_material(i);
if (surface_override_material.is_valid()) {
item_mesh->surface_set_material(i, surface_override_material);
}
}
p_library->set_item_mesh(item_id, item_mesh);
GeometryInstance3D::ShadowCastingSetting gi3d_cast_shadows_setting = mesh_instance_node->get_cast_shadows_setting();
switch (gi3d_cast_shadows_setting) {
case GeometryInstance3D::ShadowCastingSetting::SHADOW_CASTING_SETTING_OFF: {
p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_OFF);
} break;
case GeometryInstance3D::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON: {
p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON);
} break;
case GeometryInstance3D::ShadowCastingSetting::SHADOW_CASTING_SETTING_DOUBLE_SIDED: {
p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_DOUBLE_SIDED);
} break;
case GeometryInstance3D::ShadowCastingSetting::SHADOW_CASTING_SETTING_SHADOWS_ONLY: {
p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_SHADOWS_ONLY);
} break;
default: {
p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON);
} break;
}
Transform3D item_mesh_transform;
if (p_apply_xforms) {
item_mesh_transform = mesh_instance_node->get_transform();
}
p_library->set_item_mesh_transform(item_id, item_mesh_transform);
Vector<MeshLibrary::ShapeData> collisions;
for (int i = 0; i < mesh_instance_node->get_child_count(); i++) {
StaticBody3D *static_body_node = Object::cast_to<StaticBody3D>(mesh_instance_node->get_child(i));
if (!static_body_node) {
continue;
}
List<uint32_t> shapes;
static_body_node->get_shape_owners(&shapes);
for (uint32_t &E : shapes) {
if (static_body_node->is_shape_owner_disabled(E)) {
continue;
}
Transform3D shape_transform;
if (p_apply_xforms) {
shape_transform = mesh_instance_node->get_transform();
}
shape_transform *= static_body_node->get_transform() * static_body_node->shape_owner_get_transform(E);
for (int k = 0; k < static_body_node->shape_owner_get_shape_count(E); k++) {
Ref<Shape3D> collision_shape = static_body_node->shape_owner_get_shape(E, k);
if (collision_shape.is_null()) {
continue;
}
MeshLibrary::ShapeData shape_data;
shape_data.shape = collision_shape;
shape_data.local_transform = shape_transform;
collisions.push_back(shape_data);
}
}
}
p_library->set_item_shapes(item_id, collisions);
for (int i = 0; i < mesh_instance_node->get_child_count(); i++) {
NavigationRegion3D *navigation_region_node = Object::cast_to<NavigationRegion3D>(mesh_instance_node->get_child(i));
if (!navigation_region_node) {
continue;
}
Ref<NavigationMesh> navigation_mesh = navigation_region_node->get_navigation_mesh();
if (navigation_mesh.is_valid()) {
Transform3D navigation_mesh_transform = navigation_region_node->get_transform();
p_library->set_item_navigation_mesh(item_id, navigation_mesh);
p_library->set_item_navigation_mesh_transform(item_id, navigation_mesh_transform);
break;
}
}
}
Error MeshLibraryEditor::update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge, bool p_apply_xforms) {
_import_scene(p_base_scene, ml, p_merge, p_apply_xforms);
return OK;
}
void MeshLibraryEditor::_menu_cbk(int p_option) {
option = p_option;
switch (p_option) {
case MENU_OPTION_ADD_ITEM: {
mesh_library->create_item(mesh_library->get_last_unused_item_id());
} break;
case MENU_OPTION_REMOVE_ITEM: {
String p = InspectorDock::get_inspector_singleton()->get_selected_path();
if (p.begins_with("item") && p.get_slice_count("/") >= 2) {
to_erase = p.get_slicec('/', 1).to_int();
cd_remove->set_text(vformat(TTR("Remove item %d?"), to_erase));
cd_remove->popup_centered(Size2(300, 60));
}
} break;
case MENU_OPTION_IMPORT_FROM_SCENE: {
apply_xforms = false;
file->popup_file_dialog();
} break;
case MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS: {
apply_xforms = true;
file->popup_file_dialog();
} break;
case MENU_OPTION_UPDATE_FROM_SCENE: {
cd_update->set_text(vformat(TTR("Update from existing scene?:\n%s"), String(mesh_library->get_meta("_editor_source_scene"))));
cd_update->popup_centered(Size2(500, 60));
} break;
}
}
MeshLibraryEditor::MeshLibraryEditor() {
file = memnew(EditorFileDialog);
file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
//not for now?
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions);
file->clear_filters();
file->set_title(TTR("Import Scene"));
for (const String &extension : extensions) {
file->add_filter("*." + extension, extension.to_upper());
}
add_child(file);
file->connect("file_selected", callable_mp(this, &MeshLibraryEditor::_import_scene_cbk));
menu = memnew(MenuButton);
Node3DEditor::get_singleton()->add_control_to_menu_panel(menu);
menu->set_position(Point2(1, 1));
menu->set_text(TTR("MeshLibrary"));
menu->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("MeshLibrary"), EditorStringName(EditorIcons)));
menu->set_flat(false);
menu->set_theme_type_variation("FlatMenuButton");
menu->get_popup()->add_item(TTR("Add Item"), MENU_OPTION_ADD_ITEM);
menu->get_popup()->add_item(TTR("Remove Selected Item"), MENU_OPTION_REMOVE_ITEM);
menu->get_popup()->add_separator();
menu->get_popup()->add_item(TTR("Import from Scene (Ignore Transforms)"), MENU_OPTION_IMPORT_FROM_SCENE);
menu->get_popup()->add_item(TTR("Import from Scene (Apply Transforms)"), MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS);
menu->get_popup()->add_item(TTR("Update from Scene"), MENU_OPTION_UPDATE_FROM_SCENE);
menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), true);
menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &MeshLibraryEditor::_menu_cbk));
menu->hide();
cd_remove = memnew(ConfirmationDialog);
add_child(cd_remove);
cd_remove->get_ok_button()->connect(SceneStringName(pressed), callable_mp(this, &MeshLibraryEditor::_menu_remove_confirm));
cd_update = memnew(ConfirmationDialog);
add_child(cd_update);
cd_update->set_ok_button_text(TTR("Apply without Transforms"));
cd_update->get_ok_button()->connect(SceneStringName(pressed), callable_mp(this, &MeshLibraryEditor::_menu_update_confirm).bind(false));
cd_update->add_button(TTR("Apply with Transforms"))->connect(SceneStringName(pressed), callable_mp(this, &MeshLibraryEditor::_menu_update_confirm).bind(true));
}
void MeshLibraryEditorPlugin::edit(Object *p_node) {
if (Object::cast_to<MeshLibrary>(p_node)) {
mesh_library_editor->edit(Object::cast_to<MeshLibrary>(p_node));
mesh_library_editor->show();
} else {
mesh_library_editor->edit(Ref<MeshLibrary>());
mesh_library_editor->hide();
}
}
bool MeshLibraryEditorPlugin::handles(Object *p_node) const {
return p_node->is_class("MeshLibrary");
}
void MeshLibraryEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
mesh_library_editor->show();
mesh_library_editor->get_menu_button()->show();
} else {
mesh_library_editor->hide();
mesh_library_editor->get_menu_button()->hide();
}
}
MeshLibraryEditorPlugin::MeshLibraryEditorPlugin() {
mesh_library_editor = memnew(MeshLibraryEditor);
EditorNode::get_singleton()->get_gui_base()->add_child(mesh_library_editor);
mesh_library_editor->set_anchors_and_offsets_preset(Control::PRESET_TOP_WIDE);
mesh_library_editor->set_end(Point2(0, 22));
mesh_library_editor->hide();
}

View File

@@ -0,0 +1,92 @@
/**************************************************************************/
/* mesh_library_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/plugins/editor_plugin.h"
#include "scene/resources/3d/mesh_library.h"
class EditorFileDialog;
class ConfirmationDialog;
class MenuButton;
class MeshInstance3D;
class MeshLibraryEditor : public Control {
GDCLASS(MeshLibraryEditor, Control);
Ref<MeshLibrary> mesh_library;
MenuButton *menu = nullptr;
ConfirmationDialog *cd_remove = nullptr;
ConfirmationDialog *cd_update = nullptr;
EditorFileDialog *file = nullptr;
bool apply_xforms = false;
int to_erase = 0;
enum {
MENU_OPTION_ADD_ITEM,
MENU_OPTION_REMOVE_ITEM,
MENU_OPTION_UPDATE_FROM_SCENE,
MENU_OPTION_IMPORT_FROM_SCENE,
MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS
};
int option = 0;
void _import_scene_cbk(const String &p_str);
void _menu_cbk(int p_option);
void _menu_remove_confirm();
void _menu_update_confirm(bool p_apply_xforms);
static void _import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge, bool p_apply_xforms);
static void _import_scene_parse_node(Ref<MeshLibrary> p_library, HashMap<int, MeshInstance3D *> &p_mesh_instances, Node *p_node, bool p_merge, bool p_apply_xforms);
public:
MenuButton *get_menu_button() const { return menu; }
void edit(const Ref<MeshLibrary> &p_mesh_library);
static Error update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge = true, bool p_apply_xforms = false);
MeshLibraryEditor();
};
class MeshLibraryEditorPlugin : public EditorPlugin {
GDCLASS(MeshLibraryEditorPlugin, EditorPlugin);
MeshLibraryEditor *mesh_library_editor = nullptr;
public:
virtual String get_plugin_name() const override { return "MeshLibrary"; }
bool has_main_screen() const override { return false; }
virtual void edit(Object *p_node) override;
virtual bool handles(Object *p_node) const override;
virtual void make_visible(bool p_visible) override;
MeshLibraryEditorPlugin();
};

View File

@@ -0,0 +1,400 @@
/**************************************************************************/
/* multimesh_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 "multimesh_editor_plugin.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/scene/3d/node_3d_editor_plugin.h"
#include "editor/scene/scene_tree_editor.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/gui/box_container.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/option_button.h"
void MultiMeshEditor::_node_removed(Node *p_node) {
if (p_node == node) {
node = nullptr;
hide();
}
}
void MultiMeshEditor::_populate() {
if (!node) {
return;
}
Ref<Mesh> mesh;
if (mesh_source->get_text().is_empty()) {
Ref<MultiMesh> multimesh;
multimesh = node->get_multimesh();
if (multimesh.is_null()) {
err_dialog->set_text(TTR("No mesh source specified (and no MultiMesh set in node)."));
err_dialog->popup_centered();
return;
}
if (multimesh->get_mesh().is_null()) {
err_dialog->set_text(TTR("No mesh source specified (and MultiMesh contains no Mesh)."));
err_dialog->popup_centered();
return;
}
mesh = multimesh->get_mesh();
} else {
Node *ms_node = node->get_node(mesh_source->get_text());
if (!ms_node) {
err_dialog->set_text(TTR("Mesh source is invalid (invalid path)."));
err_dialog->popup_centered();
return;
}
MeshInstance3D *ms_instance = Object::cast_to<MeshInstance3D>(ms_node);
if (!ms_instance) {
err_dialog->set_text(TTR("Mesh source is invalid (not a MeshInstance3D)."));
err_dialog->popup_centered();
return;
}
mesh = ms_instance->get_mesh();
if (mesh.is_null()) {
err_dialog->set_text(TTR("Mesh source is invalid (contains no Mesh resource)."));
err_dialog->popup_centered();
return;
}
}
if (surface_source->get_text().is_empty()) {
err_dialog->set_text(TTR("No surface source specified."));
err_dialog->popup_centered();
return;
}
Node *ss_node = node->get_node(surface_source->get_text());
if (!ss_node) {
err_dialog->set_text(TTR("Surface source is invalid (invalid path)."));
err_dialog->popup_centered();
return;
}
MeshInstance3D *ss_instance = Object::cast_to<MeshInstance3D>(ss_node);
if (!ss_instance || ss_instance->get_mesh().is_null()) {
err_dialog->set_text(TTR("Surface source is invalid (no geometry)."));
err_dialog->popup_centered();
return;
}
Transform3D geom_xform = node->get_global_transform().affine_inverse() * ss_instance->get_global_transform();
Vector<Face3> geometry = ss_instance->get_mesh()->get_faces();
if (geometry.is_empty()) {
err_dialog->set_text(TTR("Surface source is invalid (no faces)."));
err_dialog->popup_centered();
return;
}
//make all faces local
int gc = geometry.size();
Face3 *w = geometry.ptrw();
for (int i = 0; i < gc; i++) {
for (int j = 0; j < 3; j++) {
w[i].vertex[j] = geom_xform.xform(w[i].vertex[j]);
}
}
Vector<Face3> faces = geometry;
int facecount = faces.size();
ERR_FAIL_COND_MSG(!facecount, "Parent has no solid faces to populate.");
const Face3 *r = faces.ptr();
float area_accum = 0;
RBMap<float, int> triangle_area_map;
for (int i = 0; i < facecount; i++) {
float area = r[i].get_area();
if (area < CMP_EPSILON) {
continue;
}
triangle_area_map[area_accum] = i;
area_accum += area;
}
ERR_FAIL_COND_MSG(triangle_area_map.is_empty(), "Couldn't map area.");
ERR_FAIL_COND_MSG(area_accum == 0, "Couldn't map area.");
Ref<MultiMesh> multimesh = memnew(MultiMesh);
multimesh->set_mesh(mesh);
int instance_count = populate_amount->get_value();
multimesh->set_transform_format(MultiMesh::TRANSFORM_3D);
multimesh->set_use_colors(false);
multimesh->set_instance_count(instance_count);
float _tilt_random = populate_tilt_random->get_value();
float _rotate_random = populate_rotate_random->get_value();
float _scale_random = populate_scale_random->get_value();
float _scale = populate_scale->get_value();
int axis = populate_axis->get_selected();
Transform3D axis_xform;
if (axis == Vector3::AXIS_Z) {
axis_xform.rotate(Vector3(1, 0, 0), -Math::PI * 0.5);
}
if (axis == Vector3::AXIS_X) {
axis_xform.rotate(Vector3(0, 0, 1), -Math::PI * 0.5);
}
for (int i = 0; i < instance_count; i++) {
float areapos = Math::random(0.0f, area_accum);
RBMap<float, int>::Iterator E = triangle_area_map.find_closest(areapos);
ERR_FAIL_COND(!E);
int index = E->value;
ERR_FAIL_INDEX(index, facecount);
// ok FINALLY get face
Face3 face = r[index];
//now compute some position inside the face...
Vector3 pos = face.get_random_point_inside();
Vector3 normal = face.get_plane().normal;
Vector3 op_axis = (face.vertex[0] - face.vertex[1]).normalized();
Transform3D xform;
xform.set_look_at(pos, pos + op_axis, normal);
xform = xform * axis_xform;
Basis post_xform;
post_xform.rotate(xform.basis.get_column(1), -Math::random(-_rotate_random, _rotate_random) * Math::PI);
post_xform.rotate(xform.basis.get_column(2), -Math::random(-_tilt_random, _tilt_random) * Math::PI);
post_xform.rotate(xform.basis.get_column(0), -Math::random(-_tilt_random, _tilt_random) * Math::PI);
xform.basis = post_xform * xform.basis;
//xform.basis.orthonormalize();
xform.basis.scale(Vector3(1, 1, 1) * (_scale + Math::random(-_scale_random, _scale_random)));
multimesh->set_instance_transform(i, xform);
}
node->set_multimesh(multimesh);
}
void MultiMeshEditor::_browsed(const NodePath &p_path) {
NodePath path = node->get_path_to(get_node(p_path));
if (browsing_source) {
mesh_source->set_text(String(path));
} else {
surface_source->set_text(String(path));
}
}
void MultiMeshEditor::_menu_option(int p_option) {
switch (p_option) {
case MENU_OPTION_POPULATE: {
if (_last_pp_node != node) {
surface_source->set_text("..");
mesh_source->set_text("..");
populate_axis->select(1);
populate_rotate_random->set_value(0);
populate_tilt_random->set_value(0);
populate_scale_random->set_value(0);
populate_scale->set_value(1);
populate_amount->set_value(128);
_last_pp_node = node;
}
populate_dialog->popup_centered(Size2(250, 380));
} break;
}
}
void MultiMeshEditor::edit(MultiMeshInstance3D *p_multimesh) {
node = p_multimesh;
}
void MultiMeshEditor::_browse(bool p_source) {
browsing_source = p_source;
Node *browsed_node = nullptr;
if (p_source) {
browsed_node = node->get_node_or_null(mesh_source->get_text());
std->set_title(TTR("Select a Source Mesh:"));
} else {
browsed_node = node->get_node_or_null(surface_source->get_text());
std->set_title(TTR("Select a Target Surface:"));
}
std->popup_scenetree_dialog(browsed_node);
}
MultiMeshEditor::MultiMeshEditor() {
options = memnew(MenuButton);
options->set_switch_on_hover(true);
Node3DEditor::get_singleton()->add_control_to_menu_panel(options);
options->set_text("MultiMesh");
options->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("MultiMeshInstance3D"), EditorStringName(EditorIcons)));
options->set_flat(false);
options->set_theme_type_variation("FlatMenuButton");
options->get_popup()->add_item(TTR("Populate Surface"));
options->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &MultiMeshEditor::_menu_option));
populate_dialog = memnew(ConfirmationDialog);
populate_dialog->set_title(TTR("Populate MultiMesh"));
add_child(populate_dialog);
VBoxContainer *vbc = memnew(VBoxContainer);
populate_dialog->add_child(vbc);
//populate_dialog->set_child_rect(vbc);
HBoxContainer *hbc = memnew(HBoxContainer);
surface_source = memnew(LineEdit);
hbc->add_child(surface_source);
surface_source->set_h_size_flags(SIZE_EXPAND_FILL);
surface_source->set_accessibility_name(TTRC("Target Surface:"));
Button *b = memnew(Button);
hbc->add_child(b);
b->set_accessibility_name(TTRC("Browse"));
b->set_text("..");
b->connect(SceneStringName(pressed), callable_mp(this, &MultiMeshEditor::_browse).bind(false));
vbc->add_margin_child(TTR("Target Surface:"), hbc);
hbc = memnew(HBoxContainer);
mesh_source = memnew(LineEdit);
hbc->add_child(mesh_source);
mesh_source->set_h_size_flags(SIZE_EXPAND_FILL);
mesh_source->set_accessibility_name(TTRC("Source Mesh:"));
b = memnew(Button);
hbc->add_child(b);
b->set_accessibility_name(TTRC("Browse"));
b->set_text("..");
vbc->add_margin_child(TTR("Source Mesh:"), hbc);
b->connect(SceneStringName(pressed), callable_mp(this, &MultiMeshEditor::_browse).bind(true));
populate_axis = memnew(OptionButton);
populate_axis->set_accessibility_name(TTRC("Mesh Up Axis:"));
populate_axis->add_item(TTR("X-Axis"));
populate_axis->add_item(TTR("Y-Axis"));
populate_axis->add_item(TTR("Z-Axis"));
populate_axis->select(2);
vbc->add_margin_child(TTR("Mesh Up Axis:"), populate_axis);
populate_rotate_random = memnew(HSlider);
populate_rotate_random->set_max(1);
populate_rotate_random->set_step(0.01);
populate_rotate_random->set_accessibility_name(TTRC("Random Rotation:"));
vbc->add_margin_child(TTR("Random Rotation:"), populate_rotate_random);
populate_tilt_random = memnew(HSlider);
populate_tilt_random->set_max(1);
populate_tilt_random->set_step(0.01);
populate_tilt_random->set_accessibility_name(TTRC("Random Tilt:"));
vbc->add_margin_child(TTR("Random Tilt:"), populate_tilt_random);
populate_scale_random = memnew(SpinBox);
populate_scale_random->set_min(0);
populate_scale_random->set_max(1);
populate_scale_random->set_value(0);
populate_scale_random->set_step(0.01);
populate_scale_random->set_accessibility_name(TTRC("Random Scale:"));
vbc->add_margin_child(TTR("Random Scale:"), populate_scale_random);
populate_scale = memnew(SpinBox);
populate_scale->set_min(0.001);
populate_scale->set_max(4096);
populate_scale->set_value(1);
populate_scale->set_step(0.01);
populate_scale->set_accessibility_name(TTRC("Scale:"));
vbc->add_margin_child(TTR("Scale:"), populate_scale);
populate_amount = memnew(SpinBox);
populate_amount->set_anchor(SIDE_RIGHT, ANCHOR_END);
populate_amount->set_begin(Point2(20, 232));
populate_amount->set_end(Point2(-5, 237));
populate_amount->set_min(1);
populate_amount->set_max(65536);
populate_amount->set_value(128);
populate_amount->set_accessibility_name(TTRC("Amount:"));
vbc->add_margin_child(TTR("Amount:"), populate_amount);
populate_dialog->set_ok_button_text(TTR("Populate"));
populate_dialog->get_ok_button()->connect(SceneStringName(pressed), callable_mp(this, &MultiMeshEditor::_populate));
std = memnew(SceneTreeDialog);
Vector<StringName> valid_types;
valid_types.push_back("MeshInstance3D");
std->set_valid_types(valid_types);
populate_dialog->add_child(std);
std->connect("selected", callable_mp(this, &MultiMeshEditor::_browsed));
_last_pp_node = nullptr;
err_dialog = memnew(AcceptDialog);
add_child(err_dialog);
}
void MultiMeshEditorPlugin::edit(Object *p_object) {
multimesh_editor->edit(Object::cast_to<MultiMeshInstance3D>(p_object));
}
bool MultiMeshEditorPlugin::handles(Object *p_object) const {
return p_object->is_class("MultiMeshInstance3D");
}
void MultiMeshEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
multimesh_editor->options->show();
} else {
multimesh_editor->options->hide();
multimesh_editor->edit(nullptr);
}
}
MultiMeshEditorPlugin::MultiMeshEditorPlugin() {
multimesh_editor = memnew(MultiMeshEditor);
EditorNode::get_singleton()->get_gui_base()->add_child(multimesh_editor);
multimesh_editor->options->hide();
}

View File

@@ -0,0 +1,100 @@
/**************************************************************************/
/* multimesh_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/plugins/editor_plugin.h"
#include "scene/3d/multimesh_instance_3d.h"
#include "scene/gui/slider.h"
#include "scene/gui/spin_box.h"
class AcceptDialog;
class ConfirmationDialog;
class MenuButton;
class OptionButton;
class SceneTreeDialog;
class MultiMeshEditor : public Control {
GDCLASS(MultiMeshEditor, Control);
friend class MultiMeshEditorPlugin;
AcceptDialog *err_dialog = nullptr;
MenuButton *options = nullptr;
MultiMeshInstance3D *_last_pp_node = nullptr;
bool browsing_source = false;
Panel *panel = nullptr;
MultiMeshInstance3D *node = nullptr;
LineEdit *surface_source = nullptr;
LineEdit *mesh_source = nullptr;
SceneTreeDialog *std = nullptr;
ConfirmationDialog *populate_dialog = nullptr;
OptionButton *populate_axis = nullptr;
HSlider *populate_rotate_random = nullptr;
HSlider *populate_tilt_random = nullptr;
SpinBox *populate_scale_random = nullptr;
SpinBox *populate_scale = nullptr;
SpinBox *populate_amount = nullptr;
enum Menu {
MENU_OPTION_POPULATE
};
void _browsed(const NodePath &p_path);
void _menu_option(int);
void _populate();
void _browse(bool p_source);
protected:
void _node_removed(Node *p_node);
public:
void edit(MultiMeshInstance3D *p_multimesh);
MultiMeshEditor();
};
class MultiMeshEditorPlugin : public EditorPlugin {
GDCLASS(MultiMeshEditorPlugin, EditorPlugin);
MultiMeshEditor *multimesh_editor = nullptr;
public:
virtual String get_plugin_name() const override { return "MultiMesh"; }
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;
MultiMeshEditorPlugin();
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,223 @@
/**************************************************************************/
/* node_3d_editor_gizmos.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "core/math/dynamic_bvh.h"
#include "core/templates/hash_map.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/node_3d.h"
#include "scene/3d/skeleton_3d.h"
class Timer;
class EditorNode3DGizmoPlugin;
class EditorNode3DGizmo : public Node3DGizmo {
GDCLASS(EditorNode3DGizmo, Node3DGizmo);
struct Instance {
RID instance;
Ref<Mesh> mesh;
Ref<Material> material;
Ref<SkinReference> skin_reference;
bool extra_margin = false;
Transform3D xform;
void create_instance(Node3D *p_base, bool p_hidden = false);
};
bool selected;
Vector<Vector3> collision_segments;
LocalVector<Ref<TriangleMesh>> collision_meshes;
Vector<Vector3> handles;
Vector<int> handle_ids;
Vector<Vector3> secondary_handles;
Vector<int> secondary_handle_ids;
real_t selectable_icon_size;
bool billboard_handle;
bool valid;
bool hidden;
Vector<Instance> instances;
Node3D *spatial_node = nullptr;
DynamicBVH::ID bvh_node_id;
void _set_node_3d(Node *p_node) { set_node_3d(Object::cast_to<Node3D>(p_node)); }
void _update_bvh();
protected:
static void _bind_methods();
EditorNode3DGizmoPlugin *gizmo_plugin = nullptr;
GDVIRTUAL0(_redraw)
GDVIRTUAL2RC(String, _get_handle_name, int, bool)
GDVIRTUAL2RC(bool, _is_handle_highlighted, int, bool)
GDVIRTUAL2RC(Variant, _get_handle_value, int, bool)
GDVIRTUAL2(_begin_handle_action, int, bool)
GDVIRTUAL4(_set_handle, int, bool, const Camera3D *, Vector2)
GDVIRTUAL4(_commit_handle, int, bool, Variant, bool)
GDVIRTUAL2RC(int, _subgizmos_intersect_ray, const Camera3D *, Vector2)
GDVIRTUAL2RC(Vector<int>, _subgizmos_intersect_frustum, const Camera3D *, TypedArray<Plane>)
GDVIRTUAL1RC(Transform3D, _get_subgizmo_transform, int)
GDVIRTUAL2(_set_subgizmo_transform, int, Transform3D)
GDVIRTUAL3(_commit_subgizmos, Vector<int>, TypedArray<Transform3D>, bool)
public:
void add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1));
void add_vertices(const Vector<Vector3> &p_vertices, const Ref<Material> &p_material, Mesh::PrimitiveType p_primitive_type, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1));
void add_mesh(const Ref<Mesh> &p_mesh, const Ref<Material> &p_material = Ref<Material>(), const Transform3D &p_xform = Transform3D(), const Ref<SkinReference> &p_skin_reference = Ref<SkinReference>());
void add_collision_segments(const Vector<Vector3> &p_lines);
void add_collision_triangles(const Ref<TriangleMesh> &p_tmesh);
void add_unscaled_billboard(const Ref<Material> &p_material, real_t p_scale = 1, const Color &p_modulate = Color(1, 1, 1));
void add_handles(const Vector<Vector3> &p_handles, const Ref<Material> &p_material, const Vector<int> &p_ids = Vector<int>(), bool p_billboard = false, bool p_secondary = false);
void add_solid_box(const Ref<Material> &p_material, Vector3 p_size, Vector3 p_position = Vector3(), const Transform3D &p_xform = Transform3D());
virtual bool is_handle_highlighted(int p_id, bool p_secondary) const;
virtual String get_handle_name(int p_id, bool p_secondary) const;
virtual Variant get_handle_value(int p_id, bool p_secondary) const;
virtual void begin_handle_action(int p_id, bool p_secondary);
virtual void set_handle(int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point);
virtual void commit_handle(int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false);
virtual int subgizmos_intersect_ray(Camera3D *p_camera, const Vector2 &p_point) const;
virtual Vector<int> subgizmos_intersect_frustum(const Camera3D *p_camera, const Vector<Plane> &p_frustum) const;
virtual Transform3D get_subgizmo_transform(int p_id) const;
virtual void set_subgizmo_transform(int p_id, Transform3D p_transform);
virtual void commit_subgizmos(const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel = false);
void set_selected(bool p_selected) { selected = p_selected; }
bool is_selected() const { return selected; }
void set_node_3d(Node3D *p_node);
Node3D *get_node_3d() const { return spatial_node; }
Ref<EditorNode3DGizmoPlugin> get_plugin() const { return gizmo_plugin; }
bool intersect_frustum(const Camera3D *p_camera, const Vector<Plane> &p_frustum);
void handles_intersect_ray(Camera3D *p_camera, const Vector2 &p_point, bool p_shift_pressed, int &r_id, bool &r_secondary);
bool intersect_ray(Camera3D *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal);
bool is_subgizmo_selected(int p_id) const;
Vector<int> get_subgizmo_selection() const;
virtual void clear() override;
virtual void create() override;
virtual void transform() override;
virtual void redraw() override;
virtual void free() override;
virtual bool is_editable() const;
void set_hidden(bool p_hidden);
void set_plugin(EditorNode3DGizmoPlugin *p_plugin);
EditorNode3DGizmo();
~EditorNode3DGizmo();
};
class EditorNode3DGizmoPlugin : public Resource {
GDCLASS(EditorNode3DGizmoPlugin, Resource);
public:
static const int VISIBLE = 0;
static const int HIDDEN = 1;
static const int ON_TOP = 2;
protected:
int current_state;
HashSet<EditorNode3DGizmo *> current_gizmos;
HashMap<String, Vector<Ref<StandardMaterial3D>>> materials;
static void _bind_methods();
virtual bool has_gizmo(Node3D *p_spatial);
virtual Ref<EditorNode3DGizmo> create_gizmo(Node3D *p_spatial);
GDVIRTUAL1RC(bool, _has_gizmo, Node3D *)
GDVIRTUAL1RC(Ref<EditorNode3DGizmo>, _create_gizmo, Node3D *)
GDVIRTUAL0RC(String, _get_gizmo_name)
GDVIRTUAL0RC(int, _get_priority)
GDVIRTUAL0RC(bool, _can_be_hidden)
GDVIRTUAL0RC(bool, _is_selectable_when_hidden)
GDVIRTUAL1(_redraw, Ref<EditorNode3DGizmo>)
GDVIRTUAL3RC(String, _get_handle_name, Ref<EditorNode3DGizmo>, int, bool)
GDVIRTUAL3RC(bool, _is_handle_highlighted, Ref<EditorNode3DGizmo>, int, bool)
GDVIRTUAL3RC(Variant, _get_handle_value, Ref<EditorNode3DGizmo>, int, bool)
GDVIRTUAL3(_begin_handle_action, Ref<EditorNode3DGizmo>, int, bool)
GDVIRTUAL5(_set_handle, Ref<EditorNode3DGizmo>, int, bool, const Camera3D *, Vector2)
GDVIRTUAL5(_commit_handle, Ref<EditorNode3DGizmo>, int, bool, Variant, bool)
GDVIRTUAL3RC(int, _subgizmos_intersect_ray, Ref<EditorNode3DGizmo>, const Camera3D *, Vector2)
GDVIRTUAL3RC(Vector<int>, _subgizmos_intersect_frustum, Ref<EditorNode3DGizmo>, const Camera3D *, TypedArray<Plane>)
GDVIRTUAL2RC(Transform3D, _get_subgizmo_transform, Ref<EditorNode3DGizmo>, int)
GDVIRTUAL3(_set_subgizmo_transform, Ref<EditorNode3DGizmo>, int, Transform3D)
GDVIRTUAL4(_commit_subgizmos, Ref<EditorNode3DGizmo>, Vector<int>, TypedArray<Transform3D>, bool)
public:
void create_material(const String &p_name, const Color &p_color, bool p_billboard = false, bool p_on_top = false, bool p_use_vertex_color = false);
void create_icon_material(const String &p_name, const Ref<Texture2D> &p_texture, bool p_on_top = false, const Color &p_albedo = Color(1, 1, 1, 1));
void create_handle_material(const String &p_name, bool p_billboard = false, const Ref<Texture2D> &p_texture = nullptr);
void add_material(const String &p_name, Ref<StandardMaterial3D> p_material);
Ref<StandardMaterial3D> get_material(const String &p_name, const Ref<EditorNode3DGizmo> &p_gizmo = Ref<EditorNode3DGizmo>());
virtual String get_gizmo_name() const;
virtual int get_priority() const;
virtual bool can_be_hidden() const;
virtual bool is_selectable_when_hidden() const;
virtual bool can_commit_handle_on_click() const;
virtual void redraw(EditorNode3DGizmo *p_gizmo);
virtual bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const;
virtual String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const;
virtual Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const;
virtual void begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary);
virtual void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point);
virtual void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false);
virtual int subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const;
virtual Vector<int> subgizmos_intersect_frustum(const EditorNode3DGizmo *p_gizmo, const Camera3D *p_camera, const Vector<Plane> &p_frustum) const;
virtual Transform3D get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const;
virtual void set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform);
virtual void commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel = false);
Ref<EditorNode3DGizmo> get_gizmo(Node3D *p_spatial);
void set_state(int p_state);
int get_state() const;
void unregister_gizmo(EditorNode3DGizmo *p_gizmo);
EditorNode3DGizmoPlugin();
virtual ~EditorNode3DGizmoPlugin();
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,121 @@
/**************************************************************************/
/* occluder_instance_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 "occluder_instance_3d_editor_plugin.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/gui/editor_file_dialog.h"
void OccluderInstance3DEditorPlugin::_bake_select_file(const String &p_file) {
if (occluder_instance) {
OccluderInstance3D::BakeError err;
if (get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root() == occluder_instance) {
err = occluder_instance->bake_scene(occluder_instance, p_file);
} else {
err = occluder_instance->bake_scene(occluder_instance->get_parent(), p_file);
}
switch (err) {
case OccluderInstance3D::BAKE_ERROR_NO_SAVE_PATH: {
String scene_path = occluder_instance->get_scene_file_path();
if (scene_path.is_empty()) {
scene_path = occluder_instance->get_owner()->get_scene_file_path();
}
if (scene_path.is_empty()) {
EditorNode::get_singleton()->show_warning(TTR("Can't determine a save path for the occluder.\nSave your scene and try again."));
break;
}
scene_path = scene_path.get_basename() + ".occ";
file_dialog->set_current_path(scene_path);
file_dialog->popup_file_dialog();
} break;
case OccluderInstance3D::BAKE_ERROR_NO_MESHES: {
EditorNode::get_singleton()->show_warning(TTR("No meshes to bake.\nMake sure there is at least one MeshInstance3D node in the scene whose visual layers are part of the OccluderInstance3D's Bake Mask property."));
break;
}
case OccluderInstance3D::BAKE_ERROR_CANT_SAVE: {
EditorNode::get_singleton()->show_warning(TTR("Could not save the new occluder at the specified path:") + " " + p_file);
break;
}
default: {
}
}
}
}
void OccluderInstance3DEditorPlugin::_bake() {
_bake_select_file("");
}
void OccluderInstance3DEditorPlugin::edit(Object *p_object) {
OccluderInstance3D *s = Object::cast_to<OccluderInstance3D>(p_object);
if (!s) {
return;
}
occluder_instance = s;
}
bool OccluderInstance3DEditorPlugin::handles(Object *p_object) const {
return p_object->is_class("OccluderInstance3D");
}
void OccluderInstance3DEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
bake->show();
} else {
bake->hide();
}
}
void OccluderInstance3DEditorPlugin::_bind_methods() {
ClassDB::bind_method("_bake", &OccluderInstance3DEditorPlugin::_bake);
}
OccluderInstance3DEditorPlugin::OccluderInstance3DEditorPlugin() {
bake = memnew(Button);
bake->set_theme_type_variation(SceneStringName(FlatButton));
bake->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Bake"), EditorStringName(EditorIcons)));
bake->set_text(TTR("Bake Occluders"));
bake->hide();
bake->connect(SceneStringName(pressed), Callable(this, "_bake"));
add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, bake);
occluder_instance = nullptr;
file_dialog = memnew(EditorFileDialog);
file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
file_dialog->add_filter("*.occ", "Occluder3D");
file_dialog->set_title(TTR("Select occluder bake file:"));
file_dialog->connect("file_selected", callable_mp(this, &OccluderInstance3DEditorPlugin::_bake_select_file));
bake->add_child(file_dialog);
}

View File

@@ -0,0 +1,62 @@
/**************************************************************************/
/* occluder_instance_3d_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/plugins/editor_plugin.h"
#include "scene/3d/occluder_instance_3d.h"
#include "scene/resources/material.h"
class EditorFileDialog;
class OccluderInstance3DEditorPlugin : public EditorPlugin {
GDCLASS(OccluderInstance3DEditorPlugin, EditorPlugin);
OccluderInstance3D *occluder_instance = nullptr;
Button *bake = nullptr;
EditorFileDialog *file_dialog = nullptr;
void _bake_select_file(const String &p_file);
void _bake();
protected:
static void _bind_methods();
public:
virtual String get_plugin_name() const override { return "OccluderInstance3D"; }
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;
OccluderInstance3DEditorPlugin();
};

View File

@@ -0,0 +1,461 @@
/**************************************************************************/
/* 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 "particles_3d_editor_plugin.h"
#include "editor/editor_node.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/scene/scene_tree_editor.h"
#include "scene/3d/cpu_particles_3d.h"
#include "scene/3d/gpu_particles_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/gui/box_container.h"
#include "scene/gui/option_button.h"
#include "scene/gui/spin_box.h"
#include "scene/resources/image_texture.h"
#include "scene/resources/particle_process_material.h"
void Particles3DEditorPlugin::_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 = edited_node->get("emitting");
if (!was_emitting) {
edited_node->set("emitting", true);
OS::get_singleton()->delay_usec(1000);
}
AABB rect;
Callable capture_aabb = Callable(edited_node, "capture_aabb");
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 = capture_aabb.call();
if (rect == AABB()) {
rect = capture;
} else {
rect.merge_with(capture);
}
running += (OS::get_singleton()->get_ticks_usec() - ticks) / 1000000.0;
}
if (!was_emitting) {
edited_node->set("emitting", false);
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Generate Visibility AABB"));
ur->add_do_property(edited_node, "visibility_aabb", rect);
ur->add_undo_property(edited_node, "visibility_aabb", edited_node->get("visibility_aabb"));
ur->commit_action();
}
void Particles3DEditorPlugin::_node_selected(const NodePath &p_path) {
Node *sel = get_node(p_path);
if (!sel) {
return;
}
if (!sel->is_class("Node3D")) {
EditorNode::get_singleton()->show_warning(vformat(TTR("\"%s\" doesn't inherit from Node3D."), sel->get_name()));
return;
}
MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(sel);
if (!mi || mi->get_mesh().is_null()) {
EditorNode::get_singleton()->show_warning(vformat(TTR("\"%s\" doesn't contain geometry."), sel->get_name()));
return;
}
geometry = mi->get_mesh()->get_faces();
if (geometry.is_empty()) {
EditorNode::get_singleton()->show_warning(vformat(TTR("\"%s\" doesn't contain face geometry."), sel->get_name()));
return;
}
Transform3D geom_xform = edited_node->get("global_transform");
geom_xform = geom_xform.affine_inverse() * mi->get_global_transform();
int gc = geometry.size();
Face3 *w = geometry.ptrw();
for (int i = 0; i < gc; i++) {
for (int j = 0; j < 3; j++) {
w[i].vertex[j] = geom_xform.xform(w[i].vertex[j]);
}
}
emission_dialog->popup_centered(Size2(300, 130));
}
void Particles3DEditorPlugin::_menu_callback(int p_idx) {
switch (p_idx) {
case MENU_OPTION_GENERATE_AABB: {
if (need_show_lifetime_dialog(generate_seconds)) {
generate_aabb->popup_centered();
} else {
_generate_aabb();
}
} break;
case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE: {
if (_can_generate_points()) {
emission_tree_dialog->popup_scenetree_dialog();
}
} break;
default: {
ParticlesEditorPlugin::_menu_callback(p_idx);
}
}
}
void Particles3DEditorPlugin::_add_menu_options(PopupMenu *p_menu) {
p_menu->add_item(TTR("Generate AABB"), MENU_OPTION_GENERATE_AABB);
p_menu->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE);
}
bool Particles3DEditorPlugin::_generate(Vector<Vector3> &r_points, Vector<Vector3> &r_normals) {
bool use_normals = emission_fill->get_selected() == 1;
if (emission_fill->get_selected() < 2) {
float area_accum = 0;
RBMap<float, int> triangle_area_map;
for (int i = 0; i < geometry.size(); i++) {
float area = geometry[i].get_area();
if (area < CMP_EPSILON) {
continue;
}
triangle_area_map[area_accum] = i;
area_accum += area;
}
if (!triangle_area_map.size() || area_accum == 0) {
EditorNode::get_singleton()->show_warning(TTR("The geometry's faces don't contain any area."));
return false;
}
int emissor_count = emission_amount->get_value();
for (int i = 0; i < emissor_count; i++) {
float areapos = Math::random(0.0f, area_accum);
RBMap<float, int>::Iterator E = triangle_area_map.find_closest(areapos);
ERR_FAIL_COND_V(!E, false);
int index = E->value;
ERR_FAIL_INDEX_V(index, geometry.size(), false);
// ok FINALLY get face
Face3 face = geometry[index];
//now compute some position inside the face...
Vector3 pos = face.get_random_point_inside();
r_points.push_back(pos);
if (use_normals) {
Vector3 normal = face.get_plane().normal;
r_normals.push_back(normal);
}
}
} else {
int gcount = geometry.size();
if (gcount == 0) {
EditorNode::get_singleton()->show_warning(TTR("The geometry doesn't contain any faces."));
return false;
}
const Face3 *r = geometry.ptr();
AABB aabb;
for (int i = 0; i < gcount; i++) {
for (int j = 0; j < 3; j++) {
if (i == 0 && j == 0) {
aabb.position = r[i].vertex[j];
} else {
aabb.expand_to(r[i].vertex[j]);
}
}
}
int emissor_count = emission_amount->get_value();
for (int i = 0; i < emissor_count; i++) {
int attempts = 5;
for (int j = 0; j < attempts; j++) {
Vector3 dir;
dir[Math::rand() % 3] = 1.0;
Vector3 ofs = (Vector3(1, 1, 1) - dir) * Vector3(Math::randf(), Math::randf(), Math::randf()) * aabb.size + aabb.position;
Vector3 ofsv = ofs + aabb.size * dir;
//space it a little
ofs -= dir;
ofsv += dir;
float max = -1e7, min = 1e7;
for (int k = 0; k < gcount; k++) {
const Face3 &f3 = r[k];
Vector3 res;
if (f3.intersects_segment(ofs, ofsv, &res)) {
res -= ofs;
float d = dir.dot(res);
if (d < min) {
min = d;
}
if (d > max) {
max = d;
}
}
}
if (max < min) {
continue; //lost attempt
}
float val = min + (max - min) * Math::randf();
Vector3 point = ofs + dir * val;
r_points.push_back(point);
break;
}
}
}
return true;
}
Particles3DEditorPlugin::Particles3DEditorPlugin() {
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);
generate_seconds->set_accessibility_name(TTRC("Generation Time (sec)"));
generate_seconds->set_min(0.1);
generate_seconds->set_max(25);
generate_seconds->set_value(2);
genvb->add_margin_child(TTR("Generation Time (sec):"), generate_seconds);
EditorNode::get_singleton()->get_gui_base()->add_child(generate_aabb);
generate_aabb->connect(SceneStringName(confirmed), callable_mp(this, &Particles3DEditorPlugin::_generate_aabb));
emission_tree_dialog = memnew(SceneTreeDialog);
Vector<StringName> valid_types;
valid_types.push_back("MeshInstance3D");
emission_tree_dialog->set_valid_types(valid_types);
EditorNode::get_singleton()->get_gui_base()->add_child(emission_tree_dialog);
emission_tree_dialog->connect("selected", callable_mp(this, &Particles3DEditorPlugin::_node_selected));
emission_dialog = memnew(ConfirmationDialog);
emission_dialog->set_title(TTR("Create Emitter"));
EditorNode::get_singleton()->get_gui_base()->add_child(emission_dialog);
VBoxContainer *emd_vb = memnew(VBoxContainer);
emission_dialog->add_child(emd_vb);
emission_amount = memnew(SpinBox);
emission_amount->set_accessibility_name(TTRC("Emission Points:"));
emission_amount->set_min(1);
emission_amount->set_max(100000);
emission_amount->set_value(512);
emd_vb->add_margin_child(TTR("Emission Points:"), emission_amount);
emission_fill = memnew(OptionButton);
emission_fill->set_accessibility_name(TTRC("Emission Source:"));
emission_fill->add_item(TTR("Surface Points"));
emission_fill->add_item(TTR("Surface Points+Normal (Directed)"));
emission_fill->add_item(TTR("Volume"));
emd_vb->add_margin_child(TTR("Emission Source:"), emission_fill);
emission_dialog->set_ok_button_text(TTR("Create"));
emission_dialog->connect(SceneStringName(confirmed), callable_mp(this, &Particles3DEditorPlugin::_generate_emission_points));
}
Node *GPUParticles3DEditorPlugin::_convert_particles() {
GPUParticles3D *particles = Object::cast_to<GPUParticles3D>(edited_node);
CPUParticles3D *cpu_particles = memnew(CPUParticles3D);
cpu_particles->convert_from_particles(particles);
cpu_particles->set_name(particles->get_name());
cpu_particles->set_transform(particles->get_transform());
cpu_particles->set_visible(particles->is_visible());
cpu_particles->set_process_mode(particles->get_process_mode());
return cpu_particles;
}
bool GPUParticles3DEditorPlugin::_can_generate_points() const {
GPUParticles3D *particles = Object::cast_to<GPUParticles3D>(edited_node);
Ref<ParticleProcessMaterial> mat = particles->get_process_material();
if (mat.is_null()) {
EditorNode::get_singleton()->show_warning(TTR("A processor material of type 'ParticleProcessMaterial' is required."));
return false;
}
return true;
}
void GPUParticles3DEditorPlugin::_generate_emission_points() {
GPUParticles3D *particles = Object::cast_to<GPUParticles3D>(edited_node);
/// hacer codigo aca
Vector<Vector3> points;
Vector<Vector3> normals;
if (!_generate(points, normals)) {
return;
}
int point_count = points.size();
int w = 2048;
int h = (point_count / 2048) + 1;
Vector<uint8_t> point_img;
point_img.resize(w * h * 3 * sizeof(float));
{
uint8_t *iw = point_img.ptrw();
memset(iw, 0, w * h * 3 * sizeof(float));
const Vector3 *r = points.ptr();
float *wf = reinterpret_cast<float *>(iw);
for (int i = 0; i < point_count; i++) {
wf[i * 3 + 0] = r[i].x;
wf[i * 3 + 1] = r[i].y;
wf[i * 3 + 2] = r[i].z;
}
}
Ref<Image> image = memnew(Image(w, h, false, Image::FORMAT_RGBF, point_img));
Ref<ImageTexture> tex = ImageTexture::create_from_image(image);
Ref<ParticleProcessMaterial> mat = particles->get_process_material();
ERR_FAIL_COND(mat.is_null());
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Create Emission Points"));
ParticleProcessMaterial *matptr = mat.ptr();
if (!normals.is_empty()) {
undo_redo->add_do_property(matptr, "emission_shape", ParticleProcessMaterial::EMISSION_SHAPE_DIRECTED_POINTS);
undo_redo->add_undo_property(matptr, "emission_shape", matptr->get_emission_shape());
Vector<uint8_t> point_img2;
point_img2.resize(w * h * 3 * sizeof(float));
{
uint8_t *iw = point_img2.ptrw();
memset(iw, 0, w * h * 3 * sizeof(float));
const Vector3 *r = normals.ptr();
float *wf = reinterpret_cast<float *>(iw);
for (int i = 0; i < point_count; i++) {
wf[i * 3 + 0] = r[i].x;
wf[i * 3 + 1] = r[i].y;
wf[i * 3 + 2] = r[i].z;
}
}
Ref<Image> image2 = memnew(Image(w, h, false, Image::FORMAT_RGBF, point_img2));
undo_redo->add_do_property(matptr, "emission_normal_texture", ImageTexture::create_from_image(image2));
undo_redo->add_undo_property(matptr, "emission_normal_texture", matptr->get_emission_normal_texture());
} else {
undo_redo->add_do_property(matptr, "emission_shape", ParticleProcessMaterial::EMISSION_SHAPE_POINTS);
undo_redo->add_undo_property(matptr, "emission_shape", matptr->get_emission_shape());
}
undo_redo->add_do_property(matptr, "emission_point_count", point_count);
undo_redo->add_undo_property(matptr, "emission_point_count", matptr->get_emission_point_count());
undo_redo->add_do_property(matptr, "emission_point_texture", tex);
undo_redo->add_undo_property(matptr, "emission_point_texture", matptr->get_emission_point_texture());
undo_redo->commit_action();
}
GPUParticles3DEditorPlugin::GPUParticles3DEditorPlugin() {
handled_type = TTRC("GPUParticles3D");
conversion_option_name = TTR("Convert to CPUParticles3D");
}
Node *CPUParticles3DEditorPlugin::_convert_particles() {
CPUParticles3D *particles = Object::cast_to<CPUParticles3D>(edited_node);
GPUParticles3D *gpu_particles = memnew(GPUParticles3D);
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());
return gpu_particles;
}
void CPUParticles3DEditorPlugin::_generate_emission_points() {
CPUParticles3D *particles = Object::cast_to<CPUParticles3D>(edited_node);
/// hacer codigo aca
Vector<Vector3> points;
Vector<Vector3> normals;
if (!_generate(points, normals)) {
return;
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Create Emission Points"));
if (normals.is_empty()) {
undo_redo->add_do_property(particles, "emission_shape", ParticleProcessMaterial::EMISSION_SHAPE_POINTS);
undo_redo->add_undo_property(particles, "emission_shape", particles->get_emission_shape());
} else {
undo_redo->add_do_property(particles, "emission_shape", ParticleProcessMaterial::EMISSION_SHAPE_DIRECTED_POINTS);
undo_redo->add_undo_property(particles, "emission_shape", particles->get_emission_shape());
undo_redo->add_do_property(particles, "emission_normals", normals);
undo_redo->add_undo_property(particles, "emission_normals", particles->get_emission_normals());
}
undo_redo->add_do_property(particles, "emission_points", points);
undo_redo->add_undo_property(particles, "emission_points", particles->get_emission_points());
undo_redo->commit_action();
}
CPUParticles3DEditorPlugin::CPUParticles3DEditorPlugin() {
handled_type = TTRC("CPUParticles3D");
conversion_option_name = TTR("Convert to GPUParticles3D");
}

View File

@@ -0,0 +1,92 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "editor/scene/particles_editor_plugin.h"
class Particles3DEditorPlugin : public ParticlesEditorPlugin {
GDCLASS(Particles3DEditorPlugin, ParticlesEditorPlugin);
enum {
MENU_OPTION_GENERATE_AABB = 300,
MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE,
};
ConfirmationDialog *generate_aabb = nullptr;
SpinBox *generate_seconds = nullptr;
SceneTreeDialog *emission_tree_dialog = nullptr;
ConfirmationDialog *emission_dialog = nullptr;
SpinBox *emission_amount = nullptr;
OptionButton *emission_fill = nullptr;
void _generate_aabb();
void _node_selected(const NodePath &p_path);
protected:
Vector<Face3> geometry;
virtual void _menu_callback(int p_idx) override;
virtual void _add_menu_options(PopupMenu *p_menu) override;
bool _generate(Vector<Vector3> &r_points, Vector<Vector3> &r_normals);
virtual bool _can_generate_points() const = 0;
virtual void _generate_emission_points() = 0;
public:
Particles3DEditorPlugin();
};
class GPUParticles3DEditorPlugin : public Particles3DEditorPlugin {
GDCLASS(GPUParticles3DEditorPlugin, Particles3DEditorPlugin);
protected:
Node *_convert_particles() override;
bool _can_generate_points() const override;
void _generate_emission_points() override;
public:
GPUParticles3DEditorPlugin();
};
class CPUParticles3DEditorPlugin : public Particles3DEditorPlugin {
GDCLASS(CPUParticles3DEditorPlugin, Particles3DEditorPlugin);
protected:
Node *_convert_particles() override;
bool _can_generate_points() const override { return true; }
void _generate_emission_points() override;
public:
CPUParticles3DEditorPlugin();
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,189 @@
/**************************************************************************/
/* path_3d_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/plugins/editor_plugin.h"
#include "editor/scene/3d/node_3d_editor_gizmos.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/path_3d.h"
class HBoxContainer;
class MenuButton;
class ConfirmationDialog;
class Path3DGizmo : public EditorNode3DGizmo {
GDCLASS(Path3DGizmo, EditorNode3DGizmo);
// Map handle id to control point id and handle type.
enum HandleType {
HANDLE_TYPE_IN,
HANDLE_TYPE_OUT,
HANDLE_TYPE_TILT,
};
struct HandleInfo {
int point_idx; // Index of control point.
HandleType type; // Type of this handle.
};
Path3D *path = nullptr;
Ref<StandardMaterial3D> debug_material;
mutable Vector3 original;
mutable float orig_in_length;
mutable float orig_out_length;
mutable float disk_size = 0.8;
// Index that should have swapped control points for achieving an outwards curve.
int swapped_control_points_idx = -1;
bool control_points_overlapped = false;
// Cache information of secondary handles.
Vector<HandleInfo> _secondary_handles_info;
void _update_transform_gizmo();
public:
virtual String get_handle_name(int p_id, bool p_secondary) const override;
virtual Variant get_handle_value(int p_id, bool p_secondary) const override;
virtual void set_handle(int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) override;
virtual void commit_handle(int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false) override;
virtual void redraw() override;
Path3DGizmo(Path3D *p_path = nullptr, float p_disk_size = 0.8);
};
class Path3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(Path3DGizmoPlugin, EditorNode3DGizmoPlugin);
float disk_size = 0.8;
// Locking basis is meant to ensure a predictable behavior during translation of the curve points in "local space transform mode".
// Without the locking, the gizmo/point, in "local space transform mode", wouldn't follow a straight path and would curve and twitch in an unpredictable way.
HashMap<int, Basis> transformation_locked_basis;
protected:
Ref<EditorNode3DGizmo> create_gizmo(Node3D *p_spatial) override;
public:
virtual bool has_gizmo(Node3D *p_spatial) override;
String get_gizmo_name() const override;
virtual void redraw(EditorNode3DGizmo *p_gizmo) override;
virtual int subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const override;
virtual Vector<int> subgizmos_intersect_frustum(const EditorNode3DGizmo *p_gizmo, const Camera3D *p_camera, const Vector<Plane> &p_frustum) const override;
virtual Transform3D get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const override;
virtual void set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) override;
virtual void commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel = false) override;
int get_priority() const override;
Path3DGizmoPlugin(float p_disk_size);
};
class Path3DEditorPlugin : public EditorPlugin {
GDCLASS(Path3DEditorPlugin, EditorPlugin);
friend class Path3DGizmo;
friend class Path3DGizmoPlugin;
Ref<Path3DGizmoPlugin> path_3d_gizmo_plugin;
HBoxContainer *topmenu_bar = nullptr;
HBoxContainer *toolbar = nullptr;
Button *curve_create = nullptr;
Button *curve_edit = nullptr;
Button *curve_edit_curve = nullptr;
Button *curve_edit_tilt = nullptr;
Button *curve_del = nullptr;
Button *curve_closed = nullptr;
Button *curve_clear_points = nullptr;
MenuButton *handle_menu = nullptr;
Button *create_curve_button = nullptr;
ConfirmationDialog *clear_points_dialog = nullptr;
float disk_size = 0.8;
enum Mode {
MODE_CREATE,
MODE_EDIT,
MODE_EDIT_CURVE,
MODE_EDIT_TILT,
MODE_DELETE,
ACTION_CLOSE
};
Path3D *path = nullptr;
void _update_theme();
void _update_toolbar();
void _mode_changed(int p_mode);
void _toggle_closed_curve();
void _handle_option_pressed(int p_option);
bool handle_clicked = false;
bool mirror_handle_angle = true;
bool mirror_handle_length = true;
void _create_curve();
void _confirm_clear_points();
void _clear_points();
void _clear_curve_points();
void _restore_curve_points(const PackedVector3Array &p_points);
enum HandleOption {
HANDLE_OPTION_ANGLE,
HANDLE_OPTION_LENGTH
};
protected:
static void _bind_methods();
public:
Path3D *get_edited_path() { return path; }
inline static Path3DEditorPlugin *singleton = nullptr;
virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override;
virtual String get_plugin_name() const override { return "Path3D"; }
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;
bool mirror_angle_enabled() { return mirror_handle_angle; }
bool mirror_length_enabled() { return mirror_handle_length; }
bool is_handle_clicked() { return handle_clicked; }
void set_handle_clicked(bool clicked) { handle_clicked = clicked; }
Path3DEditorPlugin();
};

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env.add_source_files(env.editor_sources, "*.cpp")

View File

@@ -0,0 +1,101 @@
/**************************************************************************/
/* physical_bone_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 "physical_bone_3d_editor_plugin.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/scene/3d/node_3d_editor_plugin.h"
#include "scene/3d/physics/physical_bone_3d.h"
void PhysicalBone3DEditor::_on_toggle_button_transform_joint(bool p_is_pressed) {
_set_move_joint();
}
void PhysicalBone3DEditor::_set_move_joint() {
if (selected) {
selected->_set_gizmo_move_joint(button_transform_joint->is_pressed());
Node3DEditor::get_singleton()->update_transform_gizmo();
}
}
PhysicalBone3DEditor::PhysicalBone3DEditor() {
spatial_editor_hb = memnew(HBoxContainer);
spatial_editor_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
spatial_editor_hb->set_alignment(BoxContainer::ALIGNMENT_BEGIN);
Node3DEditor::get_singleton()->add_control_to_menu_panel(spatial_editor_hb);
button_transform_joint = memnew(Button);
button_transform_joint->set_theme_type_variation(SceneStringName(FlatButton));
spatial_editor_hb->add_child(button_transform_joint);
button_transform_joint->set_text(TTR("Move Joint"));
// TODO: Rework this as a dedicated toolbar control so we can hook into theme changes and update it
// when the editor theme updates.
button_transform_joint->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("PhysicalBone3D"), EditorStringName(EditorIcons)));
button_transform_joint->set_toggle_mode(true);
button_transform_joint->connect(SceneStringName(toggled), callable_mp(this, &PhysicalBone3DEditor::_on_toggle_button_transform_joint));
hide();
}
void PhysicalBone3DEditor::set_selected(PhysicalBone3D *p_pb) {
button_transform_joint->set_pressed(false);
_set_move_joint();
selected = p_pb;
_set_move_joint();
}
void PhysicalBone3DEditor::hide() {
spatial_editor_hb->hide();
}
void PhysicalBone3DEditor::show() {
spatial_editor_hb->show();
}
void PhysicalBone3DEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
physical_bone_editor.show();
} else {
physical_bone_editor.hide();
physical_bone_editor.set_selected(nullptr);
selected = nullptr;
}
}
void PhysicalBone3DEditorPlugin::edit(Object *p_node) {
PhysicalBone3D *bone = Object::cast_to<PhysicalBone3D>(p_node);
if (bone) {
selected = bone;
physical_bone_editor.set_selected(selected);
}
}

View File

@@ -0,0 +1,71 @@
/**************************************************************************/
/* physical_bone_3d_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
class PhysicalBone3D;
class PhysicalBone3DEditor : public Object {
GDCLASS(PhysicalBone3DEditor, Object);
HBoxContainer *spatial_editor_hb = nullptr;
Button *button_transform_joint = nullptr;
PhysicalBone3D *selected = nullptr;
private:
void _on_toggle_button_transform_joint(bool p_is_pressed);
void _set_move_joint();
public:
PhysicalBone3DEditor();
void set_selected(PhysicalBone3D *p_pb);
void hide();
void show();
};
class PhysicalBone3DEditorPlugin : public EditorPlugin {
GDCLASS(PhysicalBone3DEditorPlugin, EditorPlugin);
PhysicalBone3D *selected = nullptr;
PhysicalBone3DEditor physical_bone_editor;
public:
virtual String get_plugin_name() const override { return "PhysicalBone3D"; }
virtual bool handles(Object *p_object) const override { return p_object->is_class("PhysicalBone3D"); }
virtual void make_visible(bool p_visible) override;
virtual void edit(Object *p_node) override;
};

View File

@@ -0,0 +1,611 @@
/**************************************************************************/
/* polygon_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 "polygon_3d_editor_plugin.h"
#include "core/input/input.h"
#include "core/math/geometry_2d.h"
#include "core/os/keyboard.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/scene/3d/node_3d_editor_plugin.h"
#include "editor/scene/canvas_item_editor_plugin.h"
#include "editor/settings/editor_settings.h"
#include "scene/3d/camera_3d.h"
void Polygon3DEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
button_create->set_button_icon(get_editor_theme_icon(SNAME("Edit")));
button_edit->set_button_icon(get_editor_theme_icon(SNAME("MovePoint")));
button_edit->set_pressed(true);
get_tree()->connect("node_removed", callable_mp(this, &Polygon3DEditor::_node_removed));
} break;
case NOTIFICATION_PROCESS: {
if (!node) {
return;
}
if (_get_depth() != prev_depth) {
_polygon_draw();
prev_depth = _get_depth();
}
} break;
}
}
void Polygon3DEditor::_node_removed(Node *p_node) {
if (p_node == node) {
node = nullptr;
if (imgeom->get_parent() == p_node) {
p_node->remove_child(imgeom);
}
hide();
set_process(false);
}
}
void Polygon3DEditor::_menu_option(int p_option) {
switch (p_option) {
case MODE_CREATE: {
mode = MODE_CREATE;
button_create->set_pressed(true);
button_edit->set_pressed(false);
} break;
case MODE_EDIT: {
mode = MODE_EDIT;
button_create->set_pressed(false);
button_edit->set_pressed(true);
} break;
}
}
void Polygon3DEditor::_wip_close() {
Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
ERR_FAIL_NULL_MSG(obj, "Edited object is not valid.");
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Create Polygon3D"));
undo_redo->add_undo_method(obj, "set_polygon", obj->call("get_polygon"));
undo_redo->add_do_method(obj, "set_polygon", wip);
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
wip.clear();
wip_active = false;
mode = MODE_EDIT;
button_edit->set_pressed(true);
button_create->set_pressed(false);
edited_point = -1;
undo_redo->commit_action();
}
EditorPlugin::AfterGUIInput Polygon3DEditor::forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
if (!node) {
return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
Transform3D gt = node->get_global_transform();
Transform3D gi = gt.affine_inverse();
float depth = _get_depth() * 0.5;
Vector3 n = gt.basis.get_column(2).normalized();
Plane p(n, gt.origin + n * depth);
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
Vector2 gpoint = mb->get_position();
Vector3 ray_from = p_camera->project_ray_origin(gpoint);
Vector3 ray_dir = p_camera->project_ray_normal(gpoint);
Vector3 spoint;
if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
spoint = gi.xform(spoint);
Vector2 cpoint(spoint.x, spoint.y);
//DO NOT snap here, it's confusing in 3D for adding points.
//Let the snap happen when the point is being moved, instead.
//cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint);
PackedVector2Array poly = _get_polygon();
//first check if a point is to be added (segment split)
real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
switch (mode) {
case MODE_CREATE: {
if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
if (!wip_active) {
wip.clear();
wip.push_back(cpoint);
wip_active = true;
edited_point_pos = cpoint;
snap_ignore = false;
_polygon_draw();
edited_point = 1;
return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, wip[0].y, depth))).distance_to(gpoint) < grab_threshold) {
//wip closed
_wip_close();
return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
wip.push_back(cpoint);
edited_point = wip.size();
snap_ignore = false;
_polygon_draw();
return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
} else if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && wip_active) {
_wip_close();
}
} break;
case MODE_EDIT: {
if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
if (mb->is_command_or_control_pressed()) {
if (poly.size() < 3) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Edit Poly"));
undo_redo->add_undo_method(obj, "set_polygon", poly);
poly.push_back(cpoint);
undo_redo->add_do_method(obj, "set_polygon", poly);
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
undo_redo->commit_action();
return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
//search edges
int closest_idx = -1;
Vector2 closest_pos;
real_t closest_dist = 1e10;
for (int i = 0; i < poly.size(); i++) {
const Vector2 segment_a = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth)));
const Vector2 segment_b = p_camera->unproject_position(gt.xform(Vector3(poly[(i + 1) % poly.size()].x, poly[(i + 1) % poly.size()].y, depth)));
Vector2 cp = Geometry2D::get_closest_point_to_segment(gpoint, segment_a, segment_b);
if (cp.distance_squared_to(segment_a) < CMP_EPSILON2 || cp.distance_squared_to(segment_b) < CMP_EPSILON2) {
continue; //not valid to reuse point
}
real_t d = cp.distance_to(gpoint);
if (d < closest_dist && d < grab_threshold) {
closest_dist = d;
closest_pos = cp;
closest_idx = i;
}
}
if (closest_idx >= 0) {
pre_move_edit = poly;
poly.insert(closest_idx + 1, cpoint);
edited_point = closest_idx + 1;
edited_point_pos = cpoint;
_set_polygon(poly);
_polygon_draw();
snap_ignore = true;
return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
} else {
//look for points to move
int closest_idx = -1;
Vector2 closest_pos;
real_t closest_dist = 1e10;
for (int i = 0; i < poly.size(); i++) {
Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth)));
real_t d = cp.distance_to(gpoint);
if (d < closest_dist && d < grab_threshold) {
closest_dist = d;
closest_pos = cp;
closest_idx = i;
}
}
if (closest_idx >= 0) {
pre_move_edit = poly;
edited_point = closest_idx;
edited_point_pos = poly[closest_idx];
_polygon_draw();
snap_ignore = false;
return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
} else {
snap_ignore = false;
if (edited_point != -1) {
//apply
ERR_FAIL_INDEX_V(edited_point, poly.size(), EditorPlugin::AFTER_GUI_INPUT_PASS);
poly.write[edited_point] = edited_point_pos;
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Edit Poly"));
undo_redo->add_do_method(obj, "set_polygon", poly);
undo_redo->add_undo_method(obj, "set_polygon", pre_move_edit);
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
undo_redo->commit_action();
edited_point = -1;
return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
}
if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && edited_point == -1) {
int closest_idx = -1;
Vector2 closest_pos;
real_t closest_dist = 1e10;
for (int i = 0; i < poly.size(); i++) {
Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth)));
real_t d = cp.distance_to(gpoint);
if (d < closest_dist && d < grab_threshold) {
closest_dist = d;
closest_pos = cp;
closest_idx = i;
}
}
if (closest_idx >= 0) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Edit Poly (Remove Point)"));
undo_redo->add_undo_method(obj, "set_polygon", poly);
poly.remove_at(closest_idx);
undo_redo->add_do_method(obj, "set_polygon", poly);
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
undo_redo->commit_action();
return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
} break;
}
}
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
if (edited_point != -1 && (wip_active || mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
Vector2 gpoint = mm->get_position();
Vector3 ray_from = p_camera->project_ray_origin(gpoint);
Vector3 ray_dir = p_camera->project_ray_normal(gpoint);
Vector3 spoint;
if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
spoint = gi.xform(spoint);
Vector2 cpoint(spoint.x, spoint.y);
if (snap_ignore && !Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL)) {
snap_ignore = false;
}
if (!snap_ignore && Node3DEditor::get_singleton()->is_snap_enabled()) {
cpoint = cpoint.snappedf(Node3DEditor::get_singleton()->get_translate_snap());
}
edited_point_pos = cpoint;
_polygon_draw();
}
}
return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
float Polygon3DEditor::_get_depth() {
Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
ERR_FAIL_NULL_V_MSG(obj, 0.0f, "Edited object is not valid.");
if (bool(obj->call("_has_editable_3d_polygon_no_depth"))) {
return 0.0f;
}
return float(obj->call("get_depth"));
}
PackedVector2Array Polygon3DEditor::_get_polygon() {
Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
ERR_FAIL_NULL_V_MSG(obj, PackedVector2Array(), "Edited object is not valid.");
return PackedVector2Array(obj->call("get_polygon"));
}
void Polygon3DEditor::_set_polygon(const PackedVector2Array &p_poly) {
Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
ERR_FAIL_NULL_MSG(obj, "Edited object is not valid.");
obj->call("set_polygon", p_poly);
}
void Polygon3DEditor::_polygon_draw() {
if (!node) {
return;
}
PackedVector2Array poly;
if (wip_active) {
poly = wip;
} else {
poly = _get_polygon();
}
float depth = _get_depth() * 0.5;
m->clear_surfaces();
imesh->clear_surfaces();
imgeom->set_material_override(line_material);
imesh->surface_begin(Mesh::PRIMITIVE_LINES);
Rect2 rect;
for (int i = 0; i < poly.size(); i++) {
Vector2 p, p2;
p = i == edited_point ? edited_point_pos : poly[i];
if ((wip_active && i == poly.size() - 1) || (((i + 1) % poly.size()) == edited_point)) {
p2 = edited_point_pos;
} else {
p2 = poly[(i + 1) % poly.size()];
}
if (i == 0) {
rect.position = p;
} else {
rect.expand_to(p);
}
Vector3 point = Vector3(p.x, p.y, depth);
Vector3 next_point = Vector3(p2.x, p2.y, depth);
imesh->surface_set_color(Color(1, 0.3, 0.1, 0.8));
imesh->surface_add_vertex(point);
imesh->surface_set_color(Color(1, 0.3, 0.1, 0.8));
imesh->surface_add_vertex(next_point);
//Color col=Color(1,0.3,0.1,0.8);
//vpc->draw_line(point,next_point,col,2);
//vpc->draw_texture(handle,point-handle->get_size()*0.5);
}
rect = rect.grow(1);
AABB r;
r.position.x = rect.position.x;
r.position.y = rect.position.y;
r.position.z = depth;
r.size.x = rect.size.x;
r.size.y = rect.size.y;
r.size.z = 0;
imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
imesh->surface_add_vertex(r.position);
imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
imesh->surface_add_vertex(r.position + Vector3(0.3, 0, 0));
imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
imesh->surface_add_vertex(r.position);
imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
imesh->surface_add_vertex(r.position + Vector3(0.0, 0.3, 0));
imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
imesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0));
imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
imesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) - Vector3(0.3, 0, 0));
imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
imesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0));
imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
imesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) + Vector3(0, 0.3, 0));
imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
imesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0));
imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
imesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) - Vector3(0, 0.3, 0));
imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
imesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0));
imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
imesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) + Vector3(0.3, 0, 0));
imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
imesh->surface_add_vertex(r.position + r.size);
imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
imesh->surface_add_vertex(r.position + r.size - Vector3(0.3, 0, 0));
imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
imesh->surface_add_vertex(r.position + r.size);
imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
imesh->surface_add_vertex(r.position + r.size - Vector3(0.0, 0.3, 0));
imesh->surface_end();
if (poly.is_empty()) {
return;
}
Array a;
a.resize(Mesh::ARRAY_MAX);
Vector<Vector3> va;
{
va.resize(poly.size());
Vector3 *w = va.ptrw();
for (int i = 0; i < poly.size(); i++) {
Vector2 p;
p = i == edited_point ? edited_point_pos : poly[i];
Vector3 point = Vector3(p.x, p.y, depth);
w[i] = point;
}
}
a[Mesh::ARRAY_VERTEX] = va;
m->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, a);
m->surface_set_material(0, handle_material);
}
void Polygon3DEditor::edit(Node *p_node) {
if (p_node) {
node = Object::cast_to<Node3D>(p_node);
node_resource = node->call("_get_editable_3d_polygon_resource");
if (node_resource.is_valid()) {
node_resource->connect_changed(callable_mp(this, &Polygon3DEditor::_polygon_draw));
}
//Enable the pencil tool if the polygon is empty
if (_get_polygon().is_empty()) {
_menu_option(MODE_CREATE);
}
wip.clear();
wip_active = false;
edited_point = -1;
if (imgeom->get_parent()) {
imgeom->reparent(p_node, false);
} else {
p_node->add_child(imgeom);
}
_polygon_draw();
set_process(true);
prev_depth = -1;
} else {
node = nullptr;
if (node_resource.is_valid()) {
node_resource->disconnect_changed(callable_mp(this, &Polygon3DEditor::_polygon_draw));
}
node_resource.unref();
if (imgeom->get_parent()) {
imgeom->get_parent()->remove_child(imgeom);
}
set_process(false);
}
}
void Polygon3DEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_polygon_draw"), &Polygon3DEditor::_polygon_draw);
}
Polygon3DEditor::Polygon3DEditor() {
node = nullptr;
button_create = memnew(Button);
button_create->set_theme_type_variation(SceneStringName(FlatButton));
button_create->set_tooltip_text(TTRC("Create Polygon"));
add_child(button_create);
button_create->connect(SceneStringName(pressed), callable_mp(this, &Polygon3DEditor::_menu_option).bind(MODE_CREATE));
button_create->set_toggle_mode(true);
button_edit = memnew(Button);
button_edit->set_theme_type_variation(SceneStringName(FlatButton));
button_edit->set_tooltip_text(TTRC("Edit Polygon"));
add_child(button_edit);
button_edit->connect(SceneStringName(pressed), callable_mp(this, &Polygon3DEditor::_menu_option).bind(MODE_EDIT));
button_edit->set_toggle_mode(true);
mode = MODE_EDIT;
wip_active = false;
imgeom = memnew(MeshInstance3D);
imesh.instantiate();
imgeom->set_mesh(imesh);
imgeom->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001)));
line_material.instantiate();
line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
line_material->set_albedo(Color(1, 1, 1));
handle_material.instantiate();
handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true);
handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
handle_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
handle_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
Ref<Texture2D> handle = EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Editor3DHandle"), EditorStringName(EditorIcons));
handle_material->set_point_size(handle->get_width());
handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle);
pointsm = memnew(MeshInstance3D);
imgeom->add_child(pointsm);
m.instantiate();
pointsm->set_mesh(m);
pointsm->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001)));
snap_ignore = false;
}
Polygon3DEditor::~Polygon3DEditor() {
memdelete(imgeom);
}
void Polygon3DEditorPlugin::edit(Object *p_object) {
polygon_editor->edit(Object::cast_to<Node>(p_object));
}
bool Polygon3DEditorPlugin::handles(Object *p_object) const {
return Object::cast_to<Node3D>(p_object) && bool(p_object->call("_is_editable_3d_polygon"));
}
void Polygon3DEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
polygon_editor->show();
} else {
polygon_editor->hide();
polygon_editor->edit(nullptr);
}
}
Polygon3DEditorPlugin::Polygon3DEditorPlugin() {
polygon_editor = memnew(Polygon3DEditor);
Node3DEditor::get_singleton()->add_control_to_menu_panel(polygon_editor);
polygon_editor->hide();
}

View File

@@ -0,0 +1,113 @@
/**************************************************************************/
/* polygon_3d_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/plugins/editor_plugin.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/physics/collision_polygon_3d.h"
#include "scene/gui/box_container.h"
#include "scene/resources/immediate_mesh.h"
class CanvasItemEditor;
class MenuButton;
class Polygon3DEditor : public HBoxContainer {
GDCLASS(Polygon3DEditor, HBoxContainer);
enum Mode {
MODE_CREATE,
MODE_EDIT,
};
Mode mode;
Button *button_create = nullptr;
Button *button_edit = nullptr;
Ref<StandardMaterial3D> line_material;
Ref<StandardMaterial3D> handle_material;
Panel *panel = nullptr;
Node3D *node = nullptr;
Ref<Resource> node_resource;
Ref<ImmediateMesh> imesh;
MeshInstance3D *imgeom = nullptr;
MeshInstance3D *pointsm = nullptr;
Ref<ArrayMesh> m;
MenuButton *options = nullptr;
int edited_point = 0;
Vector2 edited_point_pos;
PackedVector2Array pre_move_edit;
PackedVector2Array wip;
bool wip_active;
bool snap_ignore;
float prev_depth = 0.0f;
void _wip_close();
void _polygon_draw();
void _menu_option(int p_option);
float _get_depth();
PackedVector2Array _get_polygon();
void _set_polygon(const PackedVector2Array &p_poly);
protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
static void _bind_methods();
public:
virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event);
void edit(Node *p_node);
Polygon3DEditor();
~Polygon3DEditor();
};
class Polygon3DEditorPlugin : public EditorPlugin {
GDCLASS(Polygon3DEditorPlugin, EditorPlugin);
Polygon3DEditor *polygon_editor = nullptr;
public:
virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return polygon_editor->forward_3d_gui_input(p_camera, p_event); }
virtual String get_plugin_name() const override { return "Polygon3DEditor"; }
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;
Polygon3DEditorPlugin();
};

Some files were not shown because too many files have changed in this diff Show More