Blender  V2.59
drawanimviz.c
Go to the documentation of this file.
00001 /*
00002  * $Id: drawanimviz.c 37671 2011-06-20 17:50:59Z campbellbarton $
00003  *
00004  * ***** BEGIN GPL LICENSE BLOCK *****
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version. 
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software Foundation,
00018  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  *
00020  * The Original Code is Copyright (C) 2009 by the Blender Foundation.
00021  * All rights reserved.
00022  *
00023  * The Original Code is: all of this file.
00024  *
00025  * Contributor(s): Joshua Leung
00026  *
00027  * ***** END GPL LICENSE BLOCK *****
00028  */
00029 
00035 #include <stdlib.h>
00036 #include <string.h>
00037 #include <math.h>
00038 
00039 #include "BLO_sys_types.h"
00040 
00041 #include "DNA_anim_types.h"
00042 #include "DNA_armature_types.h"
00043 #include "DNA_scene_types.h"
00044 #include "DNA_screen_types.h"
00045 #include "DNA_view3d_types.h"
00046 #include "DNA_object_types.h"
00047 
00048 #include "BLI_blenlib.h"
00049 #include "BLI_math.h"
00050 #include "BLI_dlrbTree.h"
00051 
00052 #include "BKE_animsys.h"
00053 #include "BKE_action.h"
00054 
00055 #include "BIF_gl.h"
00056 
00057 #include "ED_armature.h"
00058 #include "ED_keyframes_draw.h"
00059 
00060 #include "BLF_api.h"
00061 
00062 #include "UI_resources.h"
00063 
00064 #include "view3d_intern.h"
00065 
00066 /* ************************************ Motion Paths ************************************* */
00067 
00068 // TODO: 
00069 //      - options to draw paths with lines
00070 //      - include support for editing the path verts
00071 
00072 /* Set up drawing environment for drawing motion paths */
00073 void draw_motion_paths_init(View3D *v3d, ARegion *ar) 
00074 {
00075         RegionView3D *rv3d= ar->regiondata;
00076         
00077         if (v3d->zbuf) glDisable(GL_DEPTH_TEST);
00078         
00079         glPushMatrix();
00080         glLoadMatrixf(rv3d->viewmat);
00081 }
00082 
00083 /* Draw the given motion path for an Object or a Bone 
00084  *      - assumes that the viewport has already been initialised properly
00085  *              i.e. draw_motion_paths_init() has been called
00086  */
00087 void draw_motion_path_instance(Scene *scene, 
00088                         Object *ob, bPoseChannel *pchan, bAnimVizSettings *avs, bMotionPath *mpath)
00089 {
00090         //RegionView3D *rv3d= ar->regiondata;
00091         bMotionPathVert *mpv, *mpv_start;
00092         int i, stepsize= avs->path_step;
00093         int sfra, efra, len;
00094         
00095         
00096         /* get frame ranges */
00097         if (avs->path_type == MOTIONPATH_TYPE_ACFRA) {
00098                 int sind;
00099                 
00100                 /* With "Around Current", we only choose frames from around 
00101                  * the current frame to draw. However, this range is still 
00102                  * restricted by the limits of the original path.
00103                  */
00104                 sfra= CFRA - avs->path_bc;
00105                 efra= CFRA + avs->path_ac;
00106                 if (sfra < mpath->start_frame) sfra= mpath->start_frame;
00107                 if (efra > mpath->end_frame) efra= mpath->end_frame;
00108                 
00109                 len= efra - sfra;
00110                 
00111                 sind= sfra - mpath->start_frame;
00112                 mpv_start= (mpath->points + sind);
00113         }
00114         else {
00115                 sfra= mpath->start_frame;
00116                 efra = sfra + mpath->length;
00117                 len = mpath->length;
00118                 mpv_start= mpath->points;
00119         }
00120 
00121         if(len <= 0) {
00122                 return;
00123         }
00124 
00125         /* draw curve-line of path */
00126         glShadeModel(GL_SMOOTH);
00127         
00128         glBegin(GL_LINE_STRIP);                                 
00129         for (i=0, mpv=mpv_start; i < len; i++, mpv++) {
00130                 short sel= (pchan) ? (pchan->bone->flag & BONE_SELECTED) : (ob->flag & SELECT);
00131                 float intensity; /* how faint */
00132                 
00133                 /* set color
00134                  *      - more intense for active/selected bones, less intense for unselected bones
00135                  *      - black for before current frame, green for current frame, blue for after current frame
00136                  *      - intensity decreases as distance from current frame increases
00137                  */
00138                 #define SET_INTENSITY(A, B, C, min, max) (((1.0f - ((C - B) / (C - A))) * (max-min)) + min) 
00139                 if ((sfra+i) < CFRA) {
00140                         /* black - before cfra */
00141                         if (sel) {
00142                                 // intensity= 0.5f;
00143                                 intensity = SET_INTENSITY(sfra, i, CFRA, 0.25f, 0.75f);
00144                         }
00145                         else {
00146                                 //intensity= 0.8f;
00147                                 intensity = SET_INTENSITY(sfra, i, CFRA, 0.68f, 0.92f);
00148                         }
00149                         UI_ThemeColorBlend(TH_WIRE, TH_BACK, intensity);
00150                 }
00151                 else if ((sfra+i) > CFRA) {
00152                         /* blue - after cfra */
00153                         if (sel) {
00154                                 //intensity = 0.5f;
00155                                 intensity = SET_INTENSITY(CFRA, i, efra, 0.25f, 0.75f);
00156                         }
00157                         else {
00158                                 //intensity = 0.8f;
00159                                 intensity = SET_INTENSITY(CFRA, i, efra, 0.68f, 0.92f);
00160                         }
00161                         UI_ThemeColorBlend(TH_BONE_POSE, TH_BACK, intensity);
00162                 }
00163                 else {
00164                         /* green - on cfra */
00165                         if (sel) {
00166                                 intensity= 0.5f;
00167                         }
00168                         else {
00169                                 intensity= 0.99f;
00170                         }
00171                         UI_ThemeColorBlendShade(TH_CFRAME, TH_BACK, intensity, 10);
00172                 }       
00173                 
00174                 /* draw a vertex with this color */ 
00175                 glVertex3fv(mpv->co);
00176         }
00177         
00178         glEnd();
00179         glShadeModel(GL_FLAT);
00180         
00181         glPointSize(1.0);
00182         
00183         /* draw little black point at each frame
00184          * NOTE: this is not really visible/noticable
00185          */
00186         glBegin(GL_POINTS);
00187         for (i=0, mpv=mpv_start; i < len; i++, mpv++) 
00188                 glVertex3fv(mpv->co);
00189         glEnd();
00190         
00191         /* Draw little white dots at each framestep value */
00192         UI_ThemeColor(TH_TEXT_HI);
00193         glBegin(GL_POINTS);
00194         for (i=0, mpv=mpv_start; i < len; i+=stepsize, mpv+=stepsize) 
00195                 glVertex3fv(mpv->co);
00196         glEnd();
00197         
00198         /* Draw big green dot where the current frame is */
00199         // NOTE: only do this when drawing keyframes for now... 
00200         if (avs->path_viewflag & MOTIONPATH_VIEW_KFRAS) {
00201                 UI_ThemeColor(TH_CFRAME);
00202                 glPointSize(6.0f);
00203                 
00204                 glBegin(GL_POINTS);
00205                         mpv = mpv_start + (CFRA - sfra);
00206                         glVertex3fv(mpv->co);
00207                 glEnd();
00208                 
00209                 glPointSize(1.0f);
00210                 UI_ThemeColor(TH_TEXT_HI);
00211         }
00212         
00213         // XXX, this isnt up to date but probably should be kept so.
00214         invert_m4_m4(ob->imat, ob->obmat);
00215         
00216         /* Draw frame numbers at each framestep value */
00217         if (avs->path_viewflag & MOTIONPATH_VIEW_FNUMS) {
00218                 unsigned char col[4];
00219                 UI_GetThemeColor3ubv(TH_TEXT_HI, col);
00220                 col[3]= 255;
00221 
00222                 for (i=0, mpv=mpv_start; i < len; i+=stepsize, mpv+=stepsize) {
00223                         char str[32];
00224                         float co[3];
00225                         
00226                         /* only draw framenum if several consecutive highlighted points don't occur on same point */
00227                         if (i == 0) {
00228                                 sprintf(str, "%d", (i+sfra));
00229                                 mul_v3_m4v3(co, ob->imat, mpv->co);
00230                                 view3d_cached_text_draw_add(co, str, 0, V3D_CACHE_TEXT_WORLDSPACE|V3D_CACHE_TEXT_ASCII, col);
00231                         }
00232                         else if ((i > stepsize) && (i < len-stepsize)) { 
00233                                 bMotionPathVert *mpvP = (mpv - stepsize);
00234                                 bMotionPathVert *mpvN = (mpv + stepsize);
00235                                 
00236                                 if ((equals_v3v3(mpv->co, mpvP->co)==0) || (equals_v3v3(mpv->co, mpvN->co)==0)) {
00237                                         sprintf(str, "%d", (sfra+i));
00238                                         mul_v3_m4v3(co, ob->imat, mpv->co);
00239                                         view3d_cached_text_draw_add(co, str, 0, V3D_CACHE_TEXT_WORLDSPACE|V3D_CACHE_TEXT_ASCII, col);
00240                                 }
00241                         }
00242                 }
00243         }
00244         
00245         /* Keyframes - dots and numbers */
00246         if (avs->path_viewflag & MOTIONPATH_VIEW_KFRAS) {
00247                 unsigned char col[4];
00248 
00249                 AnimData *adt= BKE_animdata_from_id(&ob->id);
00250                 DLRBT_Tree keys;
00251                 
00252                 /* build list of all keyframes in active action for object or pchan */
00253                 BLI_dlrbTree_init(&keys);
00254                 
00255                 if (adt) {
00256                         /* it is assumed that keyframes for bones are all grouped in a single group
00257                          * unless an option is set to always use the whole action
00258                          */
00259                         if ((pchan) && (avs->path_viewflag & MOTIONPATH_VIEW_KFACT)==0) {
00260                                 bActionGroup *agrp= action_groups_find_named(adt->action, pchan->name);
00261                                 
00262                                 if (agrp) {
00263                                         agroup_to_keylist(adt, agrp, &keys, NULL);
00264                                         BLI_dlrbTree_linkedlist_sync(&keys);
00265                                 }
00266                         }
00267                         else {
00268                                 action_to_keylist(adt, adt->action, &keys, NULL);
00269                                 BLI_dlrbTree_linkedlist_sync(&keys);
00270                         }
00271                 }
00272                 
00273                 /* Draw slightly-larger yellow dots at each keyframe */
00274                 UI_GetThemeColor3ubv(TH_VERTEX_SELECT, col);
00275                 col[3]= 255;
00276 
00277                 glPointSize(4.0f); // XXX perhaps a bit too big
00278                 glColor3ubv(col);
00279                 
00280                 glBegin(GL_POINTS);
00281                 for (i=0, mpv=mpv_start; i < len; i++, mpv++) {
00282                         float mframe= (float)(sfra + i);
00283                         
00284                         if (BLI_dlrbTree_search_exact(&keys, compare_ak_cfraPtr, &mframe))
00285                                 glVertex3fv(mpv->co);
00286                 }
00287                 glEnd();
00288                 
00289                 glPointSize(1.0f);
00290                 
00291                 /* Draw frame numbers of keyframes  */
00292                 if (avs->path_viewflag & MOTIONPATH_VIEW_KFNOS) {
00293                         float co[3];
00294                         for (i=0, mpv=mpv_start; i < len; i++, mpv++) {
00295                                 float mframe= (float)(sfra + i);
00296                                 
00297                                 if (BLI_dlrbTree_search_exact(&keys, compare_ak_cfraPtr, &mframe)) {
00298                                         char str[32];
00299                                         
00300                                         sprintf(str, "%d", (sfra+i));
00301                                         mul_v3_m4v3(co, ob->imat, mpv->co);
00302                                         view3d_cached_text_draw_add(co, str, 0, V3D_CACHE_TEXT_WORLDSPACE|V3D_CACHE_TEXT_ASCII, col);
00303                                 }
00304                         }
00305                 }
00306                 
00307                 BLI_dlrbTree_free(&keys);
00308         }
00309 }
00310 
00311 /* Clean up drawing environment after drawing motion paths */
00312 void draw_motion_paths_cleanup(View3D *v3d)
00313 {
00314         if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
00315         glPopMatrix();
00316 }
00317 
00318 #if 0 // XXX temp file guards 
00319 
00320 /* ***************************** Onion Skinning (Ghosts) ******************************** */
00321 
00322 #if 0 // XXX only for bones
00323 /* helper function for ghost drawing - sets/removes flags for temporarily 
00324  * hiding unselected bones while drawing ghosts
00325  */
00326 static void ghost_poses_tag_unselected(Object *ob, short unset)
00327 {
00328         bArmature *arm= ob->data;
00329         bPose *pose= ob->pose;
00330         bPoseChannel *pchan;
00331         
00332         /* don't do anything if no hiding any bones */
00333         if ((arm->flag & ARM_GHOST_ONLYSEL)==0)
00334                 return;
00335                 
00336         /* loop over all pchans, adding/removing tags as appropriate */
00337         for (pchan= pose->chanbase.first; pchan; pchan= pchan->next) {
00338                 if ((pchan->bone) && (arm->layer & pchan->bone->layer)) {
00339                         if (unset) {
00340                                 /* remove tags from all pchans if cleaning up */
00341                                 pchan->bone->flag &= ~BONE_HIDDEN_PG;
00342                         }
00343                         else {
00344                                 /* set tags on unselected pchans only */
00345                                 if ((pchan->bone->flag & BONE_SELECTED)==0)
00346                                         pchan->bone->flag |= BONE_HIDDEN_PG;
00347                         }
00348                 }
00349         }
00350 }
00351 #endif // XXX only for bones
00352 
00353 /* draw ghosts that occur within a frame range 
00354  *      note: object should be in posemode 
00355  */
00356 static void draw_ghost_poses_range(Scene *scene, View3D *v3d, ARegion *ar, Base *base)
00357 {
00358         Object *ob= base->object;
00359         AnimData *adt= BKE_animdata_from_id(&ob->id);
00360         bArmature *arm= ob->data;
00361         bPose *posen, *poseo;
00362         float start, end, stepsize, range, colfac;
00363         int cfrao, flago, ipoflago;
00364         
00365         start = (float)arm->ghostsf;
00366         end = (float)arm->ghostef;
00367         if (end <= start)
00368                 return;
00369         
00370         stepsize= (float)(arm->ghostsize);
00371         range= (float)(end - start);
00372         
00373         /* store values */
00374         ob->mode &= ~OB_MODE_POSE;
00375         cfrao= CFRA;
00376         flago= arm->flag;
00377         arm->flag &= ~(ARM_DRAWNAMES|ARM_DRAWAXES);
00378         ipoflago= ob->ipoflag; 
00379         ob->ipoflag |= OB_DISABLE_PATH;
00380         
00381         /* copy the pose */
00382         poseo= ob->pose;
00383         copy_pose(&posen, ob->pose, 1);
00384         ob->pose= posen;
00385         armature_rebuild_pose(ob, ob->data);    /* child pointers for IK */
00386         ghost_poses_tag_unselected(ob, 0);              /* hide unselected bones if need be */
00387         
00388         glEnable(GL_BLEND);
00389         if (v3d->zbuf) glDisable(GL_DEPTH_TEST);
00390         
00391         /* draw from first frame of range to last */
00392         for (CFRA= (int)start; CFRA < end; CFRA += (int)stepsize) {
00393                 colfac = (end - (float)CFRA) / range;
00394                 UI_ThemeColorShadeAlpha(TH_WIRE, 0, -128-(int)(120.0*sqrt(colfac)));
00395                 
00396                 BKE_animsys_evaluate_animdata(&ob->id, adt, (float)CFRA, ADT_RECALC_ALL);
00397                 where_is_pose(scene, ob);
00398                 draw_pose_bones(scene, v3d, ar, base, OB_WIRE);
00399         }
00400         glDisable(GL_BLEND);
00401         if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
00402 
00403         ghost_poses_tag_unselected(ob, 1);              /* unhide unselected bones if need be */
00404         free_pose(posen);
00405         
00406         /* restore */
00407         CFRA= cfrao;
00408         ob->pose= poseo;
00409         arm->flag= flago;
00410         armature_rebuild_pose(ob, ob->data);
00411         ob->mode |= OB_MODE_POSE;
00412         ob->ipoflag= ipoflago; 
00413 }
00414 
00415 /* draw ghosts on keyframes in action within range 
00416  *      - object should be in posemode 
00417  */
00418 static void draw_ghost_poses_keys(Scene *scene, View3D *v3d, ARegion *ar, Base *base)
00419 {
00420         Object *ob= base->object;
00421         AnimData *adt= BKE_animdata_from_id(&ob->id);
00422         bAction *act= (adt) ? adt->action : NULL;
00423         bArmature *arm= ob->data;
00424         bPose *posen, *poseo;
00425         DLRBT_Tree keys;
00426         ActKeyColumn *ak, *akn;
00427         float start, end, range, colfac, i;
00428         int cfrao, flago;
00429         
00430         start = (float)arm->ghostsf;
00431         end = (float)arm->ghostef;
00432         if (end <= start)
00433                 return;
00434         
00435         /* get keyframes - then clip to only within range */
00436         BLI_dlrbTree_init(&keys);
00437         action_to_keylist(adt, act, &keys, NULL);
00438         BLI_dlrbTree_linkedlist_sync(&keys);
00439         
00440         range= 0;
00441         for (ak= keys.first; ak; ak= akn) {
00442                 akn= ak->next;
00443                 
00444                 if ((ak->cfra < start) || (ak->cfra > end))
00445                         BLI_freelinkN((ListBase *)&keys, ak);
00446                 else
00447                         range++;
00448         }
00449         if (range == 0) return;
00450         
00451         /* store values */
00452         ob->mode &= ~OB_MODE_POSE;
00453         cfrao= CFRA;
00454         flago= arm->flag;
00455         arm->flag &= ~(ARM_DRAWNAMES|ARM_DRAWAXES);
00456         ob->ipoflag |= OB_DISABLE_PATH;
00457         
00458         /* copy the pose */
00459         poseo= ob->pose;
00460         copy_pose(&posen, ob->pose, 1);
00461         ob->pose= posen;
00462         armature_rebuild_pose(ob, ob->data);    /* child pointers for IK */
00463         ghost_poses_tag_unselected(ob, 0);              /* hide unselected bones if need be */
00464         
00465         glEnable(GL_BLEND);
00466         if (v3d->zbuf) glDisable(GL_DEPTH_TEST);
00467         
00468         /* draw from first frame of range to last */
00469         for (ak=keys.first, i=0; ak; ak=ak->next, i++) {
00470                 colfac = i/range;
00471                 UI_ThemeColorShadeAlpha(TH_WIRE, 0, -128-(int)(120.0*sqrt(colfac)));
00472                 
00473                 CFRA= (int)ak->cfra;
00474                 
00475                 BKE_animsys_evaluate_animdata(&ob->id, adt, (float)CFRA, ADT_RECALC_ALL);
00476                 where_is_pose(scene, ob);
00477                 draw_pose_bones(scene, v3d, ar, base, OB_WIRE);
00478         }
00479         glDisable(GL_BLEND);
00480         if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
00481 
00482         ghost_poses_tag_unselected(ob, 1);              /* unhide unselected bones if need be */
00483         BLI_dlrbTree_free(&keys);
00484         free_pose(posen);
00485         
00486         /* restore */
00487         CFRA= cfrao;
00488         ob->pose= poseo;
00489         arm->flag= flago;
00490         armature_rebuild_pose(ob, ob->data);
00491         ob->mode |= OB_MODE_POSE;
00492 }
00493 
00494 /* draw ghosts around current frame
00495  *      - object is supposed to be armature in posemode 
00496  */
00497 static void draw_ghost_poses(Scene *scene, View3D *v3d, ARegion *ar, Base *base)
00498 {
00499         Object *ob= base->object;
00500         AnimData *adt= BKE_animdata_from_id(&ob->id);
00501         bArmature *arm= ob->data;
00502         bPose *posen, *poseo;
00503         float cur, start, end, stepsize, range, colfac, actframe, ctime;
00504         int cfrao, flago;
00505         
00506         /* pre conditions, get an action with sufficient frames */
00507         if ELEM(NULL, adt, adt->action)
00508                 return;
00509 
00510         calc_action_range(adt->action, &start, &end, 0);
00511         if (start == end)
00512                 return;
00513 
00514         stepsize= (float)(arm->ghostsize);
00515         range= (float)(arm->ghostep)*stepsize + 0.5f;   /* plus half to make the for loop end correct */
00516         
00517         /* store values */
00518         ob->mode &= ~OB_MODE_POSE;
00519         cfrao= CFRA;
00520         actframe= BKE_nla_tweakedit_remap(adt, (float)CFRA, 0);
00521         flago= arm->flag;
00522         arm->flag &= ~(ARM_DRAWNAMES|ARM_DRAWAXES);
00523         
00524         /* copy the pose */
00525         poseo= ob->pose;
00526         copy_pose(&posen, ob->pose, 1);
00527         ob->pose= posen;
00528         armature_rebuild_pose(ob, ob->data);    /* child pointers for IK */
00529         ghost_poses_tag_unselected(ob, 0);              /* hide unselected bones if need be */
00530         
00531         glEnable(GL_BLEND);
00532         if (v3d->zbuf) glDisable(GL_DEPTH_TEST);
00533         
00534         /* draw from darkest blend to lowest */
00535         for(cur= stepsize; cur<range; cur+=stepsize) {
00536                 ctime= cur - (float)fmod(cfrao, stepsize);      /* ensures consistent stepping */
00537                 colfac= ctime/range;
00538                 UI_ThemeColorShadeAlpha(TH_WIRE, 0, -128-(int)(120.0*sqrt(colfac)));
00539                 
00540                 /* only within action range */
00541                 if (actframe+ctime >= start && actframe+ctime <= end) {
00542                         CFRA= (int)BKE_nla_tweakedit_remap(adt, actframe+ctime, NLATIME_CONVERT_MAP);
00543                         
00544                         if (CFRA != cfrao) {
00545                                 BKE_animsys_evaluate_animdata(&ob->id, adt, (float)CFRA, ADT_RECALC_ALL);
00546                                 where_is_pose(scene, ob);
00547                                 draw_pose_bones(scene, v3d, ar, base, OB_WIRE);
00548                         }
00549                 }
00550                 
00551                 ctime= cur + (float)fmod((float)cfrao, stepsize) - stepsize+1.0f;       /* ensures consistent stepping */
00552                 colfac= ctime/range;
00553                 UI_ThemeColorShadeAlpha(TH_WIRE, 0, -128-(int)(120.0*sqrt(colfac)));
00554                 
00555                 /* only within action range */
00556                 if ((actframe-ctime >= start) && (actframe-ctime <= end)) {
00557                         CFRA= (int)BKE_nla_tweakedit_remap(adt, actframe-ctime, NLATIME_CONVERT_MAP);
00558                         
00559                         if (CFRA != cfrao) {
00560                                 BKE_animsys_evaluate_animdata(&ob->id, adt, (float)CFRA, ADT_RECALC_ALL);
00561                                 where_is_pose(scene, ob);
00562                                 draw_pose_bones(scene, v3d, ar, base, OB_WIRE);
00563                         }
00564                 }
00565         }
00566         glDisable(GL_BLEND);
00567         if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
00568 
00569         ghost_poses_tag_unselected(ob, 1);              /* unhide unselected bones if need be */
00570         free_pose(posen);
00571         
00572         /* restore */
00573         CFRA= cfrao;
00574         ob->pose= poseo;
00575         arm->flag= flago;
00576         armature_rebuild_pose(ob, ob->data);
00577         ob->mode |= OB_MODE_POSE;
00578 }
00579 
00580 
00581 
00582 #endif // XXX temp file guards