SetEyeSpace:
PUBLIC
PROC[ context:
REF Context ] ~ {
in, right, up, normal: Triple;
mtx: Xfm3d;
wndw: Imager.Rectangle;
aspectRatio, viewWidth, viewHeight: REAL;
bounds: Pixels.Extent ← [
0, 0, Real.RoundC[context.viewPort.w], Real.RoundC[context.viewPort.h] ];
aspectRatio ← Real.Float[bounds.w] / bounds.h;
transform from world to eye
in ← Vector3d.Normalize[Vector3d.Sub[context.ptOfInterest, context.eyePoint]];
right ← Vector3d.Normalize[Vector3d.Cross[in, context.upDirection]];
up ← Vector3d.Normalize[Vector3d.Cross[right, in]];
normalize not really needed (to avoid roundoff problems)
context.eyeSpaceXfm ← Matrix3d.Identity[];
context.eyeSpaceXfm[0][0] ← right.x;
context.eyeSpaceXfm[1][0] ← right.y;
context.eyeSpaceXfm[2][0] ← right.z;
context.eyeSpaceXfm[3][0] ← -Vector3d.Dot[right, context.eyePoint];
context.eyeSpaceXfm[0][1] ← up.x;
context.eyeSpaceXfm[1][1] ← up.y;
context.eyeSpaceXfm[2][1] ← up.z;
context.eyeSpaceXfm[3][1] ← -Vector3d.Dot[up, context.eyePoint];
context.eyeSpaceXfm[0][2] ← in.x;
context.eyeSpaceXfm[1][2] ← in.y;
context.eyeSpaceXfm[2][2] ← in.z;
context.eyeSpaceXfm[3][2] ← -Vector3d.Dot[in, context.eyePoint];
mtx ← Matrix3d.Identity[]; -- Roll about z-axis, clockwise
mtx[0][0] ← RealFns.CosDeg[context.rollAngle];
mtx[0][1] ← -RealFns.SinDeg[context.rollAngle];
mtx[1][0] ← RealFns.SinDeg[context.rollAngle];
mtx[1][1] ← RealFns.CosDeg[context.rollAngle];
context.eyeSpaceXfm ← Matrix3d.Mul[context.eyeSpaceXfm, mtx];
viewWidth ← 2.*RealFns.TanDeg[context.fieldOfView/2.];
wndw.x ← MIN[1.0, MAX[-1.0, context.window.x] ];
wndw.w ← MIN[1.0-wndw.x, MAX[0.0, context.window.w] ];
wndw.y ← MIN[1.0, MAX[-1.0, context.window.y] ];
wndw.h ← MIN[1.0-wndw.y, MAX[0.0, context.window.h] ];
context.clippingPlanes[Near] ← [0., 0., 1., -context.hitherLimit];
context.clippingPlanes[Far] ← [0., 0., -1., context.yonLimit];
normalize plane equation for true distances
normal ← Vector3d.Normalize[[1., 0., -(wndw.x * viewWidth/2.)]];
context.clippingPlanes[Left] ← [normal.x, 0., normal.z, 0.];
normal ← Vector3d.Normalize[[-1., 0., (wndw.x + wndw.w) * viewWidth/2.]];
context.clippingPlanes[Right] ← [normal.x, 0., normal.z, 0.];
normal ← Vector3d.Normalize[[0., 1., -(wndw.y * viewWidth/2.)]];
context.clippingPlanes[Bottom] ← [0., normal.y, normal.z, 0.];
normal ← Vector3d.Normalize[[0., -1., (wndw.y + wndw.h) * viewWidth/2.]];
context.clippingPlanes[Top] ← [0., normal.y, normal.z, 0.];
compute the transformation from the eye coord sys to normalized display coordinates (-1.—1.)
IF aspectRatio > 1.0
-- set angle of view on smallest viewport dimension
THEN { viewHeight ← viewWidth; viewWidth ← viewWidth * aspectRatio; }
ELSE { viewHeight ← viewWidth / aspectRatio; };
IF wndw.w / wndw.h > 1.0
THEN { viewWidth ← viewWidth * wndw.h / wndw.w; }
ELSE { viewHeight ← viewHeight * wndw.w / wndw.h; };
context.eyeToNDC.addX ← -(wndw.x / wndw.w);
context.eyeToNDC.addY ← -(wndw.y / wndw.h);
context.eyeToNDC.addZ ← 1./(1. - context.hitherLimit/context.yonLimit);
context.eyeToNDC.scaleX ← 1./(wndw.w * viewWidth / 2.0);
context.eyeToNDC.scaleY ← 1./(wndw.h * viewHeight / 2.0);
context.eyeToNDC.scaleZ ← -context.hitherLimit/(1. - context.hitherLimit/context.yonLimit);
};
SetViewPort:
PUBLIC
PROC[context:
REF Context, size: Imager.Rectangle,
clear:
BOOLEAN ←
FALSE] ~{
This must be called before SetView since it defines the aspect ratio
bounds: Imager.Rectangle ← size;
imagerCtx: Imager.Context ←
NARROW[
Atom.GetPropFromList[context.display.props, $ImagerContext]
];
IF size.w <= 0.0
OR size.h <= 0.0
THEN SIGNAL Error[[$MisMatch, "Null clipper"]];
IF context.renderMode # $LF
THEN
FOR i:
NAT
IN [0..context.display.pixels.length)
DO
x: INTEGER ← MAX[ 0, MIN[ Real.RoundI[MAX[size.x, bounds.x]], context.display.width ] ];
y: INTEGER ← MAX[ 0, MIN[ Real.RoundI[MAX[size.y, bounds.y]], context.display.height ]];
w: INTEGER ← MIN[Real.RoundC[MIN[size.w, bounds.w] ], context.display.width];
h: INTEGER ← MIN[Real.RoundC[MIN[size.h, bounds.h] ], context.display.height];
y ← context.display.height - (y + h); -- invert for upside-down sampleMap
context.display.pixels[i].subMap.start ← [ f: x, s: y ]; -- upper left corner
context.display.pixels[i].subMap.size ← [f: w, s: h]; -- dimensions
ENDLOOP;
context.viewPort ← size;
IF imagerCtx #
NIL
THEN {
QuickViewer.Reset[imagerCtx];
Imager.ClipRectangle[imagerCtx, [size.x, size.y, size.w, size.h] ];
Imager.TranslateT[ imagerCtx, [size.x, size.y] ];
IF clear THEN FillViewPort[context, [0.0, 0.0, 0.0] ];
};
};
FillInConstantBackGround:
PROC[context:
REF Context, bkgrdClr:
RGB] ~ {
For use with alpha buffer to load background behind shapes
addOn: NAT ← 0;
bkgrdBytes: SampleSet;
alpha: REF NAT ← NARROW[ Atom.GetPropFromList[context.display.props, $Alpha] ];
depth: REF NAT ← NARROW[ Atom.GetPropFromList[context.display.props, $Depth] ];
IF depth # NIL THEN addOn ← addOn + 1;
SELECT context.renderMode
FROM
$LF => SIGNAL Error[[$MisMatch, "Improper renderMode"]];
$FullClr, $Dorado24 => {
bkgrdBytes ← Pixels.GetSampleSet[4+addOn];
bkgrdBytes[0] ← Real.FixC[bkgrdClr.R * 255.0];
bkgrdBytes[1] ← Real.FixC[bkgrdClr.G * 255.0];
bkgrdBytes[2] ← Real.FixC[bkgrdClr.B * 255.0];
};
$Grey => {
bkgrdBytes ← Pixels.GetSampleSet[2+addOn];
bkgrdBytes[0] ← Real.FixC[(bkgrdClr.R+bkgrdClr.G+bkgrdClr.B)/3.0 * 255.0];
};
ENDCASE => {
bkgrdBytes ← Pixels.GetSampleSet[2+addOn];
bkgrdBytes[0] ← ScanConvert.MappedRGB[ context.renderMode, [
Real.FixC[bkgrdClr.R * 255.0],
Real.FixC[bkgrdClr.G * 255.0],
Real.FixC[bkgrdClr.B * 255.0]
] ];
};
bkgrdBytes[alpha^] ← 255; -- fill in behind, full coverage
IF depth # NIL THEN bkgrdBytes[depth^] ← LAST[CARDINAL]; -- set uncovered to max depth
IF context.extentCovered.bottom > context.extentCovered.top
THEN Pixels.PixelOp[
-- whole viewport, nothing covered
context.display,
[ 0, 0, Real.FixC[context.viewPort.w], Real.FixC[context.viewPort.h] ],
bkgrdBytes,
$Write
]
ELSE {
Pixels.PixelOp[
-- below previously affected area
context.display,
[ 0, 0, Real.FixC[context.viewPort.w], context.extentCovered.bottom ],
bkgrdBytes,
$Write
];
IF Real.FixC[context.viewPort.h] > context.extentCovered.top
THEN Pixels.PixelOp[
context.display, -- above previously affected area
[ 0,
context.extentCovered.top,
Real.FixC[context.viewPort.w],
Real.FixC[context.viewPort.h] - context.extentCovered.top
],
bkgrdBytes,
$Write
];
Pixels.PixelOp[
-- left of previously affected area
context.display,
[ 0,
context.extentCovered.bottom,
context.extentCovered.left,
context.extentCovered.top - context.extentCovered.bottom
],
bkgrdBytes,
$Write
];
IF Real.FixC[context.viewPort.w] > context.extentCovered.right
THEN Pixels.PixelOp[
context.display, -- right of previously affected area
[ context.extentCovered.right,
context.extentCovered.bottom,
Real.FixC[context.viewPort.w] - context.extentCovered.right,
context.extentCovered.top - context.extentCovered.bottom
],
bkgrdBytes,
$Write
];
Pixels.PixelOp[
-- previously affected area
context.display,
[ context.extentCovered.left,
context.extentCovered.bottom,
context.extentCovered.right - context.extentCovered.left,
context.extentCovered.top - context.extentCovered.bottom
],
bkgrdBytes,
$WriteUnder
];
};
Set uncovered (negative width and height) indicates no alpha information
context.extentCovered ← [Real.RoundC[context.viewPort.w], 0,
Real.RoundC[context.viewPort.h], 0];
};
SetLight:
PUBLIC PROC[context:
REF Context, name: Rope.
ROPE, position: Triple,
color:
RGB ← [1.,1.,1.] ]
RETURNS[
REF ShapeInstance] ~ {
light: REF ShapeInstance ← FindShape[ context.shapes, name ! Error =>
IF reason.code = $MisMatch THEN RESUME ];
IF light =
NIL
THEN {
light ← NewShape[ name ]; -- name not used before
context.lights ← AddShape[ context.lights, light ]; -- used just for lighting
context.shapes ← AddShape[ context.shapes, light ]; -- all shapes
};
light.type ← $Light;
light.location ← position;
light.boundingRadius ← 2 * 93000000.0 * 1609.344; -- twice solar distance in meters
light.orientation ← [0.,0.,0.]; -- no orientation by default
light.positionInValid ← TRUE;
light.vtcesInValid ← TRUE;
PutShading[ light, $Color, NEW[RGB ← color] ];
light.props ← Atom.PutPropOnList[light.props, $Hidden, $DoIt]; -- hide from display routines
FOR i:
NAT
IN [0..context.shapes.length)
DO
context.shapes[i].shadingInValid ← TRUE;
ENDLOOP;
RETURN[light];
};
WriteScene:
PUBLIC PROC[context:
REF Context, output:
IO.STREAM] ~ {
Vec3toRope:
PROC[ r1, r2, r3:
REAL]
RETURNS[Rope.
ROPE] ~ {
rope: Rope.ROPE;
rope ← Rope.Cat[ " ", Convert.RopeFromReal[r1], " ", Convert.RopeFromReal[r2] ];
rope ← Rope.Cat[ rope, " ", Convert.RopeFromReal[r3], " " ];
RETURN[ rope ];
};
Vec4toRope:
PROC[ r1, r2, r3, r4:
REAL]
RETURNS[Rope.
ROPE] ~ {
rope: Rope.ROPE;
rope ← Rope.Cat[ " ", Convert.RopeFromReal[r1], " ", Convert.RopeFromReal[r2], " " ];
rope ← Rope.Cat[ rope, Convert.RopeFromReal[r3], " ", Convert.RopeFromReal[r4], " " ];
RETURN[ rope ];
};
ref: REF;
line ← Rope.Cat["
View: ",
Vec3toRope[context.eyePoint.x, context.eyePoint.y, context.eyePoint.z],
Vec3toRope[context.ptOfInterest.x, context.ptOfInterest.y, context.ptOfInterest.z],
Vec3toRope[context.upDirection.x, context.upDirection.y, context.upDirection.z]
];
IO.PutRope[ output, Rope.Cat[line,
Vec4toRope[ context.rollAngle, context.fieldOfView, context.hitherLimit, context.yonLimit ],
"\n"
] ];
IO.PutRope[ output, Rope.Cat["
ViewPort: ",
Vec4toRope[context.viewPort.x, context.viewPort.y, context.viewPort.w, context.viewPort.h],
"\n"
] ];
IO.PutRope[ output, Rope.Cat["
Window: ",
Vec4toRope[context.window.x, context.window.y, context.window.w, context.window.h],
"\n"
] ];
ref ← Atom.GetPropFromList[context.props, $BackGround]; -- get background color
IF ref #
NIL
THEN {
color: RGB ← NARROW[ref, REF RGB]^;
IO.PutRope[ output,
Rope.Cat[ "BackgroundColor: ", Vec3toRope[color.R, color.G, color.B], "\n" ]
];
};
Light Sources
FOR i:
NAT
IN [0..context.lights.length)
DO
color: REF RGB ← NARROW[ GetShading[ context.lights[i], $Color ] ];
IO.PutRope[ output, Rope.Cat["
Light: ", context.lights[i].name,
Vec3toRope[
context.lights[i].location.x, context.lights[i].location.y, context.lights[i].location.z ],
Vec3toRope[color.R, color.G, color.B],
"\n"
] ];
ENDLOOP;
Shapes
FOR i:
NAT
IN [0..context.shapes.length)
DO
shape: REF ThreeDScenes.ShapeInstance ← context.shapes[i];
IF shape #
NIL
AND Atom.GetPropFromList[shape.props, $Hidden] =
NIL
AND shape.clipState # out
AND shape.surface #
NIL
THEN {
ref: REF ANY ← NIL;
xfm: Matrix3d.Matrix ← shape.position;
color: REF RGB ← NARROW[ GetShading[ shape, $Color ] ];
shadingType: ATOM ← NARROW[GetShading[ shape, $Type ] ];
transmtnce: REF REAL ← NARROW[GetShading[ shape, $Transmittance]];
shininess: REF REAL ← NARROW[GetShading[ shape, $Shininess ] ];
texture: REF TextureMaps.TextureMap ← NARROW[ GetShading[ shape, $TextureMap ] ];
File Name, instance name, and Surface Type
line ← Rope.Cat[ "Shape: ", shape.name, " ", shape.fileName, " " ];
line ← Rope.Cat[ line, Atom.GetPName[shape.type] ];
IF shape.insideVisible
THEN line ← Rope.Cat[line, " TRUE \n"]
ELSE line ← Rope.Cat[line, " FALSE \n"];
IO.PutRope[ output, line];
Shape Transform
IF xfm = NIL THEN { SetPosition[shape]; xfm ← shape.position; };
line ← Rope.Cat[" ShapeXfm: ",
Vec4toRope[xfm[0][0], xfm[0][1], xfm[0][2], xfm[0][3]],
Vec4toRope[xfm[1][0], xfm[1][1], xfm[1][2], xfm[1][3]] ];
line ← Rope.Cat[line,
Vec4toRope[xfm[2][0], xfm[2][1], xfm[2][2], xfm[2][3]],
Vec4toRope[xfm[3][0], xfm[3][1], xfm[3][2], xfm[3][3]], "\n" ];
IO.PutRope[ output, line];
Color, Transmittance, Shininess
IF shadingType = $Smooth AND color # NIL THEN IO.PutRope[output,
Rope.Cat[" SmoothColor: ", Vec3toRope[color.R, color.G, color.B], "\n"] ];
IF shadingType = $Faceted AND color # NIL THEN IO.PutRope[output,
Rope.Cat[" FacetedColor: ", Vec3toRope[color.R, color.G, color.B], "\n"] ];
IF transmtnce # NIL THEN IO.PutRope[ output,
Rope.Cat[" Transmittance: ", Convert.RopeFromReal[transmtnce^], "\n" ] ];
IF shininess #
NIL
THEN
IO.PutRope[ output,
Rope.Cat["
Shininess: ", Convert.RopeFromReal[shininess^], "\n" ] ];
Color Files
ref ← GetShading[ shape, $VertexColorFile ];
IF ref # NIL THEN IO.PutRope[ output,
Rope.Cat[" VertexColorFile: " , NARROW[ref, Rope.ROPE], "\n"] ];
ref ← GetShading[ shape, $PatchColorFile ];
IF ref #
NIL
THEN
IO.PutRope[ output,
Rope.Cat["
PatchColorFile: " ,
NARROW[ref, Rope.
ROPE], "\n"] ];
Mapped Texture
IF texture #
NIL
THEN {
fileName: Rope.ROPE ← NARROW[ Atom.GetPropFromList[texture.props, $FileName] ];
coordType: ATOM ← NARROW[ Atom.GetPropFromList[texture.props, $CoordType] ];
coords: REF ← Atom.GetPropFromList[texture.props, $Coords];
IF coordType = NIL THEN coordType ← $NoCoords;
line ← Rope.Cat[" MapTexture: ", fileName, " ", Atom.GetPName[texture.type] ];
line ← Rope.Cat[line, " ", Atom.GetPName[coordType], " " ];
SELECT coordType
FROM
$FromVtxNos, $FromNormals => {
argList: LIST OF REAL ← NARROW[coords];
WHILE argList #
NIL
DO
line ← Rope.Cat[line, Convert.RopeFromReal[argList.first], " " ];
argList ← argList.rest;
ENDLOOP;
};
$File => line ← Rope.Cat[line, " ", NARROW[coords, Rope.ROPE] ];
ENDCASE => SIGNAL Error[[$Unimplemented, "Unknown texture coordType"]];
IO.PutRope[ output, Rope.Cat[line, "\n"] ];
};
Solid Texture
ref ← GetShading[ shape, $ShadingProcs];
IF ref #
NIL
THEN {
getVtxProc: ThreeDScenes.VtxToRealSeqProc ← NIL;
getColorProc: ScanConvert.GetColorProc ← NIL;
[getVtxProc, getColorProc] ← NARROW[ ref, REF ThreeDScenes.ShadingProcs ]^;
IF getColorProc #
NIL
THEN {
procName: Rope.ROPE ← SolidTextures.ProcToRope[getColorProc];
IO.PutRope[ output, Rope.Cat[" SolidTexture: ", procName, "\n"] ];
};
};
};
ENDLOOP;
IO.PutRope[ output, "EndOfScene:\n"];
};