home *** CD-ROM | disk | FTP | other *** search
/ PC PowerPlay 22 / PCPP #22.iso / Quake2 / q2source_12_11 / utils3 / bsp / qvis3 / flow.c next >
Encoding:
C/C++ Source or Header  |  1997-08-24  |  15.7 KB  |  768 lines

  1. #include "vis.h"
  2.  
  3. /*
  4.  
  5.   each portal will have a list of all possible to see from first portal
  6.  
  7.   if (!thread->portalmightsee[portalnum])
  8.  
  9.   portal mightsee
  10.  
  11.   for p2 = all other portals in leaf
  12.     get sperating planes
  13.     for all portals that might be seen by p2
  14.         mark as unseen if not present in seperating plane
  15.     flood fill a new mightsee
  16.     save as passagemightsee
  17.  
  18.  
  19.   void CalcMightSee (leaf_t *leaf, 
  20. */
  21.  
  22. int CountBits (byte *bits, int numbits)
  23. {
  24.     int        i;
  25.     int        c;
  26.  
  27.     c = 0;
  28.     for (i=0 ; i<numbits ; i++)
  29.         if (bits[i>>3] & (1<<(i&7)) )
  30.             c++;
  31.  
  32.     return c;
  33. }
  34.  
  35. int        c_fullskip;
  36. int        c_portalskip, c_leafskip;
  37. int        c_vistest, c_mighttest;
  38.  
  39. int        c_chop, c_nochop;
  40.  
  41. int        active;
  42.  
  43. void CheckStack (leaf_t *leaf, threaddata_t *thread)
  44. {
  45.     pstack_t    *p, *p2;
  46.  
  47.     for (p=thread->pstack_head.next ; p ; p=p->next)
  48.     {
  49. //        printf ("=");
  50.         if (p->leaf == leaf)
  51.             Error ("CheckStack: leaf recursion");
  52.         for (p2=thread->pstack_head.next ; p2 != p ; p2=p2->next)
  53.             if (p2->leaf == p->leaf)
  54.                 Error ("CheckStack: late leaf recursion");
  55.     }
  56. //    printf ("\n");
  57. }
  58.  
  59.  
  60. winding_t *AllocStackWinding (pstack_t *stack)
  61. {
  62.     int        i;
  63.  
  64.     for (i=0 ; i<3 ; i++)
  65.     {
  66.         if (stack->freewindings[i])
  67.         {
  68.             stack->freewindings[i] = 0;
  69.             return &stack->windings[i];
  70.         }
  71.     }
  72.  
  73.     Error ("AllocStackWinding: failed");
  74.  
  75.     return NULL;
  76. }
  77.  
  78. void FreeStackWinding (winding_t *w, pstack_t *stack)
  79. {
  80.     int        i;
  81.  
  82.     i = w - stack->windings;
  83.  
  84.     if (i<0 || i>2)
  85.         return;        // not from local
  86.  
  87.     if (stack->freewindings[i])
  88.         Error ("FreeStackWinding: allready free");
  89.     stack->freewindings[i] = 1;
  90. }
  91.  
  92. /*
  93. ==============
  94. ChopWinding
  95.  
  96. ==============
  97. */
  98. winding_t    *ChopWinding (winding_t *in, pstack_t *stack, plane_t *split)
  99. {
  100.     vec_t    dists[128];
  101.     int        sides[128];
  102.     int        counts[3];
  103.     vec_t    dot;
  104.     int        i, j;
  105.     vec_t    *p1, *p2;
  106.     vec3_t    mid;
  107.     winding_t    *neww;
  108.  
  109.     counts[0] = counts[1] = counts[2] = 0;
  110.  
  111. // determine sides for each point
  112.     for (i=0 ; i<in->numpoints ; i++)
  113.     {
  114.         dot = DotProduct (in->points[i], split->normal);
  115.         dot -= split->dist;
  116.         dists[i] = dot;
  117.         if (dot > ON_EPSILON)
  118.             sides[i] = SIDE_FRONT;
  119.         else if (dot < -ON_EPSILON)
  120.             sides[i] = SIDE_BACK;
  121.         else
  122.         {
  123.             sides[i] = SIDE_ON;
  124.         }
  125.         counts[sides[i]]++;
  126.     }
  127.  
  128.     if (!counts[1])
  129.         return in;        // completely on front side
  130.     
  131.     if (!counts[0])
  132.     {
  133.         FreeStackWinding (in, stack);
  134.         return NULL;
  135.     }
  136.  
  137.     sides[i] = sides[0];
  138.     dists[i] = dists[0];
  139.     
  140.     neww = AllocStackWinding (stack);
  141.  
  142.     neww->numpoints = 0;
  143.  
  144.     for (i=0 ; i<in->numpoints ; i++)
  145.     {
  146.         p1 = in->points[i];
  147.  
  148.         if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING)
  149.         {
  150.             FreeStackWinding (neww, stack);
  151.             return in;        // can't chop -- fall back to original
  152.         }
  153.  
  154.         if (sides[i] == SIDE_ON)
  155.         {
  156.             VectorCopy (p1, neww->points[neww->numpoints]);
  157.             neww->numpoints++;
  158.             continue;
  159.         }
  160.     
  161.         if (sides[i] == SIDE_FRONT)
  162.         {
  163.             VectorCopy (p1, neww->points[neww->numpoints]);
  164.             neww->numpoints++;
  165.         }
  166.         
  167.         if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
  168.             continue;
  169.             
  170.         if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING)
  171.         {
  172.             FreeStackWinding (neww, stack);
  173.             return in;        // can't chop -- fall back to original
  174.         }
  175.  
  176.     // generate a split point
  177.         p2 = in->points[(i+1)%in->numpoints];
  178.         
  179.         dot = dists[i] / (dists[i]-dists[i+1]);
  180.         for (j=0 ; j<3 ; j++)
  181.         {    // avoid round off error when possible
  182.             if (split->normal[j] == 1)
  183.                 mid[j] = split->dist;
  184.             else if (split->normal[j] == -1)
  185.                 mid[j] = -split->dist;
  186.             else
  187.                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
  188.         }
  189.             
  190.         VectorCopy (mid, neww->points[neww->numpoints]);
  191.         neww->numpoints++;
  192.     }
  193.     
  194. // free the original winding
  195.     FreeStackWinding (in, stack);
  196.     
  197.     return neww;
  198. }
  199.  
  200.  
  201. /*
  202. ==============
  203. ClipToSeperators
  204.  
  205. Source, pass, and target are an ordering of portals.
  206.  
  207. Generates seperating planes canidates by taking two points from source and one
  208. point from pass, and clips target by them.
  209.  
  210. If target is totally clipped away, that portal can not be seen through.
  211.  
  212. Normal clip keeps target on the same side as pass, which is correct if the
  213. order goes source, pass, target.  If the order goes pass, source, target then
  214. flipclip should be set.
  215. ==============
  216. */
  217. winding_t    *ClipToSeperators (winding_t *source, winding_t *pass, winding_t *target, qboolean flipclip, pstack_t *stack)
  218. {
  219.     int            i, j, k, l;
  220.     plane_t        plane;
  221.     vec3_t        v1, v2;
  222.     float        d;
  223.     vec_t        length;
  224.     int            counts[3];
  225.     qboolean        fliptest;
  226.  
  227. // check all combinations    
  228.     for (i=0 ; i<source->numpoints ; i++)
  229.     {
  230.         l = (i+1)%source->numpoints;
  231.         VectorSubtract (source->points[l] , source->points[i], v1);
  232.  
  233.     // fing a vertex of pass that makes a plane that puts all of the
  234.     // vertexes of pass on the front side and all of the vertexes of
  235.     // source on the back side
  236.         for (j=0 ; j<pass->numpoints ; j++)
  237.         {
  238.             VectorSubtract (pass->points[j], source->points[i], v2);
  239.  
  240.             plane.normal[0] = v1[1]*v2[2] - v1[2]*v2[1];
  241.             plane.normal[1] = v1[2]*v2[0] - v1[0]*v2[2];
  242.             plane.normal[2] = v1[0]*v2[1] - v1[1]*v2[0];
  243.             
  244.         // if points don't make a valid plane, skip it
  245.  
  246.             length = plane.normal[0] * plane.normal[0]
  247.             + plane.normal[1] * plane.normal[1]
  248.             + plane.normal[2] * plane.normal[2];
  249.             
  250.             if (length < ON_EPSILON)
  251.                 continue;
  252.  
  253.             length = 1/sqrt(length);
  254.             
  255.             plane.normal[0] *= length;
  256.             plane.normal[1] *= length;
  257.             plane.normal[2] *= length;
  258.  
  259.             plane.dist = DotProduct (pass->points[j], plane.normal);
  260.  
  261.         //
  262.         // find out which side of the generated seperating plane has the
  263.         // source portal
  264.         //
  265. #if 1
  266.             fliptest = false;
  267.             for (k=0 ; k<source->numpoints ; k++)
  268.             {
  269.                 if (k == i || k == l)
  270.                     continue;
  271.                 d = DotProduct (source->points[k], plane.normal) - plane.dist;
  272.                 if (d < -ON_EPSILON)
  273.                 {    // source is on the negative side, so we want all
  274.                     // pass and target on the positive side
  275.                     fliptest = false;
  276.                     break;
  277.                 }
  278.                 else if (d > ON_EPSILON)
  279.                 {    // source is on the positive side, so we want all
  280.                     // pass and target on the negative side
  281.                     fliptest = true;
  282.                     break;
  283.                 }
  284.             }
  285.             if (k == source->numpoints)
  286.                 continue;        // planar with source portal
  287. #else
  288.             fliptest = flipclip;
  289. #endif
  290.         //
  291.         // flip the normal if the source portal is backwards
  292.         //
  293.             if (fliptest)
  294.             {
  295.                 VectorSubtract (vec3_origin, plane.normal, plane.normal);
  296.                 plane.dist = -plane.dist;
  297.             }
  298. #if 1
  299.         //
  300.         // if all of the pass portal points are now on the positive side,
  301.         // this is the seperating plane
  302.         //
  303.             counts[0] = counts[1] = counts[2] = 0;
  304.             for (k=0 ; k<pass->numpoints ; k++)
  305.             {
  306.                 if (k==j)
  307.                     continue;
  308.                 d = DotProduct (pass->points[k], plane.normal) - plane.dist;
  309.                 if (d < -ON_EPSILON)
  310.                     break;
  311.                 else if (d > ON_EPSILON)
  312.                     counts[0]++;
  313.                 else
  314.                     counts[2]++;
  315.             }
  316.             if (k != pass->numpoints)
  317.                 continue;    // points on negative side, not a seperating plane
  318.                 
  319.             if (!counts[0])
  320.                 continue;    // planar with seperating plane
  321. #else
  322.             k = (j+1)%pass->numpoints;
  323.             d = DotProduct (pass->points[k], plane.normal) - plane.dist;
  324.             if (d < -ON_EPSILON)
  325.                 continue;
  326.             k = (j+pass->numpoints-1)%pass->numpoints;
  327.             d = DotProduct (pass->points[k], plane.normal) - plane.dist;
  328.             if (d < -ON_EPSILON)
  329.                 continue;            
  330. #endif
  331.         //
  332.         // flip the normal if we want the back side
  333.         //
  334.             if (flipclip)
  335.             {
  336.                 VectorSubtract (vec3_origin, plane.normal, plane.normal);
  337.                 plane.dist = -plane.dist;
  338.             }
  339.             
  340.         //
  341.         // clip target by the seperating plane
  342.         //
  343.             target = ChopWinding (target, stack, &plane);
  344.             if (!target)
  345.                 return NULL;        // target is not visible
  346.         }
  347.     }
  348.     
  349.     return target;
  350. }
  351.  
  352.  
  353.  
  354. /*
  355. ==================
  356. RecursiveLeafFlow
  357.  
  358. Flood fill through the leafs
  359. If src_portal is NULL, this is the originating leaf
  360. ==================
  361. */
  362. void RecursiveLeafFlow (int leafnum, threaddata_t *thread, pstack_t *prevstack)
  363. {
  364.     pstack_t    stack;
  365.     portal_t    *p;
  366.     plane_t        backplane;
  367.     leaf_t         *leaf;
  368.     int            i, j;
  369.     long        *test, *might, *vis, more;
  370.     int            pnum;
  371.  
  372.     thread->c_chains++;
  373.  
  374.     leaf = &leafs[leafnum];
  375. //    CheckStack (leaf, thread);
  376.  
  377.     prevstack->next = &stack;
  378.  
  379.     stack.next = NULL;
  380.     stack.leaf = leaf;
  381.     stack.portal = NULL;
  382.  
  383.     might = (long *)stack.mightsee;
  384.     vis = (long *)thread->base->portalvis;
  385.     
  386. // check all portals for flowing into other leafs    
  387.     for (i=0 ; i<leaf->numportals ; i++)
  388.     {
  389.         p = leaf->portals[i];
  390.         pnum = p - portals;
  391.  
  392.         if ( ! (prevstack->mightsee[pnum >> 3] & (1<<(pnum&7)) ) )
  393.         {
  394.             continue;    // can't possibly see it
  395.         }
  396.  
  397.     // if the portal can't see anything we haven't allready seen, skip it
  398.         if (p->status == stat_done)
  399.         {
  400.             test = (long *)p->portalvis;
  401.         }
  402.         else
  403.         {
  404.             test = (long *)p->portalflood;
  405.         }
  406.  
  407.         more = 0;
  408.         for (j=0 ; j<portallongs ; j++)
  409.         {
  410.             might[j] = ((long *)prevstack->mightsee)[j] & test[j];
  411.             more |= (might[j] & ~vis[j]);
  412.         }
  413.         
  414.         if (!more && 
  415.             (thread->base->portalvis[pnum>>3] & (1<<(pnum&7))) )
  416.         {    // can't see anything new
  417.             continue;
  418.         }
  419.  
  420.         // get plane of portal, point normal into the neighbor leaf
  421.         stack.portalplane = p->plane;
  422.         VectorSubtract (vec3_origin, p->plane.normal, backplane.normal);
  423.         backplane.dist = -p->plane.dist;
  424.         
  425. //        c_portalcheck++;
  426.         
  427.         stack.portal = p;
  428.         stack.next = NULL;
  429.         stack.freewindings[0] = 1;
  430.         stack.freewindings[1] = 1;
  431.         stack.freewindings[2] = 1;
  432.         
  433. #if 1
  434. {
  435. float d;
  436.  
  437.     d = DotProduct (p->origin, thread->pstack_head.portalplane.normal);
  438.     d -= thread->pstack_head.portalplane.dist;
  439.     if (d < -p->radius)
  440.     {
  441.         continue;
  442.     }
  443.     else if (d > p->radius)
  444.     {
  445.         stack.pass = p->winding;
  446.     }
  447.     else    
  448.     {
  449.         stack.pass = ChopWinding (p->winding, &stack, &thread->pstack_head.portalplane);
  450.         if (!stack.pass)
  451.             continue;
  452.     }
  453. }
  454. #else
  455.         stack.pass = ChopWinding (p->winding, &stack, &thread->pstack_head.portalplane);
  456.         if (!stack.pass)
  457.             continue;
  458. #endif
  459.  
  460.     
  461. #if 1
  462. {
  463. float d;
  464.  
  465.     d = DotProduct (thread->base->origin, p->plane.normal);
  466.     d -= p->plane.dist;
  467.     if (d > p->radius)
  468.     {
  469.         continue;
  470.     }
  471.     else if (d < -p->radius)
  472.     {
  473.         stack.source = prevstack->source;
  474.     }
  475.     else    
  476.     {
  477.         stack.source = ChopWinding (prevstack->source, &stack, &backplane);
  478.         if (!stack.source)
  479.             continue;
  480.     }
  481. }
  482. #else
  483.         stack.source = ChopWinding (prevstack->source, &stack, &backplane);
  484.         if (!stack.source)
  485.             continue;
  486. #endif
  487.  
  488.         if (!prevstack->pass)
  489.         {    // the second leaf can only be blocked if coplanar
  490.  
  491.             // mark the portal as visible
  492.             thread->base->portalvis[pnum>>3] |= (1<<(pnum&7));
  493.  
  494.             RecursiveLeafFlow (p->leaf, thread, &stack);
  495.             continue;
  496.         }
  497.  
  498.         stack.pass = ClipToSeperators (stack.source, prevstack->pass, stack.pass, false, &stack);
  499.         if (!stack.pass)
  500.             continue;
  501.         
  502.         stack.pass = ClipToSeperators (prevstack->pass, stack.source, stack.pass, true, &stack);
  503.         if (!stack.pass)
  504.             continue;
  505.  
  506.         // mark the portal as visible
  507.         thread->base->portalvis[pnum>>3] |= (1<<(pnum&7));
  508.  
  509.         // flow through it for real
  510.         RecursiveLeafFlow (p->leaf, thread, &stack);
  511.     }    
  512. }
  513.  
  514.  
  515. /*
  516. ===============
  517. PortalFlow
  518.  
  519. generates the portalvis bit vector
  520. ===============
  521. */
  522. void PortalFlow (int portalnum)
  523. {
  524.     threaddata_t    data;
  525.     int                i;
  526.     portal_t        *p;
  527.     int                c_might, c_can;
  528.  
  529.     p = sorted_portals[portalnum];
  530.     p->status = stat_working;
  531.  
  532.     c_might = CountBits (p->portalflood, numportals*2);
  533.  
  534.     memset (&data, 0, sizeof(data));
  535.     data.base = p;
  536.     
  537.     data.pstack_head.portal = p;
  538.     data.pstack_head.source = p->winding;
  539.     data.pstack_head.portalplane = p->plane;
  540.     for (i=0 ; i<portallongs ; i++)
  541.         ((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i];
  542.     RecursiveLeafFlow (p->leaf, &data, &data.pstack_head);
  543.  
  544.     p->status = stat_done;
  545.  
  546.     c_can = CountBits (p->portalvis, numportals*2);
  547.  
  548.     qprintf ("portal:%4i  mightsee:%4i  cansee:%4i (%i chains)\n", 
  549.         (int)(p - portals),    c_might, c_can, data.c_chains);
  550. }
  551.  
  552.  
  553. /*
  554. ===============================================================================
  555.  
  556. This is a rough first-order aproximation that is used to trivially reject some
  557. of the final calculations.
  558.  
  559.  
  560. Calculates portalfront and portalflood bit vectors
  561.  
  562. thinking about:
  563.  
  564. typedef struct passage_s
  565. {
  566.     struct passage_s    *next;
  567.     struct portal_s        *to;
  568.     stryct sep_s        *seperators;
  569.     byte                *mightsee;
  570. } passage_t;
  571.  
  572. typedef struct portal_s
  573. {
  574.     struct passage_s    *passages;
  575.     int                    leaf;        // leaf portal faces into
  576. } portal_s;
  577.  
  578. leaf = portal->leaf
  579. clear 
  580. for all portals
  581.  
  582.  
  583. calc portal visibility
  584.     clear bit vector
  585.     for all passages
  586.         passage visibility
  587.  
  588.  
  589. for a portal to be visible to a passage, it must be on the front of
  590. all seperating planes, and both portals must be behind the mew portal
  591.  
  592. ===============================================================================
  593. */
  594.  
  595. int        c_flood, c_vis;
  596.  
  597.  
  598. /*
  599. ==================
  600. SimpleFlood
  601.  
  602. ==================
  603. */
  604. void SimpleFlood (portal_t *srcportal, int leafnum)
  605. {
  606.     int        i;
  607.     leaf_t    *leaf;
  608.     portal_t    *p;
  609.     int        pnum;
  610.  
  611.     leaf = &leafs[leafnum];
  612.     
  613.     for (i=0 ; i<leaf->numportals ; i++)
  614.     {
  615.         p = leaf->portals[i];
  616.         pnum = p - portals;
  617.         if ( ! (srcportal->portalfront[pnum>>3] & (1<<(pnum&7)) ) )
  618.             continue;
  619.  
  620.         if (srcportal->portalflood[pnum>>3] & (1<<(pnum&7)) )
  621.             continue;
  622.  
  623.         srcportal->portalflood[pnum>>3] |= (1<<(pnum&7));
  624.         
  625.         SimpleFlood (srcportal, p->leaf);
  626.     }
  627. }
  628.  
  629. /*
  630. ==============
  631. BasePortalVis
  632. ==============
  633. */
  634. void BasePortalVis (int portalnum)
  635. {
  636.     int            j, k;
  637.     portal_t    *tp, *p;
  638.     float        d;
  639.     winding_t    *w;
  640.  
  641.     p = portals+portalnum;
  642.  
  643.     p->portalfront = malloc (portalbytes);
  644.     memset (p->portalfront, 0, portalbytes);
  645.  
  646.     p->portalflood = malloc (portalbytes);
  647.     memset (p->portalflood, 0, portalbytes);
  648.     
  649.     p->portalvis = malloc (portalbytes);
  650.     memset (p->portalvis, 0, portalbytes);
  651.     
  652.     for (j=0, tp = portals ; j<numportals*2 ; j++, tp++)
  653.     {
  654.         if (j == portalnum)
  655.             continue;
  656.         w = tp->winding;
  657.         for (k=0 ; k<w->numpoints ; k++)
  658.         {
  659.             d = DotProduct (w->points[k], p->plane.normal)
  660.                 - p->plane.dist;
  661.             if (d > ON_EPSILON)
  662.                 break;
  663.         }
  664.         if (k == w->numpoints)
  665.             continue;    // no points on front
  666.  
  667.         w = p->winding;
  668.         for (k=0 ; k<w->numpoints ; k++)
  669.         {
  670.             d = DotProduct (w->points[k], tp->plane.normal)
  671.                 - tp->plane.dist;
  672.             if (d < -ON_EPSILON)
  673.                 break;
  674.         }
  675.         if (k == w->numpoints)
  676.             continue;    // no points on front
  677.  
  678.         p->portalfront[j>>3] |= (1<<(j&7));
  679.     }
  680.     
  681.     SimpleFlood (p, p->leaf);
  682.  
  683.     p->nummightsee = CountBits (p->portalflood, numportals*2);
  684. //    printf ("portal %i: %i mightsee\n", portalnum, p->nummightsee);
  685.     c_flood += p->nummightsee;
  686. }
  687.  
  688.  
  689.  
  690.  
  691.  
  692. /*
  693. ===============================================================================
  694.  
  695. This is a second order aproximation 
  696.  
  697. Calculates portalvis bit vector
  698.  
  699. WAAAAAAY too slow.
  700.  
  701. ===============================================================================
  702. */
  703.  
  704. /*
  705. ==================
  706. RecursiveLeafBitFlow
  707.  
  708. ==================
  709. */
  710. void RecursiveLeafBitFlow (int leafnum, byte *mightsee, byte *cansee)
  711. {
  712.     portal_t    *p;
  713.     leaf_t         *leaf;
  714.     int            i, j;
  715.     long        more;
  716.     int            pnum;
  717.     byte        newmight[MAX_PORTALS/8];
  718.  
  719.     leaf = &leafs[leafnum];
  720.     
  721. // check all portals for flowing into other leafs    
  722.     for (i=0 ; i<leaf->numportals ; i++)
  723.     {
  724.         p = leaf->portals[i];
  725.         pnum = p - portals;
  726.  
  727.         // if some previous portal can't see it, skip
  728.         if (! (mightsee[pnum>>3] & (1<<(pnum&7)) ) )
  729.             continue;
  730.  
  731.         // if this portal can see some portals we mightsee, recurse
  732.         more = 0;
  733.         for (j=0 ; j<portallongs ; j++)
  734.         {
  735.             ((long *)newmight)[j] = ((long *)mightsee)[j] 
  736.                 & ((long *)p->portalflood)[j];
  737.             more |= ((long *)newmight)[j] & ~((long *)cansee)[j];
  738.         }
  739.  
  740.         if (!more)
  741.             continue;    // can't see anything new
  742.  
  743.         cansee[pnum>>3] |= (1<<(pnum&7));
  744.  
  745.         RecursiveLeafBitFlow (p->leaf, newmight, cansee);
  746.     }    
  747. }
  748.  
  749. /*
  750. ==============
  751. BetterPortalVis
  752. ==============
  753. */
  754. void BetterPortalVis (int portalnum)
  755. {
  756.     portal_t    *p;
  757.  
  758.     p = portals+portalnum;
  759.  
  760.     RecursiveLeafBitFlow (p->leaf, p->portalflood, p->portalvis);
  761.  
  762.     // build leaf vis information
  763.     p->nummightsee = CountBits (p->portalvis, numportals*2);
  764.     c_vis += p->nummightsee;
  765. }
  766.  
  767.  
  768.