This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/blenkernel/intern/screen.c
Jacques Lucke c55d38f00b Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.

**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
  makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
  is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
  its active or not.

**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
  When pinned, the spreadsheet still references the viewer node even
  when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
  the active viewer.

**Attribute**
* When a field is linked to the second input of the viewer node it is
  displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
  automatically. This does not work in all cases. It falls back to the
  face corner domain on meshes and the point domain on curves. When
  necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
  that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.

**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
  setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
  by disabling the "Viewer Node" option in the "View" menu.

**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
  is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
  field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
  attribute.
* The ground truth for the active viewer node is stored in the workspace
  now. Node editors, spreadsheets and viewports retrieve the active
  viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
  the viewed geometry of the corresponding object is part of the iterator
  instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
  to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
  existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
  "preferred domain" for every field input. If there is not exactly one
  preferred domain, the fallback is used.

Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
  added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
  the values might have to be normalized or some should be drawn as arrays.
  For now, we encourage users to build node groups that generate appropriate
  viewer-geometry. We might include some of that functionality in future versions.
  Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
  nvidia gpus, to be investigated.

Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00

1386 lines
38 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
/** \file
* \ingroup bke
*/
/* Allow using deprecated functionality for .blend file I/O. */
#define DNA_DEPRECATED_ALLOW
#ifdef WIN32
# include "BLI_winstuff.h"
#endif
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "MEM_guardedalloc.h"
#include "DNA_collection_types.h"
#include "DNA_defaults.h"
#include "DNA_gpencil_types.h"
#include "DNA_mask_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "DNA_text_types.h"
#include "DNA_view3d_types.h"
#include "DNA_workspace_types.h"
#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_mempool.h"
#include "BLI_rect.h"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
#include "BKE_gpencil.h"
#include "BKE_icons.h"
#include "BKE_idprop.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_node.h"
#include "BKE_screen.h"
#include "BKE_viewer_path.h"
#include "BKE_workspace.h"
#include "BLO_read_write.h"
#ifdef WITH_PYTHON
# include "BPY_extern.h"
#endif
static void screen_free_data(ID *id)
{
bScreen *screen = (bScreen *)id;
/* No animation-data here. */
LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) {
BKE_area_region_free(NULL, region);
}
BLI_freelistN(&screen->regionbase);
BKE_screen_area_map_free(AREAMAP_FROM_SCREEN(screen));
BKE_previewimg_free(&screen->preview);
/* Region and timer are freed by the window manager. */
MEM_SAFE_FREE(screen->tool_tip);
}
static void screen_foreach_id_dopesheet(LibraryForeachIDData *data, bDopeSheet *ads)
{
if (ads != NULL) {
BKE_LIB_FOREACHID_PROCESS_ID(data, ads->source, IDWALK_CB_NOP);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, ads->filter_grp, IDWALK_CB_NOP);
}
}
void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area)
{
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, area->full, IDWALK_CB_NOP);
/* TODO: this should be moved to a callback in `SpaceType`, defined in each editor's own code.
* Will be for a later round of cleanup though... */
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
switch (sl->spacetype) {
case SPACE_VIEW3D: {
View3D *v3d = (View3D *)sl;
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->camera, IDWALK_CB_NOP);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->ob_center, IDWALK_CB_NOP);
if (v3d->localvd) {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->localvd->camera, IDWALK_CB_NOP);
}
BKE_viewer_path_foreach_id(data, &v3d->viewer_path);
break;
}
case SPACE_GRAPH: {
SpaceGraph *sipo = (SpaceGraph *)sl;
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data,
screen_foreach_id_dopesheet(data, sipo->ads));
break;
}
case SPACE_PROPERTIES: {
SpaceProperties *sbuts = (SpaceProperties *)sl;
BKE_LIB_FOREACHID_PROCESS_ID(data, sbuts->pinid, IDWALK_CB_NOP);
break;
}
case SPACE_FILE:
break;
case SPACE_ACTION: {
SpaceAction *saction = (SpaceAction *)sl;
screen_foreach_id_dopesheet(data, &saction->ads);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, saction->action, IDWALK_CB_NOP);
break;
}
case SPACE_IMAGE: {
SpaceImage *sima = (SpaceImage *)sl;
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->image, IDWALK_CB_USER_ONE);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->mask_info.mask, IDWALK_CB_USER_ONE);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->gpd, IDWALK_CB_USER);
break;
}
case SPACE_SEQ: {
SpaceSeq *sseq = (SpaceSeq *)sl;
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sseq->gpd, IDWALK_CB_USER);
break;
}
case SPACE_NLA: {
SpaceNla *snla = (SpaceNla *)sl;
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data,
screen_foreach_id_dopesheet(data, snla->ads));
break;
}
case SPACE_TEXT: {
SpaceText *st = (SpaceText *)sl;
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, st->text, IDWALK_CB_NOP);
break;
}
case SPACE_SCRIPT: {
SpaceScript *scpt = (SpaceScript *)sl;
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scpt->script, IDWALK_CB_NOP);
break;
}
case SPACE_OUTLINER: {
SpaceOutliner *space_outliner = (SpaceOutliner *)sl;
if (space_outliner->treestore != NULL) {
TreeStoreElem *tselem;
BLI_mempool_iter iter;
BLI_mempool_iternew(space_outliner->treestore, &iter);
while ((tselem = BLI_mempool_iterstep(&iter))) {
BKE_LIB_FOREACHID_PROCESS_ID(data, tselem->id, IDWALK_CB_NOP);
}
}
break;
}
case SPACE_NODE: {
SpaceNode *snode = (SpaceNode *)sl;
const bool is_private_nodetree = snode->id != NULL &&
ntreeFromID(snode->id) == snode->nodetree;
BKE_LIB_FOREACHID_PROCESS_ID(data, snode->id, IDWALK_CB_NOP);
BKE_LIB_FOREACHID_PROCESS_ID(data, snode->from, IDWALK_CB_NOP);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(
data, snode->nodetree, is_private_nodetree ? IDWALK_CB_EMBEDDED : IDWALK_CB_USER_ONE);
LISTBASE_FOREACH (bNodeTreePath *, path, &snode->treepath) {
if (path == snode->treepath.first) {
/* first nodetree in path is same as snode->nodetree */
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data,
path->nodetree,
is_private_nodetree ? IDWALK_CB_EMBEDDED :
IDWALK_CB_USER_ONE);
}
else {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, path->nodetree, IDWALK_CB_USER_ONE);
}
if (path->nodetree == NULL) {
break;
}
}
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, snode->edittree, IDWALK_CB_NOP);
break;
}
case SPACE_CLIP: {
SpaceClip *sclip = (SpaceClip *)sl;
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sclip->clip, IDWALK_CB_USER_ONE);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sclip->mask_info.mask, IDWALK_CB_USER_ONE);
break;
}
case SPACE_SPREADSHEET: {
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
BKE_viewer_path_foreach_id(data, &sspreadsheet->viewer_path);
break;
}
default:
break;
}
}
}
static void screen_foreach_id(ID *id, LibraryForeachIDData *data)
{
if ((BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_INCLUDE_UI) == 0) {
return;
}
bScreen *screen = (bScreen *)id;
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_screen_foreach_id_screen_area(data, area));
}
}
static void screen_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
bScreen *screen = (bScreen *)id;
/* write LibData */
/* in 2.50+ files, the file identifier for screens is patched, forward compatibility */
BLO_write_struct_at_address_with_filecode(writer, ID_SCRN, bScreen, id_address, screen);
BKE_id_blend_write(writer, &screen->id);
BKE_previewimg_blend_write(writer, screen->preview);
/* direct data */
BKE_screen_area_map_blend_write(writer, AREAMAP_FROM_SCREEN(screen));
}
bool BKE_screen_blend_read_data(BlendDataReader *reader, bScreen *screen)
{
bool success = true;
screen->regionbase.first = screen->regionbase.last = NULL;
screen->context = NULL;
screen->active_region = NULL;
BLO_read_data_address(reader, &screen->preview);
BKE_previewimg_blend_read(reader, screen->preview);
if (!BKE_screen_area_map_blend_read_data(reader, AREAMAP_FROM_SCREEN(screen))) {
printf("Error reading Screen %s... removing it.\n", screen->id.name + 2);
success = false;
}
return success;
}
/* NOTE: file read without screens option G_FILE_NO_UI;
* check lib pointers in call below */
static void screen_blend_read_lib(BlendLibReader *reader, ID *id)
{
bScreen *screen = (bScreen *)id;
/* deprecated, but needed for versioning (will be NULL'ed then) */
BLO_read_id_address(reader, screen->id.lib, &screen->scene);
screen->animtimer = NULL; /* saved in rare cases */
screen->tool_tip = NULL;
screen->scrubbing = false;
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
BKE_screen_area_blend_read_lib(reader, &screen->id, area);
}
}
IDTypeInfo IDType_ID_SCR = {
.id_code = ID_SCR,
.id_filter = FILTER_ID_SCR,
.main_listbase_index = INDEX_ID_SCR,
.struct_size = sizeof(bScreen),
.name = "Screen",
.name_plural = "screens",
.translation_context = BLT_I18NCONTEXT_ID_SCREEN,
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_ONLY_APPEND | IDTYPE_FLAGS_NO_ANIMDATA,
.asset_type_info = NULL,
.init_data = NULL,
.copy_data = NULL,
.free_data = screen_free_data,
.make_local = NULL,
.foreach_id = screen_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
.owner_pointer_get = NULL,
.blend_write = screen_blend_write,
/* Cannot be used yet, because #direct_link_screen has a return value. */
.blend_read_data = NULL,
.blend_read_lib = screen_blend_read_lib,
.blend_read_expand = NULL,
.blend_read_undo_preserve = NULL,
.lib_override_apply_post = NULL,
};
/* ************ Space-type/region-type handling ************** */
/* keep global; this has to be accessible outside of windowmanager */
static ListBase spacetypes = {NULL, NULL};
/* not SpaceType itself */
static void spacetype_free(SpaceType *st)
{
LISTBASE_FOREACH (ARegionType *, art, &st->regiontypes) {
#ifdef WITH_PYTHON
BPY_callback_screen_free(art);
#endif
BLI_freelistN(&art->drawcalls);
LISTBASE_FOREACH (PanelType *, pt, &art->paneltypes) {
if (pt->rna_ext.free) {
pt->rna_ext.free(pt->rna_ext.data);
}
BLI_freelistN(&pt->children);
}
LISTBASE_FOREACH (HeaderType *, ht, &art->headertypes) {
if (ht->rna_ext.free) {
ht->rna_ext.free(ht->rna_ext.data);
}
}
BLI_freelistN(&art->paneltypes);
BLI_freelistN(&art->headertypes);
}
BLI_freelistN(&st->regiontypes);
}
void BKE_spacetypes_free(void)
{
LISTBASE_FOREACH (SpaceType *, st, &spacetypes) {
spacetype_free(st);
}
BLI_freelistN(&spacetypes);
}
SpaceType *BKE_spacetype_from_id(int spaceid)
{
LISTBASE_FOREACH (SpaceType *, st, &spacetypes) {
if (st->spaceid == spaceid) {
return st;
}
}
return NULL;
}
ARegionType *BKE_regiontype_from_id_or_first(const SpaceType *st, int regionid)
{
LISTBASE_FOREACH (ARegionType *, art, &st->regiontypes) {
if (art->regionid == regionid) {
return art;
}
}
printf(
"Error, region type %d missing in - name:\"%s\", id:%d\n", regionid, st->name, st->spaceid);
return st->regiontypes.first;
}
ARegionType *BKE_regiontype_from_id(const SpaceType *st, int regionid)
{
LISTBASE_FOREACH (ARegionType *, art, &st->regiontypes) {
if (art->regionid == regionid) {
return art;
}
}
return NULL;
}
const ListBase *BKE_spacetypes_list(void)
{
return &spacetypes;
}
void BKE_spacetype_register(SpaceType *st)
{
/* sanity check */
SpaceType *stype = BKE_spacetype_from_id(st->spaceid);
if (stype) {
printf("error: redefinition of spacetype %s\n", stype->name);
spacetype_free(stype);
MEM_freeN(stype);
}
BLI_addtail(&spacetypes, st);
}
bool BKE_spacetype_exists(int spaceid)
{
return BKE_spacetype_from_id(spaceid) != NULL;
}
/* ***************** Space handling ********************** */
void BKE_spacedata_freelist(ListBase *lb)
{
LISTBASE_FOREACH (SpaceLink *, sl, lb) {
SpaceType *st = BKE_spacetype_from_id(sl->spacetype);
/* free regions for pushed spaces */
LISTBASE_FOREACH (ARegion *, region, &sl->regionbase) {
BKE_area_region_free(st, region);
}
BLI_freelistN(&sl->regionbase);
if (st && st->free) {
st->free(sl);
}
}
BLI_freelistN(lb);
}
static void panel_list_copy(ListBase *newlb, const ListBase *lb)
{
BLI_listbase_clear(newlb);
BLI_duplicatelist(newlb, lb);
/* copy panel pointers */
Panel *new_panel = newlb->first;
Panel *panel = lb->first;
for (; new_panel; new_panel = new_panel->next, panel = panel->next) {
new_panel->activedata = NULL;
memset(&new_panel->runtime, 0x0, sizeof(new_panel->runtime));
panel_list_copy(&new_panel->children, &panel->children);
}
}
ARegion *BKE_area_region_copy(const SpaceType *st, const ARegion *region)
{
ARegion *newar = MEM_dupallocN(region);
memset(&newar->runtime, 0x0, sizeof(newar->runtime));
newar->prev = newar->next = NULL;
BLI_listbase_clear(&newar->handlers);
BLI_listbase_clear(&newar->uiblocks);
BLI_listbase_clear(&newar->panels_category);
BLI_listbase_clear(&newar->panels_category_active);
BLI_listbase_clear(&newar->ui_lists);
newar->visible = 0;
newar->gizmo_map = NULL;
newar->regiontimer = NULL;
newar->headerstr = NULL;
newar->draw_buffer = NULL;
/* use optional regiondata callback */
if (region->regiondata) {
ARegionType *art = BKE_regiontype_from_id(st, region->regiontype);
if (art && art->duplicate) {
newar->regiondata = art->duplicate(region->regiondata);
}
else if (region->flag & RGN_FLAG_TEMP_REGIONDATA) {
newar->regiondata = NULL;
}
else {
newar->regiondata = MEM_dupallocN(region->regiondata);
}
}
panel_list_copy(&newar->panels, &region->panels);
BLI_listbase_clear(&newar->ui_previews);
BLI_duplicatelist(&newar->ui_previews, &region->ui_previews);
return newar;
}
/* from lb_src to lb_dst, lb_dst is supposed to be freed */
static void region_copylist(SpaceType *st, ListBase *lb_dst, ListBase *lb_src)
{
/* to be sure */
BLI_listbase_clear(lb_dst);
LISTBASE_FOREACH (ARegion *, region, lb_src) {
ARegion *region_new = BKE_area_region_copy(st, region);
BLI_addtail(lb_dst, region_new);
}
}
void BKE_spacedata_copylist(ListBase *lb_dst, ListBase *lb_src)
{
BLI_listbase_clear(lb_dst); /* to be sure */
LISTBASE_FOREACH (SpaceLink *, sl, lb_src) {
SpaceType *st = BKE_spacetype_from_id(sl->spacetype);
if (st && st->duplicate) {
SpaceLink *slnew = st->duplicate(sl);
BLI_addtail(lb_dst, slnew);
region_copylist(st, &slnew->regionbase, &sl->regionbase);
}
}
}
void BKE_spacedata_draw_locks(bool set)
{
LISTBASE_FOREACH (SpaceType *, st, &spacetypes) {
LISTBASE_FOREACH (ARegionType *, art, &st->regiontypes) {
if (set) {
art->do_lock = art->lock;
}
else {
art->do_lock = false;
}
}
}
}
ARegion *BKE_spacedata_find_region_type(const SpaceLink *slink,
const ScrArea *area,
int region_type)
{
const bool is_slink_active = slink == area->spacedata.first;
const ListBase *regionbase = (is_slink_active) ? &area->regionbase : &slink->regionbase;
ARegion *region = NULL;
BLI_assert(BLI_findindex(&area->spacedata, slink) != -1);
LISTBASE_FOREACH (ARegion *, region_iter, regionbase) {
if (region_iter->regiontype == region_type) {
region = region_iter;
break;
}
}
/* Should really unit test this instead. */
BLI_assert(!is_slink_active || region == BKE_area_find_region_type(area, region_type));
return region;
}
static void (*spacedata_id_remap_cb)(struct ScrArea *area,
struct SpaceLink *sl,
ID *old_id,
ID *new_id) = NULL;
void BKE_spacedata_callback_id_remap_set(void (*func)(ScrArea *area, SpaceLink *sl, ID *, ID *))
{
spacedata_id_remap_cb = func;
}
void BKE_spacedata_id_unref(struct ScrArea *area, struct SpaceLink *sl, struct ID *id)
{
if (spacedata_id_remap_cb) {
spacedata_id_remap_cb(area, sl, id, NULL);
}
}
/**
* Avoid bad-level calls to #WM_gizmomap_tag_refresh.
*/
static void (*region_refresh_tag_gizmomap_callback)(struct wmGizmoMap *) = NULL;
void BKE_region_callback_refresh_tag_gizmomap_set(void (*callback)(struct wmGizmoMap *))
{
region_refresh_tag_gizmomap_callback = callback;
}
void BKE_screen_gizmo_tag_refresh(struct bScreen *screen)
{
if (region_refresh_tag_gizmomap_callback == NULL) {
return;
}
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (region->gizmo_map != NULL) {
region_refresh_tag_gizmomap_callback(region->gizmo_map);
}
}
}
}
/**
* Avoid bad-level calls to #WM_gizmomap_delete.
*/
static void (*region_free_gizmomap_callback)(struct wmGizmoMap *) = NULL;
void BKE_region_callback_free_gizmomap_set(void (*callback)(struct wmGizmoMap *))
{
region_free_gizmomap_callback = callback;
}
static void area_region_panels_free_recursive(Panel *panel)
{
MEM_SAFE_FREE(panel->activedata);
LISTBASE_FOREACH_MUTABLE (Panel *, child_panel, &panel->children) {
area_region_panels_free_recursive(child_panel);
}
MEM_freeN(panel);
}
void BKE_area_region_panels_free(ListBase *panels)
{
LISTBASE_FOREACH_MUTABLE (Panel *, panel, panels) {
/* Free custom data just for parent panels to avoid a double free. */
MEM_SAFE_FREE(panel->runtime.custom_data_ptr);
area_region_panels_free_recursive(panel);
}
BLI_listbase_clear(panels);
}
void BKE_area_region_free(SpaceType *st, ARegion *region)
{
if (st) {
ARegionType *art = BKE_regiontype_from_id(st, region->regiontype);
if (art && art->free) {
art->free(region);
}
if (region->regiondata) {
printf("regiondata free error\n");
}
}
else if (region->type && region->type->free) {
region->type->free(region);
}
BKE_area_region_panels_free(&region->panels);
LISTBASE_FOREACH (uiList *, uilst, &region->ui_lists) {
if (uilst->dyn_data && uilst->dyn_data->free_runtime_data_fn) {
uilst->dyn_data->free_runtime_data_fn(uilst);
}
if (uilst->properties) {
IDP_FreeProperty(uilst->properties);
}
MEM_SAFE_FREE(uilst->dyn_data);
}
if (region->gizmo_map != NULL) {
region_free_gizmomap_callback(region->gizmo_map);
}
if (region->runtime.block_name_map != NULL) {
BLI_ghash_free(region->runtime.block_name_map, NULL, NULL);
region->runtime.block_name_map = NULL;
}
BLI_freelistN(&region->ui_lists);
BLI_freelistN(&region->ui_previews);
BLI_freelistN(&region->panels_category);
BLI_freelistN(&region->panels_category_active);
}
void BKE_screen_area_free(ScrArea *area)
{
SpaceType *st = BKE_spacetype_from_id(area->spacetype);
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
BKE_area_region_free(st, region);
}
MEM_SAFE_FREE(area->global);
BLI_freelistN(&area->regionbase);
BKE_spacedata_freelist(&area->spacedata);
BLI_freelistN(&area->actionzones);
}
void BKE_screen_area_map_free(ScrAreaMap *area_map)
{
LISTBASE_FOREACH_MUTABLE (ScrArea *, area, &area_map->areabase) {
BKE_screen_area_free(area);
}
BLI_freelistN(&area_map->vertbase);
BLI_freelistN(&area_map->edgebase);
BLI_freelistN(&area_map->areabase);
}
void BKE_screen_free_data(bScreen *screen)
{
screen_free_data(&screen->id);
}
/* ***************** Screen edges & verts ***************** */
ScrEdge *BKE_screen_find_edge(const bScreen *screen, ScrVert *v1, ScrVert *v2)
{
BKE_screen_sort_scrvert(&v1, &v2);
LISTBASE_FOREACH (ScrEdge *, se, &screen->edgebase) {
if (se->v1 == v1 && se->v2 == v2) {
return se;
}
}
return NULL;
}
void BKE_screen_sort_scrvert(ScrVert **v1, ScrVert **v2)
{
if (*v1 > *v2) {
ScrVert *tmp = *v1;
*v1 = *v2;
*v2 = tmp;
}
}
void BKE_screen_remove_double_scrverts(bScreen *screen)
{
LISTBASE_FOREACH (ScrVert *, verg, &screen->vertbase) {
if (verg->newv == NULL) { /* !!! */
ScrVert *v1 = verg->next;
while (v1) {
if (v1->newv == NULL) { /* !?! */
if (v1->vec.x == verg->vec.x && v1->vec.y == verg->vec.y) {
// printf("doublevert\n");
v1->newv = verg;
}
}
v1 = v1->next;
}
}
}
/* replace pointers in edges and faces */
LISTBASE_FOREACH (ScrEdge *, se, &screen->edgebase) {
if (se->v1->newv) {
se->v1 = se->v1->newv;
}
if (se->v2->newv) {
se->v2 = se->v2->newv;
}
/* edges changed: so.... */
BKE_screen_sort_scrvert(&(se->v1), &(se->v2));
}
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->v1->newv) {
area->v1 = area->v1->newv;
}
if (area->v2->newv) {
area->v2 = area->v2->newv;
}
if (area->v3->newv) {
area->v3 = area->v3->newv;
}
if (area->v4->newv) {
area->v4 = area->v4->newv;
}
}
/* remove */
LISTBASE_FOREACH_MUTABLE (ScrVert *, verg, &screen->vertbase) {
if (verg->newv) {
BLI_remlink(&screen->vertbase, verg);
MEM_freeN(verg);
}
}
}
void BKE_screen_remove_double_scredges(bScreen *screen)
{
/* compare */
LISTBASE_FOREACH (ScrEdge *, verg, &screen->edgebase) {
ScrEdge *se = verg->next;
while (se) {
ScrEdge *sn = se->next;
if (verg->v1 == se->v1 && verg->v2 == se->v2) {
BLI_remlink(&screen->edgebase, se);
MEM_freeN(se);
}
se = sn;
}
}
}
void BKE_screen_remove_unused_scredges(bScreen *screen)
{
/* sets flags when edge is used in area */
int a = 0;
LISTBASE_FOREACH_INDEX (ScrArea *, area, &screen->areabase, a) {
ScrEdge *se = BKE_screen_find_edge(screen, area->v1, area->v2);
if (se == NULL) {
printf("error: area %d edge 1 doesn't exist\n", a);
}
else {
se->flag = 1;
}
se = BKE_screen_find_edge(screen, area->v2, area->v3);
if (se == NULL) {
printf("error: area %d edge 2 doesn't exist\n", a);
}
else {
se->flag = 1;
}
se = BKE_screen_find_edge(screen, area->v3, area->v4);
if (se == NULL) {
printf("error: area %d edge 3 doesn't exist\n", a);
}
else {
se->flag = 1;
}
se = BKE_screen_find_edge(screen, area->v4, area->v1);
if (se == NULL) {
printf("error: area %d edge 4 doesn't exist\n", a);
}
else {
se->flag = 1;
}
}
LISTBASE_FOREACH_MUTABLE (ScrEdge *, se, &screen->edgebase) {
if (se->flag == 0) {
BLI_remlink(&screen->edgebase, se);
MEM_freeN(se);
}
else {
se->flag = 0;
}
}
}
void BKE_screen_remove_unused_scrverts(bScreen *screen)
{
/* we assume edges are ok */
LISTBASE_FOREACH (ScrEdge *, se, &screen->edgebase) {
se->v1->flag = 1;
se->v2->flag = 1;
}
LISTBASE_FOREACH_MUTABLE (ScrVert *, sv, &screen->vertbase) {
if (sv->flag == 0) {
BLI_remlink(&screen->vertbase, sv);
MEM_freeN(sv);
}
else {
sv->flag = 0;
}
}
}
/* ***************** Utilities ********************** */
ARegion *BKE_region_find_in_listbase_by_type(const ListBase *regionbase, const int region_type)
{
LISTBASE_FOREACH (ARegion *, region, regionbase) {
if (region->regiontype == region_type) {
return region;
}
}
return NULL;
}
ARegion *BKE_area_find_region_type(const ScrArea *area, int region_type)
{
if (area) {
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (region->regiontype == region_type) {
return region;
}
}
}
return NULL;
}
ARegion *BKE_area_find_region_active_win(ScrArea *area)
{
if (area == NULL) {
return NULL;
}
ARegion *region = BLI_findlink(&area->regionbase, area->region_active_win);
if (region && (region->regiontype == RGN_TYPE_WINDOW)) {
return region;
}
/* fallback to any */
return BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
}
ARegion *BKE_area_find_region_xy(ScrArea *area, const int regiontype, const int xy[2])
{
if (area == NULL) {
return NULL;
}
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (ELEM(regiontype, RGN_TYPE_ANY, region->regiontype)) {
if (BLI_rcti_isect_pt_v(&region->winrct, xy)) {
return region;
}
}
}
return NULL;
}
ARegion *BKE_screen_find_region_xy(bScreen *screen, const int regiontype, const int xy[2])
{
LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) {
if (ELEM(regiontype, RGN_TYPE_ANY, region->regiontype)) {
if (BLI_rcti_isect_pt_v(&region->winrct, xy)) {
return region;
}
}
}
return NULL;
}
ScrArea *BKE_screen_find_area_from_space(struct bScreen *screen, SpaceLink *sl)
{
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (BLI_findindex(&area->spacedata, sl) != -1) {
return area;
}
}
return NULL;
}
ScrArea *BKE_screen_find_big_area(bScreen *screen, const int spacetype, const short min)
{
ScrArea *big = NULL;
int maxsize = 0;
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (ELEM(spacetype, SPACE_TYPE_ANY, area->spacetype)) {
if (min <= area->winx && min <= area->winy) {
int size = area->winx * area->winy;
if (size > maxsize) {
maxsize = size;
big = area;
}
}
}
}
return big;
}
ScrArea *BKE_screen_area_map_find_area_xy(const ScrAreaMap *areamap,
const int spacetype,
const int xy[2])
{
LISTBASE_FOREACH (ScrArea *, area, &areamap->areabase) {
if (BLI_rcti_isect_pt_v(&area->totrct, xy)) {
if (ELEM(spacetype, SPACE_TYPE_ANY, area->spacetype)) {
return area;
}
break;
}
}
return NULL;
}
ScrArea *BKE_screen_find_area_xy(bScreen *screen, const int spacetype, const int xy[2])
{
return BKE_screen_area_map_find_area_xy(AREAMAP_FROM_SCREEN(screen), spacetype, xy);
}
void BKE_screen_view3d_sync(View3D *v3d, struct Scene *scene)
{
if (v3d->scenelock && v3d->localvd == NULL) {
v3d->camera = scene->camera;
if (v3d->camera == NULL) {
LISTBASE_FOREACH (ARegion *, region, &v3d->regionbase) {
if (region->regiontype == RGN_TYPE_WINDOW) {
RegionView3D *rv3d = region->regiondata;
if (rv3d->persp == RV3D_CAMOB) {
rv3d->persp = RV3D_PERSP;
}
}
}
}
}
}
void BKE_screen_view3d_scene_sync(bScreen *screen, Scene *scene)
{
/* are there cameras in the views that are not in the scene? */
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_VIEW3D) {
View3D *v3d = (View3D *)sl;
BKE_screen_view3d_sync(v3d, scene);
}
}
}
}
void BKE_screen_view3d_shading_init(View3DShading *shading)
{
const View3DShading *shading_default = DNA_struct_default_get(View3DShading);
memcpy(shading, shading_default, sizeof(*shading));
}
ARegion *BKE_screen_find_main_region_at_xy(bScreen *screen, const int space_type, const int xy[2])
{
ScrArea *area = BKE_screen_find_area_xy(screen, space_type, xy);
if (!area) {
return NULL;
}
return BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, xy);
}
/* Magic zoom calculation, no idea what it signifies, if you find out, tell me! -zr
*
* Simple, its magic dude! Well, to be honest,
* this gives a natural feeling zooming with multiple keypad presses (ton). */
float BKE_screen_view3d_zoom_to_fac(float camzoom)
{
return powf(((float)M_SQRT2 + camzoom / 50.0f), 2.0f) / 4.0f;
}
float BKE_screen_view3d_zoom_from_fac(float zoomfac)
{
return ((sqrtf(4.0f * zoomfac) - (float)M_SQRT2) * 50.0f);
}
bool BKE_screen_is_fullscreen_area(const bScreen *screen)
{
return ELEM(screen->state, SCREENMAXIMIZED, SCREENFULL);
}
bool BKE_screen_is_used(const bScreen *screen)
{
return (screen->winid != 0);
}
void BKE_screen_header_alignment_reset(bScreen *screen)
{
int alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP;
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) {
if (ELEM(area->spacetype, SPACE_FILE, SPACE_USERPREF, SPACE_OUTLINER, SPACE_PROPERTIES)) {
region->alignment = RGN_ALIGN_TOP;
continue;
}
region->alignment = alignment;
}
if (region->regiontype == RGN_TYPE_FOOTER) {
if (ELEM(area->spacetype, SPACE_FILE, SPACE_USERPREF, SPACE_OUTLINER, SPACE_PROPERTIES)) {
region->alignment = RGN_ALIGN_BOTTOM;
continue;
}
region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_TOP : RGN_ALIGN_BOTTOM;
}
}
}
screen->do_refresh = true;
}
void BKE_screen_view3d_shading_blend_write(BlendWriter *writer, View3DShading *shading)
{
if (shading->prop) {
IDP_BlendWrite(writer, shading->prop);
}
}
void BKE_screen_view3d_shading_blend_read_data(BlendDataReader *reader, View3DShading *shading)
{
if (shading->prop) {
BLO_read_data_address(reader, &shading->prop);
IDP_BlendDataRead(reader, &shading->prop);
}
}
static void write_region(BlendWriter *writer, ARegion *region, int spacetype)
{
BLO_write_struct(writer, ARegion, region);
if (region->regiondata) {
if (region->flag & RGN_FLAG_TEMP_REGIONDATA) {
return;
}
switch (spacetype) {
case SPACE_VIEW3D:
if (region->regiontype == RGN_TYPE_WINDOW) {
RegionView3D *rv3d = region->regiondata;
BLO_write_struct(writer, RegionView3D, rv3d);
if (rv3d->localvd) {
BLO_write_struct(writer, RegionView3D, rv3d->localvd);
}
if (rv3d->clipbb) {
BLO_write_struct(writer, BoundBox, rv3d->clipbb);
}
}
else {
printf("regiondata write missing!\n");
}
break;
default:
printf("regiondata write missing!\n");
}
}
}
static void write_uilist(BlendWriter *writer, uiList *ui_list)
{
BLO_write_struct(writer, uiList, ui_list);
if (ui_list->properties) {
IDP_BlendWrite(writer, ui_list->properties);
}
}
static void write_panel_list(BlendWriter *writer, ListBase *lb)
{
LISTBASE_FOREACH (Panel *, panel, lb) {
BLO_write_struct(writer, Panel, panel);
write_panel_list(writer, &panel->children);
}
}
static void write_area(BlendWriter *writer, ScrArea *area)
{
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
write_region(writer, region, area->spacetype);
write_panel_list(writer, &region->panels);
LISTBASE_FOREACH (PanelCategoryStack *, pc_act, &region->panels_category_active) {
BLO_write_struct(writer, PanelCategoryStack, pc_act);
}
LISTBASE_FOREACH (uiList *, ui_list, &region->ui_lists) {
write_uilist(writer, ui_list);
}
LISTBASE_FOREACH (uiPreview *, ui_preview, &region->ui_previews) {
BLO_write_struct(writer, uiPreview, ui_preview);
}
}
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
LISTBASE_FOREACH (ARegion *, region, &sl->regionbase) {
write_region(writer, region, sl->spacetype);
}
SpaceType *space_type = BKE_spacetype_from_id(sl->spacetype);
if (space_type && space_type->blend_write) {
space_type->blend_write(writer, sl);
}
}
}
void BKE_screen_area_map_blend_write(BlendWriter *writer, ScrAreaMap *area_map)
{
BLO_write_struct_list(writer, ScrVert, &area_map->vertbase);
BLO_write_struct_list(writer, ScrEdge, &area_map->edgebase);
LISTBASE_FOREACH (ScrArea *, area, &area_map->areabase) {
area->butspacetype = area->spacetype; /* Just for compatibility, will be reset below. */
BLO_write_struct(writer, ScrArea, area);
BLO_write_struct(writer, ScrGlobalAreaData, area->global);
write_area(writer, area);
area->butspacetype = SPACE_EMPTY; /* Unset again, was changed above. */
}
}
static void direct_link_panel_list(BlendDataReader *reader, ListBase *lb)
{
BLO_read_list(reader, lb);
LISTBASE_FOREACH (Panel *, panel, lb) {
panel->runtime_flag = 0;
panel->activedata = NULL;
panel->type = NULL;
panel->runtime.custom_data_ptr = NULL;
direct_link_panel_list(reader, &panel->children);
}
}
static void direct_link_region(BlendDataReader *reader, ARegion *region, int spacetype)
{
memset(&region->runtime, 0x0, sizeof(region->runtime));
direct_link_panel_list(reader, &region->panels);
BLO_read_list(reader, &region->panels_category_active);
BLO_read_list(reader, &region->ui_lists);
/* The area's search filter is runtime only, so we need to clear the active flag on read. */
region->flag &= ~RGN_FLAG_SEARCH_FILTER_ACTIVE;
LISTBASE_FOREACH (uiList *, ui_list, &region->ui_lists) {
ui_list->type = NULL;
ui_list->dyn_data = NULL;
BLO_read_data_address(reader, &ui_list->properties);
IDP_BlendDataRead(reader, &ui_list->properties);
}
BLO_read_list(reader, &region->ui_previews);
if (spacetype == SPACE_EMPTY) {
/* unknown space type, don't leak regiondata */
region->regiondata = NULL;
}
else if (region->flag & RGN_FLAG_TEMP_REGIONDATA) {
/* Runtime data, don't use. */
region->regiondata = NULL;
}
else {
BLO_read_data_address(reader, &region->regiondata);
if (region->regiondata) {
if (spacetype == SPACE_VIEW3D) {
RegionView3D *rv3d = region->regiondata;
BLO_read_data_address(reader, &rv3d->localvd);
BLO_read_data_address(reader, &rv3d->clipbb);
rv3d->render_engine = NULL;
rv3d->sms = NULL;
rv3d->smooth_timer = NULL;
rv3d->rflag &= ~(RV3D_NAVIGATING | RV3D_PAINTING);
rv3d->runtime_viewlock = 0;
}
}
}
region->v2d.sms = NULL;
region->v2d.alpha_hor = region->v2d.alpha_vert = 255; /* visible by default */
BLI_listbase_clear(&region->panels_category);
BLI_listbase_clear(&region->handlers);
BLI_listbase_clear(&region->uiblocks);
region->headerstr = NULL;
region->visible = 0;
region->type = NULL;
region->do_draw = 0;
region->gizmo_map = NULL;
region->regiontimer = NULL;
region->draw_buffer = NULL;
memset(&region->drawrct, 0, sizeof(region->drawrct));
}
/* for the saved 2.50 files without regiondata */
void BKE_screen_view3d_do_versions_250(View3D *v3d, ListBase *regions)
{
LISTBASE_FOREACH (ARegion *, region, regions) {
if (region->regiontype == RGN_TYPE_WINDOW && region->regiondata == NULL) {
RegionView3D *rv3d;
rv3d = region->regiondata = MEM_callocN(sizeof(RegionView3D), "region v3d patch");
rv3d->persp = (char)v3d->persp;
rv3d->view = (char)v3d->view;
rv3d->dist = v3d->dist;
copy_v3_v3(rv3d->ofs, v3d->ofs);
copy_qt_qt(rv3d->viewquat, v3d->viewquat);
}
}
/* this was not initialized correct always */
if (v3d->gridsubdiv == 0) {
v3d->gridsubdiv = 10;
}
}
static void direct_link_area(BlendDataReader *reader, ScrArea *area)
{
BLO_read_list(reader, &(area->spacedata));
BLO_read_list(reader, &(area->regionbase));
BLI_listbase_clear(&area->handlers);
area->type = NULL; /* spacetype callbacks */
/* Should always be unset so that rna_Area_type_get works correctly. */
area->butspacetype = SPACE_EMPTY;
area->region_active_win = -1;
area->flag &= ~AREA_FLAG_ACTIVE_TOOL_UPDATE;
BLO_read_data_address(reader, &area->global);
/* if we do not have the spacetype registered we cannot
* free it, so don't allocate any new memory for such spacetypes. */
if (!BKE_spacetype_exists(area->spacetype)) {
/* Hint for versioning code to replace deprecated space types. */
area->butspacetype = area->spacetype;
area->spacetype = SPACE_EMPTY;
}
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
direct_link_region(reader, region, area->spacetype);
}
/* accident can happen when read/save new file with older version */
/* 2.50: we now always add spacedata for info */
if (area->spacedata.first == NULL) {
SpaceInfo *sinfo = MEM_callocN(sizeof(SpaceInfo), "spaceinfo");
area->spacetype = sinfo->spacetype = SPACE_INFO;
BLI_addtail(&area->spacedata, sinfo);
}
/* add local view3d too */
else if (area->spacetype == SPACE_VIEW3D) {
BKE_screen_view3d_do_versions_250(area->spacedata.first, &area->regionbase);
}
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
BLO_read_list(reader, &(sl->regionbase));
/* if we do not have the spacetype registered we cannot
* free it, so don't allocate any new memory for such spacetypes. */
if (!BKE_spacetype_exists(sl->spacetype)) {
sl->spacetype = SPACE_EMPTY;
}
LISTBASE_FOREACH (ARegion *, region, &sl->regionbase) {
direct_link_region(reader, region, sl->spacetype);
}
SpaceType *space_type = BKE_spacetype_from_id(sl->spacetype);
if (space_type && space_type->blend_read_data) {
space_type->blend_read_data(reader, sl);
}
}
BLI_listbase_clear(&area->actionzones);
BLO_read_data_address(reader, &area->v1);
BLO_read_data_address(reader, &area->v2);
BLO_read_data_address(reader, &area->v3);
BLO_read_data_address(reader, &area->v4);
}
bool BKE_screen_area_map_blend_read_data(BlendDataReader *reader, ScrAreaMap *area_map)
{
BLO_read_list(reader, &area_map->vertbase);
BLO_read_list(reader, &area_map->edgebase);
BLO_read_list(reader, &area_map->areabase);
LISTBASE_FOREACH (ScrArea *, area, &area_map->areabase) {
direct_link_area(reader, area);
}
/* edges */
LISTBASE_FOREACH (ScrEdge *, se, &area_map->edgebase) {
BLO_read_data_address(reader, &se->v1);
BLO_read_data_address(reader, &se->v2);
BKE_screen_sort_scrvert(&se->v1, &se->v2);
if (se->v1 == NULL) {
BLI_remlink(&area_map->edgebase, se);
return false;
}
}
return true;
}
void BKE_screen_area_blend_read_lib(BlendLibReader *reader, ID *parent_id, ScrArea *area)
{
BLO_read_id_address(reader, parent_id->lib, &area->full);
memset(&area->runtime, 0x0, sizeof(area->runtime));
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
SpaceType *space_type = BKE_spacetype_from_id(sl->spacetype);
if (space_type && space_type->blend_read_lib) {
space_type->blend_read_lib(reader, parent_id, sl);
}
}
}