G3dNormalCodingImpl.mesa
Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved.
Glassner, April 14, 1989 1:35:37 pm PDT
Bloomenthal, July 15, 1992 10:43 pm PDT
DIRECTORY G3dBasic, G3dNormalCoding, G3dVector, Random, Real, RealFns;
G3dNormalCodingImpl: CEDAR PROGRAM
IMPORTS G3dVector, Random, Real, RealFns
EXPORTS G3dNormalCoding
~ BEGIN
Types
Triple:     TYPE ~ G3dBasic.Triple;
RandomStream:   TYPE ~ Random.RandomStream;
Custom Types
NtoGTable:    TYPE ~ ARRAY [0 .. 17] OF ARRAY [0 ..17] OF INT;
GtoNTable:    TYPE ~ ARRAY [0 .. 255] OF Triple;
Globals
-- NOTE that the order of these calls is important
ntogTable: NtoGTable ¬ InitNtoGTable[];
gtonTable: GtoNTable ¬ InitGtoNTable[];
rs: RandomStream ¬ Random.Create[range: 500, seed: 1989];
Encode a G3dRender Context3d
EncodeAllNormals: PUBLIC PROC [context3d: Context3d] ~ {
Action: PROC ~ {
width: NAT ← Real.Fix[context3d.viewPort.w];
height: NAT ← Real.Fix[context3d.viewPort.h];
scanSeg: PixelBuffer ← ImagerPixel.NewPixels[context3d.pixels.samplesPerPixel, width];
refV: REF NATNARROW[Atom.GetPropFromList[context3d.displayProps, $NormalBuffer]];
v: NAT;
IF refV # NIL
THEN { v ← refV^; }
ELSE G3dRender.Error[$Fatal, "Normals not available"];
FOR y: NAT IN [0..height) DO
ImagerPixel.GetPixels[          -- get scanline of pixels
self: context3d.pixels, pixels: scanSeg, initIndex: [f: 0, s: y], count: width ];
FOR x: NAT IN [0..width) DO
IF scanSeg[v+2][x] > 0
THEN scanSeg[0][x] ← EncodeNormal[
LOOPHOLE[scanSeg[v ][x], INTEGER]/32768.0,
-LOOPHOLE[scanSeg[v+1][x], INTEGER]/32768.0
]
ELSE scanSeg[0][x] ← 0;
ENDLOOP;
ImagerPixel.PutPixels[           -- return modified pixels
self: context3d.pixels, pixels: scanSeg, initIndex: [f: 0, s: y], count: width
];
ENDLOOP;
IF context3d.viewer # NIL THEN context3d.class.drawInViewer[
context3d,
NEW[G3dRender.ImagerProcRec ← [G3dColorDisplaySupport.StuffBuf, NIL]]
];
};
CedarProcess.DoWithPriority[background, Action];    -- be nice to other processess
};
Normal Encoding/Decoding Procedures
EncodeNormal: PUBLIC PROC [dx, dy: REAL, dither: BOOLEAN ¬ TRUE, userRS: RandomStream ¬ NIL] RETURNS [pval: INT] ~ {
scale: REAL ¬ 8.5;  -- 17/2.0
dz: REAL ¬ 1.0 - ((dx * dx) + (dy * dy));
xentry, yentry: INT;
lrs: RandomStream ¬ IF userRS = NIL THEN rs ELSE userRS;
IF dz < 0.0
THEN RETURN[0]
ELSE {
IF dither THEN {
slip: REAL ¬ 3.0/17.0; -- use 3.0/17.0 for good results, 1.0/17.0 for debugging
scale: REAL ¬ (2.0 * slip) / 499.0;
xfuzz: REAL ¬ (scale * Random.NextInt[lrs]) - slip;
yfuzz: REAL ¬ (scale * Random.NextInt[lrs]) - slip;
dx ¬ dx + xfuzz;
dy ¬ dy + yfuzz;
dz ¬ (dx * dx) + (dy * dy);
IF dz > 1.0 THEN { dz ¬ RealFns.SqRt[dz]; dx ¬ dx / dz; dy ¬ dy / dz; };
};
xentry ¬ MIN[16, Real.Floor[(1.0 + dx) * scale]];
yentry ¬ MIN[16, Real.Floor[(1.0 + dy) * scale]];
RETURN[ntogTable[yentry][xentry]];
};
};
DecodeNormal: PUBLIC PROC [pval: INT] RETURNS [n: Triple] ~ {
RETURN[gtonTable[pval]];
};
IsValidNormalIndex: PUBLIC PROC [index: INT] RETURNS [BOOLEAN] ~ {
RETURN[index >= 6 AND index < 255];
};
Normal Table Creation
The Normal Table:
We reserve 9 entries for special use:
0: Foreground color (usually black)
255: Background color (usually white)
1: Light Source Swatch
2: Material Swatch
3-5: Reserved
Thus there are 249 entries left, in the range [6 .. 254], all of which are used.
BuildNormalTables: PUBLIC PROC [] ~ {
-- NOTE that the order of these calls is important
ntogTable ¬ InitNtoGTable[];
gtonTable ¬ InitGtoNTable[];
the following calls are in no special order
rs ¬ Random.Create[range: 500, seed: 1989];
};
InitNtoGTable: PROC [] RETURNS [ng: NtoGTable] ~ {
GridCheck: PROC [centerx, centery, size: REAL] RETURNS [BOOLEAN] ~ {
d: REAL ¬ 1.0 - ((centerx * centerx) + (centery * centery));
IF d > 0.0 THEN RETURN [TRUE]
ELSE {
lx: REAL ¬ centerx - size;
hx: REAL ¬ centerx + size;
ly: REAL ¬ centery - size;
hy: REAL ¬ centery + size;
IF (lx*lx) + (ly*ly)  < 1.0 THEN RETURN [TRUE];
IF (lx*lx) + (hy*hy) < 1.0 THEN RETURN [TRUE];
IF (hx*hx) + (hy*hy) < 1.0 THEN RETURN [TRUE];
IF (hx*hx) + (ly*ly) < 1.0 THEN RETURN [TRUE];
RETURN [FALSE];
};
};
width: INT ¬ 17;
current: INT ¬ 6;
iwidth: REAL ¬ 1.0/width;
offset: REAL ¬ iwidth - 1.0;
inc: REAL ¬ 2.0 * iwidth;
FOR y: INT IN [0 .. width) DO
ny: REAL ¬ offset + (y * inc);
FOR x: INT IN [0 .. width) DO
nx: REAL ¬ offset + (x * inc);
IF GridCheck[nx, ny, iwidth]
THEN { ng[y][x] ¬ current; current ¬ current+1; }
ELSE { ng[y][x] ¬ -1; };
ENDLOOP;
ENDLOOP;
};
-- NOTE! You must call InitNtoGTable before calling InitGtoNTable
InitGtoNTable: PROC [] RETURNS [gn: GtoNTable] ~ {
hits: INT;
offset: REAL ¬ -16.0/17.0;
inc: REAL ¬ 2.0/17.0;
FOR a: INT IN [0 .. 255] DO gn[a] ¬ [-1.0, -1.0, -1.0]; ENDLOOP;
FOR y: INT IN [0 .. 16] DO
FOR x: INT IN [0 .. 16] DO
entry: INT ¬ ntogTable[y][x];
IF entry > 0 THEN {
ulx: REAL ¬ (x * inc) - 1.0;
uly: REAL ¬ (y * inc) - 1.0;
wid: REAL ¬ inc / 5.0;
norm: Triple ¬ [0.0, 0.0, 0.0];
hits ¬ 0;
FOR i: INT IN [0 .. 5] DO
fy: REAL ¬ uly + wid*i;
FOR j: INT IN [0 .. 5] DO
fx: REAL ¬ ulx + wid*j;
fz: REAL ¬ (fx*fx)+(fy*fy);
IF fz <= 1.0 THEN {
fz ¬ RealFns.SqRt[1.0 - fz];
norm ¬ G3dVector.Add[norm, [fx, fy, -fz]];
hits ¬ hits + 1;
};
ENDLOOP;
ENDLOOP;
gn[entry] ¬ G3dVector.Unit[norm];
};
ENDLOOP;
ENDLOOP;
};
END.