home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Pascal / Libraries / GrafSys 2.0 / GrafSys 2.0 source / GrafSysScreen.p < prev    next >
Encoding:
Text File  |  1993-08-22  |  36.2 KB  |  1,010 lines  |  [TEXT/PJMM]

  1. unit GrafSysScreen;
  2.  
  3. interface
  4. uses
  5.     Matrix, Transformations, OffscreenCore, GrafSysCore;
  6.  
  7. const
  8. {$IFC UseFixedMath = FALSE}
  9.     MaxPointPerBuf = 1024 - 1; (* Maximum Points per Object : 256K *)
  10. {$ELSEC}
  11. { code for fixed math routines }
  12.     MaxPointPerBuf = 1024 - 1;
  13. {$ENDC}
  14.     MaxBuffers = 256 - 1;
  15. (* GrafSys constants *)
  16.     cPort3DType = '3Prt';
  17.     cGrafSysVersion = $00000001; (* = Version 0.01 *)
  18.  
  19. type
  20.     ProjectionTypes = (parallel, perspective);
  21.     clippingType = (none, arithmetic, fast);
  22.  
  23.     TPort3DPtr = ^TPort3D;
  24.     TPort3D = record
  25.             theWindow: CWindowRecord; (* piggy-back riding on the window's data we'll put everything else *)
  26.             versionType: OSType; (* used to veryfy that this record is really a 3dPort *)
  27.             theOffscreen: TOffscreenRec; (* for later use in off-screen drawing *)
  28.             ProjectionPlane: rect;
  29.             ViewPlane: rect;
  30.             left, right, top, bottom: integer;
  31.             center: point;
  32.             useEye: Boolean;    {if false, no eye transform necessary }
  33.             EyeKoord: Vector4;
  34.             ViewPoint: Vector4;
  35.             phi, theta, pitch: real;
  36.             ViewAngle: real;
  37.             d: real; {Perspective param set by viewangle }
  38.             MasterTransform: Matrix4; {matrix to transform all objects according to eye settings }
  39.             projection: ProjectionTypes;
  40.             clipping: ClippingType;
  41.             versionsID: longint; (* ID so objects can detect if the eye changed its specifications *)
  42.         end;
  43.  
  44.     TSPoint3D = object(TPoint3D)
  45.             size: integer; (* size of point *)
  46.             procedure Init;
  47.             override;
  48.             procedure Reset;
  49.             override;
  50.             procedure Draw;    {draw point as seen from the eye. xForm (and Eye) are  applied. No Transform call required }
  51.             override;                {xForm is automatically calculated if neccessary. A Graf3DPort must be active! }
  52.         end;
  53.  
  54.     TSLine3D = object(TLine3D)
  55.             procedure Draw;     {draw line as seen from the eye. xForm (and Eye) are  applied. No Transform call required }
  56.             override;                {xForm is automatically calculated if neccessary. A Graf3DPort must be active! }
  57.         end;
  58.  
  59.     Point3DEntry = record
  60.             koords: Vector4;
  61.             transformed: vector4; (* transformed point, used with clipping *)
  62.             Screenx: integer;
  63.             Screeny: integer;
  64. {transformedZ: real;}
  65.         end;
  66.  
  67.     Point3DBufPtr = ^Point3DBufRec;
  68.     Point3DBufRec = array[0..MaxPointperBuf] of Point3DEntry;
  69.  
  70.     TSGenericObject3D = object(Tabstract3DObject)     { this is the real 3D object that will be extended }
  71.             theBufs: array[0..MaxBuffers] of Point3DBufPtr;        { it contains only the points, nothing else }
  72.             currentBuf: Point3DBufPtr;
  73.             currentIndex: integer; (* index of current buffer *)
  74.             numPoints: longint; (* number of points in object *)
  75.             Bounds: rect; (* for auto-erase data gathering *)
  76.             oldBounds: Rect;
  77.             screenXform: Matrix4;                                {final xformation matrix including eye if useseye else}
  78.                                                                 {equal to xForm                                                         }
  79.             procedure Init;
  80.             override;
  81.             function Clone: TGenericObject;                    {duplicate point buffers as well }
  82.             override;
  83.             procedure Reset;                                    {reset all rots and attributes to default }
  84.             override;
  85.             procedure Kill;                                         {deallocate mem. Will kill all sons that inherit}
  86.             override;
  87.             procedure GenIndex (pointIndex: longint; var BufIndex, bufOffset: integer); {pointindex -> (buffer, offset) conversion }
  88.             function AddPoint (x, y, z: real): longint;        {add a point to the object's database. It returns the }
  89.                                                                 {points reference number if successful or -1 otherwise}
  90.                                                                 {point count is one-based, i.e. fist point is index 1. Note     }
  91.                                                                 {that this differs from the internal representation where }
  92.                                                                 {point count is zero-based. COMMON ERROR SOURCE!         }
  93.             function DeletePoint (index: longint): boolean;    {delete point with passed index from database. returns }
  94.                                                                 {false if operation could not be completed }
  95.                                                                 {delete does not deallocate mem if buffer is freed }
  96.                                                                 {all points beyond the one deleted will be moved to }
  97.                                                                 {compact mem}
  98.                                                                 {passing index <1 as index means delete all points}
  99.             procedure GetPoint (index: longint; var x, y, z: real);         {get points coordinate in model coords }
  100.                                                                                 {if point illegal, it returns 0,0,0}
  101.             function ChangePoint (index: longint; x, y, z: real): boolean;     {change points coords. true on success }
  102.             procedure Transform (forceCalc: boolean);        {calc trafo-matrix (if necessary) and convert   }
  103.                                                                 {all points to their screen representation           }
  104.                                                                             {WARNING: A 3D GrafPort must be open. The     }
  105.                                                                 {transformations are done for the currently ac-}
  106.                                                                 {tive 3D grafport's eye settings.}
  107.             procedure Transform2 (forceCalc: boolean);    (* calc trafo-matrix (if necessary) and convert    *)
  108.                                                                 (*all points to their screen representation. gather *)
  109.                                                                 (* information for auto-erasure                            *)
  110.  
  111.             function TransformedPoint (index: longint): Vector4;     {get transformed coordinates of point     }
  112.                                                                         {with index. if illegal proc will return     }
  113.                                                                         {0,0,0. Note that eye is not considered    }
  114.                                                                         {you have to use ToScreen for that        }
  115.             function ForeignPoint (p: Vector4): Vector4;    {as inherited except that eye trafo is included if}
  116.             override;                                                {useEye is set                                          }
  117.             function WorldToModel (wc: Vector4): Vector4;    {as inherited except that eye trafo is included}
  118.             override;                                                    {if useEye is set.                                    }
  119.             procedure CalcBounds;                                {calc bounds of object on screen and place it in    }
  120.                                                                 {the oldBounds variable.                                }
  121.         end;
  122.  
  123.  
  124. var
  125.     current3Dport: TPort3Dptr; (* currently active 3D port *)
  126.  
  127.  
  128. (* procedures to intialize GrafSys and return version numbers *)
  129. procedure InitGrafSysScreen; (* initialize the GrafSys and local variables such as current graf 3D port *)
  130. function GrafSysVersion: longint; (* higword: major release, lower word : minor release. Hex 00010001 means version 1.01 *)
  131. (* procedure to create new 3D windows and grafports *)
  132.  
  133. function GetNew3DWindow (ID: integer; behind: ptr): WindowPtr; (* allocate a new window from resource *)
  134. function New3DWindow (boundsRect: Rect; title: Str255; visible: BOOLEAN; procID: Integer; behind: WindowPtr; goAwayFlag: BOOLEAN; refCon: LongInt): WindowPtr;
  135. procedure Dispos3DWindow (theWindow: WindowPtr); (* close and release mem occupied by the window *)
  136.  
  137. (* procedures affecting CURRENT  3D GrafPort *)
  138. procedure Set3DPort (the3DPort: WindowPtr);     (* tells grafsys in which port to draw. This port MUST have been *)
  139.                                                             (* previously allocated with New3DWindow or GetNew3DWindow  *)
  140. procedure Get3DPort (var the3DPort: WindowPtr);     (* returns current 3D GrafPort *)
  141. function Is3DPort (thePort: WindowPtr): Boolean;    (* returns TRUE if thePort is a 3D Port *)
  142. procedure SetView (ProjectPlaneSize, ViewPlaneSize: Rect);     (* sets the viewing and projection plane parameters *)
  143.                                                                             (* of the currently active 3D GrafPort                     *)
  144.                                                                             (* this of course affects the center location             *)
  145. procedure SetCenter (x, y: Integer);                (* Sets center of current 3D grafport to given params  *)
  146. procedure SetEyeChar (UsesEye: Boolean; location: Vector4; thePhi, theTheta, thePitch, theViewangle: real; clipType: clippingType);
  147.                                                             (* Sets eye characteristics and calculates window's master trans- *)
  148.                                                             (* form                                                                              *)
  149. procedure GetEye (var UsesEye: Boolean; var location: Vector4; thePhi, theTheta, thePitch, theViewangle: real; var clipType: clippingType);
  150.                                                             (* returns eye characteristic of current active 3d grafport *)
  151.  
  152. procedure ToScreen (thePoint: Vector4; var h, v: INTEGER); { transforms a point with x,y,z to screen as seen under    }
  153.                                                                     {current eye settings                                        }
  154. procedure ProjectPoint (thePoint: Vector4; var h, v: integer);
  155.         {project 3D pointz to screen using projection type}
  156. implementation
  157.  
  158. (* global variables for the 3D package *)
  159. var
  160. {versionCount: longint;     (* used for syncing Master Transform of eye. If <> 0 a change to *)
  161.                                  (* the eye has occured and the eye has to be recalculated            *)
  162.  
  163.     lgMaxPoints: longint; (* maximum # of points in model *)
  164.  
  165. (* GetNew3DWindow will load the WIND template as specified but will also put the *)
  166. (* 3D eye data required on piggyback *)
  167.  
  168. procedure Init3DPort (var the3DPort: TPort3DPtr);
  169.     begin
  170.         with the3DPort^ do begin
  171.             versionType := cPort3DType;
  172.             theOffscreen.thePort := nil;
  173.             theOffscreen.theDevice := nil;
  174.             ProjectionPlane := theWindow.port.portRect;
  175.             ViewPlane := theWindow.port.portRect; (* the viewplane and project plane are set to the whole window *)
  176.             left := theWindow.port.portRect.left;
  177.             right := theWindow.port.portRect.right;
  178.             top := theWindow.port.portRect.top;
  179.             bottom := theWindow.port.portRect.bottom;
  180.             center.h := (left + right) div 2;
  181.             center.v := (top + bottom) div 2; (* center is center of window *)
  182.             useEye := False;
  183.             SetVector4(EyeKoord, 0, 0, 0);
  184.             phi := 0;
  185.             theta := 0;
  186.             pitch := 0;
  187.             ViewAngle := 0; (* parallel projection *)
  188.             d := 0;
  189.             MasterTransform := Identity;
  190.             projection := parallel;
  191.             clipping := none;
  192.             versionsID := 0; (* fresh eye *)
  193. {versionCount := versionCount + 1;}
  194.         end;
  195.     end;
  196.  
  197. procedure InitGrafSysScreen; (* initialize the GrafSys and local variables such as current graf 3D port *)
  198.     begin
  199. {versionCount := -1;}
  200.         current3Dport := nil;
  201.         lgMaxPoints := LongInt((MaxPointPerBuf + 1)) * LongInt((MaxBuffers + 1));
  202.     end;
  203.  
  204. function GrafSysVersion: longint; (* higword: major release, lower word : minor release. Hex 00010001 means version 1.01 *)
  205.     begin
  206.         GrafSysVersion := cGrafSysVersion;
  207.     end;
  208.  
  209. (* places 3D eye data piggyback to window data *)
  210. (* sets current 3D port to newly allocated window if allocated *)
  211. function GetNew3DWindow (ID: integer; behind: ptr): WindowPtr;
  212.     var
  213.         wStorage: Ptr;
  214.         theWindow: WindowPtr;
  215.  
  216.     begin
  217.         theWindow := nil;
  218.         wStorage := NewPtr(SizeOf(TPort3D));
  219.         if wStorage = nil then begin
  220.             GetNew3DWindow := nil;
  221.             Exit(GetNew3DWindow);
  222.         end;
  223.         theWindow := GetNewCWindow(ID, wStorage, Pointer(behind));
  224.         GetNew3DWindow := theWindow;
  225.         if theWindow = nil then
  226.             Exit(GetNew3DWindow); (* do not init the 3D data since window not valid. Don't set current3Dport to it *)
  227.         Init3Dport(TPort3DPtr(wStorage));
  228.         current3Dport := TPort3DPtr(wStorage);
  229.         SetPort(theWindow);
  230.     end;
  231.  
  232. (* places 3D eye data piggyback to window data *)
  233. (* sets current 3D port to newly allocated window if allocated *)
  234. function New3DWindow (boundsRect: Rect; title: Str255; visible: BOOLEAN; procID: Integer; behind: WindowPtr; goAwayFlag: BOOLEAN; refCon: LongInt): WindowPtr;
  235.     var
  236.         wStorage: Ptr;
  237.         theWindow: WindowPtr;
  238.  
  239.     begin
  240.         theWindow := nil;
  241.         wStorage := NewPtr(SizeOf(TPort3D));
  242.         if wStorage = nil then begin
  243.             New3DWindow := nil;
  244.             Exit(New3DWindow);
  245.         end;
  246.         theWindow := NewCWindow(wStorage, boundsRect, title, visible, procID, Pointer(behind), goAwayFlag, refCon);
  247.         New3DWindow := theWindow;
  248.         if theWindow = nil then
  249.             Exit(New3DWindow); (* do not init the 3D data since window not valid. Don't set current3Dport to it *)
  250.         Init3Dport(TPort3DPtr(wStorage));
  251.         current3Dport := TPort3DPtr(wStorage);
  252.         SetPort(theWindow);
  253.     end;
  254.  
  255. procedure Dispos3DWindow (theWindow: WindowPtr); (* close and release mem occupied by the window *)
  256.     var
  257.         the3DWindow: TPort3DPtr;
  258.  
  259.     begin
  260.         the3DWindow := TPort3DPtr(theWindow);
  261.         if is3DPort(theWindow) then begin
  262.             if the3DWindow^.theOffscreen.thePort <> nil then
  263.                 DisposeOffScreen(the3DWindow^.theOffscreen.thePort, the3DWindow^.theOffscreen.theDevice);
  264.         end;
  265.         CloseWindow(theWindow);
  266.         DisposPtr(ptr(theWindow));
  267.         theWindow := nil;
  268.     end;
  269.  
  270. procedure Set3DPort (the3DPort: WindowPtr);     (* tells grafsys in which port to draw. This port MUST have been *)
  271.                                                             (* previously allocated with New3DWindow or GetNew3DWindow  *)
  272.                                                             (* otherwise Set3DPort does nothing                                   *)
  273.     begin
  274.         if (GetPtrSize(Ptr(thePort)) = SizeOf(TPort3D)) and (TPort3DPtr(thePort)^.versionType = cPort3DType) then begin
  275.             current3Dport := TPort3Dptr(thePort);
  276.         end;
  277.         SetPort(the3DPort);
  278.     end;
  279.  
  280. procedure Get3DPort (var the3DPort: WindowPtr);     (* returns current 3D GrafPort *)
  281.     begin
  282.         the3Dport := WindowPtr(current3Dport);
  283.     end;
  284.  
  285.  
  286. function Is3DPort (thePort: WindowPtr): Boolean;    (* returns TRUE if thePort is a 3D Port *)
  287.     var
  288.         theResult: boolean;
  289.  
  290.     begin
  291.         theResult := GetPtrSize(Ptr(thePort)) = SizeOf(TPort3D);
  292.         if theResult then
  293.             theResult := Tport3Dptr(thePort)^.versionType = cPort3DType;
  294.         Is3Dport := theResult;
  295.     end;
  296.  
  297. procedure SetView (ProjectPlaneSize, ViewPlaneSize: Rect);     (* sets the viewing and projection plane parameters *)
  298.                                                                             (* of the currently active 3D GrafPort                     *)
  299.     begin
  300.         with current3Dport^ do begin
  301.             ProjectionPlane := ProjectPlaneSize;
  302.             ViewPlane := ViewPlaneSize;
  303.             left := ProjectPlaneSize.left;
  304.             top := ProjectPlaneSize.top;
  305.             bottom := ProjectPlaneSize.bottom;
  306.             right := ProjectPlaneSize.right;
  307.             center.h := (left + right) div 2;
  308.             center.v := (top + bottom) div 2;
  309.             versionsID := versionsID + 1; (* changes increment. Indicate we must redraw  *)
  310.         end; (* with *)
  311.         ClipRect(ViewPlaneSize);
  312.     end;
  313.  
  314.  
  315. procedure SetCenter (x, y: Integer);
  316.     begin
  317.         current3Dport^.center.h := x;
  318.         current3Dport^.center.v := y;
  319.         current3Dport^.versionsID := current3Dport^.versionsID + 1; (* changes increment. Indicate we must redraw  *)
  320.     end;
  321.  
  322. procedure SetEyeChar (UsesEye: Boolean; location: Vector4; thePhi, theTheta, thePitch, theViewangle: real; clipType: clippingType);
  323.                                                             (* Sets eye characteristics and calculates window's master trans- *)
  324.                                                             (* form                                                                              *)
  325.     var
  326.         theMatrix, theMatrixd: Matrix4;
  327.         p1, p2: Vector4;
  328.  
  329.     const
  330.         dist = 100;
  331.  
  332.  
  333.     begin
  334.         with current3Dport^ do begin
  335. (* first, assign new version ID for eye *)
  336.             versionsID := versionsID + 1;
  337. {versionCount := versionCount + 1;}
  338.             UseEye := UsesEye;
  339.             clipping := clipType;
  340. (* now we calculate the viewange. it derives the d from the viewangle and the HIGHTH of the viewwindow *)
  341. (* a setting of viewangle of zero (= nothing) or 2π  means NO PERSPECTIVE                                              *)
  342.             theViewangle := theViewangle / 2;
  343.             if (theViewangle >= 2 * Pi) or (theViewangle <= 0) then
  344.                 theViewangle := 0;
  345.             if theViewangle = 0 then
  346.                 projection := parallel
  347.             else begin
  348.                 d := center.v / tan(theViewangle);
  349.                 projection := perspective;
  350.             end;
  351.             theMatrix := Identity; (* start from scratch with 0,0,0, 0,0,0 *)
  352.  
  353.  
  354. (* Zero step : translate eye to world viewpoint *)
  355.  
  356.             MTranslate(theMatrix, -location[1], -location[2], -location[3]); (* since we move world and not eye, negative sign *)
  357.  
  358. (* 1st step : direction of looking is straight up, through the XY plane *)
  359. (*                 now deviate towards Y-achsis by phi radiants . after    *)
  360. (*                 transformation z-vector lies in XZ plane                       *)
  361.  
  362.             RotX(theMatrix, -thePhi);
  363.  
  364. (* 2nd step: rotate around y so the z-vector comes to coincide with *)
  365. (*                world coordinate system                                              *)
  366.  
  367.             RotY(theMatrix, -theTheta);
  368.  
  369.  
  370. (* 3rd step: ratate according to pitch *)
  371.  
  372.             RotZ(theMatrix, -thePitch);
  373.  
  374.             MasterTransform := theMatrix;
  375.  
  376.             phi := thePhi;
  377.             theta := theTheta;
  378.             pitch := thePitch;
  379.             viewangle := theViewangle;
  380.             EyeKoord := location;
  381. (* necessary for eye position and direction *)
  382.         end; (* with *)
  383.     end;
  384.  
  385.  
  386. procedure GetEye (var UsesEye: Boolean; var location: Vector4; thePhi, theTheta, thePitch, theViewangle: real; var clipType: clippingType);
  387.                                                             (* returns eye characteristic of current active 3d grafport *)
  388.     begin
  389.         with current3Dport^ do begin
  390.             thePhi := phi;
  391.             theTheta := theta;
  392.             thePitch := pitch;
  393.             location := EyeKoord;
  394.             theViewangle := viewangle;
  395.             UsesEye := UseEye;
  396.             clipType := Clipping;
  397.         end;
  398.     end;
  399.  
  400. procedure ToScreen (thePoint: Vector4; var h, v: INTEGER); { transforms a point with x,y,z to screen as seen under    }
  401.                                                                     {current eye settings                                        }
  402.  
  403.     var
  404.         x, y, z: Real;
  405.         zbyd: Real;
  406.  
  407.     begin
  408.         if current3DPort^.useEye then
  409.             thePoint := VMult(thePoint, current3DPort^.MasterTransform); (* transform point so we can project *)
  410.         GetVector4(thePoint, x, y, z); (* return to real *)
  411.  
  412.         if current3DPort^.projection = parallel then begin
  413.             h := Trunc(x) + current3DPort^.center.h;
  414.             v := -Trunc(y) + current3DPort^.center.v
  415.         end
  416.         else begin
  417.             zbyd := 1 / (z / current3DPort^.d + 1);
  418.             h := Trunc(x * zbyd) + current3DPort^.center.h; (* do perspective transformation *)
  419.             v := -Trunc(y * zbyd) + current3DPort^.center.v;
  420.         end
  421.  
  422.     end;
  423.  
  424.  
  425. procedure ProjectPoint (thePoint: Vector4; var h, v: integer);
  426.     var
  427.         x, y, z: real;
  428.         zbyd: Real;
  429.  
  430.     begin
  431.         GetVector4(thePoint, x, y, z); (* return to real *)
  432.  
  433.         if current3DPort^.projection = parallel then begin
  434.             h := Trunc(x) + current3DPort^.center.h;
  435.             v := -Trunc(y) + current3DPort^.center.v
  436.         end
  437.         else begin
  438.             zbyd := 1 / (z / current3DPort^.d + 1);
  439.             h := Trunc(x * zbyd) + current3DPort^.center.h; (* do perspective transformation *)
  440.             v := -Trunc(y * zbyd) + current3DPort^.center.v;
  441.         end
  442.     end;
  443.  
  444.  
  445. (* procedures for the objects *)
  446.  
  447. procedure TSGenericObject3D.Init;
  448.     override;
  449.     var
  450.         i: integer;
  451.  
  452.     begin
  453.         inherited Init; (* init all inherited fields *)
  454.         if errorCode <> 0 then
  455.             Exit(init);
  456.         SetRect(Bounds, 0, 0, 0, 0);
  457.         SetRect(oldBounds, 0, 0, 0, 0);
  458.         for i := 0 to MaxBuffers do
  459.             theBufs[i] := nil;
  460.         theBufs[0] := Point3DBufPtr(NewPtr(SIZEOF(Point3DBufRec)));
  461.         currentBuf := theBufs[0];
  462.         currentIndex := -1;
  463.         numPoints := 0;
  464.         if currentBuf = nil then
  465.             ErrorCode := cOutOfMem; (* flag error condition *)
  466.     end;
  467.  
  468. procedure TSGenericObject3D.Reset;
  469.     override;
  470.     begin
  471.         inherited Reset;
  472.         SetRect(Bounds, 0, 0, 0, 0);
  473.         SetRect(oldBounds, 0, 0, 0, 0);
  474.     end;
  475.  
  476. (* extend inherited clone method to duplicate all allocated line buffers *)
  477. function TSGenericObject3D.Clone: TGenericObject;                    {duplicate point buffers as well }
  478.     override;
  479.  
  480.     var
  481.         i: integer;
  482.         theClone: TSGenericObject3D;
  483.  
  484.     begin
  485.         theClone := TSGenericObject3D(inherited Clone);
  486.         for i := 1 to MaxBuffers do begin
  487.             if theBufs[i] <> nil then begin
  488.                 theClone.theBufs[i] := Point3DBufPtr(NewPtr(SIZEOF(Point3DBufRec)));
  489.                 theClone.theBufs[i]^ := theBufs[i]^; (* copy contents *)
  490.             end
  491.             else
  492.                 theClone.theBufs[i] := nil;
  493.         end;
  494.         Clone := theClone;
  495.     end;
  496.  
  497. procedure TSGenericObject3D.Kill;                                         {deallocate mem. Will kill all sons that inherit}
  498.     override;
  499.     var
  500.         i: integer;
  501.  
  502.     begin (* first, deallocate all buffers that have been allocated *)
  503.         for i := 0 to MaxBuffers do begin
  504.             if theBufs[i] <> nil then
  505.                 DisposPtr(Ptr(theBufs[i]));
  506.         end;
  507.         inherited Kill; (* suicide with proven method, thereby killing all sons that inherit *)
  508.     end;
  509.  
  510. {GenIndex does no sanity check. Use with caution}
  511. procedure TSGenericObject3D.GenIndex (pointIndex: longint; var BufIndex, bufOffset: integer);
  512.     begin
  513.         BufIndex := pointIndex div (MaxPointPerBuf + 1);
  514.         bufOffset := pointIndex mod (MaxPointPerBuf + 1);
  515.     end;
  516.  
  517. function TSGenericObject3D.AddPoint (x, y, z: real): longint;        {add a point to the object's database. It returns the }
  518.                                                                                 {points reference number if successful or -1 otherwise}
  519.                                                                                 {check ErrorCode if function returns -1. }
  520.                                                                                 {point count is one-based, i.e. fist point is index 1. Note     }
  521.                                                                                 {that this differs from the internal representation where }
  522.                                                                                 {point count is zero-based. COMMON ERROR SOURCE!         }
  523.  
  524.     var
  525.         theBufIndex: integer;
  526.         offsetIntoBuf: integer;
  527.         pointIndex: longint;
  528.  
  529.     begin
  530.         if self.NumPoints >= lgMaxPoints then {exit with index -1 because database is full}
  531.             begin
  532.             AddPoint := -1;
  533.             ErrorCode := cTooManyPoints; (* flag error condition *)
  534.             Exit(AddPoint);
  535.         end;
  536.         pointIndex := self.numPoints; (* self.numPoints is one-based count, pointIndex zero-based *)
  537.         self.genIndex(pointIndex, theBufIndex, offSetIntoBuf);
  538.         if theBufs[theBufIndex] = nil then begin {we need to allocate a new buffer}
  539.         {checkpoint1 : the offset into buffer must be zero. check it }
  540.             if offsetIntoBuf > 0 then
  541.                 DebugStr('ATTENTION: Addpoint req. new buffer and offset > 0');
  542.             theBufs[theBufIndex] := Point3DBufPtr(NewPtr(SIZEOF(Point3DBufRec)));
  543.             if theBufs[theBufIndex] = nil then begin
  544.                 AddPoint := -1;
  545.                 ErrorCode := cOutOfMem; (* flag error condition *)
  546.                 Exit(AddPoint);
  547.             end;
  548.         end;
  549.      {if we are here, buffer has been allocated and all is well}
  550.         SetVector4(theBufs[theBufIndex]^[pointIndex].koords, x, y, z);
  551.         self.numPoints := self.numPoints + 1;  (* update the number of points in model. always 1 greater that currently *)
  552.                                                 (* active point since pointCount is 1 based, pointIndex zero-based *)
  553.         AddPoint := self.numPoints;
  554.     end;
  555.  
  556.  
  557. function TSGenericObject3D.DeletePoint (index: longint): boolean;    {delete point with passed index from database. returns }
  558.                                                                                 {false if operation could not be completed }
  559.                                                                                 {delete does not deallocate mem if buffer is freed }
  560.                                                                                 {all points beyond the one deleted will be moved to }
  561.                                                                                 {compact mem}
  562.                                                                                 {when specifying a point, its index is zero-based}
  563.                                                                                 {passing 0 as index means delete all points}
  564.     var
  565.         temp: Point3DEntry;
  566.         buffer, offset: integer;
  567.  
  568.     begin
  569.         index := index - 1; (* make zero-based count *)
  570.         if index >= self.numPoints then begin
  571.             ErrorCode := cIllegalPointIndex;
  572.             DeletePoint := FALSE;
  573.             Exit(DeletePoint);
  574.         end;
  575.         if index <= 0 then begin
  576.             numPoints := 0;
  577.             DeletePoint := TRUE; (* remember, no memory is deallocated *)
  578.             Exit(DeletePoint);
  579.         end;
  580.         while index < self.numPoints - 1 do begin
  581.             self.GenIndex(index + 1, buffer, offset);
  582.             if theBufs[buffer] = nil then
  583.                 DebugStr('WARNING: about to access nil buffer while moving::read');
  584.             temp := theBufs[buffer]^[offset]; (* read entry *)
  585.             self.GenIndex(index, buffer, offset);
  586.             if theBufs[buffer] = nil then
  587.                 DebugStr('WARNING: about to access nil buffer while moving::write');
  588.             theBufs[buffer]^[offset] := temp; (* read entry *)
  589.             index := index + 1;
  590.         end;
  591.         self.numPoints := self.numPoints - 1;
  592.         DeletePoint := TRUE;
  593.     end;
  594.  
  595. procedure TSGenericObject3D.GetPoint (index: longint; var x, y, z: real);         {get points coordinate in model coords }
  596.     var
  597.         temp: Point3DEntry;
  598.         buffer, offset: integer;
  599.  
  600.     begin
  601.         index := index - 1; (* make zero-based count *)
  602.         if (index < 0) or (index > self.numPoints - 1) then begin
  603.             x := 0;
  604.             y := 0;
  605.             z := 0;
  606.         end
  607.         else begin
  608.             self.GenIndex(index, buffer, offset);
  609.             if theBufs[buffer] = nil then
  610.                 DebugStr('WARNING: about to access nil buffer in GetPoint');
  611.             temp := theBufs[buffer]^[offset]; (* read entry *)
  612.             GetVector4(temp.koords, x, y, z);
  613.         end;
  614.     end;
  615.  
  616.  
  617. function TSGenericObject3D.ChangePoint (index: longint; x, y, z: real): boolean;     {change points coords. true on success }
  618.     var
  619.         buffer, offset: integer;
  620.  
  621.     begin
  622.         index := index - 1; (* make zero-based count *)
  623.         if (index < 0) or (index > self.numPoints - 1) then begin
  624.             ChangePoint := FALSE;
  625.             ErrorCode := cIllegalPointIndex;
  626.             Exit(changePoint);
  627.         end;
  628.         self.GenIndex(index, buffer, offset);
  629.         SetVector4(theBufs[buffer]^[offset].koords, x, y, z);
  630.         ChangePoint := TRUE;
  631.     end;
  632.  
  633.  
  634. procedure TSGenericObject3D.Transform (forceCalc: boolean);    (* calc trafo-matrix (if necessary) and convert *)
  635.                                                                             (*all points to their screen representation         *)
  636.  
  637.     var
  638.         buffer, offset: integer;
  639.         pointIndex: longint;
  640.         thePoint: Vector4;
  641.         x, y, z: real;
  642.         zbyd: real;
  643.  
  644.     begin
  645.         if forceCalc or objChanged then (* object has changed. we must recalc it *)
  646.             begin
  647.             self.CalcTransform; (* resets versionID to zero and calcs xForm, passes on etc *)
  648.             self.screenXform := self.xForm; (* transfer it in case we don't use the eye *)
  649.             versionsID := -1; (* force check for useEye later on *)
  650.         end; (* if forcecalc or version *)
  651.  
  652.         if versionsID <> current3DPort^.versionsID then begin
  653.             self.screenXform := xForm; (* failsafe in case object didn't change but eye was switched off *)
  654.             versionsID := current3DPort^.versionsID; {sync eye and object. even if we don't use the eye anymore }
  655.             if current3DPort^.useEye then begin
  656.             (* PostConcat the Eye matrix to xForm *)
  657.                 self.screenXform := MMult(xForm, current3DPort^.MasterTransform);
  658.             end; (* if useEye fails we still have last xForm in screenXform *)
  659.         end;
  660.  
  661. (* transformation is pretty simple: for all points: fetch and multiply with xForm Matrix *)
  662. (* we will use currentBuf for faster access. If index mod pointsPerBuf returnes zero we will *)
  663. (* access a new buffer, otherwise we don't calculate anything since this is all straightforward *)
  664.  
  665.         pointIndex := 0;
  666.         offset := 0;
  667.         buffer := -1; (* will be incremented to 0 right away *)
  668.         while pointIndex < self.numPoints do begin
  669.             if offset mod (MaxPointPerBuf + 1) = 0 then (* advance buffer 1 *)
  670.                 begin
  671.                 buffer := buffer + 1;
  672.                 currentBuf := theBufs[buffer];
  673.                 if currentBuf = nil then
  674.                     DebugStr('About to access nil buffer in TSGenericObject3D.Transform');
  675.                 offset := 0;
  676.             end;
  677.         (* get the point *)
  678.             thePoint := currentBuf^[offset].koords;
  679.  
  680.         (* Transform the point *)
  681.             thePoint := VMult(thePoint, screenXform); (* transform point so we can project *)
  682.             currentBuf^[offset].transformed := thePoint; (* copy transformed point *)
  683.             GetVector4(thePoint, x, y, z); (* return to real *)
  684.  
  685. {currentBuf^[offset].transformedZ := z;}
  686.             if current3DPort^.projection = parallel then begin
  687.                 currentBuf^[offset].Screenx := Trunc(x) + current3DPort^.center.h;
  688.                 currentBuf^[offset].Screeny := -Trunc(y) + current3DPort^.center.v
  689.             end
  690.             else begin
  691.                 zbyd := 1 / (z / current3DPort^.d + 1);
  692.                 currentBuf^[offset].Screenx := Trunc(x * zbyd) + current3DPort^.center.h; (* do perspective transformation *)
  693.                 currentBuf^[offset].Screeny := -Trunc(y * zbyd) + current3DPort^.center.v;
  694.             end;
  695.  
  696.             offset := offset + 1;
  697.             pointIndex := pointIndex + 1;
  698.         end; (* while *)
  699.     end; (* Transform *)
  700.  
  701. procedure TSGenericObject3D.Transform2 (forceCalc: boolean);    (* calc trafo-matrix (if necessary) and convert    *)
  702.                                                                                 (*all points to their screen representation. gather *)
  703.                                                                                 (* information for auto-erasure                            *)
  704.                                                                                 (* points with negative z will not be collected into *)
  705.                                                                                 (* bounds rectangle                                               *)
  706.  
  707.     var
  708.         buffer, offset: integer;
  709.         pointIndex: longint;
  710.         thePoint: Vector4;
  711.         x, y, z: real;
  712.         zbyd: real;
  713.         sx, sy: integer;
  714.  
  715.     begin
  716.         if forceCalc or objChanged then (* object has changed. we must recalc it *)
  717.             begin
  718.             self.CalcTransform; (* resets versionID to zero and calcs xForm, passes on etc *)
  719.             self.screenXform := self.xForm; (* transfer it in case we don't use the eye *)
  720.             versionsID := -1; (* force check for useEye later on *)
  721.         end; (* if forcecalc or version *)
  722.  
  723.         if versionsID <> current3DPort^.versionsID then (* we must postconcat eye to xForm. result in screenXform *)
  724.             begin
  725.             self.screenXform := xForm; (* failsafe in case object didn't change but eye was switched off *)
  726.             versionsID := current3DPort^.versionsID; {sync eye and object. even if we don't use the eye anymore }
  727.             if current3DPort^.useEye then begin
  728.             (* PostConcat the Eye matrix to xForm *)
  729.                 self.screenXform := MMult(xForm, current3DPort^.MasterTransform);
  730.             end; (* if useEye fails we still have last xForm in screenXform *)
  731.         end;
  732.  
  733. (* transformation is pretty simple: for all points: fetch and multiply with xForm Matrix *)
  734. (* we will use currentBuf for faster access. If index mod pointsPerBuf returnes zero we will *)
  735. (* access a new buffer, otherwise we don't calculate anything since this is all straightforward *)
  736.         oldBounds := Bounds;
  737.         SetRect(Bounds, 32760, 32760, -32760, -32760); (* set bounds to minimal empty rect *)
  738.         pointIndex := 0;
  739.         offset := 0;
  740.         buffer := -1; (* will be incremented to 0 right away *)
  741.         while pointIndex < self.numPoints do begin
  742.             if offset mod (MaxPointPerBuf + 1) = 0 then (* advance buffer 1 *)
  743.                 begin
  744.                 buffer := buffer + 1;
  745.                 currentBuf := theBufs[buffer];
  746.                 if currentBuf = nil then
  747.                     DebugStr('About to access nil buffer in TSGenericObject3D.Transform');
  748.                 offset := 0;
  749.             end;
  750.         (* get the point *)
  751.             thePoint := currentBuf^[offset].koords;
  752.  
  753.         (* Transform the point *)
  754.             thePoint := VMult(thePoint, screenXform); (* transform point so we can project *)
  755.             currentBuf^[offset].transformed := thePoint;
  756.             GetVector4(thePoint, x, y, z); (* return to real *)
  757.  
  758. {currentBuf^[offset].transformedZ := z;}
  759.             if current3DPort^.projection = parallel then begin
  760.                 sx := Trunc(x) + current3DPort^.center.h;
  761.                 currentBuf^[offset].Screenx := sx;
  762.                 sy := -Trunc(y) + current3DPort^.center.v;
  763.                 currentBuf^[offset].Screeny := sy
  764.             end
  765.             else begin
  766.                 zbyd := 1 / (z / current3DPort^.d + 1);
  767.                 sx := Trunc(x * zbyd) + current3DPort^.center.h; (* do perspective transformation *)
  768.                 currentBuf^[offset].Screenx := sx;
  769.                 sy := -Trunc(y * zbyd) + current3DPort^.center.v;
  770.                 currentBuf^[offset].Screeny := sy;
  771.             end;
  772.  
  773.             if z >= 0 then (* do this only if point is drawn *)
  774.                 begin
  775.                 if sx < Bounds.left then (* gather data for autoerase *)
  776.                     Bounds.left := sx - 1;
  777.                 if sx > Bounds.right then
  778.                     Bounds.right := sx + 1;
  779.                 if sy < Bounds.top then
  780.                     Bounds.top := sy - 1;
  781.                 if sy > Bounds.bottom then
  782.                     Bounds.bottom := sy + 1;
  783.             end;
  784.  
  785.             offset := offset + 1;
  786.             pointIndex := pointIndex + 1;
  787.         end; (* while *)
  788. {insetRect(Bounds, -1, -1); {now done in draw and GetLineData }
  789.     end; (* Transform *)
  790.  
  791. procedure TSGenericObject3D.CalcBounds;            {calc bounds of object on screen and place it in }
  792.                                                                 {the oldBounds variable                            }
  793.     var
  794.         buffer, offset: integer;
  795.         pointIndex: longint;
  796.         x, y: integer;
  797.  
  798.     begin
  799.         SetRect(oldBounds, 32760, 32760, -32760, -32760); (* set bounds to minimal empty rect *)
  800.         pointIndex := 0;
  801.         offset := 0;
  802.         buffer := -1; (* will be incremented to 0 right away *)
  803.         while pointIndex < self.numPoints do begin
  804.             if offset mod (MaxPointPerBuf + 1) = 0 then (* advance buffer 1 *)
  805.                 begin
  806.                 buffer := buffer + 1;
  807.                 currentBuf := theBufs[buffer];
  808.                 if currentBuf = nil then
  809.                     DebugStr('About to access nil buffer in TSGenericObject3D.Transform');
  810.                 offset := 0;
  811.             end;
  812.  
  813.             x := currentBuf^[offset].Screenx; (* access them screen-coords *)
  814.             y := currentBuf^[offset].Screeny;
  815.             if x < oldBounds.left then (* gather data for autoerase *)
  816.                 oldBounds.left := x - 1;
  817.             if x > oldBounds.right then
  818.                 oldBounds.right := x + 1;
  819.             if y < oldBounds.top then
  820.                 oldBounds.top := y - 1;
  821.             if y > oldBounds.bottom then
  822.                 oldBounds.bottom := y + 1;
  823.  
  824.             offset := offset + 1;
  825.             pointIndex := pointIndex + 1;
  826.         end; (* while *)
  827.         insetRect(oldBounds, -1, -1);
  828.     end;
  829.  
  830. function TSGenericObject3D.TransformedPoint (index: longint): Vector4;
  831.                                                                                     {get transformed coordinates of point     }
  832.                                                                                     {with index. if illegal proc will return     }
  833.                                                                                     {0,0,0.      }
  834.                                                                                     {does not apply eye settings to it         }
  835.                                                                                     {you have to use ToScreen for that        }
  836.                                                                                     {calls transform if versionId <> 0        }
  837.  
  838.     var
  839.         buffer, offset: integer;
  840.         theVector: Vector4;
  841.  
  842.     begin
  843.         if objChanged then (* object has changed. we must recalc it *)
  844.             begin
  845.             self.CalcTransform; (* resets versionID to zero and calcs xForm, passes on etc *)
  846.             self.screenXform := self.xForm; (* transfer it in case we don't use the eye *)
  847.             versionsID := -1; (* force check for useEye later on *)
  848.         end; (* if forcecalc or version *)
  849.  
  850.         if versionsID <> current3DPort^.versionsID then (* we must postconcat eye to xForm. result in screenXform *)
  851.             begin
  852.             self.screenXform := xForm; (* failsafe in case object didn't change but eye was switched off *)
  853.             versionsID := current3DPort^.versionsID; {sync eye and object. even if we don't use the eye anymore }
  854.             if current3DPort^.useEye then begin
  855.             (* PostConcat the Eye matrix to xForm *)
  856.                 self.screenXform := MMult(xForm, current3DPort^.MasterTransform);
  857.             end; (* if useEye fails we still have last xForm in screenXform *)
  858.         end;
  859.  
  860.  
  861.         SetVector4(theVector, 0, 0, 0);
  862.         if (index < 1) or (index > self.numPoints) then begin
  863.             ErrorCode := cIllegalPointIndex;
  864.             TransformedPoint := theVector;
  865.             Exit(TransformedPoint);
  866.         end;
  867.         self.GenIndex(index - 1, buffer, offset);
  868.         theVector := theBufs[buffer]^[offset].koords;
  869.         TransformedPoint := VMult(theVector, self.screenXform);
  870.     end;
  871.  
  872. function TSGenericObject3D.ForeignPoint (p: Vector4): Vector4;
  873.     override;
  874.  
  875.     begin
  876.         if objChanged then (* object has changed. we must recalc it *)
  877.             begin
  878.             self.CalcTransform; (* resets versionID to zero and calcs xForm, passes on etc *)
  879.             self.screenXform := self.xForm; (* transfer it in case we don't use the eye *)
  880.             versionsID := -1; (* force check for useEye later on *)
  881.         end; (* if forcecalc or version *)
  882.  
  883.         if versionsID <> current3DPort^.versionsID then (* we must postconcat eye to xForm. result in screenXform *)
  884.             begin
  885.             self.screenXform := xForm; (* failsafe in case object didn't change but eye was switched off *)
  886.             versionsID := current3DPort^.versionsID; {sync eye and object. even if we don't use the eye anymore }
  887.             if current3DPort^.useEye then begin
  888.             (* PostConcat the Eye matrix to xForm *)
  889.                 self.screenXform := MMult(xForm, current3DPort^.MasterTransform);
  890.             end; (* if useEye fails we still have last xForm in screenXform *)
  891.         end;
  892.         ForeignPoint := VMult(p, self.screenXform)
  893.     end;
  894.  
  895.  
  896. function TSGenericObject3D.WorldToModel (wc: Vector4): Vector4; {xform world coordinates to model coordinates}
  897.     override;
  898.  
  899.     var
  900.         wcOrigin: Vector4;
  901.         Origin: vector4;
  902.  
  903.     begin
  904.     {we don't have to change anything because Foreign point is overriden and checks itself }
  905.     {for changes in the object and sceenXform}
  906.         SetVector4(Origin, 0, 0, 0); (* model coordinate origin *)
  907.         wcOrigin := ForeignPoint(Origin); (* get the origin in wc *)
  908.         WorldToModel := VSub(wc, wcOrigin); (* subtract global origin from global point to get local point *)
  909.     end;
  910.  
  911. procedure TSPoint3D.Reset;
  912.     begin
  913.         size := 2;
  914.         SetVector4(Koord, 0, 0, 0);
  915.         inherited Reset;
  916.     end;
  917.  
  918. procedure TSPoint3D.Init;
  919.     begin
  920.         size := 2;
  921.         SetVector4(Koord, 0, 0, 0);
  922.         inherited Init;
  923.     end;
  924.  
  925. procedure TSPoint3D.Draw;
  926.     override;
  927.     var
  928.         thePoint: Vector4;
  929.         x, y, z: real;
  930.         h, v: integer;
  931.         theRect: rect;
  932.         zbyd: real;
  933.  
  934.     begin
  935.         if (objChanged) then begin
  936.             self.CalcTransform; (* resets versionID to zero and calcs xForm, passes on etc *)
  937.         end;
  938.         if current3DPort^.useEye then begin
  939.             (* first check to see if the eye needs a recalc -- NOT IMPLEMENTED SINCE EYE ALWAYS RECALCS IMMEDIATELY *)
  940.             (* PostConcat the Eye matrix to xForm *)
  941.             self.xForm := MMult(xForm, current3DPort^.MasterTransform);
  942.         end;
  943.  
  944.         thePoint := VMult(Koord, xForm);
  945.         GetVector4(thePoint, x, y, z);
  946.         if current3DPort^.projection = parallel then
  947.  
  948.             begin
  949.             h := Trunc(x) + current3DPort^.center.h;
  950.             v := -Trunc(y) + current3DPort^.center.v
  951.         end
  952.         else begin
  953.             zbyd := 1 / (z / current3DPort^.d + 1);
  954.             h := Trunc(x * zbyd) + current3DPort^.center.h; (* do perspective transformation *)
  955.             v := -Trunc(y * zbyd) + current3DPort^.center.v;
  956.         end;
  957.         SetRect(theRect, h, v, h, v);
  958.         InsetRect(theRect, -size, -size);
  959.         PaintRect(theRect);
  960.     end;
  961.  
  962.  
  963. procedure TSLine3D.Draw;
  964.     override;
  965.     var
  966.         thePoint: Vector4;
  967.         x, y, z: real;
  968.         h, v: integer;
  969.         zbyd: real;
  970.  
  971.     begin
  972.         if (objChanged) then begin
  973.             self.CalcTransform; (* resets versionID to zero and calcs xForm, passes on etc *)
  974.         end;
  975.         if current3DPort^.useEye then begin
  976.             (* first check to see if the eye needs a recalc -- NOT IMPLEMENTED SINCE EYE ALWAYS RECALCS IMMEDIATELY *)
  977.             (* PostConcat the Eye matrix to xForm *)
  978.             self.xForm := MMult(xForm, current3DPort^.MasterTransform);
  979.         end;
  980.  
  981.         thePoint := VMult(FromLoc, xForm);
  982.         GetVector4(thePoint, x, y, z);
  983.         if current3DPort^.projection = parallel then
  984.  
  985.             begin
  986.             h := Trunc(x) + current3DPort^.center.h;
  987.             v := -Trunc(y) + current3DPort^.center.v
  988.         end
  989.         else begin
  990.             zbyd := 1 / (z / current3DPort^.d + 1);
  991.             h := Trunc(x * zbyd) + current3DPort^.center.h; (* do perspective transformation *)
  992.             v := -Trunc(y * zbyd) + current3DPort^.center.v;
  993.         end;
  994.         MoveTo(h, v);
  995.         thePoint := VMult(ToLoc, xForm);
  996.         GetVector4(thePoint, x, y, z);
  997.         if current3DPort^.projection = parallel then
  998.  
  999.             begin
  1000.             h := Trunc(x) + current3DPort^.center.h;
  1001.             v := -Trunc(y) + current3DPort^.center.v
  1002.         end
  1003.         else begin
  1004.             zbyd := 1 / (z / current3DPort^.d + 1);
  1005.             h := Trunc(x * zbyd) + current3DPort^.center.h; (* do perspective transformation *)
  1006.             v := -Trunc(y * zbyd) + current3DPort^.center.v;
  1007.         end;
  1008.         LineTo(h, v);
  1009.     end;
  1010. end. {impl }