source: GTP/trunk/Lib/Vis/Preprocessing/src/ViewCell.h @ 1867

Revision 1867, 20.2 KB checked in by bittner, 18 years ago (diff)

merge, global lines, rss sampling updates

Line 
1#ifndef _ViewCell_H__
2#define _ViewCell_H__
3
4#include "Mesh.h"
5#include "Containers.h"
6#include "Ray.h"
7#include "Statistics.h"
8#include "Material.h"
9#include "gzstream.h"
10
11
12namespace GtpVisibilityPreprocessor {
13
14struct Triangle3;
15
16class BspInterior;
17class BspPvs;
18class BspLeaf;
19class VspLeaf;
20class KdLeaf;
21class ViewCellInterior;
22class MergeCandidate;
23class ViewCellsManager;
24class ViewCellLeaf;
25
26
27
28/** Statistics for a view cell partition.
29*/
30
31class ViewCellsStatistics: public StatisticsBase
32{
33public:
34
35        /// number of view cells
36        int viewCells;
37        /// cost of the PVS
38        float pvsCost;
39        /// largest PVS of all view cells
40        float maxPvs;
41        /// smallest PVS of all view cells
42        float minPvs;
43        /// view cells with empty PVS
44        int emptyPvs;
45        /// number of leaves covering the view space
46        int leaves;
47        /// largest number of leaves covered by one view cell
48        int maxLeaves;
49        /// number of invalid view cells
50        int invalid;
51
52    // Constructor
53        ViewCellsStatistics()
54        {
55                Reset();
56        }
57
58        double AvgLeaves() const {return (double)leaves / (double)viewCells;};
59        double AvgPvs() const {return (double)pvsCost / (double)viewCells;};
60
61        void Reset()
62        {
63                viewCells = 0;
64                pvsCost = 0;
65                maxPvs = 0;
66
67                minPvs = 999999;
68                emptyPvs = 0;
69                leaves = 0;
70                maxLeaves = 0;
71                invalid = 0;
72        }
73
74        void Print(ostream &app) const;
75
76        friend ostream &operator<<(ostream &s, const ViewCellsStatistics &stat)
77        {
78                stat.Print(s);
79                return s;
80        }
81};
82
83
84class ViewCellsTreeStats
85{
86public:
87        int mPass;
88   
89        int mNumViewCells;
90               
91        float mRenderCostDecrease;
92
93    float mTotalRenderCost;
94   
95        float mCurrentPvsCost;
96                                                       
97        float mExpectedCost;
98   
99        float mAvgRenderCost;
100       
101        float mDeviation;
102                       
103        float mTotalPvsCost;
104       
105        int mEntriesInPvs;
106   
107        float mMemoryCost;
108       
109        int mPvsSizeDecr;
110
111 
112        float mVolume;
113
114
115        void Reset()
116        {
117                mPass = 0;
118                mNumViewCells = 0;
119                mRenderCostDecrease = 0;
120                mTotalRenderCost = 0;
121                mCurrentPvsCost = 0;
122                mExpectedCost = 0;
123                mAvgRenderCost = 0;
124                mDeviation = 0;
125                mTotalPvsCost = 0;
126                mEntriesInPvs = 0;
127                mMemoryCost = 0;
128                mPvsSizeDecr = 0;
129                mVolume = 0;
130        }
131
132
133        void Print(ostream &app) const;
134
135        friend ostream &operator<<(ostream &s, const ViewCellsTreeStats &stat)
136        {
137                stat.Print(s);
138                return s;
139        }
140};
141
142
143/**
144        A view cell. View cells are regions in space. The visibility informations,
145        i.e., the primitives seen by the view cell are stored in a PVs.
146        A view cell can be represented in many different ways, e.g.,
147        a mesh representation.
148*/
149class ViewCell: public MeshInstance
150{
151        friend class ViewCellsTree;
152        friend class ViewCellsManager;
153
154public:
155        ViewCell();
156
157        /** Constructor taking a mesh representing the shape of the viewcell.
158        */
159        ViewCell(Mesh *mesh);
160
161        /** Default destructor.
162        */
163        virtual ~ViewCell();
164        /** Returns Pvs.
165        */
166        const ObjectPvs &GetPvs() const;
167        /** Returns pvs.
168        */
169        ObjectPvs &GetPvs();
170        /** Completely substitutes the pvs.
171        */
172        void SetPvs(const ObjectPvs &pvs);
173        /** Type of view cells.
174        */
175        int Type() const;
176        /** Adds a passing ray to the passing ray container.
177        */
178        void AddPassingRay(const Ray &ray, const int contributions);
179        /** Returns volume of the view cell.
180        */
181        float GetVolume() const;
182        /** Returns area of the view cell.
183        */
184        float GetArea() const;
185        /** Sets the volume of the view cell.
186        */
187        void SetVolume(float volume);
188        /** Sets the area of the view cell.
189        */
190        void SetArea(float area);
191        /** if this view cell is the root of a view cell hierarchy
192        */
193        bool IsRoot() const;
194        /** Returns parent view cell.
195        */
196        ViewCellInterior *GetParent() const;
197        /** Sets parent of this view cell.
198        */
199        void SetParent(ViewCellInterior *parent);
200        /** Sets the mesh for this view cell.
201        */
202        void SetMesh(Mesh *mesh);
203
204        /** Sets this view cell to be a valid view cell according to some criteria.
205        */
206        void SetValid(const bool valid);
207        /** Returns true if this view cell is considered to be valid according to
208                some criteria.
209        */
210        bool GetValid() const;
211
212        /** Returns estimated render cost of this view cell.
213        */
214        float GetRenderCost() const;
215
216        /** set color for visiualizations.
217        */
218        void SetColor(const RgbColor &color);
219
220        /** get color for visualuzations.
221        */
222    RgbColor GetColor() const;
223
224        /** Adds a sample to the pvs.
225                @param sample the sample to be added
226                @param pdf a continuos measure of visibility
227                @param contribution returns the contribution of this sample to the pvs
228        */
229        bool AddPvsSample(Intersectable *sample, const float pdf, float &contribution);
230
231        /** if this is a view cell correspending to a leaf in a hierarchy.
232        */
233        virtual bool IsLeaf() const = 0;
234
235        static bool SmallerPvs(const ViewCell *a, const ViewCell *b)
236        {
237                // HACK: take scalar value because pvs may not have been stored properly
238#if 1
239                return a->mPvsCost < b->mPvsCost;
240#else
241                return a->GetPvs().EvalPvsCost() < b->GetPvs().EvalPvsCost();
242#endif
243        }
244
245  static bool GreaterOrEqualPvs(const ViewCell *a, const ViewCell *b) {
246        return !SmallerPvs(a, b);
247  }
248
249        static bool SmallerRenderCost(const ViewCell *a, const ViewCell *b)
250        {
251                return a->GetRenderCost() < b->GetRenderCost();
252        }
253
254        static bool LargerRenderCost(const ViewCell *a, const ViewCell *b)
255        {
256                return a->GetRenderCost() > b->GetRenderCost();
257        }
258
259        /** Sets merge cost used for merging this view cell from other cells.
260                @hack The function is available for leaves also to have a common interface,
261                but it should be less than zero for leaves.
262                */
263        void SetMergeCost(const float mergeCost);
264
265        /** Returns merge cost needed to merge this leaf from other cells.
266                @hack The function is available for leaves also to have a common interface,
267                but it should be less than zero for leaves.
268        */
269        float GetMergeCost() const;
270
271  void UpdatePvsCost() {
272        mPvsCost = GetPvs().EvalPvsCost();
273  }
274
275  void SetPvsCost(const float c) {
276        mPvsCost = c;
277  }
278
279  float GetPvsCost() const {
280        return mPvsCost;
281  }
282
283        //////////
284        //-- mailing stuff
285
286  //    static void NewMail(const int reserve = 1)
287  //    {
288  //            sMailId += sReservedMailboxes;
289  //            sReservedMailboxes = reserve;
290  //    }
291
292  //    void Mail() { mMailbox = sMailId; }
293  //    bool Mailed() const { return mMailbox == sMailId; }
294
295  //    void Mail(const int mailbox) { mMailbox = sMailId + mailbox; }
296  //    bool Mailed(const int mailbox) const { return mMailbox == sMailId + mailbox; }
297
298  //    int IncMail() { return ++ mMailbox - sMailId; }
299
300
301        // last mail id -> warning not thread safe!
302        // both mailId and mailbox should be unique for each thread!!!
303
304  //static int sMailId;
305  //static int sReservedMailboxes;
306       
307  int GetFilteredPvsSize() const {
308        return mFilteredPvsSize;
309  }
310
311  void SetFilteredPvsSize(const int s) {
312        mFilteredPvsSize = s;
313  }
314
315protected:
316
317        /// parent view cell in the view cell hierarchy
318        ViewCellInterior *mParent;
319        /// the potentially visible objects
320        ObjectPvs mPvs;
321        /// the volume of this view cell
322        float mVolume;
323        /// the area of this view cell
324        float mArea;
325        /// the cost that were paid for merging this view cells from two others.
326        float mMergeCost;
327        /// if the view cell is valid view space
328        bool mValid;
329        /// color used for consistent visualization
330        RgbColor mColor;
331        /// store pvs size, used for evaluation purpose when pvss are stored only in the leaves
332        float mPvsCost;
333        /** stores number of entries in pvs
334            this variable has the same value as mPvsSize for object pvs,
335                but usually not for kd cell based pvs
336        */
337        int mEntriesInPvs;
338        /** if the pvs size scalar (+ entries into pvs)
339                is up to date and corresponding to the real pvs size
340        */
341        bool mPvsSizeValid;
342
343  /// Filter cost of the pvs
344  int mFilteredPvsSize;
345
346};
347
348
349class ViewCellInterior: public ViewCell
350{
351        friend class ViewCellsManager;
352
353public:
354        ViewCellInterior();
355        ~ViewCellInterior();
356
357        ViewCellInterior(Mesh *mesh);
358       
359        /** Sets pointer from parent to child and vice versa.
360        */
361        void SetupChildLink(ViewCell *l);
362        void ReplaceChildLink(ViewCell *prev, ViewCell *cur);
363
364        void RemoveChildLink(ViewCell *l);
365        bool IsLeaf() const;
366
367        void SetCost(const float c) {
368                mCost = c;
369        }
370       
371        float GetCost() const {
372                return mCost;
373        }
374 
375  ViewCellContainer mChildren;
376
377protected:
378  /** overall cost resulting from the merge */
379  float mCost;
380};
381
382
383/**
384        Leaf of the view cell.
385*/
386class ViewCellLeaf: public ViewCell
387{
388public:
389        ViewCellLeaf()  {  mActiveViewCell = this; }
390        ViewCellLeaf(Mesh *mesh):
391        ViewCell(mesh) { mActiveViewCell = this; }
392
393        bool IsLeaf() const
394        {
395                return true;
396        }
397
398        /** Returns active view cell, i.e. this view cell or
399                a parent view cell which is set as active view cell.
400        */
401        ViewCell *GetActiveViewCell() const
402        { return mActiveViewCell; }
403
404        /** Sets this view cell to be an active view cell.
405        */
406        void SetActiveViewCell(ViewCell *vc)
407        { mActiveViewCell = vc;}
408       
409        /** points to the currently active view cell. This is the
410                view cell representing the current brach.
411        */
412        ViewCell *mActiveViewCell;
413};
414
415
416/** Leaf of the view cell hierarchy corresponding
417        to a leaf in a spatial hierarchy.
418*/
419template<typename T>
420class HierarchyLeafViewCell: public ViewCellLeaf
421{
422public:
423
424        HierarchyLeafViewCell<T>(): ViewCellLeaf() {  }
425        HierarchyLeafViewCell<T>(Mesh *mesh):
426        ViewCellLeaf(mesh) {  }
427               
428        bool IsLeaf() const
429        {
430                return true;
431        }
432
433        /// Leaves of some hierarchy which contains this view cell.
434        vector<T> mLeaves;
435};
436
437
438typedef HierarchyLeafViewCell<VspLeaf *> VspViewCell;
439typedef HierarchyLeafViewCell<BspLeaf *> BspViewCell;
440typedef HierarchyLeafViewCell<KdLeaf *> KdViewCell;
441
442
443
444
445class ViewCellsTree
446{
447        friend class ViewCellsManager;
448        friend class ViewCellsParseHandlers;
449
450public:
451        ViewCellsTree();
452        /** View cells tree constructor taking a view cell mnanager as parameter
453        */
454        ViewCellsTree(ViewCellsManager *vcm);
455        ~ViewCellsTree();
456
457        /** Returns number of leaves this view cell consists of.
458        */
459        int GetNumInitialViewCells(ViewCell *vc) const;
460
461        /** Collects leaves corresponding to a view cell.
462        */
463        void CollectLeaves(ViewCell *vc, ViewCellContainer &leaves) const;
464
465        /** Merges view cells according to some cost heuristics.
466        */
467        int ConstructMergeTree(const VssRayContainer &rays, const ObjectContainer &objects);
468       
469        /** Refines view cells using shuffling, i.e., border leaves
470                of two view cells are exchanged if the resulting view cells
471                are tested to be "better" than the old ones.
472                @returns number of refined view cells
473        */
474        int RefineViewCells(const VssRayContainer &rays, const ObjectContainer &objects);
475       
476        /** Assign colors to the viewcells so that they can be renderered interactively without
477            color flickering. 
478        */
479        void AssignRandomColors();
480
481        /** Updates view cell stats for this particular view cell.
482        */
483        void UpdateViewCellsStats(ViewCell *vc, ViewCellsStatistics &vcStat);
484
485        /** Get costs resulting from each merge step.
486        */
487        void GetCostFunction(vector<float> &costFunction);
488       
489        /** Returns storage cost resulting from each merge step.
490        */
491        void GetStorageFunction(vector<int> &storageCost);
492
493        /** Returns optimal set of view cells for a given number of view cells.
494        */
495        void CollectBestViewCellSet(ViewCellContainer &viewCells, const int numViewCells);
496
497        /** Root of view cells tree.
498        */
499        ViewCell *GetRoot() const;
500
501        /** Returns pvs of view cell.
502                @note pvs is returned per reference if tree is not compressed,
503                per copy else.
504        */
505        void GetPvs(ViewCell *vc, ObjectPvs &pvs) const;
506
507        /** Returns pvs size (i.e. the render cost of the stored objects)
508        */
509        float GetPvsCost(ViewCell *vc) const;
510 
511
512        /** Returns number of entries associated with this view cell.
513
514                This returns the same value as the "GetPvsSize" function for object pvs
515                but most likely different values if we use object space grouping.
516                E.g., using bounding volumes.
517        */
518        int GetPvsEntries(ViewCell *vc) const;
519
520        /** Returns the number of physically stored entries in the view cells sub tree.
521                This can vary based on the current storage method
522        */
523        int CountStoredPvsEntries(ViewCell *root) const;
524
525        /** Returns memory cost of this view cell.
526        */
527        float GetMemoryCost(ViewCell *vc) const;
528
529        /** Sets method of storage for view cells.
530        */
531        void SetViewCellsStorage(int type);
532
533        /** pvs storage methods
534        */
535        enum {PVS_IN_INTERIORS, COMPRESSED, PVS_IN_LEAVES};
536
537        /** If view cells in this tree have compressed pvs.
538        */
539        int ViewCellsStorage() const;
540
541        /** Returns active view cell that is in the path of this view cell.
542        */
543        ViewCell *GetActiveViewCell(ViewCellLeaf *vc) const;
544
545        /** Sets the leaves to be the currently active view cells.
546        */
547    void SetActiveSetToLeaves();
548
549        /** Propagates pvs up the tree to the root and downwards the tree.
550        */
551        void PropagatePvs(ViewCell *vc);
552
553        /** Exports view cells to file.
554        */
555        bool Export(OUT_STREAM &stream, const bool exportPvs = false);
556
557        /** Export statistics of this view cell tree.
558        */
559        void ExportStats(const string &mergeStats);
560
561        /** Sets root of hierarchy.
562        */
563        void SetRoot(ViewCell *root);
564
565        /** Assignes unique ids to view cells.
566        */
567        void CreateUniqueViewCellsIds();
568
569        /** Resets pvs of whole tree.
570        */
571        void ResetPvs();
572
573        /** Counts pvs of the view cell taking the kd cells into account.
574        */
575        int CountKdPvs(const ViewCellLeaf *vc) const;
576
577        /** Sets pointer to view cells manager.
578        */
579        void SetViewCellsManager(ViewCellsManager *vcm);
580
581        void Update();
582
583protected:
584
585        /** Reads the environment and sets member variables.
586        */
587        void ReadEnvironment();
588
589        /////////////////////////////////////////////////////////////////
590        //                    merge related stuff                      //
591        /////////////////////////////////////////////////////////////////
592
593        /** Computes render cost of the merged pvs.
594        */
595        float ComputeMergedPvsCost(const ObjectPvs &pvs1, const ObjectPvs &pvs2) const;
596
597        /** Returns cost of this leaf according to current heuristics.
598        */
599        float GetCostHeuristics(ViewCell *vc) const;
600
601        /** Returns cost of leaf.
602        */
603        float GetRenderCost(ViewCell *vc) const;
604
605        /** Evaluates the merge cost of this merge candidate pair.
606        */
607        void EvalMergeCost(MergeCandidate &mc) const;
608
609        /** Variance of leaf.
610        */
611        float GetVariance(ViewCell *vc) const;
612
613        /** Standard deviation of leaf.
614        */
615        float GetDeviation(ViewCell *vc) const;
616
617        /** Tries to set this merge candidate to valid.
618                @returns false if both view cells are the same
619        */
620        bool ValidateMergeCandidate(MergeCandidate &mc) const;
621
622        /** Merge view cells of leaves l1 and l2.
623                @returns difference in pvs size
624        */
625        ViewCellInterior *MergeViewCells(ViewCell *l, ViewCell *r, float &pvsDiff); //const;
626
627        /** Shuffles, i.e. takes border leaf from view cell 1 and adds it
628                to view cell 2.
629        */
630        void ShuffleLeaf(ViewCell *leaf, ViewCellInterior *vc1, ViewCellInterior *vc2) const;   
631               
632        /** Shuffles the leaves, i.e., tests if exchanging
633                the leaves helps in improving the view cells.
634        */
635        bool ShuffleLeaves(MergeCandidate &mc) const;
636
637        /** Calculates cost for merge of view cell 1 and 2.
638        */
639        float EvalShuffleCost(ViewCell *leaf,
640                                                  ViewCellInterior *vc1,
641                                                  ViewCellInterior *vc2) const;
642
643        /** Exports a snapshot of the merged view cells to disc.
644        */
645        void ExportMergedViewCells(ViewCellContainer &viewCells,
646                                                           const ObjectContainer &objects,
647                                                           const int numNewViewCells);
648
649        /** Merge queue must be reset after some time because expected value
650                may not be valid.
651        */
652        void ResetMergeQueue();
653
654        /** Updates the current cut of view cells.
655                @returns number of newly merged view cells
656        */
657        int UpdateActiveViewCells(ViewCellContainer &viewCells);
658
659        /** Helper function pullling pvs as high up in the tree as possible.
660        */
661        void PullUpVisibility(ViewCellInterior *interior);
662
663        /** Compress pvs of view cell and children.
664        */
665        void CompressViewCellsPvs(ViewCell *root);
666
667        /** Returns memory usage of view cells.
668        */
669        float GetMemUsage() const;
670
671        /**     Exports single view cell.
672                NOTE: should be in exporter!!
673        */
674        void ExportViewCell(ViewCell *viewCell, OUT_STREAM &stream, const bool exportPvs);     
675
676        /** Exports pvs of a view cell.
677        */
678        void ExportPvs(ViewCell *viewCell, OUT_STREAM &stream);
679
680        /** Counts the logical number of entries in the pvs this view cell.
681                The pvs is assumed to be stored using lossless compression.
682        */
683        int GetEntriesInPvsForCompressedStorage(ViewCell *vc) const;
684
685        /** Computes pvs size of this view cell.
686                The pvs is assumed to be stored using lossless compression.
687        */
688        float GetPvsCostForCompressedStorage(ViewCell *vc) const;
689       
690        /** Computes pvs size of this view cell.
691                The pvs is assumed to be stored in the leaves.
692        */
693        float GetPvsCostForLeafStorage(ViewCell *vc) const;
694
695        /** Counts the logical number of entries in the pvs this view cell.
696                The pvs is assumed to be stored using the leaves.
697        */
698        int GetEntriesInPvsForLeafStorage(ViewCell *vc) const;
699
700        /** Update stats for the log.
701        */
702        void UpdateStats(ofstream &stats,
703                                         const ViewCellsTreeStats &vcStats);
704
705       
706
707        //////////////////////////////////////
708
709        /// if the view cell tree hold compressed pvs
710        int mViewCellsStorage;
711        /// pointer to the view cells manager
712        ViewCellsManager *mViewCellsManager;
713        /// the root of the view cells hierarchy
714        ViewCell *mRoot;
715
716        /// if merge visualization should be shown
717        bool mExportMergedViewCells;
718        /// intermediate container of merged view cells.
719        ViewCellContainer mMergedViewCells;
720        /// if merged view cells are refined.
721        bool mRefineViewCells;
722        /// weights between variance and render cost increase in the range [0 .. 1].
723        float mRenderCostWeight;
724
725        /// overall cost used to normalize cost ratio
726        float mOverallCost;
727        float mExpectedCost;
728    float mDeviation;
729        float mAvgRenderCost;
730       
731        /// the area is used for pvs heuristics
732        int mUseAreaForPvs;
733        /// number of currently active view cells (=current cut)
734        int mNumActiveViewCells;
735        /// minimal number of view cells
736        int mMergeMinViewCells;
737        /// maximal cost ratio for the merge
738        float mMergeMaxCostRatio;
739
740        typedef priority_queue<MergeCandidate> MergeQueue;
741
742        MergeQueue mMergeQueue;
743
744        float mMaxMemory;
745
746        int mMaxMergesPerPass;
747        float mAvgCostMaxDeviation;
748};
749
750
751/**
752        Candidate for leaf merging based on priority.
753*/
754class MergeCandidate
755
756        friend class ViewCellsTree;
757
758public:
759
760        MergeCandidate(ViewCell *l, ViewCell *r);
761
762        /** If this merge pair is still valid.
763        */
764        bool IsValid() const;
765
766       
767        friend bool operator<(const MergeCandidate &leafa, const MergeCandidate &leafb)
768        {
769                return leafb.GetMergeCost() < leafa.GetMergeCost();
770        }
771
772        void SetLeftViewCell(ViewCell *l);
773        void SetRightViewCell(ViewCell *l);
774
775        ViewCell *GetLeftViewCell() const;
776        ViewCell *GetRightViewCell() const;
777
778        /** Returns leaf view cell initially associated with this merge candidate.
779        */
780        ViewCell *GetInitialLeftViewCell() const;
781        /** Returns leaf view cell initially associated with this merge candidate.
782        */
783        ViewCell *GetInitialRightViewCell() const;
784
785        /** Returns the increase of the standard deviation of this merge candidate.
786        */
787        float GetDeviationIncr() const;
788
789        /** Merge cost of this candidate pair.
790        */
791        float GetMergeCost() const;
792
793        /** Render cost of this candidate.
794        */
795        float GetRenderCost() const;
796       
797        static float sRenderCostWeight;
798
799protected:
800
801        /// render cost increase by this merge
802        float mRenderCost;
803        /// increase / decrease of standard deviation
804        float mDeviationIncr;
805
806        ViewCell *mLeftViewCell;
807        ViewCell *mRightViewCell;
808
809        ViewCell *mInitialLeftViewCell;
810        ViewCell *mInitialRightViewCell;
811};
812
813
814class MergeStatistics: public StatisticsBase
815{
816public:
817       
818        int merged;
819        int siblings;
820        int candidates;
821        int nodes;
822
823        int accTreeDist;
824        int maxTreeDist;
825       
826        Real collectTime;
827        Real mergeTime;
828
829        Real overallCost;
830
831        Real expectedRenderCost;
832        Real deviation;
833        Real heuristics;
834
835        // Constructor
836        MergeStatistics()
837        {
838                Reset();
839        }
840       
841        double AvgTreeDist() const {return (double)accTreeDist / (double)merged;};
842
843        void Reset()
844        {
845                nodes = 0;
846                merged = 0;
847                siblings = 0;
848                candidates = 0;
849       
850                accTreeDist = 0;
851                maxTreeDist = 0;
852
853                collectTime = 0;
854                mergeTime = 0;
855                overallCost = 0;
856
857                expectedRenderCost = 0;
858                deviation = 0;
859                heuristics = 0;
860
861        }
862
863        void Print(ostream &app) const;
864
865        friend ostream &operator<<(ostream &s, const MergeStatistics &stat)
866        {
867                stat.Print(s);
868                return s;
869        }
870};
871
872}
873
874#endif
Note: See TracBrowser for help on using the repository browser.