-- 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.