<> <> <> <> <> DIRECTORY AIS, CastRays, CoordSys, CSG, CSGGraphics, Graphics, GraphicsColor, IO, Matrix3d, Preprocess3d, Real, Rope, Shading, SV2d, SV3d, SVArtwork, SVBoundBox, SVError, SVFancyRays, SVImage, SVModelTypes, SVRayTypes, SVVector3d; CastRaysImplA: PROGRAM IMPORTS CastRays, CoordSys, CSG, GraphicsColor, IO, Matrix3d, Preprocess3d, Real, Rope, Shading, SVArtwork, SVBoundBox, SVError, SVFancyRays, SVImage, SVVector3d EXPORTS CastRays = BEGIN Artwork: TYPE = SVModelTypes.Artwork; BoundBox: TYPE = SVModelTypes.BoundBox; BoundSphere: TYPE = SVModelTypes.BoundSphere; Camera: TYPE = SVModelTypes.Camera; Color: TYPE = GraphicsColor.Color; Composite: TYPE = SVRayTypes.Composite; CoordSystem: TYPE = SVModelTypes.CoordSystem; CSGTree: TYPE = SVRayTypes.CSGTree; LightSourceList: TYPE = SVModelTypes.LightSourceList; NotifyOfProgressProc: TYPE = CastRays.NotifyOfProgressProc; Point3d: TYPE = SV3d.Point3d; Point2d: TYPE = SV2d.Point2d; PointSetOp: TYPE = SVRayTypes.PointSetOp; Primitive: TYPE = SVRayTypes.Primitive; Matrix4by4: TYPE = SV3d.Matrix4by4; Ray: TYPE = SVRayTypes.Ray; Surface: TYPE = REF ANY; Vector: TYPE = SV3d.Vector; Classification: TYPE = REF ClassificationObj; ClassificationObj: TYPE = SVRayTypes.ClassificationObj; SurfaceArray: TYPE = REF SurfaceArrayObj; SurfaceArrayObj: TYPE = SVRayTypes.SurfaceArrayObj; ParameterArray: TYPE = SVRayTypes.ParameterArray; InOutArray: TYPE = SVRayTypes.InOutArray; NormalArray: TYPE = SVRayTypes.NormalArray; PrimitiveArray: TYPE = SVRayTypes.PrimitiveArray; CompactArray: TYPE = REF CompactArrayObj; CompactArrayObj: TYPE = ARRAY [1..SVRayTypes.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, makeStream: BOOL, f: IO.STREAM, indent: NAT] = { <> <> opname: Rope.ROPE; IF NOT makeStream THEN RETURN; Indent[f, indent]; SELECT comp.operation FROM union => opname _ "union"; intersection => opname _ "intersection"; difference => opname _ "difference"; ENDCASE => ERROR; f.PutF["Composite %g [op: %g] returns class: [count: %g]\n", [rope[comp.name]],[rope[opname]], [integer[class.count]]]; WritePrimNames[class, f, indent]; }; -- end of WriteStreamComp Indent: PRIVATE PROC [f: IO.STREAM, indent: NAT] = { FOR i: NAT IN[1..indent] DO f.PutChar[IO.TAB]; ENDLOOP; }; WritePrimNames: PRIVATE 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, makeStream: BOOL, f: IO.STREAM, indent: NAT] = { IF NOT makeStream THEN RETURN; 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: PRIVATE 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 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, makeStream: BOOL _ FALSE, f: IO.STREAM _ NIL, 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, makeStream, f, 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, makeStream, f, 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, makeStream, f, indent]; RETURN}; intersection => IF NOT leftHit THEN RETURN[leftClass] ELSE { ReturnClassToPool[leftClass]; class _ EmptyClass[]; WriteStreamComp[comp, class, makeStream, f, indent]; RETURN}; difference => {class _ leftClass; WriteStreamComp[comp, class, makeStream, f, 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, makeStream, f, 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, makeStream, f, indent]; RETURN}; prim: Primitive => { localRay: Ray; <> localRay _ CSG.TransformRay[worldRay, prim.worldWRTPrim]; -- (takes a new ray from the pool) class _ prim.rayCast[cameraPoint, localRay, prim.mo, prim]; WriteStreamPrim[prim, class, makeStream, f, 0]; CSG.ReturnRayToPool[localRay]; -- returns ray to pool RETURN}; ENDCASE => ERROR; }; -- end of RayCast RayCastNoBBoxes: PUBLIC PROC [worldRay: Ray, node: REF ANY, consolidate: BOOL _ TRUE, makeStream: BOOL _ FALSE, f: IO.STREAM _ NIL, 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, makeStream, f, 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, makeStream, f, indent]; RETURN}; <> <<>> <<3) If hit, or union, then cast right ray.>> <<>> rightClass _ RayCastNoBBoxes[worldRay, comp.rightSolid, consolidate, makeStream, f, 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, makeStream, f, indent]; RETURN}; prim: Primitive => { localRay: Ray; <> localRay _ CSG.TransformRay[worldRay, prim.worldWRTPrim]; -- (takes a new ray from the pool) class _ prim.rayCastNoBBoxes[localRay, prim.mo, prim]; WriteStreamPrim[prim, class, makeStream, f, 0]; CSG.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, makeStream: FALSE]; hits _ DoesHit[class]; ReturnClassToPool[class]; RETURN[hits]; }; FirstHit: PUBLIC PROC [worldRay: Ray, tree: CSGTree, useBoundSpheres: BOOL, makeStream: BOOL _ FALSE, f: IO.STREAM _ NIL, 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, f: f, indent: indent] ELSE class _ CastRays.RayCastBoundingSpheres [worldRay: worldRay, node: node, makeStream: makeStream, f: f, 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 <> <> <