-- File: CSGGraphicsImpl.mesa -- Last edited by Bier on July 6, 1983 4:20 pm -- Author: Eric Allan Bier in the summer of 1982 -- Contents: Implementation of a simple graphics package for 3d renderings. DIRECTORY CoordSys, CGColor, CSGGraphics, DisplayList3d, Graphics, GraphicsColor, Inline, Matrix3d, Shading, SVArtwork, SVPolygon3d, Real, SVVector3d; CSGGraphicsImpl: PROGRAM IMPORTS CoordSys, Graphics, GraphicsColor, Matrix3d, Shading, SVVector3d EXPORTS CSGGraphics = BEGIN Artwork: TYPE = REF ArtworkObj; ArtworkObj: TYPE = SVArtwork.ArtworkObj; Color: TYPE = GraphicsColor.Color; CoordSysObj: TYPE = CoordSys.CoordSysObj; CoordSystem: TYPE = REF CoordSysObj; DrawStyle: TYPE = CSGGraphics.DrawStyle; -- {wire, shaded, rayCast, normals}; LightSource: TYPE = REF LightSourceObj; LightSourceList: TYPE = Shading.LightSourceList; -- LIST OF LightSource; LightSourceObj: TYPE = Shading.LightSourceObj; Matrix4by4: TYPE = Matrix3d.Matrix4by4; Point2d: TYPE = Matrix3d.Point2d; Point3d: TYPE = Matrix3d.Point3d; Poly3d: TYPE = REF Poly3dObj; Poly3dObj: TYPE = SVPolygon3d.Poly3dObj; QualityMode: TYPE = CSGGraphics.QualityMode; StrokeEnds: TYPE = Graphics.StrokeEnds; Vector: TYPE = SVVector3d.Vector; Camera: TYPE = REF CameraObj; CameraObj: TYPE = CSGGraphics.CameraObj; FrameBox: TYPE = REF FrameBoxObj; FrameBoxObj: TYPE = CSGGraphics.FrameBoxObj; globalCPx, globalCPy: REAL _ 0; globalStrokeWidth: REAL _ 0; globalLine: Graphics.Path _ Graphics.NewPath[2]; CreateCamera: PUBLIC PROC [coordSys: CoordSystem, screenCS: CoordSystem, focalLength: REAL, style: DrawStyle, resolution: REAL _ 72.0] RETURNS [camera: Camera] = { frameBox: FrameBox _ NEW[FrameBoxObj _ [[0,0], [0,0], TRUE]]; camera _ NEW[CameraObj _ [coordSys, screenCS, focalLength, style, TRUE, resolution, frameBox, fast]]; }; -- end of CreateCamera PlaceCamera: PUBLIC PROC [camera: Camera, focus: Point3d, origin: Point3d] = { -- for now, tilt angle is zero. zAxis: Vector _ SVVector3d.Difference[origin, focus]; -- xAxis should be normal to zAxis, parallel to the xz plane, and counter-clockwise from the projection of z onto the xz plane. If z is vertical, I arbitrarily chose x axis aligned with world x axis. camera.coordSys.mat _ Matrix3d.MakeHorizontalMatFromZAxis[zAxis, origin]; }; SetFocalLengthCamera: PUBLIC PROC [camera: Camera, focalLength: REAL] = { camera.focalLength _ focalLength; }; SetQualityCamera: PUBLIC PROC [camera: Camera, qual: QualityMode] = { -- how you wish to make the speed/print quality trade off camera.quality _ qual; }; ColorFilmCamera: PUBLIC PROC [camera: Camera, colorFilm: BOOL] = { camera.colorFilm _ colorFilm; }; Clip: PUBLIC PROC [dc: Graphics.Context, camera: Camera] = { clipPath: Graphics.Path; downLeft, upRight: Point2d; IF camera.frame.fullScreen THEN RETURN; clipPath _ Graphics.NewPath[4]; downLeft _ CoordSys.CameraToScreen[camera.frame.downLeft, camera.screenCS]; -- puts into SCREEN coords upRight _ CoordSys.CameraToScreen[camera.frame.upRight, camera.screenCS]; Graphics.MoveTo[clipPath, downLeft[1], downLeft[2]]; Graphics.LineTo[clipPath, downLeft[1], upRight[2]]; Graphics.LineTo[clipPath, upRight[1], upRight[2]]; Graphics.LineTo[clipPath, upRight[1], downLeft[2]]; Graphics.ClipArea[dc, clipPath, FALSE, FALSE]; }; DrawFrame: PUBLIC PROC [dc: Graphics.Context, camera: Camera] = { downLeft, upRight: Point2d; IF camera.frame.fullScreen THEN RETURN; downLeft _ CoordSys.CameraToScreen[camera.frame.downLeft, camera.screenCS]; upRight _ CoordSys.CameraToScreen[camera.frame.upRight, camera.screenCS]; Graphics.SetCP[dc, downLeft[1], downLeft[2]]; Graphics.DrawTo[dc, downLeft[1], upRight[2]]; Graphics.DrawTo[dc, upRight[1], upRight[2]]; Graphics.DrawTo[dc, upRight[1], downLeft[2]]; Graphics.DrawTo[dc, downLeft[1], downLeft[2]]; }; -- fast, low quality GetPerspective: PUBLIC PROC [point3d: Point3d, camera: Camera] RETURNS [point2d: Point2d] = { point2d _ Matrix3d.GetPerspective[point3d, camera.focalLength]; }; -- assumes point3d expressed in CAMERA coordinate system. LocalToCamera: PUBLIC PROC [localPoint: Point3d, localCS: CoordSystem] RETURNS [cameraPoint: Point3d] = { cameraPoint _ Matrix3d.Update[localCS.wrtCamera,localPoint]; -- puts in CAMERA }; LocalToWorld: PUBLIC PROC [localPt: Point3d, localCS: CoordSystem] RETURNS [worldPt: Point3d] = { worldPt _ Matrix3d.Update[localCS.wrtWorld, localPt]; }; VectorToWorld: PUBLIC PROC [vector: Vector, localCS: CoordSystem] RETURNS [worldVector: Vector] = { worldVector _ Matrix3d.UpdateVectorWithInverse[localCS.worldWRTlocal,vector]; }; SetCP: PUBLIC PROC [dc: Graphics.Context, point3d: Point3d, camera: Camera, localCS: CoordSystem] = { screenPoint: Point2d; point3d _ LocalToCamera[point3d, localCS]; -- puts in CAMERA screenPoint _ Matrix3d.GetPerspective[point3d, camera.focalLength]; screenPoint _ CoordSys.CameraToScreen[screenPoint, camera.screenCS]; -- puts into SCREEN coords IF camera.quality = fast THEN Graphics.SetCP[dc, screenPoint[1], screenPoint[2]] ELSE {Graphics.MoveTo[globalLine, screenPoint[1], screenPoint[2]]; Graphics.SetCP[dc, screenPoint[1], screenPoint[2]]}; }; MoveTo: PUBLIC PROC [path: Graphics.Path, point3d: Point3d, camera: Camera, localCS: CoordSystem] = { objPoint: Point2d; point3d _ LocalToCamera[point3d, localCS]; -- puts in CAMERA objPoint _ Matrix3d.GetPerspective[point3d, camera.focalLength]; objPoint _ CoordSys.CameraToScreen[objPoint, camera.screenCS]; -- puts into SCREEN coords Graphics.MoveTo[path, objPoint[1], objPoint[2]]; }; SetCPAbsolute: PUBLIC PROC [dc: Graphics.Context, point3d: Point3d, camera: Camera] = { objPoint: Point2d; objPoint _ Matrix3d.GetPerspective[point3d, camera.focalLength]; objPoint _ CoordSys.CameraToScreen[objPoint, camera.screenCS]; -- puts into SCREEN coords IF camera.quality = fast THEN Graphics.SetCP[dc, objPoint[1], objPoint[2]] ELSE {Graphics.MoveTo[globalLine, objPoint[1], objPoint[2]]; Graphics.SetCP[dc, objPoint[1], objPoint[2]]; }; }; MoveToAbsolute: PUBLIC PROC [path: Graphics.Path, point3d: Point3d, camera: Camera] = { objPoint: Point2d; objPoint _ Matrix3d.GetPerspective[point3d, camera.focalLength]; objPoint _ CoordSys.CameraToScreen[objPoint, camera.screenCS]; -- puts into SCREEN coords Graphics.MoveTo[path, objPoint[1], objPoint[2]]; }; -- assumes point3d is a point in CAMERA coordinate system LineTo: PUBLIC PROC [path: Graphics.Path, point3d: Point3d, camera: Camera, localCS: CoordSystem] = { objPoint: Point2d; point3d _ LocalToCamera[point3d, localCS]; -- puts in CAMERA objPoint _ Matrix3d.GetPerspective[point3d, camera.focalLength]; objPoint _ CoordSys.CameraToScreen[objPoint, camera.screenCS]; -- puts into SCREEN coords Graphics.LineTo[path, objPoint[1], objPoint[2]]; }; DrawTo: PUBLIC PROC [dc: Graphics.Context, point3d: Point3d, camera: Camera, localCS: CoordSystem] = { screenPoint: Point2d; point3d _ LocalToCamera[point3d, localCS]; -- puts in CAMERA screenPoint _ Matrix3d.GetPerspective[point3d, camera.focalLength]; screenPoint _ CoordSys.CameraToScreen[screenPoint, camera.screenCS]; -- puts into SCREEN coords IF camera.quality = fast THEN Graphics.DrawTo[dc, screenPoint[1], screenPoint[2]] ELSE {Graphics.LineTo[globalLine, screenPoint[1], screenPoint[2]]; Graphics.DrawStroke[dc, globalLine, 1, FALSE, round]; Graphics.MoveTo[globalLine, screenPoint[1], screenPoint[2]]; -- flush path }; }; LineToAbsolute: PUBLIC PROC [path: Graphics.Path, point3d: Point3d, camera: Camera] = { objPoint: Point2d; objPoint _ Matrix3d.GetPerspective[point3d, camera.focalLength]; objPoint _ CoordSys.CameraToScreen[objPoint, camera.screenCS]; -- puts into SCREEN coords Graphics.LineTo[path, objPoint[1], objPoint[2]]; }; DrawToAbsolute: PUBLIC PROC [dc: Graphics.Context, point3d: Point3d, camera: Camera] = { objPoint: Point2d; objPoint _ Matrix3d.GetPerspective[point3d, camera.focalLength]; objPoint _ CoordSys.CameraToScreen[objPoint, camera.screenCS]; -- puts into SCREEN coords IF camera.quality = fast THEN Graphics.DrawTo[dc, objPoint[1], objPoint[2]] ELSE {Graphics.LineTo[globalLine, objPoint[1], objPoint[2]]; Graphics.DrawStroke[dc, globalLine, 1, FALSE, round]; }; }; DrawStroke: PUBLIC PROC [dc: Graphics.Context, path: Graphics.Path, width: REAL _ 1, closed: BOOLEAN _ FALSE, ends: StrokeEnds _ butt] = { Graphics.DrawStroke[dc, path, width, closed, ends]; }; DrawLine: PUBLIC PROC [dc: Graphics.Context, start: Point3d, end: Point3d, camera: Camera, localCS: CoordSystem] = { objPoint: Point2d; start _ LocalToCamera[start, localCS]; -- puts in CAMERA objPoint _ Matrix3d.GetPerspective[start, camera.focalLength]; objPoint _ CoordSys.CameraToScreen[objPoint, camera.screenCS]; -- puts into SCREEN coords IF camera.quality = quality THEN Graphics.MoveTo[globalLine, objPoint[1], objPoint[2]] ELSE Graphics.SetCP[dc, objPoint[1], objPoint[2]]; end _ LocalToCamera[end, localCS]; -- puts in CAMERA objPoint _ Matrix3d.GetPerspective[end, camera.focalLength]; objPoint _ CoordSys.CameraToScreen[objPoint, camera.screenCS]; -- puts into SCREEN coords IF camera.quality = quality THEN { Graphics.LineTo[globalLine, objPoint[1], objPoint[2]]; Graphics.DrawStroke[dc, globalLine, 1, FALSE, butt]; } ELSE Graphics.DrawTo[dc, objPoint[1], objPoint[2]]; }; -- assumes point3d is a point in CAMERA coordinate system SetColor: PRIVATE PROC [dc: Graphics.Context, camera: Camera, color: Color] = { IF camera.colorFilm THEN Graphics.SetColor [dc, color] ELSE { intensity: REAL _ GraphicsColor.ColorToIntensity[color]; Graphics.SetColor[dc, GraphicsColor.IntensityToColor[intensity]]}; }; DrawArea: PUBLIC PROC [dc: Graphics.Context, normal: Vector, poly3d: Poly3d, artwork: Artwork, lightSources: LightSourceList, camera: Camera, localCS: CoordSystem] = { -- given an ordered list (array) of Point3d's, defined in the current local coordinate system, find the corresponding WORLD points, and project the polygon they define onto the image plane with a perspective projection. worldPoint3d, cameraPoint3d: Point3d; objPoint: Point2d; colorshade: Graphics.Color; cameraNormal: Vector; worldNormal: Vector; eyepoint: Point3d; outline: Graphics.Path _ Graphics.NewPath[poly3d.len]; -- Find the real normal cameraNormal _ Matrix3d.UpdateVectorWithInverse[localCS.cameraWRTlocal, normal]; worldNormal _ Matrix3d.UpdateVectorWithInverse[localCS.worldWRTlocal, normal]; -- a reverse-facing surface is one which is not visible to the camera. In our current world of opaque surfaces, we need not draw reverse-facing surfaces. To detect a reverse-facing surface, take its normal N. Find the dot product of N with the vector from the a point on the surface to the camera lens. If the result is negative, the surface is reverse-facing. -- a simpler test which removes most reverse-facing surfaces is this one: IF cameraNormal[3]<=0 THEN RETURN; -- don't draw reverse-facing surfaces -- Move to the first point worldPoint3d _ Matrix3d.Update[localCS.wrtWorld, poly3d[0]]; cameraPoint3d _ LocalToCamera[poly3d[0], localCS]; objPoint _ Matrix3d.GetPerspective[cameraPoint3d, camera.focalLength]; objPoint _ CoordSys.CameraToScreen[objPoint, camera.screenCS]; -- puts into SCREEN coords Graphics.MoveTo[outline, objPoint[1], objPoint[2]]; -- use this point to estimate surface color eyepoint _ LocalToWorld[[0,0,camera.focalLength], camera.coordSys]; SELECT artwork.material FROM plastic => colorshade _ Shading.DiffuseAndSpecularReflectance [eyepoint, worldNormal, worldPoint3d, artwork.color, lightSources]; chalk => colorshade _ Shading.DiffuseReflectance [worldNormal, worldPoint3d, artwork.color, lightSources]; ENDCASE => ERROR; -- create a path which forms the edges of this area FOR i: NAT IN[1..poly3d.len) DO cameraPoint3d _ LocalToCamera[poly3d[i], localCS]; -- now that we know where the point really is, calculate brightness (implement later) objPoint _ Matrix3d.GetPerspective[cameraPoint3d, camera.focalLength]; objPoint _ CoordSys.CameraToScreen[objPoint, camera.screenCS]; -- puts into SCREEN coords Graphics.LineTo[outline, objPoint[1], objPoint[2]]; ENDLOOP; SetColor[dc, camera, colorshade]; Graphics.DrawArea[dc, outline]; }; DrawAreaNormalAbsolute: PUBLIC PROC [dc: Graphics.Context, normal: Vector, poly3d: Poly3d, artwork: Artwork, lightSources: LightSourceList, camera: Camera, localCS: CoordSystem] = { -- normal is assumed to be in Camera coordinates. poly3d in local coordinates. -- given an ordered list (array) of Point3d's, defined in the current local coordinate system, find the corresponding WORLD points, and project the polygon they define onto the image plane with a perspective projection. worldPoint3d, cameraPoint3d: Point3d; objPoint: Point2d; colorshade: Graphics.Color; worldNormal: Vector; eyepoint: Point3d; outline: Graphics.Path _ Graphics.NewPath[poly3d.len]; -- Find the real normal worldNormal _ Matrix3d.UpdateVectorWithInverse[camera.coordSys.worldWRTlocal, normal]; -- a reverse-facing surface is one which is not visible to the camera. In our current world of opaque surfaces, we need not draw reverse-facing surfaces. To detect a reverse-facing surface, take its normal N. Find the dot product of N with the vector from the a point on the surface to the camera lens. If the result is negative, the surface is reverse-facing. -- a simpler test which removes most reverse-facing surfaces is this one: IF normal[3]<=0 THEN RETURN; -- Move to the first point worldPoint3d _ Matrix3d.Update[localCS.wrtWorld, poly3d[0]]; cameraPoint3d _ LocalToCamera[poly3d[0], localCS]; objPoint _ Matrix3d.GetPerspective[cameraPoint3d, camera.focalLength]; objPoint _ CoordSys.CameraToScreen[objPoint, camera.screenCS]; -- puts into SCREEN coords Graphics.MoveTo[outline, objPoint[1], objPoint[2]]; -- use this point to estimate surface color eyepoint _ LocalToWorld[[0,0,camera.focalLength], camera.coordSys]; SELECT artwork.material FROM plastic => colorshade _ Shading.DiffuseAndSpecularReflectance [eyepoint, worldNormal, worldPoint3d, artwork.color, lightSources]; chalk => colorshade _ Shading.DiffuseReflectance [worldNormal, worldPoint3d, artwork.color, lightSources]; ENDCASE => ERROR; -- create a path which forms the edges of this area FOR i: NAT IN[1..poly3d.len) DO cameraPoint3d _ LocalToCamera[poly3d[i], localCS]; -- now that we know where the point really is, calculate brightness (implement later) objPoint _ Matrix3d.GetPerspective[cameraPoint3d, camera.focalLength]; objPoint _ CoordSys.CameraToScreen[objPoint, camera.screenCS]; -- puts into SCREEN coords Graphics.LineTo[outline, objPoint[1], objPoint[2]]; ENDLOOP; SetColor[dc, camera, colorshade]; Graphics.DrawArea[dc, outline]; }; DrawAreaAbsolute: PUBLIC PROC [dc: Graphics.Context, poly3d: Poly3d, camera: Camera] = { -- assumes poly3d in camera coords outline: Graphics.Path _ Graphics.NewPath[poly3d.len]; cameraPoint3d: Point3d; objPoint: Point2d; cameraPoint3d _ poly3d[0]; objPoint _ Matrix3d.GetPerspective[cameraPoint3d, camera.focalLength]; objPoint _ CoordSys.CameraToScreen[objPoint, camera.screenCS]; -- puts into SCREEN coords Graphics.MoveTo[outline, objPoint[1], objPoint[2]]; FOR i: NAT IN[1..poly3d.len) DO cameraPoint3d _ poly3d[i]; objPoint _ Matrix3d.GetPerspective[cameraPoint3d, camera.focalLength]; objPoint _ CoordSys.CameraToScreen[objPoint, camera.screenCS]; -- puts into SCREEN coords Graphics.LineTo[outline, objPoint[1], objPoint[2]]; ENDLOOP; Graphics.DrawArea[dc, outline]; }; -- end of DrawAreaAbsolute END.