<> <> <> <> <> DIRECTORY AIS, AtomButtonsTypes, BasicTime, CoordSys, Feedback, Imager, ImagerColor, IO, Matrix3d, Preprocess3d, Real, Rope, Shading, SV2d, SV3d, SVArtwork, SVBasicTypes, SVBoundBox, SVCastRays, SVFancyRays, SVGraphics, SVImage, SVModelTypes, SVRay, SVSceneTypes, SVVector3d, ViewerClasses; SVCastRaysImplA: CEDAR PROGRAM IMPORTS BasicTime, SVCastRays, CoordSys, SVRay, ImagerColor, IO, Matrix3d, Preprocess3d, Real, Rope, Shading, SVArtwork, SVBoundBox, Feedback, SVFancyRays, SVImage, SVVector3d EXPORTS SVCastRays = BEGIN Artwork: TYPE = SVModelTypes.Artwork; BoundBox: TYPE = SVBasicTypes.BoundBox; BoundSphere: TYPE = SVBasicTypes.BoundSphere; Camera: TYPE = SVModelTypes.Camera; Color: TYPE = Imager.Color; Composite: TYPE = SVSceneTypes.Composite; CoordSystem: TYPE = SVModelTypes.CoordSystem; CSGTree: TYPE = SVSceneTypes.CSGTree; FeedbackData: TYPE = AtomButtonsTypes.FeedbackData; LightSourceList: TYPE = SVModelTypes.LightSourceList; NotifyOfProgressProc: TYPE = SVCastRays.NotifyOfProgressProc; Point3d: TYPE = SV3d.Point3d; Point2d: TYPE = SV2d.Point2d; PointSetOp: TYPE = SVSceneTypes.PointSetOp; Primitive: TYPE = SVSceneTypes.Primitive; Matrix4by4: TYPE = SV3d.Matrix4by4; Ray: TYPE = SVSceneTypes.Ray; Surface: TYPE = REF ANY; Vector3d: TYPE = SV3d.Vector3d; Viewer: TYPE = ViewerClasses.Viewer; Classification: TYPE = REF ClassificationObj; ClassificationObj: TYPE = SVSceneTypes.ClassificationObj; SurfaceArray: TYPE = REF SurfaceArrayObj; SurfaceArrayObj: TYPE = SVSceneTypes.SurfaceArrayObj; ParameterArray: TYPE = SVSceneTypes.ParameterArray; InOutArray: TYPE = SVSceneTypes.InOutArray; NormalArray: TYPE = SVSceneTypes.NormalArray; PrimitiveArray: TYPE = SVSceneTypes.PrimitiveArray; CompactArray: TYPE = REF CompactArrayObj; CompactArrayObj: TYPE = ARRAY [1..SVSceneTypes.maxSceneDepth] OF BOOL; Image: TYPE = REF ImageObj; ImageObj: TYPE = SVImage.ImageObj; globalPoolCount: NAT = 10; globalPoolPointer: NAT; Pool: TYPE = REF PoolObj; PoolObj: TYPE = RECORD [seq: SEQUENCE maxClasses: NAT OF Classification]; globalPool: Pool; globalCompactPoolCount: NAT = 10; globalCompactPoolPointer: NAT; CompactPool: TYPE = REF CompactPoolObj; CompactPoolObj: TYPE = ARRAY[1..globalCompactPoolCount] OF CompactArray; globalCompactPool: CompactPool; WriteStreamComp: PUBLIC PROC [comp: Composite, class: Classification, feedback: FeedbackData, makeStream: BOOL, indent: NAT] = { <> opname, leftName, rightName: Rope.ROPE; f: IO.STREAM; IF NOT makeStream THEN RETURN; f _ Feedback.GetTypescriptStream[$Solidviews]; Indent[f, indent]; SELECT comp.operation FROM union => opname _ "union"; intersection => opname _ "intersection"; difference => opname _ "difference"; ENDCASE => ERROR; WITH comp.leftSolid SELECT FROM p: Primitive => leftName _ p.name; c: Composite => leftName _ c.name; ENDCASE => ERROR; WITH comp.rightSolid SELECT FROM p: Primitive => rightName _ p.name; c: Composite => rightName _ c.name; ENDCASE => ERROR; f.PutF["Composite %g [op: %g] (%g %g) returns class: [count: %g]\n", [rope[comp.name]],[rope[opname]], [rope[leftName]], [rope[rightName]], [integer[class.count]]]; WritePrimNames[class, f, indent]; }; -- end of WriteStreamComp Indent: PROC [f: IO.STREAM, indent: NAT] = { FOR i: NAT IN[1..indent] DO f.PutChar[IO.TAB]; ENDLOOP; }; WritePrimNames: PROC [class: Classification, f: IO.STREAM, indent: NAT] = { FOR i: NAT IN[1..class.count] DO Indent[f, indent+1]; f.PutF["%g) %g at t = %g\n", [integer[i]], [rope[class.primitives[i].name]], [real[class.params[i]]]]; ENDLOOP; }; -- end of WritePrimNames WriteStreamPrim: PUBLIC PROC [prim: Primitive, class: Classification, feedback: FeedbackData, makeStream: BOOL, indent: NAT] = { f: IO.STREAM; IF NOT makeStream THEN RETURN; f _ Feedback.GetTypescriptStream[$Solidviews]; Indent[f, indent]; f.PutF["Primitive %g returns class: [count: %g]\n", [rope[prim.name]], [integer[class.count]]]; WriteParams[class, f, indent]; }; -- end of WriteStreamPrim WriteParams: PROC [class: Classification, f: IO.STREAM, indent: NAT] = { FOR i: NAT IN[1..class.count] DO Indent[f, indent+1]; f.PutF["%g) %g at t = %g\n", [integer[i]], [rope[class.primitives[i].name]], [real[class.params[i]]]]; ENDLOOP; }; -- end of WriteParams DoesHit: PROC [class: Classification] RETURNS [BOOL] = { RETURN[class.count > 0 OR class.classifs[1] = TRUE]; }; RayCast: PUBLIC PROC [cameraPoint: Point2d, worldRay: Ray, node: REF ANY, consolidate: BOOL _ TRUE, feedback: FeedbackData, makeStream: BOOL _ FALSE, indent: NAT _ 0] RETURNS [class: Classification] = { <> IF node = NIL THEN {class _ EmptyClass[]; RETURN}; WITH node SELECT FROM comp: Composite => { leftClass, rightClass: Classification; leftBoxHit, leftHit, rightBoxHit, rightHit: BOOL; totalMiss: BOOL _ FALSE; boundBox: BoundBox; <> <> <<1) Check ray for left bound box. Set leftBoxHit if appropriate.>> <<2) If leftBoxHit then cast the ray. Set leftHit if appropriate.>> <<3) If not leftHit then if comp.operation = intersection or difference, return miss.>> <<4) If hit, or union, then right box test. Set RightBoxMiss if appropriate.>> <<5) If miss then return: leftclass for difference, empty for intersection, leftClass for union. >> <<6) Else cast ray.>> <<7) Return rightclass or combination if appropriate>> <<1) Check ray for left bound box. Set leftBoxHit if appropriate.>> <<>> WITH comp.leftSolid SELECT FROM p: Primitive => boundBox _ p.boundBox; c: Composite => boundBox _ c.boundBox; ENDCASE => ERROR; leftBoxHit _ SVBoundBox.PointInBoundBox[cameraPoint, boundBox]; <<2) If leftBoxHit then cast the ray. Set leftHit if appropriate.>> <<>> IF leftBoxHit THEN { leftClass _ RayCast[cameraPoint, worldRay, comp.leftSolid, consolidate, feedback, makeStream, indent]; leftHit _ DoesHit[leftClass]; } ELSE {leftHit _ FALSE; leftClass _ EmptyClass[]}; <<3) If not leftHit then if comp.operation = intersection or difference, return miss.>> <<>> IF NOT leftHit THEN IF comp.operation = intersection OR comp.operation = difference THEN { class _ leftClass; WriteStreamComp[comp, class, feedback, makeStream, indent]; RETURN}; <> <<>> <<4) If hit, or union, then right box test. Set RightBoxMiss if appropriate. (we don't have to test for this state. It is the only one left.)>> <<>> WITH comp.rightSolid SELECT FROM p: Primitive => boundBox _ p.boundBox; c: Composite => boundBox _ c.boundBox; ENDCASE => ERROR; rightBoxHit _ SVBoundBox.PointInBoundBox[cameraPoint, boundBox]; <<5) If miss then return EmptyClass. Else cast ray.>> <<>> IF NOT rightBoxHit THEN <> SELECT comp.operation FROM union => {class _ leftClass; WriteStreamComp[comp, class, feedback, makeStream, indent]; RETURN}; intersection => IF NOT leftHit THEN RETURN[leftClass] ELSE { ReturnClassToPool[leftClass]; class _ EmptyClass[]; WriteStreamComp[comp, class, feedback, makeStream, indent]; RETURN}; difference => {class _ leftClass; WriteStreamComp[comp, class, feedback, makeStream, indent]; RETURN}; ENDCASE => ERROR; <<6) Else cast ray. We have Union, or (intersection/difference) with left hit. Ray hits box.>> <<>> rightClass _ RayCast[cameraPoint, worldRay, comp.rightSolid, consolidate, feedback, makeStream, indent]; rightHit _ DoesHit[rightClass]; <<7) Return rightclass, combination or empty if appropriate>> <<>> SELECT comp.operation FROM union => IF rightHit THEN { IF leftHit THEN class _ UnionCombine[leftClass, rightClass, consolidate] ELSE {ReturnClassToPool[leftClass]; class _ rightClass} } ELSE { ReturnClassToPool[rightClass]; class _ leftClass}; intersection => IF rightHit THEN { IF leftHit THEN class _ IntersectionCombine[leftClass, rightClass, consolidate] ELSE {ReturnClassToPool[rightClass]; class _ leftClass;} } ELSE IF leftHit THEN {ReturnClassToPool[leftClass]; class _ rightClass} ELSE {ReturnClassToPool[rightClass]; class _ leftClass}; difference => IF rightHit THEN { IF leftHit THEN class _ DifferenceCombine[leftClass, rightClass, consolidate] ELSE {ReturnClassToPool[rightClass]; class _ leftClass} -- leftClass null } ELSE {ReturnClassToPool[rightClass]; class _ leftClass}; ENDCASE => ERROR; WriteStreamComp[comp, class, feedback, makeStream, indent]; RETURN}; prim: Primitive => { localRay: Ray; IF prim.ignoreMe THEN {class _ SVCastRays.GetClassFromPool[]; SVCastRays.MakeClassAMiss[class]; RETURN}; localRay _ SVRay.TransformRay[worldRay, prim.worldWRTPrim]; -- (takes a new ray from the pool) class _ prim.rayCast[cameraPoint, localRay, prim.sliceD, prim]; WriteStreamPrim[prim, class, feedback, makeStream, 0]; SVRay.ReturnRayToPool[localRay]; -- returns ray to pool RETURN}; ENDCASE => ERROR; }; -- end of RayCast RayCastNoBBoxes: PUBLIC PROC [worldRay: Ray, node: REF ANY, consolidate: BOOL _ TRUE, feedback: FeedbackData, makeStream: BOOL _ FALSE, indent: NAT _ 0] RETURNS [class: Classification] = { <> <> IF node = NIL THEN {class _ EmptyClass[]; RETURN}; WITH node SELECT FROM comp: Composite => { leftClass, rightClass: Classification; leftHit, rightHit: BOOL; totalMiss: BOOL _ FALSE; <> <<1) Cast the left ray. Set leftHit if appropriate.>> <<2) If not leftHit then if comp.operation = intersection or difference, return miss.>> <<3) If hit, or union, then cast right ray.>> <<4) Return rightclass or combination if appropriate>> <<1) Cast the left ray. Set leftHit if appropriate.>> <<>> leftClass _ RayCastNoBBoxes[worldRay, comp.leftSolid, consolidate, feedback, makeStream, indent]; leftHit _ DoesHit[leftClass]; <<2) If not leftHit then if comp.operation = intersection or difference, return miss.>> <<>> IF NOT leftHit THEN IF comp.operation = intersection OR comp.operation = difference THEN { class _ leftClass; WriteStreamComp[comp, class, feedback, makeStream, indent]; RETURN}; <> <<>> <<3) If hit, or union, then cast right ray.>> <<>> rightClass _ RayCastNoBBoxes[worldRay, comp.rightSolid, consolidate, feedback, makeStream, indent]; rightHit _ DoesHit[rightClass]; <<4) Return rightclass, combination or empty if appropriate>> <<>> SELECT comp.operation FROM union => IF rightHit THEN { IF leftHit THEN class _ UnionCombine[leftClass, rightClass, consolidate] ELSE {ReturnClassToPool[leftClass]; class _ rightClass} } ELSE { ReturnClassToPool[rightClass]; class _ leftClass}; intersection => IF rightHit THEN { IF leftHit THEN class _ IntersectionCombine[leftClass, rightClass, consolidate] ELSE {ReturnClassToPool[rightClass]; class _ leftClass;} } ELSE IF leftHit THEN {ReturnClassToPool[leftClass]; class _ rightClass} ELSE {ReturnClassToPool[rightClass]; class _ leftClass}; difference => IF rightHit THEN { IF leftHit THEN class _ DifferenceCombine[leftClass, rightClass, consolidate] ELSE {ReturnClassToPool[rightClass]; class _ leftClass} -- leftClass null } ELSE {ReturnClassToPool[rightClass]; class _ leftClass}; ENDCASE => ERROR; WriteStreamComp[comp, class, feedback, makeStream, indent]; RETURN}; prim: Primitive => { localRay: Ray; IF prim.ignoreMe THEN {class _ SVCastRays.GetClassFromPool[]; SVCastRays.MakeClassAMiss[class]; RETURN}; localRay _ SVRay.TransformRay[worldRay, prim.worldWRTPrim]; -- (takes a new ray from the pool) class _ prim.rayCastNoBBoxes[localRay, prim.sliceD, prim]; WriteStreamPrim[prim, class, feedback, makeStream, 0]; SVRay.ReturnRayToPool[localRay]; -- returns ray to pool RETURN}; ENDCASE => ERROR; }; -- end of RayCastNoBboxes HitsTree: PUBLIC PROC [worldRay: Ray, tree: CSGTree] RETURNS [BOOL] = { node: REF ANY _ tree.son; class: Classification; hits: BOOL; class _ RayCastNoBBoxes [worldRay: worldRay, node: node, feedback: NIL, makeStream: FALSE]; hits _ DoesHit[class]; ReturnClassToPool[class]; RETURN[hits]; }; FirstHit: PUBLIC PROC [worldRay: Ray, tree: CSGTree, useBoundSpheres: BOOL, feedback: FeedbackData, makeStream: BOOL _ FALSE, indent: NAT _ 0] RETURNS [hits: BOOL, t: REAL] = { <> node: REF ANY _ tree.son; class: Classification; IF NOT useBoundSpheres THEN class _ RayCastNoBBoxes [worldRay: worldRay, node: node, makeStream: makeStream, feedback: feedback, indent: indent] ELSE class _ SVCastRays.RayCastBoundingSpheres [worldRay: worldRay, node: node, feedback: feedback, makeStream: makeStream, indent: indent]; hits _ FALSE; t _ 0.0; FOR i: NAT IN [1..class.count] DO IF NOT class.classifs[i] THEN GOTO BeenOut; REPEAT BeenOut => { hits _ FALSE; FOR j: NAT IN [i+1..class.count+1] DO IF class.classifs[j] THEN { hits _ TRUE; t _ class.params[j-1]; ReturnClassToPool[class]; RETURN; }; ENDLOOP; ReturnClassToPool[class]; RETURN; }; FINISHED => { ReturnClassToPool[class]; RETURN; }; ENDLOOP; }; EmptyClass: PUBLIC PROC RETURNS [class: Classification] = { class _ GetClassFromPool[]; class.count _ 0; class.classifs[1] _ FALSE; }; -- end of EmptyClass <> SceneExceedsMaximumDepth: SIGNAL = CODE; UnionCombine: PUBLIC PROC [leftClass, rightClass: Classification, consolidate: BOOL] RETURNS [combinedClass: Classification] = { <> lPtr, rPtr: NAT; combinedClass _ GetClassFromPool[]; lPtr _ rPtr _ 1; combinedClass.count _ leftClass.count + rightClass.count; IF combinedClass.count > SVSceneTypes.maxSceneDepth THEN SIGNAL SceneExceedsMaximumDepth; FOR i: NAT IN[1..combinedClass.count] DO IF rPtr > rightClass.count THEN GOTO RPtrWentOver; IF lPtr > leftClass.count THEN GOTO LPtrWentOver; IF leftClass.params[lPtr] < rightClass.params[rPtr] THEN { combinedClass.normals[i] _ leftClass.normals[lPtr]; combinedClass.params[i] _ leftClass.params[lPtr]; combinedClass.surfaces[i] _ leftClass.surfaces[lPtr]; combinedClass.stills[i] _ leftClass.stills[lPtr]; combinedClass.primitives[i] _ leftClass.primitives[lPtr]; combinedClass.classifs[i] _ leftClass.classifs[lPtr] OR rightClass.classifs[rPtr]; lPtr _ lPtr + 1; } ELSE { combinedClass.normals[i] _ rightClass.normals[rPtr]; combinedClass.params[i] _ rightClass.params[rPtr]; combinedClass.surfaces[i] _ rightClass.surfaces[rPtr]; combinedClass.stills[i] _ rightClass.stills[rPtr]; combinedClass.primitives[i] _ rightClass.primitives[rPtr]; combinedClass.classifs[i] _ leftClass.classifs[lPtr] OR rightClass.classifs[rPtr]; rPtr _ rPtr + 1; }; REPEAT RPtrWentOver => { -- finish up with lPtr data FOR k: NAT _ i, k+1 UNTIL k > combinedClass.count DO combinedClass.normals[k] _ leftClass.normals[lPtr]; combinedClass.params[k] _ leftClass.params[lPtr]; combinedClass.surfaces[k] _ leftClass.surfaces[lPtr]; combinedClass.stills[k] _ leftClass.stills[lPtr]; combinedClass.primitives[k] _ leftClass.primitives[lPtr]; combinedClass.classifs[k] _ leftClass.classifs[lPtr] OR rightClass.classifs[rPtr]; lPtr _ lPtr + 1; ENDLOOP}; LPtrWentOver => { -- finish up with rPtr data FOR k: NAT _ i, k+1 UNTIL k > combinedClass.count DO combinedClass.normals[k] _ rightClass.normals[rPtr]; combinedClass.params[k] _ rightClass.params[rPtr]; combinedClass.surfaces[k] _ rightClass.surfaces[rPtr]; combinedClass.stills[k] _ rightClass.stills[rPtr]; combinedClass.primitives[k] _ rightClass.primitives[rPtr]; combinedClass.classifs[k] _ leftClass.classifs[lPtr] OR rightClass.classifs[rPtr]; rPtr _ rPtr + 1; ENDLOOP}; ENDLOOP; combinedClass.classifs[combinedClass.count+1] _ leftClass.classifs[lPtr] OR rightClass.classifs[rPtr]; IF consolidate THEN ConsolidateClassification[combinedClass]; ReturnClassToPool[leftClass]; ReturnClassToPool[rightClass]; }; -- end of UnionCombine IntersectionCombine: PUBLIC PROC [leftClass, rightClass: Classification, consolidate: BOOL] RETURNS [combinedClass: Classification] = { <> lPtr, rPtr: NAT; combinedClass _ GetClassFromPool[]; lPtr _ rPtr _ 1; combinedClass.count _ leftClass.count + rightClass.count; IF combinedClass.count > SVSceneTypes.maxSceneDepth THEN SIGNAL SceneExceedsMaximumDepth; FOR i: NAT IN[1..combinedClass.count] DO IF rPtr > rightClass.count THEN GOTO RPtrWentOver; IF lPtr > leftClass.count THEN GOTO LPtrWentOver; IF leftClass.params[lPtr] < rightClass.params[rPtr] THEN { combinedClass.normals[i] _ leftClass.normals[lPtr]; combinedClass.params[i] _ leftClass.params[lPtr]; combinedClass.surfaces[i] _ leftClass.surfaces[lPtr]; combinedClass.stills[i] _ leftClass.stills[lPtr]; combinedClass.primitives[i] _ leftClass.primitives[lPtr]; combinedClass.classifs[i] _ leftClass.classifs[lPtr] AND rightClass.classifs[rPtr]; lPtr _ lPtr + 1; } ELSE { combinedClass.normals[i] _ rightClass.normals[rPtr]; combinedClass.params[i] _ rightClass.params[rPtr]; combinedClass.surfaces[i] _ rightClass.surfaces[rPtr]; combinedClass.stills[i] _ rightClass.stills[rPtr]; combinedClass.primitives[i] _ rightClass.primitives[rPtr]; combinedClass.classifs[i] _ leftClass.classifs[lPtr] AND rightClass.classifs[rPtr]; rPtr _ rPtr + 1; }; REPEAT RPtrWentOver => { -- finish up with lPtr data FOR k: NAT _ i, k+1 UNTIL k > combinedClass.count DO combinedClass.normals[k] _ leftClass.normals[lPtr]; combinedClass.params[k] _ leftClass.params[lPtr]; combinedClass.surfaces[k] _ leftClass.surfaces[lPtr]; combinedClass.stills[k] _ leftClass.stills[lPtr]; combinedClass.primitives[k] _ leftClass.primitives[lPtr]; combinedClass.classifs[k] _ leftClass.classifs[lPtr] AND rightClass.classifs[rPtr]; lPtr _ lPtr + 1; ENDLOOP}; LPtrWentOver => { -- finish up with rPtr data FOR k: NAT _ i, k+1 UNTIL k > combinedClass.count DO combinedClass.normals[k] _ rightClass.normals[rPtr]; combinedClass.params[k] _ rightClass.params[rPtr]; combinedClass.surfaces[k] _ rightClass.surfaces[rPtr]; combinedClass.stills[k] _ rightClass.stills[rPtr]; combinedClass.primitives[k] _ rightClass.primitives[rPtr]; combinedClass.classifs[k] _ leftClass.classifs[lPtr] AND rightClass.classifs[rPtr]; rPtr _ rPtr + 1; ENDLOOP}; ENDLOOP; combinedClass.classifs[combinedClass.count+1] _ leftClass.classifs[lPtr] AND rightClass.classifs[rPtr]; IF consolidate THEN ConsolidateClassification[combinedClass]; ReturnClassToPool[leftClass]; ReturnClassToPool[rightClass]; }; -- end of IntersectionCombine DifferenceCombine: PUBLIC PROC [leftClass, rightClass: Classification, consolidate: BOOL] RETURNS [combinedClass: Classification] = { <> lPtr, rPtr: NAT; combinedClass _ GetClassFromPool[]; IF combinedClass.count > SVSceneTypes.maxSceneDepth THEN SIGNAL SceneExceedsMaximumDepth; lPtr _ rPtr _ 1; combinedClass.count _ leftClass.count + rightClass.count; FOR i: NAT IN[1..combinedClass.count] DO IF rPtr > rightClass.count THEN GOTO RPtrWentOver; IF lPtr > leftClass.count THEN GOTO LPtrWentOver; IF leftClass.params[lPtr] < rightClass.params[rPtr] THEN { combinedClass.normals[i] _ leftClass.normals[lPtr]; combinedClass.params[i] _ leftClass.params[lPtr]; combinedClass.surfaces[i] _ leftClass.surfaces[lPtr]; combinedClass.stills[i] _ leftClass.stills[lPtr]; combinedClass.primitives[i] _ leftClass.primitives[lPtr]; combinedClass.classifs[i] _ leftClass.classifs[lPtr] AND NOT rightClass.classifs[rPtr]; lPtr _ lPtr + 1; } ELSE { combinedClass.normals[i] _ SVVector3d.Negate[rightClass.normals[rPtr]]; combinedClass.params[i] _ rightClass.params[rPtr]; combinedClass.surfaces[i] _ rightClass.surfaces[rPtr]; combinedClass.stills[i] _ rightClass.stills[rPtr]; combinedClass.primitives[i] _ rightClass.primitives[rPtr]; combinedClass.classifs[i] _ leftClass.classifs[lPtr] AND NOT rightClass.classifs[rPtr]; rPtr _ rPtr + 1; }; REPEAT RPtrWentOver => { -- finish up with lPtr data FOR k: NAT _ i, k+1 UNTIL k > combinedClass.count DO combinedClass.normals[k] _ leftClass.normals[lPtr]; combinedClass.params[k] _ leftClass.params[lPtr]; combinedClass.surfaces[k] _ leftClass.surfaces[lPtr]; combinedClass.stills[k] _ leftClass.stills[lPtr]; combinedClass.primitives[k] _ leftClass.primitives[lPtr]; combinedClass.classifs[k] _ leftClass.classifs[lPtr] AND NOT rightClass.classifs[rPtr]; lPtr _ lPtr + 1; ENDLOOP}; LPtrWentOver => { -- finish up with rPtr data FOR k: NAT _ i, k+1 UNTIL k > combinedClass.count DO combinedClass.normals[k] _ SVVector3d.Negate[rightClass.normals[rPtr]]; combinedClass.params[k] _ rightClass.params[rPtr]; combinedClass.surfaces[k] _ rightClass.surfaces[rPtr]; combinedClass.stills[k] _ rightClass.stills[rPtr]; combinedClass.primitives[k] _ rightClass.primitives[rPtr]; combinedClass.classifs[k] _ leftClass.classifs[lPtr] AND NOT rightClass.classifs[rPtr]; rPtr _ rPtr + 1; ENDLOOP}; ENDLOOP; combinedClass.classifs[combinedClass.count+1] _ leftClass.classifs[lPtr] AND NOT rightClass.classifs[rPtr]; IF consolidate THEN ConsolidateClassification[combinedClass]; ReturnClassToPool[leftClass]; ReturnClassToPool[rightClass]; }; -- end of DifferenceCombine ConsolidateClassification: PROC [class: Classification] = { <> <> currentlyWorkingOn: BOOL; compact: CompactArray _ GetCompactFromPool[]; currentlyWorkingOn _ class.classifs[1]; FOR i: NAT IN[2..class.count+1] DO IF class.classifs[i] = currentlyWorkingOn THEN -- this is not a transition so throw it out compact[i-1] _ FALSE -- don't keep it ELSE {compact[i-1] _ TRUE; currentlyWorkingOn _ class.classifs[i];}; ENDLOOP; CompactClassification[class, compact]; ReturnCompactToPool[compact]; }; -- end of ConsolidateClassification CompactClassification: PROC [class: Classification, compact: CompactArray] = { <> newCount: NAT; newCount _ 0; FOR i: NAT IN[1..class.count] DO IF compact[i] THEN { newCount _ newCount + 1; class.params[newCount] _ class.params[i]; class.classifs[newCount] _ class.classifs[i]; class.normals[newCount] _ class.normals[i]; class.surfaces[newCount] _ class.surfaces[i]; class.stills[newCount] _ class.stills[i]; class.primitives[newCount] _ class.primitives[i];}; ENDLOOP; class.classifs[newCount+1] _ class.classifs[class.count+1]; <> class.count _ newCount; }; SingleRay: PUBLIC PROC [cameraPoint: Point2d, tree: CSGTree, lightSources: LightSourceList, camera: Camera, feedback: FeedbackData, makeStream: BOOL _ FALSE] RETURNS [color: Color] = { <> cameraRay, worldRay: Ray; cameraWRTWorld: Matrix3d.Matrix4by4; boundBox: BoundBox; boundSphere: BoundSphere; r,g,b: REAL; cameraRay _ SVRay.CreateRay[]; [boundBox, boundSphere] _ Preprocess3d.PreprocessForImage[tree, camera]; -- must call this before casting rays SVRay.StuffCameraRay[cameraRay, cameraPoint, camera]; <> <> cameraWRTWorld _ CoordSys.WRTWorld[camera.coordSys]; worldRay _ SVRay.TransformRayToWorld[cameraRay, cameraWRTWorld]; -- allocates ray from pool <> [r,g,b] _ TopColorCast[cameraPoint, worldRay, tree, lightSources, camera, boundBox, boundSphere, feedback, makeStream, 0]; color _ Shading.NormalizeRGB[r,g,b]; <> SVRay.ReturnRayToPool[worldRay]; }; -- end of SingleRay SingleRay2: PUBLIC PROC [cameraPoint: Point2d, tree: CSGTree, camera: Camera, consolidate: BOOL _ TRUE, feedback: FeedbackData, makeStream: BOOL _ FALSE] RETURNS [class: Classification, rayWorld: Ray] = { <> <> topNode: REF ANY _ tree.son; rayCamera: Ray; cameraWRTWorld: Matrix4by4 _ CoordSys.WRTWorld[camera.coordSys]; rayCamera _ SVRay.GetRayFromPool[]; SVRay.StuffCameraRay[rayCamera, cameraPoint, camera]; rayWorld _ SVRay.TransformRayToWorld[rayCamera, cameraWRTWorld]; -- allocates ray from pool SVRay.ReturnRayToPool[rayCamera]; class _ RayCast[cameraPoint, rayWorld, topNode, consolidate, feedback, makeStream, 0]; }; -- end of SingleRay2 NodeToRope: PROC [node: REF ANY, depth: NAT] RETURNS [r: Rope.ROPE] = { IF node = NIL THEN RETURN[NIL]; WITH node SELECT FROM prim: Primitive => {r _ prim.name; RETURN}; comp: Composite => {r _ comp.name; IF depth < 2 THEN RETURN ELSE {r1: Rope.ROPE; r2: Rope.ROPE; leftSon: REF ANY _ comp.leftSolid; rightSon: REF ANY _ comp.rightSolid; r1 _ NodeToRope[leftSon, depth - 1]; r2 _ NodeToRope[rightSon, depth - 1]; r _ Rope.Cat[r,": ",r1,"/",r2]; RETURN}; }; ENDCASE => ERROR; }; -- end of NodeToRope OutputTreeInfo: PROC [node: REF ANY, I: Image, feedback: FeedbackData] = { debugName: Rope.ROPE; debugName _ NodeToRope[node, 2]; Feedback.PutF[feedback, oneLiner, "About to Draw Tree: %g (%g by %g)...", [rope[debugName]], [integer[I.bwWindow.fref.raster.scanCount]], [integer[I.bwWindow.fref.raster.scanLength]]]; Feedback.Blink[feedback]; }; -- end of OutputTreeInfo GetXStepRayInWorld: PROC [stepSize: REAL, cameraWRTWorld: Matrix4by4, camera: Camera] RETURNS [ray: Ray] = { cameraXStepRay1, cameraXStepRay2: Ray; worldXStepRay1, worldXStepRay2: Ray; cameraXStepRay1 _ SVRay.CreateRay[]; IF camera.projection = perspective THEN { SVRay.StuffCameraRay[cameraXStepRay1, [0,0], camera]; cameraXStepRay2 _ SVRay.CreateRay[]; SVRay.StuffCameraRay[cameraXStepRay2, [stepSize,0], camera]; worldXStepRay1 _ SVRay.TransformRayToWorld[cameraXStepRay1, cameraWRTWorld]; worldXStepRay2 _ SVRay.TransformRayToWorld[cameraXStepRay2, cameraWRTWorld]; ray _ SVRay.SubtractRays[worldXStepRay2, worldXStepRay1]; <> SVRay.ReturnRayToPool[worldXStepRay1]; SVRay.ReturnRayToPool[worldXStepRay2]; } ELSE { -- orthographic projection SVRay.StuffCameraRayLiterally[cameraXStepRay1, [stepSize,0,0], [0,0,0]]; ray _ SVRay.TransformNewRay[cameraXStepRay1, cameraWRTWorld]; <> } }; -- end of GetXStepRayInWorld MasterObjectColorFromPrimitive: PROC [primitive: Primitive, t: REAL, worldRay: Ray, primitiveNormal: Vector3d] RETURNS [color: Color] = { localRay: Ray; point3d: Point3d; artwork: Artwork _ primitive.artwork; SELECT artwork.class FROM justColor => color _ artwork.color; simpleSurface => { localRay _ SVRay.TransformRay[worldRay, CoordSys.FindWorldInTermsOf[artwork.coordSys]]; point3d _ SVRay.EvaluateLocalRay[localRay, t]; SVRay.ReturnRayToPool[localRay]; color _ SVArtwork.FindColorAtSurfacePoint[artwork, point3d, primitiveNormal]; }; spaceFunction => { localRay _ SVRay.TransformRay[worldRay, CoordSys.FindWorldInTermsOf[artwork.coordSys]]; point3d _ SVRay.EvaluateLocalRay[localRay, t]; SVRay.ReturnRayToPool[localRay]; color _ SVArtwork.FindColorAtSpacePoint[artwork, point3d, primitiveNormal]; }; ENDCASE => ERROR; }; ColorFromClass: PROC [class: Classification, x, y: REAL, lightSources: LightSourceList, camera: Camera, worldRay: Ray, tree: CSGTree, feedback: FeedbackData, makeStream: BOOL _ FALSE, indent: NAT _ 0] RETURNS [r,g,b: REAL] = { <> <> surf: Surface; surfColor: Color; eyePoint, surfacePt, p: Point3d; d: Vector3d; primitive: Primitive; visibleLights: LightSourceList; t: REAL; worldNormal, primitiveNormal: Vector3d; IF class.count = 0 THEN { IF NOT class.classifs[1] THEN [r,g,b] _ Shading.ExtractRGB[tree.backgroundColor] ELSE r _ g _ b _ 0.0; RETURN}; surf _ class.surfaces[1]; t _ class.params[1];-- the parameter of the ray intersection primitive _ class.primitives[1]; primitiveNormal _ class.normals[1]; surfColor _ MasterObjectColorFromPrimitive[primitive, t, worldRay, primitiveNormal]; worldNormal _ Matrix3d.UpdateVectorWithInverse[primitive.worldWRTPrim, primitiveNormal]; surfacePt _ SVRay.EvaluateLocalRay[worldRay, t]; [p, d] _ SVRay.GetLocalRay[worldRay]; eyePoint _ SVVector3d.Sub[p, d]; visibleLights _ IF tree.shadows THEN SVFancyRays.VisibleLights[lightSources, surfacePt, tree, camera.useBoundSpheresForShadows, feedback, makeStream, indent] ELSE lightSources; <> SELECT primitive.artwork.material FROM chalk => [r,g,b] _ Shading.DiffuseReflectance[worldNormal, surfacePt, surfColor, visibleLights]; plastic => [r,g,b] _ Shading.DiffuseAndSpecularReflectance[eyePoint, worldNormal, surfacePt, surfColor, visibleLights]; ENDCASE => ERROR; }; -- end of ColorFromClass ScanLine: TYPE = REF ScanLineObj; ScanLineObj: TYPE = RECORD [ seq: SEQUENCE lineLen: NAT OF Color]; CreateScanLine: PROC [len: NAT] RETURNS [scanLine: ScanLine] = { scanLine _ NEW[ScanLineObj[len]]; }; CopyScanLine: PROC [from: ScanLine, to: ScanLine] = { FOR i: NAT IN [0..to.lineLen) DO to[i] _ from[i]; ENDLOOP; }; PutColorInScanLine: PROC [scanLine: ScanLine, index: NAT, color: Color] = { scanLine[index] _ color; }; TopColorCast: PROC [cameraPoint: Point2d, worldRay: Ray, tree: CSGTree, lightSources: LightSourceList, camera: Camera, sceneBox: BoundBox, boundSphere: BoundSphere, feedback: FeedbackData, makeStream: BOOL _ FALSE, indent: NAT _ 0] RETURNS [r,g,b: REAL] = { node: REF ANY _ tree.son; class: Classification; IF tree.son = NIL THEN { [r,g,b] _ Shading.ExtractRGB[tree.backgroundColor]; RETURN; }; IF camera.useBoundBoxes THEN { IF SVBoundBox.PointInBoundBox[cameraPoint, sceneBox] THEN { finalClassCount, firstClassCount: NAT; firstClassCount _ NumberOfClassesInPool[]; -- for debugging purposes. class _ RayCast[cameraPoint, worldRay, node, TRUE, feedback, makeStream, indent]; [r,g,b] _ ColorFromClass[class, cameraPoint[1], cameraPoint[2], lightSources, camera, worldRay, tree, feedback, makeStream, indent]; ReturnClassToPool[class]; finalClassCount _ NumberOfClassesInPool[]; -- for debugging purposes. IF finalClassCount < firstClassCount THEN { Feedback.PutF[feedback, oneLiner, "WARNING: A Classification was lost while casting a ray at [%g, %g]", [real[cameraPoint[1]]], [real[cameraPoint[2]]]]; }; } ELSE [r,g,b] _ Shading.ExtractRGB[tree.backgroundColor]; } ELSE { -- Use Bounding Spheres IF SVRay.RayHitsBoundSphere[worldRay, boundSphere] THEN { finalClassCount, firstClassCount: NAT; firstClassCount _ NumberOfClassesInPool[]; -- for debugging purposes. class _ SVCastRays.RayCastBoundingSpheres[worldRay, node, TRUE, feedback, makeStream, indent]; [r,g,b] _ ColorFromClass[class, cameraPoint[1], cameraPoint[2], lightSources, camera, worldRay, tree, feedback, makeStream, indent]; ReturnClassToPool[class]; finalClassCount _ NumberOfClassesInPool[]; -- for debugging purposes. IF finalClassCount < firstClassCount THEN { Feedback.PutF[feedback, oneLiner, "WARNING: A Classification was lost while casting a ray at [%g, %g]", [real[cameraPoint[1]]], [real[cameraPoint[2]]]]; }; } ELSE [r,g,b] _ Shading.ExtractRGB[tree.backgroundColor]; }; }; <> <> <> <> <> <<};>> SetUpRayTrace: PROC [boundBox: BoundBox, camera: Camera, aisRope: Rope.ROPE, bAndWOnly: BOOL, resolution: REAL, feedback: FeedbackData] RETURNS [I: Image, xSamples, ySamples: NAT, stepSize, xStart, yStart: REAL] = { <> extentX, extentY, projectionX, projectionY, trueExtentX, trueExtentY: REAL; stepSize _ 72.0/resolution; -- in screen dots per sample <> IF camera.frame.fullScreen THEN { [I, xSamples, ySamples] _ SVImage.OpenImage[aisRope, bAndWOnly, boundBox.loX, boundBox.loY, boundBox.hiX, boundBox.hiY, resolution, feedback]; extentX _ boundBox.hiX - boundBox.loX; extentY _ boundBox.hiY - boundBox.loY; } ELSE { [I, xSamples, ySamples] _ SVImage.OpenImage[aisRope, bAndWOnly, camera.frame.downLeft[1], camera.frame.downLeft[2], camera.frame.upRight[1], camera.frame.upRight[2], resolution, feedback]; extentX _ camera.frame.upRight[1] - camera.frame.downLeft[1]; extentY _ camera.frame.upRight[2] - camera.frame.downLeft[2]; }; <> trueExtentX _ Real.Float[xSamples-1]*stepSize; trueExtentY _ Real.Float[ySamples-1]*stepSize; projectionX _ (trueExtentX - extentX)/2.0; projectionY _ (trueExtentY - extentY)/2.0; IF camera.frame.fullScreen THEN { xStart _ boundBox.loX - projectionX; yStart _ boundBox.loY - projectionY; } ELSE { xStart _ camera.frame.downLeft[1] - projectionX; yStart _ camera.frame.downLeft[2] - projectionY; }; <> xStart _ xStart - stepSize/2.0; yStart _ yStart - stepSize/2.0; }; -- end of SetUpRayTrace ShutDownRayTrace: PROC [aisRope: Rope.ROPE, I: Image, camera: Camera, startTime: BasicTime.GMT, feedback: FeedbackData] = { comment: Rope.ROPE; totalTime: INT; endTime: BasicTime.GMT; endTime _ BasicTime.Now[]; totalTime _ BasicTime.Period[from: startTime, to: endTime]; comment _ IO.PutFR["res: %g dpi, rayTraceTime: (%r)", [real[camera.resolution]], [integer[totalTime]]]; SVImage.CloseImage[I, aisRope, comment, feedback]; }; AbortDrawTree: PROC [I: Image, aisRope: Rope.ROPE, camera: Camera, startTime: BasicTime.GMT, feedback: FeedbackData] = { comment: Rope.ROPE; totalTime: INT; endTime: BasicTime.GMT; endTime _ BasicTime.Now[]; totalTime _ BasicTime.Period[from: startTime, to: endTime]; comment _ IO.PutFR["res: %g dpi, rayTraceTime: (%r)", [real[camera.resolution]], [integer[totalTime]]]; SVImage.CloseImage[I, aisRope, comment, feedback]; Feedback.Append[feedback, "CastRays aborted. Partial files saved.", oneLiner]; }; UpdateMaxSamples: PROC [maxSamples: MaxSamples, r,g,b: REAL] = { maxSamples.maxRed _ MAX[maxSamples.maxRed, r]; maxSamples.maxGreen _ MAX[maxSamples.maxRed, g]; maxSamples.maxBlue _ MAX[maxSamples.maxRed, b]; }; FillScanLine: PROC [startX, stepSize: REAL, xSamples: NAT, y: REAL, cameraXStepRayInWorld: Ray, worldRay: Ray, tree: CSGTree, lightSources: LightSourceList, camera: Camera, boundBox: BoundBox, boundSphere: BoundSphere, scanLine: ScanLine, feedback: FeedbackData, maxSamples: MaxSamples] = { color: Color; r,g,b: REAL; thisX: REAL; <> [r,g,b] _ TopColorCast[[startX, y], worldRay, tree, lightSources, camera, boundBox, boundSphere, feedback]; UpdateMaxSamples[maxSamples, r, g, b]; color _ Shading.NormalizeRGB[r,g,b]; PutColorInScanLine[scanLine, 0, color]; SVRay.AddRay[cameraXStepRayInWorld, worldRay]; -- updates worldRay FOR j: INTEGER IN[1..xSamples] DO -- left to right thisX _ startX+Real.Float[j]*stepSize; [r,g,b] _ TopColorCast[[thisX, y], worldRay, tree, lightSources, camera, boundBox, boundSphere, feedback]; UpdateMaxSamples[maxSamples, r, g, b]; color _ Shading.NormalizeRGB[r,g,b]; PutColorInScanLine[scanLine, j, color]; SVRay.AddRay[cameraXStepRayInWorld, worldRay]; -- updates worldRay ENDLOOP; }; OutputCameraInfo: PROC [camera: Camera, feedback: FeedbackData] = { IF camera.useBoundBoxes THEN Feedback.PutF[feedback, oneLiner, "Use Bounding Boxes.\n"] ELSE Feedback.PutF[feedback, oneLiner, "Use Bounding Spheres.\n"]; IF camera.useBoundSpheresForShadows THEN Feedback.PutF[feedback, oneLiner, "Use Bound Spheres for Shadows.\n"] ELSE Feedback.PutF[feedback, oneLiner, "Use nothing for Shadows.\n"]; }; DrawTreeWithStartLine: PUBLIC PROC [startLine: REAL, tree: CSGTree, lightSources: LightSourceList, camera: Camera, aisRope: Rope.ROPE, bAndWOnly: BOOL, notify: NotifyOfProgressProc _ NoOpNotifyOfProgress, clientData: REF ANY _ NIL, feedback: FeedbackData] RETURNS [success: BOOL, maxRed, maxGreen, maxBlue, maxBlack: NAT] = { <= startCameraPoint[2]. So we start with i = (startCameraPoint[2]-yStart)/stepSize.>> topNode: REF ANY; -- tree.son. The top active node of the SVRay Tree I: Image; boundBox: BoundBox; boundSphere: BoundSphere; cameraWRTWorld: Matrix4by4; cameraXStepRayInWorld, cameraRay, worldRay: Ray; stepSize, xStart, yStart, yMiddleStart, thisY: REAL; maxSamples: MaxSamples _ NEW[MaxSamplesObj]; xSamples, ySamples, iStart: NAT; color: Color; scanLine1, scanLine2: ScanLine; startTime: BasicTime.GMT; startTime _ BasicTime.Now[]; success _ TRUE; topNode _ tree.son; camera.abort _ FALSE; -- if camera.abort becomes TRUE, close files and return. <> [boundBox, boundSphere] _ Preprocess3d.PreprocessForImage[tree, camera]; IF camera.frame.fullScreen AND boundBox = NIL THEN { ComplainInfiniteScene[feedback]; success _ FALSE; RETURN}; <> [I, xSamples, ySamples, stepSize, xStart, yStart] _ SetUpRayTrace [boundBox, camera, aisRope, bAndWOnly, camera.resolution, feedback]; OutputTreeInfo[topNode, I, feedback]; OutputCameraInfo[camera, feedback]; <> cameraRay _ SVRay.CreateRay[]; -- DrawTree recycles its own ray scanLine1 _ CreateScanLine[xSamples+1]; scanLine2 _ CreateScanLine[xSamples+1]; <> cameraWRTWorld _ CoordSys.WRTWorld[camera.coordSys]; cameraXStepRayInWorld _ GetXStepRayInWorld[stepSize, cameraWRTWorld, camera]; <> iStart _ Real.Fix[(startLine-yStart)/stepSize]; yMiddleStart _ yStart+iStart*stepSize; SVRay.StuffCameraRay[cameraRay, [xStart, yMiddleStart], camera]; worldRay _ SVRay.TransformRayToWorld[cameraRay, cameraWRTWorld]; -- allocates ray from pool FillScanLine [xStart, stepSize, xSamples, yMiddleStart, cameraXStepRayInWorld, worldRay, tree, lightSources, camera, boundBox, boundSphere, scanLine1, feedback, maxSamples]; SVRay.ReturnRayToPool[worldRay]; FOR i: INTEGER IN[iStart+1..ySamples] DO -- For each ray bottom to top. IF camera.abort = TRUE THEN {AbortDrawTree[I, aisRope, camera, startTime, feedback]; RETURN}; notify[yStart+i*stepSize, xStart, yStart, xStart+xSamples*stepSize, yStart+ySamples*stepSize, clientData]; -- tell the user interface that we have just cast line i - 1. <> thisY _ yStart+i*stepSize; SVRay.StuffCameraRay[cameraRay, [xStart, thisY], camera]; worldRay _ SVRay.TransformRayToWorld[cameraRay, cameraWRTWorld]; -- from pool FillScanLine [xStart, stepSize, xSamples, thisY, cameraXStepRayInWorld, worldRay, tree, lightSources, camera, boundBox, boundSphere, scanLine2, feedback, maxSamples]; SVRay.ReturnRayToPool[worldRay]; <> FOR k: NAT IN[0..xSamples) DO <> <> color _ ColorAverage[scanLine1[k], scanLine1[k+1], scanLine2[k], scanLine2[k+1]]; SVImage.PutImage[I, i, k, color, xSamples, ySamples]; ENDLOOP; CopyScanLine [scanLine2, scanLine1]; ENDLOOP; ShutDownRayTrace[aisRope, I, camera, startTime, feedback]; [maxRed, maxGreen, maxBlue, maxBlack] _ SVImage.RGBTo8Bits[maxSamples.maxRed, maxSamples.maxGreen, maxSamples.maxBlue]; }; -- end of DrawTreeWithStartLine ComplainInfiniteScene: PROC [feedback: FeedbackData] = { Feedback.Append[feedback, "Infinite Scene. Please define a bounding frame.", oneLiner]; Feedback.Blink[feedback]; }; MaxSamples: TYPE = REF MaxSamplesObj; MaxSamplesObj: TYPE = RECORD [maxRed, maxGreen, maxBlue: REAL _ 0]; DrawTree: PUBLIC PROC [tree: CSGTree, lightSources: LightSourceList, camera: Camera, aisRope: Rope.ROPE, bAndWOnly: BOOL, notify: NotifyOfProgressProc _ NoOpNotifyOfProgress, clientData: REF ANY _ NIL, feedback: FeedbackData] RETURNS [success: BOOL, maxRed, maxGreen, maxBlue, maxBlack: NAT] = { topNode: REF ANY; -- tree.son. The top active node of the SVRay Tree I: Image; boundBox: BoundBox; boundSphere: BoundSphere; cameraWRTWorld: Matrix4by4; cameraXStepRayInWorld, cameraRay, worldRay: Ray; stepSize, xStart, yStart, thisY: REAL; xSamples, ySamples: NAT; maxSamples: MaxSamples _ NEW[MaxSamplesObj]; color: Color; scanLine1, scanLine2: ScanLine; startTime: BasicTime.GMT; startTime _ BasicTime.Now[]; success _ TRUE; topNode _ tree.son; camera.abort _ FALSE; -- if camera.abort becomes TRUE, close files and return. <> [boundBox, boundSphere] _ Preprocess3d.PreprocessForImage[tree, camera]; IF camera.frame.fullScreen AND boundBox = NIL THEN { ComplainInfiniteScene[feedback]; success _ FALSE; RETURN}; <> [I, xSamples, ySamples, stepSize, xStart, yStart] _ SetUpRayTrace [boundBox, camera, aisRope, bAndWOnly, camera.resolution, feedback]; OutputTreeInfo[topNode, I, feedback]; OutputCameraInfo[camera, feedback]; <> scanLine1 _ CreateScanLine[xSamples+1]; scanLine2 _ CreateScanLine[xSamples+1]; cameraRay _ SVRay.CreateRay[]; -- DrawTree recycles its own ray <> cameraWRTWorld _ CoordSys.WRTWorld[camera.coordSys]; cameraXStepRayInWorld _ GetXStepRayInWorld[stepSize, cameraWRTWorld, camera]; <> SVRay.StuffCameraRay[cameraRay, [xStart, yStart], camera]; worldRay _ SVRay.TransformRayToWorld[cameraRay, cameraWRTWorld]; -- allocates ray from pool FillScanLine [xStart, stepSize, xSamples, yStart, cameraXStepRayInWorld, worldRay, tree, lightSources, camera, boundBox, boundSphere, scanLine1, feedback, maxSamples]; SVRay.ReturnRayToPool[worldRay]; FOR i: INTEGER IN[1..ySamples] DO -- For each ray bottom to top. IF camera.abort = TRUE THEN {AbortDrawTree[I, aisRope, camera, startTime, feedback]; RETURN}; notify[yStart+i*stepSize, xStart, yStart, xStart+xSamples*stepSize, yStart+ySamples*stepSize, clientData]; -- tell the user interface that we have just cast line i - 1. <> thisY _ yStart+i*stepSize; SVRay.StuffCameraRay[cameraRay, [xStart, thisY], camera]; worldRay _ SVRay.TransformRayToWorld[cameraRay, cameraWRTWorld]; -- allocates ray from pool FillScanLine [xStart, stepSize, xSamples, thisY, cameraXStepRayInWorld, worldRay, tree, lightSources, camera, boundBox, boundSphere, scanLine2, feedback, maxSamples]; SVRay.ReturnRayToPool[worldRay]; <> FOR k: NAT IN[0..xSamples) DO <> <> color _ ColorAverage[scanLine1[k], scanLine1[k+1], scanLine2[k], scanLine2[k+1]]; SVImage.PutImage[I, i, k, color, xSamples, ySamples]; ENDLOOP; CopyScanLine [scanLine2, scanLine1]; ENDLOOP; ShutDownRayTrace[aisRope, I, camera, startTime, feedback]; [maxRed, maxGreen, maxBlue, maxBlack] _ SVImage.RGBTo8Bits[maxSamples.maxRed, maxSamples.maxGreen, maxSamples.maxBlue]; }; -- end of DrawTree MoreOrLessTheSame: PROC [a, b, c, d: REAL] RETURNS [BOOL] = { min, max: REAL; min _ max _ a; IF b < min THEN min _ b ELSE IF b > max THEN max _ b; IF c < min THEN min _ c ELSE IF c > max THEN max _ c; IF d < min THEN min _ d ELSE IF d > max THEN max _ d; IF max - min > 10 THEN RETURN[FALSE] ELSE RETURN[TRUE]; }; -- end of MoreOrLessTheSame ColorAverage: PROC [a, b, c, d: Color] RETURNS [avgColor: Color] = { ar, ag, ab, br, bg, bb, cr, cg, cb, dr, dg, db, red, green, blue: REAL; [ar, ag, ab] _ Shading.ExtractRGB[a]; [br, bg, bb] _ Shading.ExtractRGB[b]; [cr, cg, cb] _ Shading.ExtractRGB[c]; [dr, dg, db] _ Shading.ExtractRGB[d]; red _ (ar + br + cr + dr)/4.0; green _ (ag + bg + cg + dg)/4.0; blue _ (ab + bb + cb + db)/4.0; avgColor _ ImagerColor.ColorFromRGB[[red, green, blue]]; }; -- end of ColorAverage <> <> <> <<>> <> <> <> <> <> <> <> <<>> <> <> <> <> <<>> <> <> <> <> <<>> <> <> <> <> <<>> <> <> <> <> <<>> <> <> <> <> <<>> <> <> <> <> <<>> <> <<>> <<}; -- end of CastMoreRays>> NoOpNotifyOfProgress: PUBLIC NotifyOfProgressProc = {}; GetClassFromPool: PUBLIC PROC RETURNS [class: Classification] = { IF globalPoolPointer = 0 THEN AddAClass[]; class _ globalPool[globalPoolPointer - 1]; globalPoolPointer _ globalPoolPointer - 1; }; ClassPoolEmpty: SIGNAL = CODE; ReturnClassToPool: PUBLIC PROC [class: Classification] = { IF globalPoolPointer = globalPool.maxClasses THEN SIGNAL ClassPoolFull; globalPoolPointer _ globalPoolPointer + 1; globalPool[globalPoolPointer - 1] _ class; }; ClassPoolFull: SIGNAL = CODE; NumberOfClassesInPool: PUBLIC PROC RETURNS [count: NAT] = { count _ globalPoolPointer; }; AddAClass: PROC = { <> newPool: Pool _ NEW[PoolObj[globalPool.maxClasses+1]]; IF globalPool.maxClasses > 50 THEN {-- there must be a leak in the classification system Feedback.AppendTypescriptRaw[$Solidviews, "SVCastRaysImplA Warning: More than 50 Classifications!!", oneLiner]; }; FOR i: NAT IN [0..globalPoolPointer) DO newPool[i] _ globalPool[i]; ENDLOOP; globalPoolPointer _ globalPoolPointer + 1; globalPool _ newPool; globalPool[globalPoolPointer - 1] _ NEW[ClassificationObj]; globalPool[globalPoolPointer - 1].surfaces _ NEW[SurfaceArrayObj]; }; GetCompactFromPool: PROC RETURNS [compact: CompactArray] = { IF globalCompactPoolPointer = 0 THEN SIGNAL CompactPoolEmpty; compact _ globalCompactPool[globalCompactPoolPointer]; globalCompactPoolPointer _ globalCompactPoolPointer -1; }; CompactPoolEmpty: SIGNAL = CODE; ReturnCompactToPool: PROC [compact: CompactArray] = { IF globalCompactPoolPointer = globalCompactPoolCount THEN SIGNAL CompactPoolFull; globalCompactPoolPointer _ globalCompactPoolPointer + 1; globalCompactPool[globalCompactPoolPointer] _ compact; }; CompactPoolFull: SIGNAL = CODE; MakeClassAMiss: PUBLIC PROC [class: Classification] = { class.count _ 0; class.classifs[1] _ FALSE; }; Init: PROC = { <> globalPool _ NEW[PoolObj[globalPoolCount]]; FOR i: NAT IN[0..globalPoolCount) DO globalPool[i] _ NEW[ClassificationObj]; globalPool[i].surfaces _ NEW[SurfaceArrayObj]; ENDLOOP; globalPoolPointer _ globalPoolCount; <> <<>> globalCompactPool _ NEW[CompactPoolObj]; FOR i: NAT IN[1..globalCompactPoolCount] DO globalCompactPool[i] _ NEW[CompactArrayObj]; ENDLOOP; globalCompactPoolPointer _ globalCompactPoolCount; }; Init[]; END.