{ "X3D": {
    "encoding":"UTF-8",
    "@profile":"Immersive",
    "@version":"3.0",
    "@xsd:noNamespaceSchemaLocation":"https://www.web3d.org/specifications/x3d-3.0.xsd",
    "JSON schema":"https://www.web3d.org/specifications/x3d-4.0-JSONSchema.autogenerated.json",
    "head": {
        "meta": [
          {
            "@name":"title",
            "@content":"MaterialChoicePrototype.x3d"
          },
          {
            "@name":"description",
            "@content":"Prototype definition for toggle-able Material node that can choose among an array of material nodes, and is switchable at run time. This prototype also demonstrates why it is important that only the first node in a ProtoBody can render: so that it can serve as a special node type (such as Material)."
          },
          {
            "@name":"creator",
            "@content":"Don Brutzman and MV4205 class"
          },
          {
            "@name":"created",
            "@content":"29 April 2004"
          },
          {
            "@name":"modified",
            "@content":"28 November 2019"
          },
          {
            "@name":"reference",
            "@content":"MaterialChoiceExample.x3d"
          },
          {
            "@name":"identifier",
            "@content":"https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/MaterialChoicePrototype.x3d"
          },
          {
            "@name":"generator",
            "@content":"X3D-Edit 3.2, https://www.web3d.org/x3d/tools/X3D-Edit"
          },
          {
            "@name":"license",
            "@content":"../../license.html"
          },
          {
            "@name":"translated",
            "@content":"26 March 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":"MaterialChoicePrototype.x3d"
            }
          },
          { "ProtoDeclare":
            {
              "@name":"MaterialChoice",
              "@appinfo":"MaterialChoice selects one of several different Material values",
              "ProtoInterface": {
                  "field": [
                    {
                      "@name":"set_index",
                      "@accessType":"inputOnly",
                      "@type":"SFInt32"
                    },
                    {
                      "@name":"index",
                      "@accessType":"initializeOnly",
                      "@appinfo":"which Material node is chosen, with array index starting at 0",
                      "@type":"SFInt32",
                      "@value":0
                    },
                    {
                      "@name":"index_changed",
                      "@accessType":"outputOnly",
                      "@type":"SFInt32"
                    },
                    {
                      "@name":"set_fraction",
                      "@accessType":"inputOnly",
                      "@type":"SFFloat"
                    },
                    {
                      "@name":"fraction",
                      "@accessType":"initializeOnly",
                      "@appinfo":"fraction to interpolate between current and next Material node (if any) across all field values.",
                      "@type":"SFFloat",
                      "@value":0
                    },
                    {
                      "@name":"next",
                      "@accessType":"inputOnly",
                      "@appinfo":"Trigger next Material node",
                      "@type":"SFBool"
                    },
                    {
                      "@name":"previous",
                      "@accessType":"inputOnly",
                      "@appinfo":"Trigger previous Material node",
                      "@type":"SFBool"
                    },
                    {
                      "@name":"materials",
                      "@accessType":"initializeOnly",
                      "@appinfo":"array of available Material nodes defaults to zeroth Material",
                      "@type":"MFNode",
                      "-children":[
                        {
                          "#comment":"no default initialization node since it crashes Xj3D"
                        }
                      ]
                    },
                    {
                      "@name":"appendMaterial",
                      "@accessType":"inputOnly",
                      "@appinfo":"append another Material node to materials array",
                      "@type":"SFNode"
                    },
                    {
                      "@name":"deleteMaterial",
                      "@accessType":"inputOnly",
                      "@appinfo":"delete Material node indicated by input index value",
                      "@type":"SFInt32"
                    },
                    {
                      "@name":"deleteAllMaterials",
                      "@accessType":"inputOnly",
                      "@appinfo":"deletes all Material nodes resets scene to default Material values defined in X3D Specification",
                      "@type":"SFBool"
                    }
                  ]
              },
              "ProtoBody": {
                  "-children":[
                    {
                      "#comment":"first node is node type for this prototype, other nodes are active but do not render"
                    },
                    { "Material":
                      {
                        "@DEF":"ViewedMaterialNode"
                      }
                    },
                    { "Script":
                      {
                        "@DEF":"MaterialAnimationScript",
                        "@directOutput":true,
                        "field": [
                          {
                            "@name":"traceEnabled",
                            "@accessType":"initializeOnly",
                            "@type":"SFBool",
                            "@value":false
                          },
                          {
                            "@name":"set_index",
                            "@accessType":"inputOnly",
                            "@type":"SFInt32"
                          },
                          {
                            "@name":"index",
                            "@accessType":"initializeOnly",
                            "@type":"SFInt32"
                          },
                          {
                            "@name":"index_changed",
                            "@accessType":"outputOnly",
                            "@type":"SFInt32"
                          },
                          {
                            "@name":"set_fraction",
                            "@accessType":"inputOnly",
                            "@type":"SFFloat"
                          },
                          {
                            "@name":"fraction",
                            "@accessType":"initializeOnly",
                            "@type":"SFFloat"
                          },
                          {
                            "@name":"next",
                            "@accessType":"inputOnly",
                            "@type":"SFBool"
                          },
                          {
                            "@name":"previous",
                            "@accessType":"inputOnly",
                            "@type":"SFBool"
                          },
                          {
                            "@name":"viewedMaterial",
                            "@accessType":"initializeOnly",
                            "@type":"SFNode",
                            "-children":[
                              { "Material":
                                {
                                  "@USE":"ViewedMaterialNode"
                                }
                              }
                            ]
                          },
                          {
                            "@name":"defaultMaterial",
                            "@accessType":"initializeOnly",
                            "@type":"SFNode",
                            "-children":[
                              { "Material":
                                {
                                }
                              }
                            ]
                          },
                          {
                            "@name":"materials",
                            "@accessType":"initializeOnly",
                            "@type":"MFNode"
                          },
                          {
                            "@name":"appendMaterial",
                            "@accessType":"inputOnly",
                            "@appinfo":"append another Material node to materials array",
                            "@type":"SFNode"
                          },
                          {
                            "@name":"deleteMaterial",
                            "@accessType":"inputOnly",
                            "@appinfo":"delete Material node indicated by input index value",
                            "@type":"SFInt32"
                          },
                          {
                            "@name":"deleteAllMaterials",
                            "@accessType":"inputOnly",
                            "@appinfo":"deletes all Material nodes resets scene to default Material values defined in X3D Specification",
                            "@type":"SFBool"
                          },
                          {
                            "@name":"priorBadIndex",
                            "@accessType":"initializeOnly",
                            "@type":"SFInt32",
                            "@value":-1
                          }
                        ],
                        "IS": {
                            "connect": [
                              {
                                "@nodeField":"set_index",
                                "@protoField":"set_index"
                              },
                              {
                                "@nodeField":"index",
                                "@protoField":"index"
                              },
                              {
                                "@nodeField":"index_changed",
                                "@protoField":"index_changed"
                              },
                              {
                                "@nodeField":"set_fraction",
                                "@protoField":"set_fraction"
                              },
                              {
                                "@nodeField":"fraction",
                                "@protoField":"fraction"
                              },
                              {
                                "@nodeField":"next",
                                "@protoField":"next"
                              },
                              {
                                "@nodeField":"previous",
                                "@protoField":"previous"
                              },
                              {
                                "@nodeField":"materials",
                                "@protoField":"materials"
                              },
                              {
                                "@nodeField":"appendMaterial",
                                "@protoField":"appendMaterial"
                              },
                              {
                                "@nodeField":"deleteMaterial",
                                "@protoField":"deleteMaterial"
                              },
                              {
                                "@nodeField":"deleteAllMaterials",
                                "@protoField":"deleteAllMaterials"
                              }
                            ]
                        },
                        "#sourceCode":[
"",
"",
"ecmascript:",
"",
"function initialize ()",
"{",
"   tracePrint ('initialize() begin...');",
"   if (materials[index] != null)",
"   {",
"\tviewedMaterial.diffuseColor     = materials[index].diffuseColor;",
"\tviewedMaterial.emissiveColor    = materials[index].emissiveColor;",
"\tviewedMaterial.specularColor    = materials[index].specularColor;",
"\tviewedMaterial.shininess        = materials[index].shininess;",
"\tviewedMaterial.ambientIntensity = materials[index].ambientIntensity;",
"\tviewedMaterial.transparency     = materials[index].transparency;",
"\tinterpolateMaterialValues();",
"   }",
"   else alwaysPrint ('warning: no initialization Material node provided for materials[' + index + ']'",
"                    + ', materials.length=' + materials.length);",
"",
"   tracePrint ('initialize() complete');",
"}",
"",
"function set_index (value, timestamp)",
"{",
"   if (value == index) return;",
"   else if ((value >= 0) && (value < materials.length))",
"   {",
"\tindex = value;",
"\tviewedMaterial.diffuseColor     = materials[index].diffuseColor;",
"\tviewedMaterial.emissiveColor    = materials[index].emissiveColor;",
"\tviewedMaterial.specularColor    = materials[index].specularColor;",
"\tviewedMaterial.shininess        = materials[index].shininess;",
"\tviewedMaterial.ambientIntensity = materials[index].ambientIntensity;",
"\tviewedMaterial.transparency     = materials[index].transparency;",
"\tinterpolateMaterialValues();",
"\tindex_changed = value; // chain input event to output event",
"\ttracePrint ('set_index(' + value + ') complete');",
"   }",
"   else if (value == -1)",
"   {",
"\ttracePrint ('set_index(' + value + ') out of range, use default Material values');",
"\tindex = value;",
"\tviewedMaterial.diffuseColor     = defaultMaterial.diffuseColor;",
"\tviewedMaterial.emissiveColor    = defaultMaterial.emissiveColor;",
"\tviewedMaterial.specularColor    = defaultMaterial.specularColor;",
"\tviewedMaterial.shininess        = defaultMaterial.shininess;",
"\tviewedMaterial.ambientIntensity = defaultMaterial.ambientIntensity;",
"\tviewedMaterial.transparency     = defaultMaterial.transparency;",
"\tindex_changed = value; // chain input event to output event",
"   }",
"   else if (priorBadIndex != value)",
"   {",
"\talwaysPrint ('set_index(' + value + ') out of range (only '",
"\t\t+ materials.length + ' Material nodes)');",
"\tpriorBadIndex = value;",
"   }",
"}",
"",
"function set_fraction(value, timestamp)",
"{",
"\ttracePrint ('set_fraction(' + value + ')');",
"\tif (fraction != value) // only update if value is changed",
"\t{",
"\t\tfraction = value;",
"\t\tinterpolateMaterialValues();",
"\t}",
"}",
"",
"function interpolateMaterialValues()",
"{",
"\tf = fraction % 1;  // nonzero remainder (modulo 1) within range [0..1]",
"\ttracePrint ('interpolateMaterialValues(index=' + index + ', fraction=' + fraction +  ', f=' + f + ')');",
"\t// reset Material index if fraction is above 1",
"\tif (Math.floor(fraction) >= 1)",
"\t{",
"\t\tindex = Math.floor(fraction);",
"\t\ttracePrint ('index=' + index + ' (reset from fraction)');",
"\t}",
"\tfraction = f;  // no need to retain integral part of fraction once index is set correspondingly",
"\t// check OK to proceed",
"\tif (index <  0) return;\t\t\t\t// avoid index underflow",
"\tif (index >= materials.length - 1) return;\t// avoid index overflow",
"\tif (f == 0.0) return; \t\t\t\t// avoid zero interpolation",
"\tviewedMaterial.diffuseColor.r     = materials[index].diffuseColor.r +",
"\t\tf * (materials[index+1].diffuseColor.r - materials[index].diffuseColor.r);",
"\tviewedMaterial.diffuseColor.g     = materials[index].diffuseColor.g +",
"\t\tf * (materials[index+1].diffuseColor.g - materials[index].diffuseColor.g);",
"\tviewedMaterial.diffuseColor.b     = materials[index].diffuseColor.r +",
"\t\tf * (materials[index+1].diffuseColor.b - materials[index].diffuseColor.b);",
"\ttracePrint ('viewedMaterial.diffuseColor=' + viewedMaterial.diffuseColor);",
"",
"\tviewedMaterial.emissiveColor.r    = materials[index].emissiveColor.r +",
"\t\tf * (materials[index+1].emissiveColor.r - materials[index].emissiveColor.r);",
"\tviewedMaterial.emissiveColor.g    = materials[index].emissiveColor.r +",
"\t\tf * (materials[index+1].emissiveColor.g - materials[index].emissiveColor.g);",
"\tviewedMaterial.emissiveColor.b    = materials[index].emissiveColor.b +",
"\t\tf * (materials[index+1].emissiveColor.b - materials[index].emissiveColor.b);",
"",
"\tviewedMaterial.specularColor.r    = materials[index].specularColor.r +",
"\t\tf * (materials[index+1].specularColor.r - materials[index].specularColor.r);",
"\tviewedMaterial.specularColor.g    = materials[index].specularColor.g +",
"\t\tf * (materials[index+1].specularColor.g - materials[index].specularColor.g);",
"\tviewedMaterial.specularColor.b    = materials[index].specularColor.b +",
"\t\tf * (materials[index+1].specularColor.b - materials[index].specularColor.b);",
"",
"\tviewedMaterial.shininess        = materials[index].shininess +",
"\t\tf * (materials[index+1].shininess - materials[index].shininess);",
"\tviewedMaterial.ambientIntensity = materials[index].ambientIntensity +",
"\t\tf * (materials[index+1].ambientIntensity - materials[index].ambientIntensity);",
"\tviewedMaterial.transparency     = materials[index].transparency +",
"\t\tf * (materials[index+1].transparency - materials[index].transparency);",
"}",
"",
"function next(trigger)",
"{",
"\ttracePrint ('next(' + trigger + ')');",
"\tif ((trigger == true) && (materials.length > 0))",
"\t{",
"\t\tvalue = index + 1;",
"\t\tif (value >= materials.length) value = 0;",
"\t\tset_index (value); // update Material, test, etc.",
"\t}",
"}",
"",
"function previous(trigger)",
"{",
"\ttracePrint ('previous(' + trigger + ')');",
"\tif ((trigger == true) && (materials.length > 0))",
"\t{",
"\t\tvalue = index - 1;",
"\t\tif (value < 0) value = materials.length - 1;",
"\t\tset_index (value); // update Material, test, etc.",
"\t}",
"}",
"",
"function appendMaterial(newMaterial)",
"{",
"\ttracePrint ('appendMaterial(' + newMaterial + ')');",
"\tnewMaterialCopy = new SFNode ('Material {}');",
"\ttracePrint ('newMaterial.diffuseColor=' + newMaterial.diffuseColor);",
"\tnewMaterialCopy.diffuseColor     = newMaterial.diffuseColor;",
"\tnewMaterialCopy.emissiveColor    = newMaterial.emissiveColor;",
"\tnewMaterialCopy.specularColor    = newMaterial.specularColor;",
"\tnewMaterialCopy.shininess        = newMaterial.shininess;",
"\tnewMaterialCopy.ambientIntensity = newMaterial.ambientIntensity;",
"\tnewMaterialCopy.transparency     = newMaterial.transparency;",
"\tmaterials[materials.length]      = newMaterialCopy;",
"\tset_index (materials.length-1); // update Material, test, etc.",
"}",
"",
"function deleteAllMaterials(trigger)",
"{",
"\tif ((trigger == true) && (materials.length > 0))",
"\t{",
"\t\tmaterials.length = 0; // deletes all objects in materials MFNode array",
"\t\tindex = 0; // must be different to trigger node reset by set_index()",
"\t\tset_index (-1);",
"\t\ttracePrint ('deleteAllMaterials() materials.length=' + materials.length);",
"\t}",
"}",
"",
"function deleteMaterial(materialIndex)",
"{",
"\tif ((materialIndex >= materials.length) || materialIndex < 0)",
"\t\talwaysPrint ('deleteMaterial(materialIndex=' + materialIndex + ') out of range, materials.length=' +  materials.length);",
"\telse",
"\t{",
"\t\tfor (i = materialIndex; i <= materials.length - 2; i++)",
"\t\t{",
"\t\t\tmaterials[i] = materials[i+1];",
"\t\t}",
"\t\tmaterials.length = materials.length - 1;",
"\t\tif ((index == 0) && (materialIndex == 0) && (materials.length > 0))",
"\t\t{",
"\t\t\tindex = -1; // allow reset back to 0",
"\t\t\tset_index (0);",
"\t\t}",
"\t\telse if (index >= materialIndex) set_index (index-1); // decrement to remain same",
"\t\ttracePrint ('deleteMaterial(materialIndex=' + materialIndex + ') complete,  materials.length=' + materials.length);",
"\t}",
"}",
"",
"function tracePrint(outputString)",
"{",
"\tif (traceEnabled) Browser.println ('[MaterialChoice] ' + outputString);",
"}",
"function alwaysPrint(outputString)",
"{",
"\tBrowser.println ('[MaterialChoice] ' + outputString);",
"}",
"",
""
]
                      }
                    }
                  ]
              }
            }
          },
          {
            "#comment":"===================="
          },
          { "Anchor":
            {
              "@description":"MaterialChoiceExample",
              "@url":["MaterialChoiceExample.x3d","https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/MaterialChoiceExample.x3d","MaterialChoiceExample.wrl","https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/MaterialChoiceExample.wrl"],
              "-children":[
                { "Shape":
                  {
                    "-geometry":
                      { "Text":
                        {
                          "@string":["MaterialChoicePrototype","defines a prototype","Click text to see example scene","MaterialChoiceExample"],
                          "-fontStyle":
                            { "FontStyle":
                              {
                                "@justify":["MIDDLE","MIDDLE"],
                                "@size":0.9
                              }
                            }
                        }
                      },
                    "-appearance":
                      { "Appearance":
                        {
                          "-material":
                            { "Material":
                              {
                                "@diffuseColor":[1,1,0.2]
                              }
                            }
                        }
                      }
                  }
                }
              ]
            }
          }
        ]
    }
  }
}