home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Pascal / Libraries / GrafSys 2.0 / GrafSys 2.0 source / GrafSysObject.p < prev    next >
Encoding:
Text File  |  1993-07-21  |  24.9 KB  |  615 lines  |  [TEXT/PJMM]

  1. unit GrafSysObject;
  2.  
  3. interface
  4.     uses
  5.         Matrix, Transformations, OffscreenCore, GrafSysCore, GrafSysScreen;
  6.  
  7.     const
  8.         MaxLine = 8000;
  9.  
  10.     type
  11.  
  12.         LineEntry = record
  13.                 fromP, toP: longint; (* max 8000 lines per model supported in this incarnation.  *)
  14.                 hs, vs, he, ve: integer; (* for fast drawing. buffers transformed locations *)
  15.                 newline: boolean; (* for optimization. if true, no MoveTo required *)
  16.                 newLineColor: boolean;
  17.                 LineColor: RGBColor;
  18.             end;
  19.  
  20.         LineBufPtr = ^LineBufRec;
  21.         LineBufRec = array[1..MaxLine] of LineEntry;
  22.  
  23.         TSObject3D = object(TSGenericObject3D)
  24.                 Lines: LineBufPtr;
  25.                 numLines: integer;
  26.                 AutoErase: Boolean;
  27.                 UseBounds: Boolean;
  28.                 procedure Init;
  29.                 override;
  30.                 function Clone: TGenericObject;    {also clone line description buffer}
  31.                 override;
  32.                 procedure Reset;
  33.                 override;
  34.                 procedure Kill;
  35.                 override;
  36.                 function AddLine (fIndex, tIndex: longint): integer;        {add line to objects database. returns line index or -1}
  37.                 function ChangeLine (LineIndex, fIndex, tIndex: longint): boolean;    {change line description of line with index }
  38.                                                                                     {lineIndex. True if successful                }
  39.                 function ChangeLineColor (LineIndex: longint; theColor: RGBColor): boolean;
  40.                                                                         {change the color from this line on for all following }
  41.                                                                         {until the next ChangeColor command                      }
  42.                 function GetLineColor (LineIndex: longint; var theColor: RGBColor; var ChangeHere: boolean): Boolean;
  43.                                                                         {returns the currently active color of specified line}
  44.                 function KeepLineColor (LineIndex: longint): boolean;    {deletes change linecolor information. This line and }
  45.                                                                         {all following will have the same color as the pre-  }
  46.                                                                         {vious                                                      }
  47.                 function DeleteLine (LineIndex: integer): Boolean;        {delete whole line from model. True on success}
  48.                 function DeletePoint (index: longint): boolean;            {override inherited proc of this kind. This one checks}
  49.                 override;                                                        {first if point is referenced to by a point. If so, it }
  50.                                                                         {returns false and doesn't delete the point            }
  51.                 procedure GetLine (lineIndex: integer; var src, tgt: LongInt); {returns start and endpoint of line}
  52.                 procedure BuildNewLines;    {should not be called from the outside}
  53.                 procedure CollectLineData; {internal use only. fill the screen vals from point definition into line array}
  54.                 procedure SetAutoerase (TurnOn: Boolean);                {controls setting of autoerase flag if switched on, }
  55.                                                                         {this procedure will initialize the oldBounds var    }
  56.                 procedure SetUseBounds (TurnOn: Boolean);                {tells Draw and fDraw to collect bouding box data}
  57.                 procedure Draw;                                            {recalcs if neccessary, erases old image if auto- }
  58.                 override;                                                        {erase on, redraws all objects lines                     }
  59.                 procedure fDraw;                                            {like Draw but it collects data prior to drawing }
  60.                                                                         {thus making the actual drawing process a bit  }
  61.                                                                         {faster but the whole call is slower than Draw }
  62.                 procedure Erase;                                            {erase image of myself. this calcs and uses bounds}
  63.             end;
  64.  
  65. {Global Procedures for GrafSys}
  66.     procedure InitGrafSys;
  67.     procedure ArithmeticClip (var startV, endV: Point3DEntry; var skipThis, clippedThis: boolean; var sx, sy, ex, ey: integer);                                                                        {arithmetically clips a line that connects startV,endV }
  68.                                                                         {if it intersects the Z=0 plane. If it is completely behind }
  69.                                                                         {the Z=0 plane, skipThis is TRUE, if it intersects with }
  70.                                                                         {the plane, clippedThis becomes true and sx..ey contain}
  71.                                                                         {the new screen coordinates                                        }
  72.  
  73.  
  74. implementation
  75.  
  76.     type
  77.         screenBuffer = array[1..MaxLine] of record
  78.                 sx, sy: integer;
  79.                 ex, ey: integer;
  80.                 newLine: boolean;
  81.                 newLineColor: boolean;
  82.                 LineColor: RGBColor;
  83.             end;
  84.         screenBufPtr = ^screenBuffer;
  85.  
  86.  
  87.     var
  88.         theBlack: RGBColor;
  89.         lineBuffer: screenBufPtr;
  90.         center: Point; (* screen center in local coords of current 3d grafport *)
  91.         thed: real;
  92.         screenBufNumLines: integer; (* number of lines in scren buffer *)
  93.  
  94.     procedure InitGrafSys;
  95.     begin
  96.         InitMatrix; (* initialize the Matrix Package *)
  97.         lineBuffer := screenBufPtr(NewPtr(SIZEOF(screenBuffer)));
  98.         InitGrafSysScreen;
  99.         theBlack.red := 0;
  100.         theBlack.green := $0000;
  101.         theBlack.blue := 0;
  102.     end;
  103.  
  104. (* Clipping works the following way:  Eye orientation is looking in direction of positive z !!!!                               *)
  105. (*    - if both start and endpoint are behind the xy plane (have negative z-vals)  then the line is not shown at all. *)
  106. (*    - if both points have negative z-vals, the line is drawn entirely, no clipping required                                   *)
  107. (*    - otherwise the line is intersected with the xy plane and drawn from the point with positive z value to the  *)
  108. (*       intersection point                                                                                                                                    *)
  109.  
  110. (* new clipping algorithm :                                                                                                     *)
  111. (* first get start and endpoint                                                                                                 *)
  112. (* clipping only required if on opposite sides of the projection screen                                       *)
  113. (* if on opposite sides then we have to clip. the point to clip is always the endpoint of line, so   *)
  114. (*     we have to switch the two points if the endpoint is on the POSITIVE (=legal) side of plane  *)
  115.  
  116. (* the vars have the folloving meaning :                 *)
  117. (* s    : vector -- startpoint                                   *)
  118. (* e    : vector -- endpoint                                     *)
  119. (* dir : vector -- direction                                    *)
  120. (* t    : real -- parameter to calculate intersection *)
  121. (* d    : vector -- Intersection Point                       *)
  122.  
  123.  
  124.     procedure ArithmeticClip (var startV, endV: Point3DEntry; var skipThis, clippedThis: boolean; var sx, sy, ex, ey: integer);
  125.  
  126.         type
  127.             realV = array[1..3] of real;
  128.  
  129.         var
  130.             xform, xform2: Matrix4;
  131.             thePoint, dir, d, dummyV: realV;
  132.             zbyd: Real;
  133.             lineCount: integer;
  134.             clipstart: boolean;
  135.             t: Real;
  136.             eyeSafetyDist: real;
  137.             s, e: realV;
  138.  
  139.     begin
  140.         skipThis := FALSE;
  141.         clippedthis := FALSE;
  142. {startV := theScrnObj^.Point[sp];}
  143. {endV := theScrnObj^.Point[ep];       (* now we have start & endpoint for clipping in 3D *)
  144.         GetVector4(startV.transformed, s[1], s[2], s[3]);
  145.         GetVector4(endV.transformed, e[1], e[2], e[3]);
  146.         if ((s[3] <= 0) and (e[3] <= 0)) or ((s[3] > 0) and (e[3] > 0)) then
  147.             begin
  148.                 if ((s[3] <= 0) and (e[3] <= 0)) then (* no line is drawn *)
  149.                     skipThis := TRUE
  150.                 else
  151.                     begin (* whole line can be drawn, transfer it to the line buffer *)
  152.                         sx := startV.screenx; (* perspective xform has been applied already *)
  153.                         sy := startV.screeny;
  154.                         ex := endV.screenx;
  155.                         ey := endV.screeny;
  156.                     end;
  157.             end
  158.         else (* we have to clip. will always clip endpoint *)
  159.             begin
  160.                 clippedThis := TRUE;
  161.                 if s[3] < 0 then (* we have to switch start and endpoint since endpoint is legal one *)
  162.                     begin
  163.                         dummyV := s;
  164.                         s := e;
  165.                         e := dummyV;
  166.                         sx := endV.screenx; (* these screen coords don't have to be *)
  167.                         sy := endV.screeny; (* recalculated *)
  168.                     end
  169.                 else
  170.                     begin
  171.                         sx := startV.screenX;
  172.                         sy := startV.screenY;
  173.                     end; (* no switch *)
  174.  
  175.                 dir[1] := e[1] - s[1]; (* now calc direction vector *)
  176.                 dir[2] := e[2] - s[2];
  177.                 dir[3] := e[3] - s[3];
  178.  
  179.                 t := (0 - s[3]) / dir[3]; (* calc parameter for intersection *)
  180.                 d[1] := s[1] + (t * dir[1]); (* calc intersection Point *)
  181.                 d[2] := s[2] + (t * dir[2]);
  182.                 d[3] := 0;
  183.  
  184. (* now we have to perspective-project the intersection point *)
  185.                 if current3Dport^.projection = perspective then
  186.                     begin
  187.                         zbyd := 1 / (d[3] / thed + 1);
  188.                         ex := Trunc((d[1] * zbyd)) + center.h; (* do perspective transformation *)
  189.                         ey := -Trunc((d[2] * zbyd)) + center.v;
  190.                     end
  191.                 else
  192.                     begin
  193.                         ex := Trunc(d[1]) + center.h; (* do parallel projection *)
  194.                         ey := -Trunc(d[2]) + center.v;
  195.                     end; (* parallel *)
  196.             end; (* else we have to clip *)
  197.  
  198.     end; (* arithmetic clip *)
  199.  
  200.  
  201.  
  202.  
  203.     procedure TSObject3D.Init;
  204.     begin
  205.         inherited Init;
  206.         if ErrorCode <> noErr then
  207.             Exit(Init);
  208.         numLines := 0;
  209.         SetRect(Bounds, 0, 0, 0, 0);
  210.         oldBounds := Bounds;
  211.         AutoErase := False;
  212.         UseBounds := FALSE;
  213.         Lines := LineBufPtr(NewPtr(SIZEOF(LineBufRec)));
  214.         if Lines = nil then
  215.             ErrorCode := cOutOfMem;
  216.     end;
  217.  
  218.     procedure TSObject3D.Reset;
  219.         override;
  220.     begin
  221.         inherited Reset;
  222.         AutoErase := FALSE;
  223.         UseBounds := False;
  224.     end;
  225.  
  226. {Clone: extend this procedure to also allocate a line buffer and copy all data from }
  227. {          the original                                                                        }
  228.  
  229.     function TSObject3D.Clone: TGenericObject;
  230.         override;
  231.  
  232.         var
  233.             theClone: TSObject3D;
  234.  
  235.     begin
  236.         theClone := TSObject3D(inherited Clone);
  237.         theClone.Lines := LineBufPtr(NewPtr(SIZEOF(LineBufRec)));
  238.         if theClone.Lines = nil then
  239.             theClone.ErrorCode := cOutOfMem;
  240.         theClone.Lines^ := self.Lines^; (* copy the whole structure *)
  241.         Clone := theClone;
  242.     end;
  243.  
  244.     procedure TSObject3D.Kill;
  245.         override;
  246.     begin
  247.         DisposPtr(Ptr(Lines));
  248.         inherited Kill;
  249.     end;
  250.  
  251.     procedure TSObject3D.BuildNewLines;    {should not be called from the outside}
  252.         var
  253.             index: integer;
  254.  
  255.     begin
  256.         index := 2; (* check all lines starting with line two *)
  257.         while index <= numLines do
  258.             begin
  259.                 if Lines^[index].fromP = Lines^[index - 1].toP then
  260.                     Lines^[index].newLine := False
  261.                 else
  262.                     Lines^[index].newline := TRUE;
  263.                 index := index + 1;
  264.             end;
  265.         if numLines > 0 then
  266.             Lines^[1].newLine := TRUE; (* first line always true *)
  267.     end;
  268.  
  269.     function TSObject3D.AddLine (fIndex, tIndex: longint): integer; {add line to objects database. returns line index or -1 }
  270.     begin
  271.         fIndex := fIndex - 1;
  272.         if (fIndex < 0) or (fIndex > numPoints) then
  273.             begin
  274.                 ErrorCode := cIllegalPointIndex;
  275.                 AddLine := -1;
  276.                 Exit(AddLine);
  277.             end;
  278.         tIndex := tIndex - 1; (* make f and t zero-based *)
  279.         if (tIndex < 0) or (tIndex > numPoints) then
  280.             begin
  281.                 ErrorCode := cIllegalPointIndex;
  282.                 AddLine := -1;
  283.                 Exit(AddLine);
  284.             end;
  285.  
  286.         if numLines < MaxLine then
  287.             begin
  288.                 numLines := numLines + 1;
  289.                 Lines^[numLines].fromP := fIndex;
  290.                 Lines^[numLines].toP := tIndex;
  291.                 Lines^[numlines].newLineColor := FALSE;
  292.                 if numLines > 1 then
  293.                     if Lines^[numLines].fromP = Lines^[numLines - 1].toP then
  294.                         Lines^[numLines].newLine := False
  295.                     else
  296.                         Lines^[numLines].newline := TRUE
  297.                 else (* numLines = 1 *)
  298.                     Lines^[numLines].newline := TRUE;
  299.                 AddLine := numLines;
  300.                 objChanged := TRUE;
  301.             end
  302.         else
  303.             begin
  304.                 ErrorCode := cTooManyLines;
  305.                 AddLine := -1;
  306.             end;
  307.     end;
  308.  
  309.     function TSObject3D.ChangeLineColor (LineIndex: longint; theColor: RGBColor): boolean;
  310.                                                                         {change the color from this line on for all following }
  311.                                                                         {until the next ChangeColor command                      }
  312.     begin
  313.         if LineIndex <= numLines then
  314.             begin
  315.                 Lines^[LineIndex].newLineColor := TRUE;
  316.                 Lines^[LineIndex].LineColor := theColor;
  317.                 ChangeLineColor := TRUE;
  318.             end
  319.         else
  320.             begin
  321.                 ErrorCode := cIllegalLineIndex;
  322.                 ChangeLineColor := FALSE;
  323.             end;
  324.     end;
  325.  
  326.     function TSObject3D.GetLineColor (LineIndex: longint; var theColor: RGBColor; var ChangeHere: boolean): Boolean;
  327.                                                                         {returns the currently active color of specified line}
  328.         var
  329.             index: longint;
  330.  
  331.     begin
  332.         GetLineColor := TRUE;
  333.         theColor.red := 0;
  334.         theColor.green := 0;
  335.         theColor.blue := 0;
  336.         if LineIndex <= numLines then
  337.             begin
  338.                 index := 1;
  339.                 while index <= LineIndex do (* walk down all lines and change line color if neccessary *)
  340.                     begin
  341.                         ChangeHere := Lines^[LineIndex].newLineColor;
  342.                         if ChangeHere then
  343.                             theColor := Lines^[LineIndex].LineColor;
  344.                         index := index + 1;
  345.                     end;
  346.             end
  347.         else
  348.             begin
  349.                 ErrorCode := cIllegalLineIndex;
  350.                 GetLineColor := FALSE;
  351.             end;
  352.     end;
  353.  
  354.  
  355.     function TSObject3D.KeepLineColor (LineIndex: longint): boolean;
  356.                                                                         {deletes change linecolor information. This line and }
  357.                                                                         {all following will have the same color as the pre-  }
  358.                                                                         {vious                                                      }
  359.     begin
  360.         if LineIndex <= numLines then
  361.             begin
  362.                 Lines^[LineIndex].newLineColor := FALSE;
  363.                 KeepLineColor := TRUE;
  364.             end
  365.         else
  366.             begin
  367.                 ErrorCode := cIllegalLineIndex;
  368.                 KeepLineColor := FALSE;
  369.             end;
  370.     end;
  371.  
  372.     function TSObject3D.ChangeLine (LineIndex, fIndex, tIndex: longint): boolean;    {change line description of line with wLine := Lines^[index].newLine or clippedLast;
  373.                 GenIndex(Lines^[index].toP, BufIndex, bufOffset); (* this is executed anyways *)
  374.                 tempE := theBufs[BufIndex]^[bufOffset]; (* read entry *)
  375.                 GenIndex(Lines^[index].fromP, BufIndex, bufOffset);
  376.                 tempS := theBufs[BufIndex]^[bufOffset]; (* read entry *)
  377.                 LineBuffer^[lBufIndex].newLineColor := Lines^[index].newLineColor;
  378.                 LineBuffer^[lBufIndex].lineColor := Lines^[index].lineColor;
  379. (* do clipping *)
  380.  
  381.                 case clipMode of
  382.                     none: 
  383.                         begin (* do nothing, just copy *)
  384.                             LineBuffer^[lBufIndex].sx := tempS.screenx;
  385.                             LineBuffer^[lBufIndex].sy := tempS.screeny;
  386.                             LineBuffer^[lBufIndex].ex := tempE.screenx;
  387.                             LineBuffer^[lBufIndex].ey := tempE.screeny;
  388.                             LineBuffer^[lBufIndex].newLine := newLine;
  389.                             lBufIndex := lBufIndex + 1;
  390.                         end;
  391.  
  392.                     arithmetic: 
  393.                         begin
  394.                             ArithmeticClip(tempS, tempE, skipThis, clippedThis, sx, sy, ex, ey);
  395.                             if skipThis then
  396.                                 clippedLast := TRUE
  397.                             else
  398.                                 begin
  399.                                     LineBuffer^[lBufIndex].sx := sx; (* copy data to buffer *)
  400.                                     LineBuffer^[lBufIndex].sy := sy;
  401.                                     LineBuffer^[lBufIndex].ex := ex;
  402.                                     LineBuffer^[lBufIndex].ey := ey;
  403.                                     LineBuffer^[lBufIndex].newLine := newLine or clippedThis;
  404.                                     lBufIndex := lBufIndex + 1;
  405.                     (* if something was clipped we might have to update the bounds array     *)
  406.                     (* the point that didn't get included was always the endpoint that got         *)
  407.                     (* clipped to the projection plane                                                             *)
  408.                                     if newLine or clippedThis then
  409.                                         begin
  410.                                             if ex < Bounds.left then (* gather data for autoerase *)
  411.                                                 Bounds.left := ex - 1;
  412.                                             if ex > Bounds.right then
  413.                                                 Bounds.right := ex + 1;
  414.                                             if ey < Bounds.top then
  415.                                                 Bounds.top := ey - 1;
  416.                                             if ey > Bounds.bottom then
  417.                                                 Bounds.bottom := ey + 1;
  418.                                         end;
  419.                                     clippedLast := clippedThis;
  420.                                 end;
  421.                         end;
  422.  
  423.  
  424.                     fast: (* very simple clipping method : remove all lines that fall at least partwise off the screen *)
  425.                         begin
  426.                             clippedLast := FALSE;
  427.                             GetVector4(tempS.transformed, startx, starty, startz);
  428.                             GetVector4(tempE.transformed, endx, endy, endz);
  429.                             if (startz < 0) or (endz < 0) then
  430.                                 begin
  431.                                     clippedLast := TRUE; (* don't copy line *)
  432.                                 end
  433.                             else
  434.                                 begin
  435.                                     LineBuffer^[lBufIndex].sx := tempS.screenx;
  436.                                     LineBuffer^[lBufIndex].sy := tempS.screeny;
  437.                                     LineBuffer^[lBufIndex].ex := tempE.screenx;
  438.                                     LineBuffer^[lBufIndex].ey := tempE.screeny;
  439.                                     LineBuffer^[lBufIndex].newLine := newLine;
  440.                                     lBufIndex := lBufIndex + 1;
  441.                                     clippedLast := FALSE;
  442.                                 end;
  443.                         end;
  444.  
  445.                     otherwise
  446.                         DebugStr('Unknown clipping method. TSObject3D.CollectLineDat')
  447.                 end; (* case clipMode *)
  448.  
  449. (* end of clipping *)
  450.  
  451.                 index := index + 1;
  452.             end; (* while index *)
  453.         screenBufNumLines := lBufIndex - 1; (* store number of lines in screenBuf *)
  454.         insetRect(Bounds, -1, -1); (* just do it anyways *)
  455.     end;
  456.  
  457.  
  458.     procedure TSObject3D.Draw;
  459.         override;
  460.         var
  461.             index: integer;
  462.             BufIndex, bufOffset: integer;
  463.             temp, tempS, tempE: point3DEntry;
  464.             theColor: RGBColor;
  465.             skippedLast: Boolean;
  466.             needMoveTo: Boolean;
  467.             skipThis, clippedThis: boolean;
  468.             sx, sy, ex, ey: integer;
  469.             ClipMode: ClippingType;
  470.             startx, starty, startz: real;
  471.             endx, endy, endz: real;
  472.  
  473.     begin
  474. (* first, set the current color to black *)
  475.         RGBForeColor(theBlack);
  476.         if Autoerase or UseBounds then
  477.             begin
  478.                 Transform2(FALSE); (* calc transform (if neccessary), transfor and gather autoerase data as well *)
  479.                 if Autoerase then
  480.                     EraseRect(oldBounds); (* erase old image. Its rect was stored in oldRect *)
  481.             end
  482.         else
  483.             Transform(FALSE);
  484.  
  485.     (* now begin drawing all lines of the object *)
  486.         ClipMode := current3DPort^.clipping;
  487.         center := current3DPort^.center;
  488.         thed := current3DPort^.d;
  489.         skippedLast := false;
  490.         index := 1;
  491.         while index <= numLines do
  492.             begin
  493.  
  494.                 if Lines^[index].newLineColor then
  495.                     RGBForeColor(LineBuffer^[index].LineColor);
  496.                 needMoveTo := skippedLast or Lines^[index].newLine; (* test if we need to use MoveTo call *)
  497.                 skippedLast := False; (* reset for this round of clipping *)
  498.  
  499. (* new we calculate clipping *)
  500.  
  501.                 case clipMode of
  502.                     none: 
  503.                         begin (* do nothing, just draw *)
  504.                             if needMoveTo then (* do only if we have to do a moveTo *)
  505.                                 begin
  506.                                     GenIndex(Lines^[index].fromP, BufIndex, bufOffset);
  507.                                     temp := theBufs[BufIndex]^[bufOffset]; (* read entry *)
  508.                                     Lines^[index].hs := temp.screenx;
  509.                                     Lines^[index].vs := temp.screeny;
  510.                                     MoveTo(temp.screenx, temp.screeny);
  511.                                 end;
  512.                             GenIndex(Lines^[index].toP, BufIndex, bufOffset);
  513.                             temp := theBufs[BufIndex]^[bufOffset]; (* read entry *)
  514.                             Lines^[index].he := temp.screenx;
  515.                             Lines^[index].ve := temp.screeny;
  516.                             LineTo(temp.screenx, temp.screeny);
  517.                         end;
  518.  
  519.                     arithmetic: 
  520.                         begin
  521.                             GenIndex(Lines^[index].fromP, BufIndex, bufOffset);
  522.                             tempS := theBufs[BufIndex]^[bufOffset]; (* read entry *)
  523.                             GenIndex(Lines^[index].toP, BufIndex, bufOffset);
  524.                             tempE := theBufs[BufIndex]^[bufOffset]; (* read entry *)
  525.                             ArithmeticClip(tempS, tempE, skipThis, clippedThis, sx, sy, ex, ey);
  526.                             if skipThis then
  527.                                 skippedLast := TRUE
  528.                             else
  529.                                 begin
  530.                                     if clippedThis then (* do this only if point is drawn *)
  531.                                         begin
  532.                                             if ex < Bounds.left then     (* gather data for autoerase. only if clipped         *)
  533.                                                 Bounds.left := ex - 1;    (* the point that needs to be checked is always     *)
  534.                                             if ex > Bounds.right then    (* the endpoint (sx,sy)                            *)
  535.                                                 Bounds.right := ex + 1;
  536.                                             if ey < Bounds.top then
  537.                                                 Bounds.top := ey - 1;
  538.                                             if ey > Bounds.bottom then
  539.                                                 Bounds.bottom := ey + 1;
  540.                                         end;
  541.                                     if needMoveTo or clippedThis then (* we need move to *)
  542.                                         MoveTo(sx, sy);
  543.                                     LineTo(ex, ey); (* draw it *)
  544.                                     skippedLast := clippedThis; (* indicate we need a moveto *)
  545.                                 end;
  546.                         end;
  547.  
  548.  
  549.                     fast: (* very simple clipping method : remove all lines that fall at least partwise off the screen *)
  550.                         begin
  551.                             GenIndex(Lines^[index].fromP, BufIndex, bufOffset);
  552.                             tempS := theBufs[BufIndex]^[bufOffset]; (* read entry *)
  553.                             GenIndex(Lines^[index].toP, BufIndex, bufOffset);
  554.                             tempE := theBufs[BufIndex]^[bufOffset]; (* read entry *)
  555.                             GetVector4(tempS.transformed, startx, starty, startz);
  556.                             GetVector4(tempE.transformed, endx, endy, endz);
  557.                             if (startz < 0) or (endz < 0) then
  558.                                 begin
  559.                                     skippedLast := TRUE; (* don't copy line *)
  560.                                 end
  561.                             else
  562.                                 begin (* draw line *)
  563.                                     if needMoveTo then
  564.                                         begin
  565.                         {Lines^[index].hs := tempS.screenx;}
  566.                         {Lines^[index].vs := tempS.screeny;}
  567.                                             MoveTo(tempS.screenx, tempS.screeny);
  568.                                         end;
  569.  
  570.                     {Lines^[index].he := tempE.screenx;}
  571.                     {Lines^[index].ve := tempE.screeny;}
  572.                                     LineTo(tempE.screenx, tempE.screeny);
  573.                                     skippedLast := FALSE;
  574.                                 end;
  575.                         end;
  576.                     otherwise
  577.                         DebugStr('Unknown clipping method');
  578.                 end; (* case *)
  579.  
  580. (* end of clipping *)
  581.  
  582.                 index := index + 1;
  583.             end; (* while index *)
  584.         insetRect(Bounds, -1, -1);
  585.         hasChanged := FALSE;
  586.     end;
  587.  
  588.  
  589.     procedure TSObject3D.fDraw;
  590.         override;
  591.         var
  592.             index: integer;
  593.  
  594.     begin
  595. (* first, set the current color to black *)
  596.         RGBForeColor(theBlack);
  597.         if Autoerase or useBounds then
  598.             begin
  599.                 Transform2(FALSE); (* calc transform (if neccessary), transfor and gather autoerase data as well *)
  600.                                 (* transform2 will move bounds -> oldBounds for ersure of old image *)
  601.             end
  602.         else
  603.             Transform(FALSE);
  604.  
  605.     (* now begin drawing all lines of the object *)
  606.  
  607.         CollectLineData; (* pre-gather all line-data for faster drawing. This includes clipping *)
  608.         index := 1;
  609.  
  610.         if AutoErase then
  611.             EraseRect(oldBounds); (* erase old image. Its rect was stored in oldBounds *)
  612.  
  613.         while index <= screenBufNumLines do
  614.             begin
  615.                 if LineBuffer^[index].