{ "X3D": {
    "encoding":"UTF-8",
    "@profile":"Immersive",
    "@version":"3.3",
    "@xsd:noNamespaceSchemaLocation":"https://www.web3d.org/specifications/x3d-3.3.xsd",
    "JSON schema":"https://www.web3d.org/specifications/x3d-4.0-JSONSchema.autogenerated.json",
    "head": {
        "-children":[
          {
            "#comment":"javascript code for rotation calculations was derived from:"
          }
        ],
        "meta": [
          {
            "@name":"title",
            "@content":"ChangingFog.x3d"
          },
          {
            "@name":"description",
            "@content":"A Fog node that adjusts as the viewer's orientation and position changes. This is a good candidate to become a Prototype since Fog does not automatically bind when inlined."
          },
          {
            "@name":"creator",
            "@content":"Matthew Braun"
          },
          {
            "@name":"created",
            "@content":"20 September 2001"
          },
          {
            "@name":"modified",
            "@content":"20 October 2019"
          },
          {
            "@name":"reference",
            "@content":"http://astronomy.swin.edu.au/pbourke/geometry/rotate/"
          },
          {
            "@name":"rights",
            "@content":"Copyright (c) Matthew Braun 2001"
          },
          {
            "@name":"subject",
            "@content":"Fog"
          },
          {
            "@name":"identifier",
            "@content":"https://www.web3d.org/x3d/content/examples/X3dForWebAuthors/KelpForestExhibit/ChangingFog.x3d"
          },
          {
            "@name":"generator",
            "@content":"X3D-Edit 3.3, https://www.web3d.org/x3d/tools/X3D-Edit"
          },
          {
            "@name":"license",
            "@content":"../license.html"
          },
          {
            "@name":"translated",
            "@content":"17 April 2026"
          },
          {
            "@name":"generator",
            "@content":"X3dToJson.xslt, https://www.web3d.org/x3d/stylesheets/X3dToJson.html"
          },
          {
            "@name":"reference",
            "@content":"X3D JSON encoding: https://www.web3d.org/wiki/index.php/X3D_JSON_Encoding"
          }
        ]
    },
    "Scene": {
        "-children":[
          { "WorldInfo":
            {
              "@title":"ChangingFog.x3d"
            }
          },
          { "NavigationInfo":
            {
              "@avatarSize":[0.01,0,0]
            }
          },
          { "Viewpoint":
            {
              "@description":"Start",
              "@fieldOfView":0.9,
              "@position":[0,0,0]
            }
          },
          { "Viewpoint":
            {
              "@description":"Looking up from start",
              "@orientation":[1,0,0,1.57],
              "@position":[0,0,0]
            }
          },
          { "Viewpoint":
            {
              "@description":"10m above, looking straight up",
              "@orientation":[1,0,0,1.57],
              "@position":[0,10,0]
            }
          },
          { "Viewpoint":
            {
              "@description":"10m above start",
              "@position":[0,10,0]
            }
          },
          { "Viewpoint":
            {
              "@description":"10m above, looking straight down",
              "@orientation":[1,0,0,-1.57],
              "@position":[0,10,0]
            }
          },
          { "Viewpoint":
            {
              "@description":"10m below, looking down",
              "@orientation":[1,0,0,-1.57],
              "@position":[0,-10,0]
            }
          },
          { "Viewpoint":
            {
              "@description":"10m below start",
              "@position":[0,-10,0]
            }
          },
          { "Viewpoint":
            {
              "@description":"10m below, looking up",
              "@orientation":[1,0,0,1.57],
              "@position":[0,-10,0]
            }
          },
          { "Fog":
            {
              "@DEF":"Water",
              "@color":[0.2,0.2,0.4],
              "@fogType":"EXPONENTIAL"
            }
          },
          {
            "#comment":"Proximity sensor must be large enough to encompass the entire scene"
          },
          { "ProximitySensor":
            {
              "@DEF":"ProxSensor",
              "@size":[1000,1000,1000]
            }
          },
          {
            "#comment":"TimeSensor triggering reduces frequency of calculations for performance reasons."
          },
          { "TimeSensor":
            {
              "@DEF":"Clock",
              "@loop":true
            }
          },
          { "Script":
            {
              "@DEF":"ChangeVisibility",
              "field": [
                {
                  "@name":"get_clock_hit",
                  "@accessType":"inputOnly",
                  "@type":"SFTime"
                },
                {
                  "@name":"run_script",
                  "@accessType":"initializeOnly",
                  "@type":"SFBool",
                  "@value":false
                },
                {
                  "@name":"get_depth",
                  "@accessType":"inputOnly",
                  "@type":"SFVec3f"
                },
                {
                  "@name":"visibility_changed",
                  "@accessType":"outputOnly",
                  "@type":"SFFloat"
                },
                {
                  "@name":"set_visibility",
                  "@accessType":"inputOnly",
                  "@type":"SFRotation"
                }
              ],
              "-children":[
                {
                  "#comment":"<field accessType='initializeOnly' name='checked' type='SFBool' value='false'/> <field accessType='initializeOnly' name='moved' type='SFBool' value='false'/>"
                }
              ],
              "#sourceCode":[
"",
"",
"ecmascript:",
"// REF: http://astronomy.swin.edu.au/pbourke/geometry/rotate/",
"",
"function initialize () {",
"   visibility = 20;",
"   depth = 0;",
"   pos = (0,0,0);",
"   Browser.println ('Position output from ProximitySensor.');",
"}",
"",
"function get_clock_hit (clock_msg) {",
"     run_script = true;",
"}",
"",
"function get_depth ( position ) {",
"",
"   pos = position;",
"   depth = position[1] - 30;   ",
"",
"}",
"",
"function set_visibility( rotation ) {",
"",
" if (run_script) {",
"",
"//z coordinate of the default viewpoint direction(0,0,-1)",
"   initZ = -1;  ",
"",
"   rX = rotation[0];  // x coordinate of the rotation",
"   rY = rotation[1];  // y coordinate of the rotation",
"   rZ = rotation[2];  // z coordinate of the rotation",
"",
"   theta = rotation[3];  // angle of rotation in radians",
"\t",
"Browser.println ('theta:' + theta);",
"",
"   cosTheta = Math.cos(theta);",
"   sinTheta = Math.sin(theta);",
"",
"Browser.println ('cosTheta:' + cosTheta + ' sinTheta:'+ sinTheta);",
"",
"",
"// calculate the y coordinate of the point after rotation",
"/* there are 8 other terms in the full conversion, but 6 are equal",
"to zero because of the choice of a starting point on the z-axis. The",
"other two are not calculated since all we need is the y coordinate",
"*/",
"   finalY = ((1 - cosTheta) * rY * rZ - rX * sinTheta) * initZ;",
"",
"Browser.println ('final y:' + finalY);",
"",
"//calculate the elevation/depression angle of the final point location",
"",
"   elevation = Math.asin(finalY);",
"",
"Browser.println ('elevation:' + elevation);",
"",
"   directionFactor = 1 + 0.2 * (4 * elevation / Math.PI);",
"   depthAdjust = (60 + depth)/60",
"   depthFactor = Math.max(depthAdjust,0.05);",
"",
"   visibility_changed =  60 * depthFactor * directionFactor; ",
"   Browser.println ('depth=' + depth + ', elevation=' + elevation + ",
"        ', visibility_changed=' + visibility_changed);",
"   run_script = false;",
"",
" }",
"}",
"",
""
]
            }
          },
          { "ROUTE":
            {
              "@fromField":"cycleTime",
              "@fromNode":"Clock",
              "@toField":"get_clock_hit",
              "@toNode":"ChangeVisibility"
            }
          },
          { "ROUTE":
            {
              "@fromField":"position_changed",
              "@fromNode":"ProxSensor",
              "@toField":"get_depth",
              "@toNode":"ChangeVisibility"
            }
          },
          { "ROUTE":
            {
              "@fromField":"orientation_changed",
              "@fromNode":"ProxSensor",
              "@toField":"set_visibility",
              "@toNode":"ChangeVisibility"
            }
          },
          { "ROUTE":
            {
              "@fromField":"visibility_changed",
              "@fromNode":"ChangeVisibility",
              "@toField":"visibilityRange",
              "@toNode":"Water"
            }
          },
          {
            "#comment":"A set of arrows is used to show visibility and direction"
          },
          { "Transform":
            {
              "@DEF":"Pointer",
              "@translation":[0,0,-15],
              "-children":[
                { "Transform":
                  {
                    "@translation":[0,4,0],
                    "-children":[
                      { "Shape":
                        {
                          "-geometry":
                            { "Cone":
                              {
                                "@bottomRadius":0.4
                              }
                            },
                          "-appearance":
                            { "Appearance":
                              {
                                "@DEF":"ShapeApp",
                                "-material":
                                  { "Material":
                                    {
                                      "@ambientIntensity":0.8,
                                      "@diffuseColor":[1,1,0.3],
                                      "@shininess":0.6
                                    }
                                  }
                              }
                            }
                        }
                      }
                    ]
                  }
                },
                { "Shape":
                  {
                    "-geometry":
                      { "Cylinder":
                        {
                          "@height":6,
                          "@radius":0.2
                        }
                      },
                    "-appearance":
                      { "Appearance":
                        {
                          "@USE":"ShapeApp"
                        }
                      }
                  }
                }
              ]
            }
          },
          { "Transform":
            {
              "@translation":[0,10,0],
              "-children":[
                { "Transform":
                  {
                    "@USE":"Pointer"
                  }
                }
              ]
            }
          },
          { "Transform":
            {
              "@translation":[0,-10,0],
              "-children":[
                { "Transform":
                  {
                    "@USE":"Pointer"
                  }
                }
              ]
            }
          },
          {
            "#comment":"A pair of disks used to show visibility"
          },
          { "Transform":
            {
              "@translation":[0,15,0],
              "-children":[
                { "Shape":
                  {
                    "@DEF":"Disk",
                    "-geometry":
                      { "Cylinder":
                        {
                          "@height":0.01
                        }
                      },
                    "-appearance":
                      { "Appearance":
                        {
                          "@USE":"ShapeApp"
                        }
                      }
                  }
                }
              ]
            }
          },
          { "Transform":
            {
              "@translation":[0,-15,0],
              "-children":[
                { "Shape":
                  {
                    "@USE":"Disk"
                  }
                }
              ]
            }
          },
          {
            "#comment":"An indexed face set box used to bound the working area"
          },
          { "Transform":
            {
              "@scale":[20,20,20],
              "-children":[
                { "Shape":
                  {
                    "@DEF":"IFSBox",
                    "-appearance":
                      { "Appearance":
                        {
                          "-material":
                            { "Material":
                              {
                                "@diffuseColor":[1,1,1]
                              }
                            }
                        }
                      },
                    "-geometry":
                      { "IndexedFaceSet":
                        {
                          "@ccw":false,
                          "@colorIndex":[0,2,2,2,2,1],
                          "@colorPerVertex":false,
                          "@coordIndex":[0,1,2,3,-1,7,6,5,4,-1,0,4,5,1,-1,1,5,6,2,-1,2,6,7,3,-1,3,7,4,0],
                          "-coord":
                            { "Coordinate":
                              {
                                "@point":[-1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,-1.0,-1.0,1.0,-1.0,-1.0,-1.0,1.0,1.0,-1.0,1.0,1.0,-1.0,-1.0,-1.0,-1.0,-1.0]
                              }
                            },
                          "-color":
                            { "Color":
                              {
                                "@color":[1,1,1,0,0,0,0.2,0.2,0.8]
                              }
                            }
                        }
                      }
                  }
                }
              ]
            }
          }
        ]
    }
  }
}