Add Chromium-only Blender WebEngine parity work
This commit is contained in:
30
blender-5.2.0/source/blender/editors/scene/CMakeLists.txt
Normal file
30
blender-5.2.0/source/blender/editors/scene/CMakeLists.txt
Normal file
@@ -0,0 +1,30 @@
|
||||
# SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
../include
|
||||
../../makesrna
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
scene_edit.cc
|
||||
scene_fps.cc
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::blentranslation
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::sequencer
|
||||
PRIVATE bf::windowmanager
|
||||
)
|
||||
|
||||
|
||||
blender_add_lib(bf_editor_scene "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
604
blender-5.2.0/source/blender/editors/scene/scene_edit.cc
Normal file
604
blender-5.2.0/source/blender/editors/scene/scene_edit.cc
Normal file
@@ -0,0 +1,604 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edscene
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
|
||||
#include "DNA_sequence_types.h"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "ED_render.hh"
|
||||
#include "ED_scene.hh"
|
||||
#include "ED_screen.hh"
|
||||
#include "ED_util.hh"
|
||||
|
||||
#include "SEQ_relations.hh"
|
||||
#include "SEQ_select.hh"
|
||||
#include "SEQ_sequencer.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Scene Utilities
|
||||
* \{ */
|
||||
|
||||
static Scene *scene_add(Main *bmain, Scene *scene_old, eSceneCopyMethod method)
|
||||
{
|
||||
Scene *scene_new = nullptr;
|
||||
if (method == SCE_COPY_NEW) {
|
||||
scene_new = BKE_scene_add(bmain, DATA_("Scene"));
|
||||
}
|
||||
else { /* different kinds of copying */
|
||||
/* We are going to deep-copy collections, objects and various object data, we need to have
|
||||
* up-to-date obdata for that. */
|
||||
BLI_assert(scene_old != nullptr);
|
||||
if (method == SCE_COPY_FULL) {
|
||||
ED_editors_flush_edits(bmain);
|
||||
}
|
||||
|
||||
scene_new = BKE_scene_duplicate(bmain,
|
||||
scene_old,
|
||||
method,
|
||||
static_cast<eDupli_ID_Flags>(U.dupflag | USER_DUP_OBJECT),
|
||||
LIB_ID_DUPLICATE_IS_ROOT_ID);
|
||||
}
|
||||
|
||||
return scene_new;
|
||||
}
|
||||
|
||||
Scene *ED_scene_sequencer_add(Main *bmain, bContext *C, eSceneCopyMethod method)
|
||||
{
|
||||
Scene *active_scene = CTX_data_scene(C);
|
||||
Scene *scene_new = scene_add(bmain, active_scene, method);
|
||||
|
||||
return scene_new;
|
||||
}
|
||||
|
||||
Scene *ED_scene_add(Main *bmain, bContext *C, wmWindow *win, eSceneCopyMethod method)
|
||||
{
|
||||
Scene *scene_old = WM_window_get_active_scene(win);
|
||||
Scene *scene_new = scene_add(bmain, scene_old, method);
|
||||
|
||||
WM_window_set_active_scene(bmain, C, win, scene_new);
|
||||
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_SCENEBROWSE, scene_new);
|
||||
|
||||
return scene_new;
|
||||
}
|
||||
|
||||
bool ED_scene_replace_active_for_deletion(bContext &C, Main &bmain, Scene &scene, Scene *scene_new)
|
||||
{
|
||||
BLI_assert(!scene_new || &scene != scene_new);
|
||||
if (!BKE_scene_can_be_removed(&bmain, &scene)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!scene_new) {
|
||||
scene_new = BKE_scene_find_replacement(bmain, scene);
|
||||
}
|
||||
if (!scene_new) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* NOTE: Usages of BPy_..._ALLOW_THREADS macros below are necessary because this code is also
|
||||
* called from RNA (and therefore BPY). */
|
||||
|
||||
/* Cancel animation playback. */
|
||||
if (bScreen *screen = ED_screen_animation_playing(CTX_wm_manager(&C))) {
|
||||
ScreenAnimData *sad = static_cast<ScreenAnimData *>(screen->animtimer->customdata);
|
||||
if (sad->scene == &scene) {
|
||||
#ifdef WITH_PYTHON
|
||||
BPy_BEGIN_ALLOW_THREADS;
|
||||
#endif
|
||||
ED_screen_animation_play(&C, 0, 0);
|
||||
#ifdef WITH_PYTHON
|
||||
BPy_END_ALLOW_THREADS;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Kill running jobs. */
|
||||
wmWindowManager *wm = static_cast<wmWindowManager *>(bmain.wm.first);
|
||||
WM_jobs_kill_all_from_owner(wm, &scene);
|
||||
|
||||
for (wmWindow &win : wm->windows) {
|
||||
if (win.parent != nullptr) { /* We only care about main windows here... */
|
||||
continue;
|
||||
}
|
||||
if (win.scene == &scene) {
|
||||
#ifdef WITH_PYTHON
|
||||
BPy_BEGIN_ALLOW_THREADS;
|
||||
#endif
|
||||
WM_window_set_active_scene(&bmain, &C, &win, scene_new);
|
||||
#ifdef WITH_PYTHON
|
||||
BPy_END_ALLOW_THREADS;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Update scenes used by the sequencer. */
|
||||
for (WorkSpace &workspace : bmain.workspaces) {
|
||||
if (workspace.sequencer_scene == &scene) {
|
||||
workspace.sequencer_scene = scene_new;
|
||||
WM_event_add_notifier(&C, NC_WINDOW, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
/* In theory, the call to #WM_window_set_active_scene above should have handled this through
|
||||
* calls to #ED_screen_scene_change. But there can be unusual cases (e.g. on file opening in
|
||||
* background mode) where the state of available Windows may prevent this from happening. */
|
||||
if (CTX_data_scene(&C) == &scene) {
|
||||
#ifdef WITH_PYTHON
|
||||
BPy_BEGIN_ALLOW_THREADS;
|
||||
#endif
|
||||
CTX_data_scene_set(&C, scene_new);
|
||||
#ifdef WITH_PYTHON
|
||||
BPy_END_ALLOW_THREADS;
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ED_scene_delete(bContext *C, Main *bmain, Scene *scene)
|
||||
{
|
||||
if (ED_scene_replace_active_for_deletion(*C, *bmain, *scene)) {
|
||||
BKE_id_delete(bmain, scene);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ED_scene_change_update(Main *bmain, Scene *scene, ViewLayer *layer)
|
||||
{
|
||||
Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, layer);
|
||||
/* When switching to a scene for the first time after loading a file, the dependency graph above
|
||||
* will not be active. This can cause issues (e.g. #151159) because there might be code running
|
||||
* immediately after that expects this dependency graph to be active and then silently fail.
|
||||
* Similar to #CTX_data_depsgraph_pointer. */
|
||||
DEG_make_active(depsgraph);
|
||||
|
||||
BKE_scene_set_background(bmain, scene);
|
||||
DEG_graph_relations_update(depsgraph);
|
||||
DEG_tag_on_visible_update(bmain, false);
|
||||
|
||||
ED_render_engine_changed(bmain, false);
|
||||
ED_update_for_newframe(bmain, depsgraph);
|
||||
}
|
||||
|
||||
static bool view_layer_remove_poll(const Scene *scene, const ViewLayer *layer)
|
||||
{
|
||||
const int act = BLI_findindex(&scene->view_layers, layer);
|
||||
|
||||
if (act == -1) {
|
||||
return false;
|
||||
}
|
||||
if ((scene->view_layers.first == scene->view_layers.last) && (scene->view_layers.first == layer))
|
||||
{
|
||||
/* ensure 1 layer is kept */
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void view_layer_remove_unset_nodetrees(const Main *bmain, Scene *scene, ViewLayer *layer)
|
||||
{
|
||||
int act_layer_index = BLI_findindex(&scene->view_layers, layer);
|
||||
|
||||
for (Scene *sce = static_cast<Scene *>(bmain->scenes.first); sce;
|
||||
sce = static_cast<Scene *>(sce->id.next))
|
||||
{
|
||||
if (sce->compositing_node_group) {
|
||||
bke::node_tree_remove_layer_n(sce->compositing_node_group, scene, act_layer_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ED_scene_view_layer_delete(Main *bmain, Scene *scene, ViewLayer *layer, ReportList *reports)
|
||||
{
|
||||
if (view_layer_remove_poll(scene, layer) == false) {
|
||||
if (reports) {
|
||||
BKE_reportf(reports,
|
||||
RPT_ERROR,
|
||||
"View layer '%s' could not be removed from scene '%s'",
|
||||
layer->name,
|
||||
scene->id.name + 2);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* We need to unset node-trees before removing the layer, otherwise its index will be -1. */
|
||||
view_layer_remove_unset_nodetrees(bmain, scene, layer);
|
||||
|
||||
/* Return whether the given screen uses the to-be-removed view layer. */
|
||||
const auto is_using_view_layer = [&](const bScreen &screen) -> bool {
|
||||
const ScreenAnimData *sad = static_cast<const ScreenAnimData *>(screen.animtimer->customdata);
|
||||
return (sad && sad->scene == scene && sad->view_layer == layer);
|
||||
};
|
||||
|
||||
/* Stop animation playback of this layer before removing it, as the ScreenAnimData struct
|
||||
* has a pointer to it. */
|
||||
wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
|
||||
ED_screen_animation_stop(bmain, wm, is_using_view_layer);
|
||||
|
||||
BLI_remlink(&scene->view_layers, layer);
|
||||
BLI_assert(scene->view_layers.is_empty() == false);
|
||||
|
||||
/* Remove from windows. */
|
||||
for (wmWindow &win : wm->windows) {
|
||||
if (win.scene == scene && STREQ(win.view_layer_name, layer->name)) {
|
||||
ViewLayer *first_layer = BKE_view_layer_default_view(scene);
|
||||
STRNCPY_UTF8(win.view_layer_name, first_layer->name);
|
||||
}
|
||||
}
|
||||
|
||||
BKE_scene_free_view_layer_depsgraph(scene, layer);
|
||||
|
||||
/* Update any sequencer scene strips referencing this view layer by name. */
|
||||
seq::relations_update_view_layer_scene_strips(bmain, scene, layer->name, nullptr);
|
||||
|
||||
BKE_view_layer_free(layer);
|
||||
|
||||
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
|
||||
DEG_relations_tag_update(bmain);
|
||||
WM_main_add_notifier(NC_SCENE | ND_LAYER | NA_REMOVED, scene);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Scene New Operator
|
||||
* \{ */
|
||||
|
||||
static wmOperatorStatus scene_new_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
int type = RNA_enum_get(op->ptr, "type");
|
||||
|
||||
ED_scene_add(bmain, C, win, eSceneCopyMethod(type));
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static EnumPropertyItem scene_new_items[] = {
|
||||
{SCE_COPY_NEW, "NEW", 0, "New", "Add a new, empty scene with default settings"},
|
||||
{SCE_COPY_EMPTY,
|
||||
"EMPTY",
|
||||
0,
|
||||
"Copy Settings",
|
||||
"Add a new, empty scene, and copy settings from the current scene"},
|
||||
{SCE_COPY_LINK_COLLECTION,
|
||||
"LINK_COPY",
|
||||
0,
|
||||
"Linked Copy",
|
||||
"Link in the collections from the current scene (shallow copy)"},
|
||||
{SCE_COPY_FULL, "FULL_COPY", 0, "Full Copy", "Make a full copy of the current scene"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static void SCENE_OT_new(wmOperatorType *ot)
|
||||
{
|
||||
|
||||
/* identifiers */
|
||||
ot->name = "New Scene";
|
||||
ot->description = "Add new scene by type";
|
||||
ot->idname = "SCENE_OT_new";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->exec = scene_new_exec;
|
||||
ot->invoke = WM_menu_invoke;
|
||||
ot->poll = WM_operator_winactive;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
ot->prop = RNA_def_enum(ot->srna, "type", scene_new_items, SCE_COPY_NEW, "Type", "");
|
||||
RNA_def_property_translation_context(ot->prop, BLT_I18NCONTEXT_ID_SCENE);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Scene New Sequencer Operator
|
||||
* \{ */
|
||||
|
||||
static wmOperatorStatus scene_new_sequencer_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
int type = RNA_enum_get(op->ptr, "type");
|
||||
Scene *sequencer_scene = CTX_data_sequencer_scene(C);
|
||||
Strip *strip = seq::select_active_get(sequencer_scene);
|
||||
BLI_assert(strip != nullptr);
|
||||
|
||||
if (!strip->scene) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
Scene *scene_new = scene_add(bmain, strip->scene, eSceneCopyMethod(type));
|
||||
if (!scene_new) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
strip->scene = scene_new;
|
||||
/* Do a refresh of the sequencer data. */
|
||||
seq::relations_invalidate_cache_raw(sequencer_scene, strip);
|
||||
DEG_id_tag_update(&sequencer_scene->id, ID_RECALC_AUDIO | ID_RECALC_SEQUENCER_STRIPS);
|
||||
DEG_relations_tag_update(bmain);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static bool scene_new_sequencer_poll(bContext *C)
|
||||
{
|
||||
Scene *scene = CTX_data_sequencer_scene(C);
|
||||
const Strip *strip = seq::select_active_get(scene);
|
||||
return (strip && (strip->type == STRIP_TYPE_SCENE));
|
||||
}
|
||||
|
||||
static const EnumPropertyItem *scene_new_sequencer_enum_itemf(bContext *C,
|
||||
PointerRNA * /*ptr*/,
|
||||
PropertyRNA * /*prop*/,
|
||||
bool *r_free)
|
||||
{
|
||||
EnumPropertyItem *item = nullptr;
|
||||
int totitem = 0;
|
||||
uint item_index;
|
||||
|
||||
item_index = RNA_enum_from_value(scene_new_items, SCE_COPY_NEW);
|
||||
RNA_enum_item_add(&item, &totitem, &scene_new_items[item_index]);
|
||||
|
||||
bool has_scene_or_no_context = false;
|
||||
if (C == nullptr) {
|
||||
/* For documentation generation. */
|
||||
has_scene_or_no_context = true;
|
||||
}
|
||||
else {
|
||||
Scene *scene = CTX_data_sequencer_scene(C);
|
||||
Strip *strip = seq::select_active_get(scene);
|
||||
if (strip && (strip->type == STRIP_TYPE_SCENE) && (strip->scene != nullptr)) {
|
||||
has_scene_or_no_context = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_scene_or_no_context) {
|
||||
int values[] = {SCE_COPY_EMPTY, SCE_COPY_LINK_COLLECTION, SCE_COPY_FULL};
|
||||
for (int i = 0; i < ARRAY_SIZE(values); i++) {
|
||||
item_index = RNA_enum_from_value(scene_new_items, values[i]);
|
||||
RNA_enum_item_add(&item, &totitem, &scene_new_items[item_index]);
|
||||
}
|
||||
}
|
||||
|
||||
RNA_enum_item_end(&item, &totitem);
|
||||
*r_free = true;
|
||||
return item;
|
||||
}
|
||||
|
||||
static void SCENE_OT_new_sequencer(wmOperatorType *ot)
|
||||
{
|
||||
|
||||
/* identifiers */
|
||||
ot->name = "New Scene";
|
||||
ot->description = "Add new scene by type in the sequence editor and assign to active strip";
|
||||
ot->idname = "SCENE_OT_new_sequencer";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->exec = scene_new_sequencer_exec;
|
||||
ot->invoke = WM_menu_invoke;
|
||||
ot->poll = scene_new_sequencer_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
ot->prop = RNA_def_enum(ot->srna, "type", scene_new_items, SCE_COPY_NEW, "Type", "");
|
||||
RNA_def_enum_funcs(ot->prop, scene_new_sequencer_enum_itemf);
|
||||
RNA_def_property_flag(ot->prop, PROP_ENUM_NO_TRANSLATE);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name New Sequencer Scene Operator
|
||||
* \{ */
|
||||
|
||||
static wmOperatorStatus new_sequencer_scene_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
WorkSpace *workspace = CTX_wm_workspace(C);
|
||||
Scene *scene_old = CTX_data_sequencer_scene(C);
|
||||
eSceneCopyMethod type = eSceneCopyMethod(RNA_enum_get(op->ptr, "type"));
|
||||
/* When there is no scene to copy from, force new. */
|
||||
if (scene_old == nullptr) {
|
||||
type = SCE_COPY_NEW;
|
||||
}
|
||||
Scene *new_scene = scene_add(bmain, scene_old, eSceneCopyMethod(type));
|
||||
seq::editing_ensure(new_scene);
|
||||
|
||||
/* Unlikely but not impossible as poll doesn't check for this. */
|
||||
if (workspace != nullptr) [[unlikely]] {
|
||||
workspace->sequencer_scene = new_scene;
|
||||
}
|
||||
|
||||
/* Switching the active scene to the newly created sequencer scene should prevent confusion among
|
||||
* new users to the VSE. For example, this prevents the case where attempting to change
|
||||
* resolution properties would have no effect.
|
||||
*
|
||||
* FIXME: This logic is meant to address a temporary paper-cut and may be removed later in 5.1+
|
||||
* when properties for scenes and sequencer scenes can be more properly separated. */
|
||||
WM_window_set_active_scene(bmain, C, win, new_scene);
|
||||
BKE_reportf(
|
||||
op->reports, RPT_WARNING, TIP_("Active scene changed to '%s'"), new_scene->id.name + 2);
|
||||
|
||||
WM_event_add_notifier(C, NC_WINDOW, nullptr);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static wmOperatorStatus new_sequencer_scene_invoke(bContext *C,
|
||||
wmOperator *op,
|
||||
const wmEvent *event)
|
||||
{
|
||||
if (CTX_data_sequencer_scene(C) == nullptr) {
|
||||
/* When there is no sequencer scene set, create a blank new one. */
|
||||
RNA_enum_set(op->ptr, "type", SCE_COPY_NEW);
|
||||
return new_sequencer_scene_exec(C, op);
|
||||
}
|
||||
return WM_menu_invoke(C, op, event);
|
||||
}
|
||||
|
||||
static void SCENE_OT_new_sequencer_scene(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "New Sequencer Scene";
|
||||
ot->description = "Add new scene to be used by the sequencer";
|
||||
ot->idname = "SCENE_OT_new_sequencer_scene";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->exec = new_sequencer_scene_exec;
|
||||
ot->invoke = new_sequencer_scene_invoke;
|
||||
ot->poll = ED_operator_screenactive;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
ot->prop = RNA_def_enum(ot->srna, "type", scene_new_items, SCE_COPY_NEW, "Type", "");
|
||||
RNA_def_property_translation_context(ot->prop, BLT_I18NCONTEXT_ID_SCENE);
|
||||
RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Scene Delete Operator
|
||||
* \{ */
|
||||
|
||||
static bool scene_delete_poll(bContext *C)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
return BKE_scene_can_be_removed(bmain, scene);
|
||||
}
|
||||
|
||||
static wmOperatorStatus scene_delete_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
||||
if (ED_scene_delete(C, CTX_data_main(C), scene) == false) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (G.debug & G_DEBUG) {
|
||||
printf("scene delete %p\n", scene);
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_SCENE | NA_REMOVED, scene);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void SCENE_OT_delete(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Delete Scene";
|
||||
ot->description = "Delete active scene";
|
||||
ot->idname = "SCENE_OT_delete";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->exec = scene_delete_exec;
|
||||
ot->poll = scene_delete_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Drop Scene Asset
|
||||
* \{ */
|
||||
|
||||
static wmOperatorStatus drop_scene_asset_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene_asset = reinterpret_cast<Scene *>(
|
||||
WM_operator_properties_id_lookup_from_name_or_session_uid(bmain, op->ptr, ID_SCE));
|
||||
if (!scene_asset) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
WM_window_set_active_scene(bmain, C, win, scene_asset);
|
||||
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_SCENEBROWSE, scene_asset);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void SCENE_OT_drop_scene_asset(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Drop Scene";
|
||||
ot->description = "Import scene and set it as the active one in the window";
|
||||
ot->idname = "SCENE_OT_drop_scene_asset";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = drop_scene_asset_exec;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
||||
|
||||
WM_operator_properties_id_lookup(ot, false);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Registration
|
||||
* \{ */
|
||||
|
||||
void ED_operatortypes_scene()
|
||||
{
|
||||
WM_operatortype_append(SCENE_OT_new);
|
||||
WM_operatortype_append(SCENE_OT_delete);
|
||||
WM_operatortype_append(SCENE_OT_new_sequencer);
|
||||
WM_operatortype_append(SCENE_OT_new_sequencer_scene);
|
||||
|
||||
WM_operatortype_append(SCENE_OT_drop_scene_asset);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
128
blender-5.2.0/source/blender/editors/scene/scene_fps.cc
Normal file
128
blender-5.2.0/source/blender/editors/scene/scene_fps.cc
Normal file
@@ -0,0 +1,128 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edscene
|
||||
*
|
||||
* Define `ED_scene_fps_*` functions.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "ED_scene.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Ring buffer of recent frame timestamps; FPS is `(samples - 1) / (newest - oldest)`.
|
||||
*
|
||||
* \note Stores timestamps rather than per-sample rates on purpose: averaging `1/interval`
|
||||
* is biased by short intervals and spikes during uneven timer cadence.
|
||||
*/
|
||||
struct ScreenFrameRateInfo {
|
||||
/** When the target FPS is not a whole number. */
|
||||
bool fps_target_is_fractional;
|
||||
|
||||
/** The target FPS, use to reset on change. */
|
||||
float fps_target;
|
||||
/** Final result, ignore when -1.0. */
|
||||
float fps_average;
|
||||
|
||||
int times_index;
|
||||
int times_num;
|
||||
/** Number of `times` set, never exceeds `times_num`. */
|
||||
int times_num_set;
|
||||
|
||||
/** Over-allocated, containing `times_num` elements. */
|
||||
double times[0];
|
||||
};
|
||||
|
||||
void ED_scene_fps_average_clear(Scene *scene)
|
||||
{
|
||||
if (scene->fps_info == nullptr) {
|
||||
return;
|
||||
}
|
||||
MEM_delete(static_cast<ScreenFrameRateInfo *>(scene->fps_info));
|
||||
scene->fps_info = nullptr;
|
||||
}
|
||||
|
||||
void ED_scene_fps_average_accumulate(Scene *scene, const short fps_samples, const double ltime)
|
||||
{
|
||||
const float fps_target = float(scene->frames_per_second());
|
||||
/* Averaging intervals needs +1 timestamps.
|
||||
* Needed because the intervals are computed *between* the time-steps. */
|
||||
const int times_num = ((fps_samples > 0) ? fps_samples : std::max(1, int(ceilf(fps_target)))) +
|
||||
1;
|
||||
|
||||
ScreenFrameRateInfo *fpsi = static_cast<ScreenFrameRateInfo *>(scene->fps_info);
|
||||
if (fpsi) {
|
||||
/* Reset when the target FPS changes.
|
||||
* Needed redraw times from when a different FPS was set do not contribute
|
||||
* to an average that is over/under the new target. */
|
||||
if ((fpsi->fps_target != fps_target) || (fpsi->times_num != times_num)) {
|
||||
MEM_delete(fpsi);
|
||||
fpsi = nullptr;
|
||||
scene->fps_info = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* If there isn't any info, initialize it first. */
|
||||
if (fpsi == nullptr) {
|
||||
scene->fps_info = MEM_new_zeroed(sizeof(ScreenFrameRateInfo) +
|
||||
(sizeof(ScreenFrameRateInfo::times[0]) * times_num),
|
||||
__func__);
|
||||
fpsi = static_cast<ScreenFrameRateInfo *>(scene->fps_info);
|
||||
fpsi->fps_target = fps_target;
|
||||
fpsi->times_num = times_num;
|
||||
|
||||
/* Use 100 for 2 decimal places (currently used for FPS display), could be configurable. */
|
||||
const double decimal_places = 100.0;
|
||||
fpsi->fps_target_is_fractional = std::round(fps_target) !=
|
||||
(std::round(fps_target * decimal_places) / decimal_places);
|
||||
}
|
||||
|
||||
fpsi->times[fpsi->times_index] = ltime;
|
||||
fpsi->times_index = (fpsi->times_index + 1) % fpsi->times_num;
|
||||
if (fpsi->times_num_set < fpsi->times_num) {
|
||||
fpsi->times_num_set++;
|
||||
}
|
||||
|
||||
/* Mark as outdated. */
|
||||
fpsi->fps_average = -1.0f;
|
||||
}
|
||||
|
||||
bool ED_scene_fps_average_calc(const Scene *scene, SceneFPS_State *r_state)
|
||||
{
|
||||
ScreenFrameRateInfo *fpsi = static_cast<ScreenFrameRateInfo *>(scene->fps_info);
|
||||
if (fpsi == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Need at least two timestamps to define an interval. */
|
||||
if (fpsi->times_num_set < 2) [[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fpsi->fps_average == -1.0f) {
|
||||
const int newest_index = ((fpsi->times_index - 1) + fpsi->times_num) % fpsi->times_num;
|
||||
const int oldest_index = (fpsi->times_num_set < fpsi->times_num) ? 0 : fpsi->times_index;
|
||||
const double delta = fpsi->times[newest_index] - fpsi->times[oldest_index];
|
||||
BLI_assert(delta >= 0.0);
|
||||
fpsi->fps_average = (delta > 0.0) ? float(double(fpsi->times_num_set - 1) / delta) : 0.0f;
|
||||
}
|
||||
|
||||
r_state->fps_average = fpsi->fps_average;
|
||||
r_state->fps_target = fpsi->fps_target;
|
||||
r_state->fps_target_is_fractional = fpsi->fps_target_is_fractional;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
Reference in New Issue
Block a user