File: ArnonSphereClassImpl.mesa
Last edited by Bier on January 10, 1985 5:22:13 pm PST
Author: Dennis Arnon and Eric Bier on January 10, 1985 10:08:46 pm PST
DIRECTORY
Algebra3d,
CastRays,
CSG,
DisplayList3d,
IO,
Polynomial,
Rope,
SV2d,
SV3d,
SVModelTypes,
SVRayTypes,
SVSceneTypes,
SVVector3d,
TFI3d;
ArnonSphereClassImpl: PROGRAM
IMPORTS Algebra3d, CastRays, CSG, DisplayList3d, IO, Polynomial, SVVector3d, TFI3d
EXPORTS =
BEGIN
Camera: TYPE = SVModelTypes.Camera;
Point2d: TYPE = SV2d.Point2d;
Point3d: TYPE = SV3d.Point3d;
Shape: TYPE = SVSceneTypes.Shape;
Vector: TYPE = SV3d.Vector;
RAY CASTING TYPES
Assembly: TYPE = SVSceneTypes.Assembly;
Classification: TYPE = SVRayTypes.Classification;
Composite: TYPE = SVRayTypes.Composite;
MasterObject: TYPE = SVSceneTypes.MasterObject;
MasterObjectClass: TYPE = SVSceneTypes.MasterObjectClass;
MasterObjectClassList: TYPE = SVSceneTypes.MasterObjectClassList; -- LIST OF MasterObjectClass
Primitive: TYPE = SVRayTypes.Primitive;
Ray: TYPE = SVRayTypes.Ray;
globalPoly: Polynomial.Ref;
arnonSphereClass: MasterObjectClass;
ArnonSphereCast: PUBLIC PROC [cameraPoint: Point2d, localRay: Ray, masterObject: REF ANY, prim: Primitive]
RETURNS [class: Classification] = {
The ArnonSphere is:
2 2 2
F(x,y,z) = x + y + z - 1 = 0

arnonSphereData: ArnonSphereRec;
x0, x1, y0, y1, z0, z1: REAL;
a2, a1, a0: REAL;
p: Point3d;
d: Vector;
tArray: ARRAY[1..2] OF REAL;
rootCount: NAT;
ArnonSphereGrad: PROC [x, y, z: REAL] RETURNS [Vector] ~ {
gradx, grady, gradz: REAL;
gradx ← 2. * x;
grady ← 2. * y;
gradz ← 2. * z;
RETURN [[gradx, grady, gradz]];
};
pp: REAL;
t1, t2: REAL;
class ← CastRays.GetClassFromPool[];
arnonSphereData ← NARROW[arnonSphereRec];
[p, d] ← CSG.GetLocalRay[localRay]; -- p is base point, d is directiona
x0 ← p[1]; x1 ← d[1];
y0 ← p[2]; y1 ← d[2];
z0 ← p[3]; z1 ← d[3];
The result of substituting
F, x: x1 * t + x0, y: y1 * t + y0, z: z1 * t + z0;
in Macsyma:

2 2 2 2 2 2 2 2
(d4) t z1 + 2 t z0 z1 + z0 + t y1 + 2 t y0 y1 + y0 + t x1 + 2 t x0 x1
2
+ x0 - 1

a2 ← z1 * z1 + y1 * y1 + x1 * x1;
a1 ← 2.* ( z0 * z1 + y0 * y1 + x0 * x1 );
a0 ← z0 * z0 + y0 * y0 + x0 * x0 - 1;
[tArray, rootCount, ----] ← PositiveRoots[a2,a1,a0];
SELECT rootCount FROM
0 => {-- complete miss.
class.count ← 0;
class.classifs[1] ← FALSE;
};
1 => {
If the ray originates inside the sphere, treat as a single hit. Otherwise treat as a miss.
pp ← SVVector3d.DotProduct[p,p];
IF pp <1.0 THEN { -- ray originates inside the sphere
class.count ← 1;
t1 ← tArray[1];
class.classifs[1] ← TRUE;
class.classifs[2] ← FALSE;
class.params[1] ← t1;
class.surfaces[1] ← NIL;
class.normals[1][1] ← p[1] + d[1]*t1;
class.normals[1][2] ← p[2] + d[2]*t1;
class.normals[1][3] ← p[3] + d[3]*t1;
class.primitives[1] ← prim;
}
ELSE CastRays.MakeClassAMiss[class];
};
2 => {-- cuts one cross section of the ArnonSphere. Sort roots by size.
x, y, z: REAL;
class.count ← 2;
t1 ← tArray[1]; t2 ← tArray[2]; -- since tArray[1] < tArray[2] is guaranteed
class.params[1] ← t1; class.params[2] ← t2;
class.surfaces[1] ← NIL; class.surfaces[2] ← NIL;
class.classifs[1] ← FALSE; class.classifs[2] ← TRUE; class.classifs[3] ← FALSE;
class.primitives[1] ← class.primitives[2] ← prim;
Calculate the surface normals at the two hit points.
x ← x0 + class.params[1]*x1; y ← y0 + class.params[1]*y1; z ← z0 + class.params[1]*z1;
class.normals[1] ← ArnonSphereGrad[x, y, z];
To normalize, divide by norm. (I won't do this since the lighting model normalizes anyway)
x ← x0 + class.params[2]*x1; y ← y0 + class.params[2]*y1; z ← z0 + class.params[2]*z1;
class.normals[2] ← ArnonSphereGrad[x, y, z];
};
ENDCASE => ERROR;
};
PositiveRoots: PROCEDURE [a, b, c: REAL] RETURNS [rootArray: ARRAY[1..2] OF REAL, rootCount, totalRealRoots: NAT] = {
Use only the positive roots. Preserves the order of the roots, so they will still be in increasing order.
i: NAT ← 1;
[rootArray, rootCount] ← Algebra3d.QuadraticFormula[a, b, c];
IF rootCount = 0 THEN RETURN;
WHILE i <= rootCount DO
IF rootArray[i] <= 0 THEN {
FOR j: NAT IN [i..rootCount-1] DO
rootArray[j] ← rootArray[j+1];
ENDLOOP;
rootCount ← rootCount - 1;
}
ELSE {i ← i + 1};
ENDLOOP;
};
MakeMasterObject: PROC [name: Rope.ROPE] RETURNS [mo: MasterObject] = {
mainBody: REF ANYNIL;
lineBody: REF ANYNIL;
shadeBody: REF ANY ← lineBody;
rayCastBody: REF ANYNIL;
mo ← DisplayList3d.CreateMasterObject[name, arnonSphereClass, mainBody, lineBody, shadeBody, rayCastBody];
};
ArnonSphereFileout: PUBLIC PROC [f: IO.STREAM, mo: MasterObject] = {
Spheres can be built from scratch.
f.PutChar[IO.TAB];
f.PutF["data: procedural\n"];
};
ArnonSphereFilein: PUBLIC PROC [f: IO.STREAM, name: Rope.ROPE] RETURNS [mo: MasterObject] = {
TFI3d.ReadRope[f, "data: procedural"];
TFI3d.ReadBlank[f];
mo ← MakeMasterObject[name];
};
Init: PROC = {
arnonSphere: MasterObject;
globalPoly ← Polynomial.Quartic[[0,0,0,0,0]];
arnonSphereClass ← DisplayList3d.RegisterMasterObjectClass[
"arnonSphere",
ArnonSphereFilein,
ArnonSphereFileout,
DisplayList3d.NoOpFileoutPoly,
ArnonSphereCast,
DisplayList3d.NoOpRayCastNoBBoxes,
DisplayList3d.NoOpRayCastBoundingSpheres,
DisplayList3d.NoOpBoundHedron,
DisplayList3d.NoOpPreprocess,
DisplayList3d.NoOpLineDraw,
DisplayList3d.NoOpNormalsDraw,
DisplayList3d.NoOpCountPlanarSurfaces,
DisplayList3d.NoOpGetPlanarSurfaces,
DisplayList3d.NoOpDrawPlanarSurface,
DisplayList3d.NoOpDrawSubBoxes,
DisplayList3d.NoOpDrawSubSpheres];
arnonSphere ← MakeMasterObject["arnonSphere"];
DisplayList3d.RegisterMasterObject[arnonSphere];
};
Init[];
END.