<<>> <> <> <> DIRECTORY CedarProcess, G3dBasic, G3dDraw, G3dLight, G3dMatrix, G3dRayTrace, G3dVector, G3dView, Imager, ImagerColor, --ImagerFullColorContext,-- ImagerPixel, ImagerSample, IO, Real, RealFns, Rope, RuntimeError--, Terminal--; G3dRayTraceImpl: CEDAR PROGRAM IMPORTS CedarProcess, G3dDraw, G3dLight, G3dVector, --ImagerFullColorContext,-- ImagerSample, IO, Real, RealFns, RuntimeError--, Terminal-- EXPORTS G3dRayTrace ~ BEGIN <> Triple: TYPE ~ G3dBasic.Triple; Matrix: TYPE ~ G3dMatrix.Matrix; RayProc: TYPE ~ G3dRayTrace.RayProc; RayData: TYPE ~ G3dRayTrace.RayData; RayDataRep: TYPE ~ G3dRayTrace.RayDataRep; Viewport: TYPE ~ G3dMatrix.Viewport; Context: TYPE ~ Imager.Context; RGB: TYPE ~ ImagerColor.RGB; PixelMap: TYPE ~ ImagerPixel.PixelMap; ROPE: TYPE ~ Rope.ROPE; origin: Triple ~ G3dBasic.origin; yAxis: Triple ~ G3dBasic.yAxis; zAxis: Triple ~ G3dBasic.zAxis; <> RayTrace: PUBLIC PROC [ display: PixelMap, -- place to create the ray-traced image eyePoint: Triple ¬ origin, -- position of the eye eyeView: Triple ¬ zAxis, -- view direction upDirection: Triple ¬ yAxis, -- orientation fieldOfView: REAL ¬ 40.0, -- half horizontal view in degrees jitter: BOOL ¬ FALSE, -- stochastic ray from pixel center nPixelSamples: NAT ¬ 1, -- sub-sampling of pixels adaptiveLimit: NAT ¬ 0, -- # levels of adaptive refinement rayProc: RayProc ¬ NIL, -- client supplied ray-tracing proc clientData: REF ANY ¬ NIL] -- passed to the rayProc RETURNS [r: RayData] ~ { r ¬ NEW[RayDataRep]; r.eyePoint ¬ eyePoint; r.eyeView ¬ G3dVector.Unit[eyeView]; r.upDirection ¬ upDirection; r.fieldOfView ¬ fieldOfView; r.jitter ¬ jitter; r.nPixelSamples ¬ nPixelSamples; r.adaptiveLimit ¬ adaptiveLimit; r.display ¬ display; r.clientData ¬ clientData; r.rayProc ¬ rayProc; r.process ¬ CedarProcess.Fork[RayTracer, r, [priority: background, usePriority: TRUE]]; }; PrepareRayTracer: PUBLIC PROC [r: RayData] ~ { r.eyeView ¬ G3dVector.Unit[r.eyeView]; r.lights ¬ G3dLight.PrepareLights[r.lights, r.eyeView]; }; StartRayTracing: PUBLIC PROC [r: RayData] ~ { IF r.process = NIL OR CedarProcess.GetStatus[r.process] # busy THEN r.process ¬ CedarProcess.Fork[RayTracer, r, [background, TRUE]]; }; StopRayTracing: PUBLIC PROC [r: RayData] ~ { IF CedarProcess.GetStatus[r.process] = busy THEN CedarProcess.Abort[r.process]; }; ResetRayTracing: PUBLIC PROC [r: RayData] ~ {r.nextPixel ¬ [0, 0]}; SetNextPixel: PUBLIC PROC [r: RayData] ~ { IF (r.nextPixel.x ¬ r.nextPixel.x+1) = INTEGER[r.x+r.w] THEN { r.nextPixel.x ¬ r.x; IF (r.nextPixel.y ¬ r.nextPixel.y+1) = INTEGER[r.y+r.h] THEN r.nextPixel.y ¬ r.y; }; }; SetRay: PUBLIC PROC [r: RayData, x, y: NAT] ~ { xHalf: REAL ¬ 0.5*(r.w-r.x); yHalf: REAL ¬ 0.5*(r.h-r.y); aspect: REAL ¬ AspectRatio[r]; tangent: REAL ¬ RealFns.TanDeg[r.fieldOfView]; xPortion: REAL ¬ -(REAL[x-r.x]-xHalf)/xHalf; -- +/- 1 yPortion: REAL ¬ -(REAL[y-r.y]-yHalf)/yHalf; -- +/- 1 xVec: Triple ¬ G3dVector.SetVectorLength[ G3dVector.Cross[r.eyeView, r.upDirection], tangent*xPortion]; yVec: Triple ¬ G3dVector.SetVectorLength[r.upDirection, tangent*yPortion*aspect]; r.ray.axis ¬ [r.eyeView.x+xVec.x+yVec.x, r.eyeView.y+xVec.y+yVec.y, r.eyeView.z+xVec.z+yVec.z]; r.ray.base ¬ r.eyePoint; }; stopEverything: BOOL ¬ FALSE; -- for emergencies RayTracer: CedarProcess.ForkableProc ~ { Luminance: PROC [rgb: RGB] RETURNS [NAT] ~ { v: REAL ¬ IF rgb.R=rgb.G AND rgb.R=rgb.B THEN rgb.R ELSE .3*rgb.R+.59*rgb.G+.11*rgb.B; RETURN[Real.Round[255.0*v]]; }; r: RayData ¬ NARROW[data]; <> <> PrepareRayTracer[r]; <> r.display[0] ¬ ImagerSample.Clip[r.display[0], [[r.y, r.x], [r.y+r.h, r.x+r.w]]]; r.display[1] ¬ ImagerSample.Clip[r.display[1], [[r.y, r.x], [r.y+r.h, r.x+r.w]]]; r.redo ¬ ImagerSample.NewSampleMap[r.display.box, 1]; WHILE NOT stopEverything DO ENABLE RuntimeError.BoundsFault => CONTINUE; x, y: NAT; CedarProcess.CheckAbort[]; SetRay[r, x ¬ r.nextPixel.x, y ¬ r.nextPixel.y]; r.rgb ¬ r.rayProc[[r.ray.base, G3dVector.Unit[r.ray.axis]], r.clientData]; SELECT r.display.samplesPerPixel FROM 1 => ImagerSample.Put[r.display[0], [y, x], Luminance[r.rgb]]; 3 => { ImagerSample.Put[r.display[0], [y, x], Real.Round[255.0*r.rgb.R]]; ImagerSample.Put[r.display[1], [y, x], Real.Round[255.0*r.rgb.G]]; ImagerSample.Put[r.display[2], [y, x], Real.Round[255.0*r.rgb.B]]; }; ENDCASE; SetNextPixel[r]; IF r.nextPixel = [r.x, r.y] THEN { IF r.finishProc # NIL THEN [] ¬ CedarProcess.Fork[Finisher, r]; EXIT; }; ENDLOOP; }; Finisher: CedarProcess.ForkableProc ~ { r: RayData ¬ NARROW[data]; r.finishProc[r.clientData]; }; <> AspectRatio: PUBLIC PROC [r: RayData] RETURNS [REAL] ~ { RETURN[REAL[r.h-r.y]/REAL[r.w-r.x]]; }; RayImageScreenCorners: PUBLIC PROC [r: RayData] RETURNS [p1, p2, p3, p4: Triple] ~ { tangent: REAL ¬ RealFns.TanDeg[r.fieldOfView]; aspect: REAL ¬ AspectRatio[r]; xVec: Triple ¬ G3dVector.SetVectorLength[ G3dVector.Cross[r.eyeView, r.upDirection], tangent]; yVec: Triple ¬ G3dVector.SetVectorLength[r.upDirection, tangent*aspect]; at: Triple ¬ G3dVector.Add[r.eyePoint, r.eyeView]; p1 ¬ [at.x+xVec.x+yVec.x, at.y+xVec.y+yVec.y, at.z+xVec.z+yVec.z]; p2 ¬ [at.x+xVec.x-yVec.x, at.y+xVec.y-yVec.y, at.z+xVec.z-yVec.z]; p3 ¬ [at.x-xVec.x-yVec.x, at.y-xVec.y-yVec.y, at.z-xVec.z-yVec.z]; p4 ¬ [at.x-xVec.x+yVec.x, at.y-xVec.y+yVec.y, at.z-xVec.z+yVec.z]; }; DrawRayTip: PUBLIC PROC [context: Context, r: RayData, view: Matrix, viewport: Viewport] ~ { G3dDraw.Mark[context, G3dVector.Add[r.ray.base, r.ray.axis], view, viewport,, dot]; }; DrawRayImageScreen: PUBLIC PROC [ context: Context, r: RayData, view: Matrix, viewport: Viewport ¬ []] ~ { p1, p2, p3, p4: Triple; [p1, p2, p3, p4] ¬ RayImageScreenCorners[r]; G3dDraw.Segment[context, p1, p2, view, viewport]; G3dDraw.Segment[context, p2, p3, view, viewport]; G3dDraw.Segment[context, p3, p4, view, viewport]; G3dDraw.Segment[context, p4, p1, view, viewport]; G3dDraw.Segment[context, r.eyePoint, p1, view, viewport]; G3dDraw.Segment[context, r.eyePoint, p2, view, viewport]; G3dDraw.Segment[context, r.eyePoint, p3, view, viewport]; G3dDraw.Segment[context, r.eyePoint, p4, view, viewport]; }; ParametersMessage: PUBLIC PROC [r: RayData] RETURNS [rope: ROPE] ~ { rope ¬ IO.PutFLR["display w, h: %g, %g, %gjitter, %g subSamples, %g adaptLimit", LIST[ IO.int[r.w], IO.int[r.h], IO.rope[IF r.jitter THEN NIL ELSE "not "], IO.int[r.nPixelSamples], IO.int[r.adaptiveLimit]]]; }; END.