<> <> <> <> DIRECTORY AIS, AtomButtonsTypes, SVCastRays, CoordSys, SVRay, DisplayListToTree, IO, Matrix3d, SV2d, SV3d, SVBasicTypes, SVModelTypes, SVSceneTypes; SVCastRaysImplB: CEDAR PROGRAM IMPORTS SVCastRays, CoordSys, SVRay, DisplayListToTree, Matrix3d EXPORTS SVCastRays = BEGIN Slice: TYPE = SVSceneTypes.Slice; BoundSphere: TYPE = SVBasicTypes.BoundSphere; Camera: TYPE = SVModelTypes.Camera; Classification: TYPE = SVSceneTypes.Classification; Composite: TYPE = SVSceneTypes.Composite; CoordSystem: TYPE = SVModelTypes.CoordSystem; FeedbackData: TYPE = AtomButtonsTypes.FeedbackData; Point2d: TYPE = SV2d.Point2d; Point3d: TYPE = SV3d.Point3d; Primitive: TYPE = SVSceneTypes.Primitive; Ray: TYPE = SVSceneTypes.Ray; Vector3d: TYPE = SV3d.Vector3d; DoesHit: PROC [class: Classification] RETURNS [BOOL] = { RETURN[class.count > 0 OR class.classifs[1] = TRUE]; }; RayCastBoundingSpheres: PUBLIC PROC [worldRay: Ray, node: REF ANY, consolidate: BOOL _ TRUE, feedback: FeedbackData, makeStream: BOOL _ FALSE, indent: NAT _ 0] RETURNS [class: Classification] = { <> OPEN SVCastRays; IF node = NIL THEN {class _ EmptyClass[]; RETURN}; WITH node SELECT FROM comp: Composite => { leftClass, rightClass: Classification; leftBoxHit, leftHit, rightBoxHit, rightHit: BOOL; totalMiss: BOOL _ FALSE; boundSphere: BoundSphere; <> <> <<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 => boundSphere _ p.boundSphere; c: Composite => boundSphere _ c.boundSphere; ENDCASE => ERROR; leftBoxHit _ SVRay.RayHitsBoundSphere[worldRay, boundSphere]; <<2) If leftBoxHit then cast the ray. Set leftHit if appropriate.>> <<>> IF leftBoxHit THEN { leftClass _ RayCastBoundingSpheres[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 sphere 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 => boundSphere _ p.boundSphere; c: Composite => boundSphere _ c.boundSphere; ENDCASE => ERROR; rightBoxHit _ SVRay.RayHitsBoundSphere[worldRay, boundSphere]; <<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 _ RayCastBoundingSpheres[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.rayCastBoundingSpheres[localRay, prim.sliceD, prim]; WriteStreamPrim[prim, class, feedback, makeStream, 0]; SVRay.ReturnRayToPool[localRay]; -- returns ray to pool RETURN}; ENDCASE => ERROR; }; -- end of RayCastBoundingSpheres ClassAssign: PROC [to: Classification, i: NAT, from: Classification, j: NAT] = { to.params[i] _ from.params[j]; to.classifs[i] _ from.classifs[j]; to.normals[i] _ from.normals[j]; to.surfaces[i] _ from.surfaces[j]; to.primitives[i] _ from.primitives[j]; }; <> <> <> <> <> <> <> <<>> <> <> <> <<>> <> <> <> <<>> <> <> <> <<>> <> <> <> <<>> <> <> <> <<};>> <<>> SortClassByPrimitive: PUBLIC PROC [class: Classification] = { <> newClass: Classification; used: ARRAY [1..SVSceneTypes.maxSceneDepth] OF BOOL; thisPrim: Primitive; newClass _ SVRay.CopyClass[class]; FOR i: NAT IN [1..SVSceneTypes.maxSceneDepth] DO used[i] _ FALSE; ENDLOOP; thisPrim _ newClass.primitives[1]; FOR i: NAT IN [1..class.count] DO FOR j: NAT IN [1..class.count] DO IF NOT used[j] AND newClass.primitives[j] = thisPrim THEN { ClassAssign[class, i, newClass, j]; used[j] _ TRUE; GOTO Found; }; REPEAT Found => NULL; FINISHED => FOR k: NAT IN [1..class.count] DO IF NOT used[k] THEN { thisPrim _ newClass.primitives[k]; ClassAssign[class, i, newClass, k]; used[k] _ TRUE; EXIT; }; ENDLOOP; ENDLOOP; ENDLOOP; }; <> NthSurfacePointAndNormal: PUBLIC PROC [class: Classification, rayWorld: Ray, n: NAT] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector3d] = { <> firstPrim: Primitive; primitiveNormal: Vector3d; t: REAL; count: NAT _ 0; IF n = 0 OR n > class.count THEN ERROR; FOR i: NAT IN [1..class.count] DO IF class.stills[i] THEN { count _ count + 1; IF count = n THEN GOTO Found; }; REPEAT Found => { t _ class.params[i]; -- the parameter of the ray intersection primitiveNormal _ class.normals[i]; firstPrim _ class.primitives[i]; normalWorld _ Matrix3d.UpdateVectorWithInverse[firstPrim.worldWRTPrim, primitiveNormal]; surfacePtWorld _ SVRay.EvaluateWorldRay[rayWorld, t]; }; FINISHED => { surfacePtWorld _ [0,0,0]; normalWorld _ [0,0,1]; }; ENDLOOP; }; LastOfFirstPointAndNormal: PUBLIC PROC [class: Classification, rayWorld: Ray] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector3d] = { <> primitiveNormal: Vector3d; t: REAL; lastNum: NAT _ class.count; lastOfFirstIndex: NAT _ 1; firstPrim: Primitive _ class.primitives[1]; IF lastNum = 0 THEN RETURN; FOR i: NAT DECREASING IN [2..lastNum] DO IF class.primitives[i] = firstPrim THEN GOTO Found; REPEAT Found => lastOfFirstIndex _ i; ENDLOOP; t _ class.params[lastOfFirstIndex]; -- the parameter of the last ray intersection primitiveNormal _ class.normals[lastOfFirstIndex]; normalWorld _ Matrix3d.UpdateVectorWithInverse[firstPrim.worldWRTPrim, primitiveNormal]; surfacePtWorld _ SVRay.EvaluateWorldRay[rayWorld, t]; }; LastOfFirstHitData: PUBLIC PROC [class: Classification] RETURNS [hitData: REF ANY] = { <> lastNum: NAT _ class.count; lastOfFirstIndex: NAT _ 1; firstPrim: Primitive _ class.primitives[1]; IF lastNum = 0 THEN RETURN; FOR i: NAT DECREASING IN [2..lastNum] DO IF class.primitives[i] = firstPrim THEN GOTO Found; REPEAT Found => lastOfFirstIndex _ i; ENDLOOP; hitData _ class.surfaces[lastOfFirstIndex]; }; LastTopLevelPointAndNormal: PUBLIC PROC [class: Classification, rayWorld: Ray, root: Slice] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector3d] = { <> primitiveNormal: Vector3d; t: REAL; lastNum: NAT _ class.count; lastTopLevelIndex: NAT _ 1; firstPrim: Primitive _ class.primitives[1]; lastPrim: Primitive; firstAssembly, thisAssembly: Slice; firstTopLevel: Slice; firstAssembly _ NARROW[firstPrim.assembly]; firstTopLevel _ DisplayListToTree.AncestorAtLevel[firstAssembly, root, 1]; IF lastNum = 0 THEN RETURN; FOR i: NAT DECREASING IN [2..lastNum] DO thisAssembly _ NARROW[class.primitives[i].assembly]; IF DisplayListToTree.IsSuccessorOf[thisAssembly, firstTopLevel] THEN GOTO Found; REPEAT Found => lastTopLevelIndex _ i; ENDLOOP; t _ class.params[lastTopLevelIndex]; -- the parameter of the last ray intersection primitiveNormal _ class.normals[lastTopLevelIndex]; lastPrim _ class.primitives[lastTopLevelIndex]; normalWorld _ Matrix3d.UpdateVectorWithInverse[lastPrim.worldWRTPrim, primitiveNormal]; surfacePtWorld _ SVRay.EvaluateWorldRay[rayWorld, t]; }; NthAssemblyAndPrimitive: PUBLIC PROC [class: Classification, n: NAT] RETURNS [assembly: Slice, primitive: Primitive] = { <> count: NAT _ 0; IF n = 0 OR n > class.count THEN RETURN[NIL, NIL]; FOR i: NAT IN [1..class.count] DO IF class.stills[i] THEN { count _ count + 1; IF count = n THEN GOTO Found; }; REPEAT Found => { primitive _ class.primitives[i]; assembly _ NARROW[primitive.assembly]; }; FINISHED => { primitive _ NIL; assembly _ NIL; }; ENDLOOP; }; NthHitData: PUBLIC PROC [class: Classification, n: NAT] RETURNS [hitData: REF ANY] = { <> count: NAT _ 0; IF n = 0 OR n > class.count THEN RETURN[NIL]; FOR i: NAT IN [1..class.count] DO IF class.stills[i] THEN { count _ count + 1; IF count = n THEN GOTO Found; }; REPEAT Found => { hitData _ class.surfaces[i]; }; FINISHED => { hitData _ NIL; }; ENDLOOP; }; LastTopLevelAssemAndPrim: PUBLIC PROC [class: Classification, root: Slice] RETURNS [assembly: Slice, primitive: Primitive] = { <> lastNum: NAT _ class.count; lastTopLevelIndex: NAT _ 1; firstPrim: Primitive _ class.primitives[1]; firstTopLevel, thisAssembly: Slice; IF lastNum = 0 THEN RETURN[NIL, NIL]; thisAssembly _ NARROW[firstPrim.assembly]; firstTopLevel _ DisplayListToTree.AncestorAtLevel[thisAssembly, root, 1]; FOR i: NAT DECREASING IN [2..lastNum] DO thisAssembly _ NARROW[class.primitives[i].assembly]; IF DisplayListToTree.IsSuccessorOf[thisAssembly, firstTopLevel] THEN GOTO Found; REPEAT Found => lastTopLevelIndex _ i; ENDLOOP; primitive _ class.primitives[lastTopLevelIndex]; <> assembly _ firstTopLevel; }; LastTopLevelHitData: PUBLIC PROC [class: Classification, root: Slice] RETURNS [hitData: REF ANY] = { <> lastNum: NAT _ class.count; lastTopLevelIndex: NAT _ 1; firstPrim: Primitive _ class.primitives[1]; firstTopLevel, thisAssembly: Slice; IF lastNum = 0 THEN RETURN[NIL]; thisAssembly _ NARROW[firstPrim.assembly]; firstTopLevel _ DisplayListToTree.AncestorAtLevel[thisAssembly, root, 1]; FOR i: NAT DECREASING IN [2..lastNum] DO thisAssembly _ NARROW[class.primitives[i].assembly]; IF DisplayListToTree.IsSuccessorOf[thisAssembly, firstTopLevel] THEN GOTO Found; REPEAT Found => lastTopLevelIndex _ i; ENDLOOP; hitData _ class.surfaces[lastTopLevelIndex]; }; RayMeetsZConstantPlane: PUBLIC PROC [localRay: Ray, z: REAL] RETURNS [hitPoint: Point3d, parallel: BOOL] = { <> <> <> <> <> almostZero: REAL _ 1.0e-12; t: REAL; p: Point3d; d: Vector3d; [p, d] _ SVRay.GetLocalRay[localRay]; IF ABS[d[3]] < almostZero THEN { hitPoint _ [0,0,0]; parallel _ TRUE; RETURN; } ELSE { t _ (z - p[3])/d[3]; hitPoint[1] _ p[1] + t*d[1]; hitPoint[2] _ p[2] + t*d[2]; hitPoint[3] _ z; parallel _ FALSE; }; }; RayMeetsPlaneOfCoordSystem: PUBLIC PROC [coordSys: CoordSystem, rayWorld: Ray, whichPlane: NAT, depth: REAL _ 0] RETURNS [pointWorld: Point3d, parallel: BOOL] = { <> <> <> <> <> almostZero: REAL _ 1.0e-12; t: REAL; localRay: Ray; hitPoint: Point3d; p: Point3d; d: Vector3d; localRay _ SVRay.TransformRay[rayWorld, CoordSys.FindWorldInTermsOf[coordSys]]; [p, d] _ SVRay.GetLocalRay[localRay]; SELECT whichPlane FROM 1 => { -- The x = depth plane IF ABS[d[1]] < almostZero THEN { pointWorld _ [depth,0,0]; parallel _ TRUE; RETURN; } ELSE { t _ (depth - p[1])/d[1]; hitPoint[1] _ depth; hitPoint[2] _ p[2] + t*d[2]; hitPoint[3] _ p[3] + t*d[3]; parallel _ FALSE; }; }; 2 => { -- The y = depth plane IF ABS[d[2]] < almostZero THEN { pointWorld _ [0,depth,0]; parallel _ TRUE; RETURN; } ELSE { t _ (depth - p[2])/d[2]; hitPoint[1] _ p[1] + t*d[1]; hitPoint[2] _ depth; hitPoint[3] _ p[3] + t*d[3]; parallel _ FALSE; }; }; 3 => { -- The z = depth plane IF ABS[d[3]] < almostZero THEN { pointWorld _ [0,0,depth]; parallel _ TRUE; RETURN; } ELSE { t _ (depth - p[3])/d[3]; hitPoint[1] _ p[1] + t*d[1]; hitPoint[2] _ p[2] + t*d[2]; hitPoint[3] _ depth; parallel _ FALSE; }; }; ENDCASE => ERROR; SVRay.ReturnRayToPool[localRay]; pointWorld _ Matrix3d.Update[hitPoint, CoordSys.WRTWorld[coordSys]]; }; END.