{
  Copyright 2002-2013 Michalis Kamburelis.

  This file is part of "Castle Game Engine".

  "Castle Game Engine" is free software; see the file COPYING.txt,
  included in this distribution, for details about the copyright.

  "Castle Game Engine" is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

  ----------------------------------------------------------------------------
}

{$ifdef read_interface}
  { }
  TBoxNode = class(TAbstractX3DGeometryNode)
  public
    procedure CreateNode; override;
    class function ClassNodeTypeName: string; override;
    class function URNMatching(const URN: string): boolean; override;

    private FFdSize: TSFVec3f;
    public property FdSize: TSFVec3f read FFdSize;

    private FFdSolid: TSFBool;
    public property FdSolid: TSFBool read FFdSolid;

    private FFdTexCoord: TSFNode;
    public property FdTexCoord: TSFNode read FFdTexCoord;
    function TexCoordField: TSFNode; override;

    function Proxy(var State: TX3DGraphTraverseState;
      const OverTriangulate: boolean): TAbstractGeometryNode; override;

    function BoundingBox(State: TX3DGraphTraverseState;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): TBox3D; override;
    function LocalBoundingBox(State: TX3DGraphTraverseState;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): TBox3D; override;
    function TrianglesCount(State: TX3DGraphTraverseState; OverTriangulate: boolean;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): Cardinal; override;
    function AutoGenerate3DTexCoords: boolean; override;
  end;

  TConeNode = class(TAbstractX3DGeometryNode)
  protected
    procedure DirectEnumerateActive(Func: TEnumerateChildrenFunction); override;
  public
    procedure CreateNode; override;
    class function ClassNodeTypeName: string; override;
    class function URNMatching(const URN: string): boolean; override;

    private FFdBottom: TSFBool;
    public property FdBottom: TSFBool read FFdBottom;

    private FFdBottomRadius: TSFFloat;
    public property FdBottomRadius: TSFFloat read FFdBottomRadius;

    private FFdHeight: TSFFloat;
    public property FdHeight: TSFFloat read FFdHeight;

    private FFdSide: TSFBool;
    public property FdSide: TSFBool read FFdSide;

    private FFdSolid: TSFBool;
    public property FdSolid: TSFBool read FFdSolid;

    class function ForVRMLVersion(const Version: TX3DVersion): boolean;
      override;

    private FFdTexCoord: TSFNode;
    public property FdTexCoord: TSFNode read FFdTexCoord;
    function TexCoordField: TSFNode; override;

    function Proxy(var State: TX3DGraphTraverseState;
      const OverTriangulate: boolean): TAbstractGeometryNode; override;

    function BoundingBox(State: TX3DGraphTraverseState;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): TBox3D; override;
    function LocalBoundingBox(State: TX3DGraphTraverseState;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): TBox3D; override;
    function TrianglesCount(State: TX3DGraphTraverseState; OverTriangulate: boolean;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): Cardinal; override;
    function AutoGenerate3DTexCoords: boolean; override;
  end;
  TConeNode_2 = TConeNode;

  TCylinderNode = class(TAbstractX3DGeometryNode)
  protected
    procedure DirectEnumerateActive(Func: TEnumerateChildrenFunction); override;
  public
    procedure CreateNode; override;
    class function ClassNodeTypeName: string; override;
    class function URNMatching(const URN: string): boolean; override;

    private FFdBottom: TSFBool;
    public property FdBottom: TSFBool read FFdBottom;

    private FFdHeight: TSFFloat;
    public property FdHeight: TSFFloat read FFdHeight;

    private FFdRadius: TSFFloat;
    public property FdRadius: TSFFloat read FFdRadius;

    private FFdSide: TSFBool;
    public property FdSide: TSFBool read FFdSide;

    private FFdSolid: TSFBool;
    public property FdSolid: TSFBool read FFdSolid;

    private FFdTop: TSFBool;
    public property FdTop: TSFBool read FFdTop;

    class function ForVRMLVersion(const Version: TX3DVersion): boolean;
      override;

    function Proxy(var State: TX3DGraphTraverseState;
      const OverTriangulate: boolean): TAbstractGeometryNode; override;

    private FFdTexCoord: TSFNode;
    public property FdTexCoord: TSFNode read FFdTexCoord;
    function TexCoordField: TSFNode; override;

    function BoundingBox(State: TX3DGraphTraverseState;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): TBox3D; override;
    function LocalBoundingBox(State: TX3DGraphTraverseState;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): TBox3D; override;
    function TrianglesCount(State: TX3DGraphTraverseState; OverTriangulate: boolean;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): Cardinal; override;
    function AutoGenerate3DTexCoords: boolean; override;
  end;
  TCylinderNode_2 = TCylinderNode;

  TElevationGridNode = class(TAbstractX3DGeometryNode)
  private
    procedure EventSet_HeightReceive(
      Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
  public
    procedure CreateNode; override;
    class function ClassNodeTypeName: string; override;
    class function URNMatching(const URN: string): boolean; override;

    { Event: MFFloat, in } { }
    private FEventSet_height: TX3DEvent;
    public property EventSet_height: TX3DEvent read FEventSet_height;

    private FFdAttrib: TMFNode;
    public property FdAttrib: TMFNode read FFdAttrib;

    private FFdColor: TSFNode;
    public property FdColor: TSFNode read FFdColor;

    private FFdFogCoord: TSFNode;
    public property FdFogCoord: TSFNode read FFdFogCoord;

    private FFdNormal: TSFNode;
    public property FdNormal: TSFNode read FFdNormal;

    private FFdTexCoord: TSFNode;
    public property FdTexCoord: TSFNode read FFdTexCoord;

    private FFdCcw: TSFBool;
    public property FdCcw: TSFBool read FFdCcw;

    private FFdColorPerVertex: TSFBool;
    public property FdColorPerVertex: TSFBool read FFdColorPerVertex;

    private FFdCreaseAngle: TSFFloat;
    public property FdCreaseAngle: TSFFloat read FFdCreaseAngle;

    private FFdHeight: TMFFloat;
    public property FdHeight: TMFFloat read FFdHeight;

    private FFdNormalPerVertex: TSFBool;
    public property FdNormalPerVertex: TSFBool read FFdNormalPerVertex;

    private FFdSolid: TSFBool;
    public property FdSolid: TSFBool read FFdSolid;

    private FFdXDimension: TSFInt32;
    public property FdXDimension: TSFInt32 read FFdXDimension;

    private FFdXSpacing: TSFFloat;
    public property FdXSpacing: TSFFloat read FFdXSpacing;

    private FFdZDimension: TSFInt32;
    public property FdZDimension: TSFInt32 read FFdZDimension;

    private FFdZSpacing: TSFFloat;
    public property FdZSpacing: TSFFloat read FFdZSpacing;

    { This checks whether xDimension and zDimension are >= 2,
      xSpacing and zSpacing are > 0 and height has at least the
      required number of values. If this returns @false then
      it is understood that ElevationGrid is not rendered, doesn't
      have any vertices/triangles etc. }
    function IsNotEmpty: boolean;

    function Color: TMFVec3f; override;
    function ColorRGBA: TMFColorRGBA; override;
    function FogCoord: TMFFloat; override;
    function Attrib: TMFNode; override;

    function Proxy(var State: TX3DGraphTraverseState;
      const OverTriangulate: boolean): TAbstractGeometryNode; override;
    function ProxyUsesOverTriangulate: boolean; override;

    function LocalBoundingBox(State: TX3DGraphTraverseState;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): TBox3D; override;
    function VerticesCount(State: TX3DGraphTraverseState; OverTriangulate: boolean;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): Cardinal; override;
    function TrianglesCount(State: TX3DGraphTraverseState; OverTriangulate: boolean;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): Cardinal; override;
  end;

  TExtrusionNode = class(TAbstractX3DGeometryNode)
  protected
    procedure DirectEnumerateActive(Func: TEnumerateChildrenFunction); override;
  private
    procedure Eventset_crossSectionReceive(
      Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
    procedure Eventset_orientationReceive(
      Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
    procedure Eventset_scaleReceive(
      Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
    procedure Eventset_spineReceive(
      Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
  public
    procedure CreateNode; override;
    class function ClassNodeTypeName: string; override;
    class function URNMatching(const URN: string): boolean; override;

    { Event: MFVec2f, in } { }
    private FEventSet_crossSection: TX3DEvent;
    public property EventSet_crossSection: TX3DEvent read FEventSet_crossSection;

    { Event: MFRotation, in } { }
    private FEventSet_orientation: TX3DEvent;
    public property EventSet_orientation: TX3DEvent read FEventSet_orientation;

    { Event: MFVec2f, in } { }
    private FEventSet_scale: TX3DEvent;
    public property EventSet_scale: TX3DEvent read FEventSet_scale;

    { Event: MFVec3f, in } { }
    private FEventSet_spine: TX3DEvent;
    public property EventSet_spine: TX3DEvent read FEventSet_spine;

    private FFdBeginCap: TSFBool;
    public property FdBeginCap: TSFBool read FFdBeginCap;

    private FFdCcw: TSFBool;
    public property FdCcw: TSFBool read FFdCcw;

    private FFdConvex: TSFBool;
    public property FdConvex: TSFBool read FFdConvex;
    function Convex: boolean; override;

    private FFdCreaseAngle: TSFFloat;
    public property FdCreaseAngle: TSFFloat read FFdCreaseAngle;

    private FFdCrossSection: TMFVec2f;
    public property FdCrossSection: TMFVec2f read FFdCrossSection;

    private FFdEndCap: TSFBool;
    public property FdEndCap: TSFBool read FFdEndCap;

    private FFdOrientation: TMFRotation;
    public property FdOrientation: TMFRotation read FFdOrientation;

    private FFdScale: TMFVec2f;
    public property FdScale: TMFVec2f read FFdScale;

    private FFdSolid: TSFBool;
    public property FdSolid: TSFBool read FFdSolid;

    private FFdSpine: TMFVec3f;
    public property FdSpine: TMFVec3f read FFdSpine;

    private FFdTexCoord: TSFNode;
    public property FdTexCoord: TSFNode read FFdTexCoord;
    function TexCoordField: TSFNode; override;

    { For given Index, return appropriate FdCrossIndex item as 3D vertex.
      That is, uses FdCrossIndex values as X, Z of 3D vertex and sets Y = 0
      (that's how Extrusion is defined in VRML / X3D). }
    function CrossSection3D(Index: integer): TVector3Single;

    { Check is spine closed. This happens when "spine" field is non-empty
      and it's first and last points are equal. }
    function SpineClosed: boolean;

    { Check is crossSection closed. This happens when "crossSection"
      field is non-empty and it's first and last points are equal. }
    function CrossSectionClosed: boolean;

    function Proxy(var State: TX3DGraphTraverseState;
      const OverTriangulate: boolean): TAbstractGeometryNode; override;
    function ProxyUsesOverTriangulate: boolean; override;

    function LocalBoundingBox(State: TX3DGraphTraverseState;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): TBox3D; override;
    function BoundingBox(State: TX3DGraphTraverseState;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): TBox3D; override;
    function TrianglesCount(State: TX3DGraphTraverseState; OverTriangulate: boolean;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): Cardinal; override;
    function AutoGenerate3DTexCoords: boolean; override;
  end;

  TIndexedFaceSetNode = class(TAbstractComposedGeometryNode)
  private
    procedure Eventset_colorIndexReceive(
      Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
    procedure Eventset_coordIndexReceive(
      Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
    procedure Eventset_normalIndexReceive(
      Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
    procedure Eventset_texCoordIndexReceive(
      Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
  public
    procedure CreateNode; override;
    class function ClassNodeTypeName: string; override;
    class function URNMatching(const URN: string): boolean; override;

    { Event: MFInt32, in } { }
    private FEventSet_colorIndex: TX3DEvent;
    public property EventSet_colorIndex: TX3DEvent read FEventSet_colorIndex;

    { Event: MFInt32, in } { }
    private FEventSet_coordIndex: TX3DEvent;
    public property EventSet_coordIndex: TX3DEvent read FEventSet_coordIndex;

    { Event: MFInt32, in } { }
    private FEventSet_normalIndex: TX3DEvent;
    public property EventSet_normalIndex: TX3DEvent read FEventSet_normalIndex;

    { Event: MFInt32, in } { }
    private FEventSet_texCoordIndex: TX3DEvent;
    public property EventSet_texCoordIndex: TX3DEvent read FEventSet_texCoordIndex;

    private FFdColorIndex: TMFInt32;
    public property FdColorIndex: TMFInt32 read FFdColorIndex;

    private FFdConvex: TSFBool;
    public property FdConvex: TSFBool read FFdConvex;
    function Convex: boolean; override;

    private FFdCoordIndex: TMFInt32;
    public property FdCoordIndex: TMFInt32 read FFdCoordIndex;

    private FFdCreaseAngle: TSFFloat;
    public property FdCreaseAngle: TSFFloat read FFdCreaseAngle;

    private FFdNormalIndex: TMFInt32;
    public property FdNormalIndex: TMFInt32 read FFdNormalIndex;

    private FFdTexCoordIndex: TMFInt32;
    public property FdTexCoordIndex: TMFInt32 read FFdTexCoordIndex;

    class function ForVRMLVersion(const Version: TX3DVersion): boolean;
      override;

    function CoordIndex: TMFLong; override;

    procedure CoordPolygons(
      State: TX3DGraphTraverseState;
      PolygonHandler: TIndexedPolygonHandler); override;

    function TrianglesCount(State: TX3DGraphTraverseState; OverTriangulate: boolean;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): Cardinal; override;
  end;
  TIndexedFaceSetNode_2 = TIndexedFaceSetNode;

  TSphereNode = class(TAbstractX3DGeometryNode)
  public
    procedure CreateNode; override;
    class function ClassNodeTypeName: string; override;
    class function URNMatching(const URN: string): boolean; override;

    private FFdRadius: TSFFloat;
    public property FdRadius: TSFFloat read FFdRadius;

    private FFdSolid: TSFBool;
    public property FdSolid: TSFBool read FFdSolid;

    class function ForVRMLVersion(const Version: TX3DVersion): boolean;
      override;

    private FFdTexCoord: TSFNode;
    public property FdTexCoord: TSFNode read FFdTexCoord;
    function TexCoordField: TSFNode; override;

    function Proxy(var State: TX3DGraphTraverseState;
      const OverTriangulate: boolean): TAbstractGeometryNode; override;

    function BoundingBox(State: TX3DGraphTraverseState;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): TBox3D; override;
    function LocalBoundingBox(State: TX3DGraphTraverseState;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): TBox3D; override;
    function TrianglesCount(State: TX3DGraphTraverseState; OverTriangulate: boolean;
      ProxyGeometry: TAbstractGeometryNode; ProxyState: TX3DGraphTraverseState): Cardinal; override;
    function AutoGenerate3DTexCoords: boolean; override;
  end;
  TSphereNode_2 = TSphereNode;

{$endif read_interface}

{$ifdef read_implementation}
procedure TBoxNode.CreateNode;
begin
  inherited;

  FFdSize := TSFVec3f.Create(Self, 'size', Vector3Single(2, 2, 2));
   FdSize.Exposed := false;
   FdSize.ChangesAlways := [chGeometry];
  Fields.Add(FFdSize);
  { X3D specification comment: (0,Inf) }

  FFdSolid := TSFBool.Create(Self, 'solid', true);
   FdSolid.Exposed := false;
   FdSolid.ChangesAlways := [chGeometry];
  Fields.Add(FFdSolid);

  FFdTexCoord := TSFNode.Create(Self, 'texCoord', [TTextureCoordinateGeneratorNode, TProjectedTextureCoordinateNode, TMultiTextureCoordinateNode]);
   FdTexCoord.ChangesAlways := [chGeometry];
  Fields.Add(FFdTexCoord);
end;

class function TBoxNode.ClassNodeTypeName: string;
begin
  Result := 'Box';
end;

class function TBoxNode.URNMatching(const URN: string): boolean;
begin
  Result := (inherited URNMatching(URN)) or
    (URN = URNVRML97Nodes + ClassNodeTypeName) or
    (URN = URNX3DNodes + ClassNodeTypeName);
end;

function TBoxNode.TexCoordField: TSFNode;
begin
  Result := FdTexCoord;
end;

function TBoxNode.AutoGenerate3DTexCoords: boolean;
begin
  Result := (FdTexCoord.Value = nil) or not FdTexCoord.CurrentChildAllowed;
end;

procedure TConeNode.CreateNode;
begin
  inherited;

  FFdBottom := TSFBool.Create(Self, 'bottom', true);
   FdBottom.Exposed := false;
   FdBottom.ChangesAlways := [chGeometry];
  Fields.Add(FFdBottom);

  FFdBottomRadius := TSFFloat.Create(Self, 'bottomRadius', 1);
   FdBottomRadius.Exposed := false;
   FdBottomRadius.ChangesAlways := [chGeometry];
  Fields.Add(FFdBottomRadius);
  { X3D specification comment: (0,Inf) }

  FFdHeight := TSFFloat.Create(Self, 'height', 2);
   FdHeight.Exposed := false;
   FdHeight.ChangesAlways := [chGeometry];
  Fields.Add(FFdHeight);
  { X3D specification comment: (0,Inf) }

  FFdSide := TSFBool.Create(Self, 'side', true);
   FdSide.Exposed := false;
   FdSide.ChangesAlways := [chGeometry];
  Fields.Add(FFdSide);

  FFdSolid := TSFBool.Create(Self, 'solid', true);
   FdSolid.Exposed := false;
   FdSolid.ChangesAlways := [chGeometry];
  Fields.Add(FFdSolid);

  FFdTexCoord := TSFNode.Create(Self, 'texCoord', [TTextureCoordinateGeneratorNode, TProjectedTextureCoordinateNode, TMultiTextureCoordinateNode]);
   FdTexCoord.ChangesAlways := [chGeometry];
  Fields.Add(FFdTexCoord);
end;

class function TConeNode.ClassNodeTypeName: string;
begin
  Result := 'Cone';
end;

class function TConeNode.URNMatching(const URN: string): boolean;
begin
  Result := (inherited URNMatching(URN)) or
    (URN = URNVRML97Nodes + ClassNodeTypeName) or
    (URN = URNX3DNodes + ClassNodeTypeName);
end;

class function TConeNode.ForVRMLVersion(const Version: TX3DVersion): boolean;
begin
  Result := Version.Major >= 2;
end;

procedure TConeNode.DirectEnumerateActive(Func: TEnumerateChildrenFunction);
begin
  inherited;
  FdtexCoord.EnumerateValid(Func);
end;

function TConeNode.TexCoordField: TSFNode;
begin
  Result := FdTexCoord;
end;

function TConeNode.AutoGenerate3DTexCoords: boolean;
begin
  Result := (FdTexCoord.Value = nil) or not FdTexCoord.CurrentChildAllowed;
end;

procedure TCylinderNode.CreateNode;
begin
  inherited;

  FFdBottom := TSFBool.Create(Self, 'bottom', true);
   FdBottom.Exposed := false;
   FdBottom.ChangesAlways := [chGeometry];
  Fields.Add(FFdBottom);

  FFdHeight := TSFFloat.Create(Self, 'height', 2);
   FdHeight.Exposed := false;
   FdHeight.ChangesAlways := [chGeometry];
  Fields.Add(FFdHeight);
  { X3D specification comment: (0,Inf) }

  FFdRadius := TSFFloat.Create(Self, 'radius', 1);
   FdRadius.Exposed := false;
   FdRadius.ChangesAlways := [chGeometry];
  Fields.Add(FFdRadius);
  { X3D specification comment: (0,Inf) }

  FFdSide := TSFBool.Create(Self, 'side', true);
   FdSide.Exposed := false;
   FdSide.ChangesAlways := [chGeometry];
  Fields.Add(FFdSide);

  FFdSolid := TSFBool.Create(Self, 'solid', true);
   FdSolid.Exposed := false;
   FdSolid.ChangesAlways := [chGeometry];
  Fields.Add(FFdSolid);

  FFdTop := TSFBool.Create(Self, 'top', true);
   FdTop.Exposed := false;
   FdTop.ChangesAlways := [chGeometry];
  Fields.Add(FFdTop);

  FFdTexCoord := TSFNode.Create(Self, 'texCoord', [TTextureCoordinateGeneratorNode, TProjectedTextureCoordinateNode, TMultiTextureCoordinateNode]);
   FdTexCoord.ChangesAlways := [chGeometry];
  Fields.Add(FFdTexCoord);
end;

class function TCylinderNode.ClassNodeTypeName: string;
begin
  Result := 'Cylinder';
end;

class function TCylinderNode.URNMatching(const URN: string): boolean;
begin
  Result := (inherited URNMatching(URN)) or
    (URN = URNVRML97Nodes + ClassNodeTypeName) or
    (URN = URNX3DNodes + ClassNodeTypeName);
end;

class function TCylinderNode.ForVRMLVersion(const Version: TX3DVersion): boolean;
begin
  Result := Version.Major >= 2;
end;

procedure TCylinderNode.DirectEnumerateActive(Func: TEnumerateChildrenFunction);
begin
  inherited;
  FdtexCoord.EnumerateValid(Func);
end;

function TCylinderNode.TexCoordField: TSFNode;
begin
  Result := FdTexCoord;
end;

function TCylinderNode.AutoGenerate3DTexCoords: boolean;
begin
  Result := (FdTexCoord.Value = nil) or not FdTexCoord.CurrentChildAllowed;
end;

procedure TElevationGridNode.CreateNode;
begin
  inherited;

  FEventSet_height := TX3DEvent.Create(Self, 'set_height', TMFFloat, true);
  Events.Add(FEventSet_height);
  Eventset_height.OnReceive.Add(@EventSet_HeightReceive);

  FFdAttrib := TMFNode.Create(Self, 'attrib', [TAbstractVertexAttributeNode]);
   FdAttrib.ChangesAlways := [chGeometry];
  Fields.Add(FFdAttrib);

  FFdColor := TSFNode.Create(Self, 'color', [TAbstractColorNode]);
   FdColor.ChangesAlways := [chGeometry];
  Fields.Add(FFdColor);

  FFdFogCoord := TSFNode.Create(Self, 'fogCoord', [TFogCoordinateNode]);
   FdFogCoord.ChangesAlways := [chGeometry];
  Fields.Add(FFdFogCoord);

  FFdNormal := TSFNode.Create(Self, 'normal', [TAbstractNormalNode]);
   FdNormal.ChangesAlways := [chGeometry];
  Fields.Add(FFdNormal);

  FFdTexCoord := TSFNode.Create(Self, 'texCoord', [TAbstractTextureCoordinateNode]);
   FdTexCoord.ChangesAlways := [chGeometry];
  Fields.Add(FFdTexCoord);

  FFdCcw := TSFBool.Create(Self, 'ccw', true);
   FdCcw.Exposed := false;
   FdCcw.ChangesAlways := [chGeometry];
  Fields.Add(FFdCcw);

  FFdColorPerVertex := TSFBool.Create(Self, 'colorPerVertex', true);
   FdColorPerVertex.Exposed := false;
   FdColorPerVertex.ChangesAlways := [chGeometry];
  Fields.Add(FFdColorPerVertex);

  FFdCreaseAngle := TSFFloat.Create(Self, 'creaseAngle', 0);
   FdCreaseAngle.Angle := true;
   FdCreaseAngle.Exposed := false;
   FdCreaseAngle.ChangesAlways := [chGeometry];
  Fields.Add(FFdCreaseAngle);
  { X3D specification comment: [0,Inf) }

  FFdHeight := TMFFloat.Create(Self, 'height', []);
   FdHeight.Exposed := false;
   FdHeight.ChangesAlways := [chGeometry];
  Fields.Add(FFdHeight);
  { X3D specification comment: (-Inf,Inf) }

  FFdNormalPerVertex := TSFBool.Create(Self, 'normalPerVertex', true);
   FdNormalPerVertex.Exposed := false;
   FdNormalPerVertex.ChangesAlways := [chGeometry];
  Fields.Add(FFdNormalPerVertex);

  FFdSolid := TSFBool.Create(Self, 'solid', true);
   FdSolid.Exposed := false;
   FdSolid.ChangesAlways := [chGeometry];
  Fields.Add(FFdSolid);

  FFdXDimension := TSFInt32.Create(Self, 'xDimension', 0);
   FdXDimension.Exposed := false;
   FdXDimension.ChangesAlways := [chGeometry];
  Fields.Add(FFdXDimension);
  { X3D specification comment: [0,Inf) }

  FFdXSpacing := TSFFloat.Create(Self, 'xSpacing', 1.0);
   FdXSpacing.Exposed := false;
   FdXSpacing.ChangesAlways := [chGeometry];
  Fields.Add(FFdXSpacing);
  { X3D specification comment: (0,Inf) }

  FFdZDimension := TSFInt32.Create(Self, 'zDimension', 0);
   FdZDimension.Exposed := false;
   FdZDimension.ChangesAlways := [chGeometry];
  Fields.Add(FFdZDimension);
  { X3D specification comment: [0,Inf) }

  FFdZSpacing := TSFFloat.Create(Self, 'zSpacing', 1.0);
   FdZSpacing.Exposed := false;
   FdZSpacing.ChangesAlways := [chGeometry];
  Fields.Add(FFdZSpacing);
  { X3D specification comment: (0,Inf) }
end;

class function TElevationGridNode.ClassNodeTypeName: string;
begin
  Result := 'ElevationGrid';
end;

class function TElevationGridNode.URNMatching(const URN: string): boolean;
begin
  Result := (inherited URNMatching(URN)) or
    (URN = URNVRML97Nodes + ClassNodeTypeName) or
    (URN = URNX3DNodes + ClassNodeTypeName);
end;

function TElevationGridNode.IsNotEmpty: boolean;
begin
  Result :=
    (FdXDimension.Value >= 2) and
    (FdZDimension.Value >= 2) and
    { VRML spec says that xSpacing and ySpacing shall be > 0.
      So I understand that when they are = 0 (or < 0) nothing
      should be rendered. }
    (FdXSpacing.Value > 0) and
    (FdZSpacing.Value > 0) and
    (FdHeight.Count >= FdXDimension.Value * FdZDimension.Value);
end;

function TElevationGridNode.Color: TMFVec3f;
begin
  if (FdColor.Value <> nil) and
     (FdColor.Value is TColorNode) then
    Result := TColorNode(FdColor.Value).FdColor else
    Result := nil;
end;

function TElevationGridNode.ColorRGBA: TMFColorRGBA;
begin
  if (FdColor.Value <> nil) and
     (FdColor.Value is TColorRGBANode) then
    Result := TColorRGBANode(FdColor.Value).FdColor else
    Result := nil;
end;

function TElevationGridNode.FogCoord: TMFFloat;
begin
  if (FdFogCoord.Value <> nil) and
     (FdFogCoord.Value is TFogCoordinateNode) then
    Result := TFogCoordinateNode(FdFogCoord.Value).FdDepth else
    Result := nil;
end;

function TElevationGridNode.Attrib: TMFNode;
begin
  Result := FdAttrib;
end;

procedure TElevationGridNode.EventSet_HeightReceive(
  Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
begin
  FdHeight.Assign(Value);
  FdHeight.Changed;
end;

procedure TExtrusionNode.CreateNode;
begin
  inherited;

  FEventSet_crossSection := TX3DEvent.Create(Self, 'set_crossSection', TMFVec2f, true);
  Events.Add(FEventSet_crossSection);
  Eventset_crossSection.OnReceive.Add(@EventSet_crossSectionReceive);

  FEventSet_orientation := TX3DEvent.Create(Self, 'set_orientation', TMFRotation, true);
  Events.Add(FEventSet_orientation);
  Eventset_orientation.OnReceive.Add(@EventSet_orientationReceive);

  FEventSet_scale := TX3DEvent.Create(Self, 'set_scale', TMFVec2f, true);
  Events.Add(FEventSet_scale);
  Eventset_scale.OnReceive.Add(@EventSet_scaleReceive);

  FEventSet_spine := TX3DEvent.Create(Self, 'set_spine', TMFVec3f, true);
  Events.Add(FEventSet_spine);
  Eventset_spine.OnReceive.Add(@EventSet_spineReceive);

  FFdBeginCap := TSFBool.Create(Self, 'beginCap', true);
   FdBeginCap.Exposed := false;
   FdBeginCap.ChangesAlways := [chGeometry];
  Fields.Add(FFdBeginCap);

  FFdCcw := TSFBool.Create(Self, 'ccw', true);
   FdCcw.Exposed := false;
   FdCcw.ChangesAlways := [chGeometry];
  Fields.Add(FFdCcw);

  FFdConvex := TSFBool.Create(Self, 'convex', true);
   FdConvex.Exposed := false;
   FdConvex.ChangesAlways := [chGeometry];
  Fields.Add(FFdConvex);

  FFdCreaseAngle := TSFFloat.Create(Self, 'creaseAngle', 0);
   FdCreaseAngle.Angle := true;
   FdCreaseAngle.Exposed := false;
   FdCreaseAngle.ChangesAlways := [chGeometry];
  Fields.Add(FFdCreaseAngle);
  { X3D specification comment: [0,Inf) }

  FFdCrossSection := TMFVec2f.Create(Self, 'crossSection', [ Vector2Single(1, 1), Vector2Single(1, -1),  Vector2Single(-1, -1), Vector2Single(-1, 1), Vector2Single(1, 1) ]);
   FdCrossSection.Exposed := false;
   FdCrossSection.ChangesAlways := [chGeometry];
  Fields.Add(FFdCrossSection);
  { X3D specification comment: (-Inf,Inf) }

  FFdEndCap := TSFBool.Create(Self, 'endCap', true);
   FdEndCap.Exposed := false;
   FdEndCap.ChangesAlways := [chGeometry];
  Fields.Add(FFdEndCap);

  FFdOrientation := TMFRotation.Create(Self, 'orientation', [ Vector4Single(0, 0, 1, 0) ]);
   FdOrientation.Exposed := false;
   FdOrientation.ChangesAlways := [chGeometry];
  Fields.Add(FFdOrientation);
  { X3D specification comment: [-1,1] or (-Inf,Inf) }

  FFdScale := TMFVec2f.Create(Self, 'scale', Vector2Single(1, 1));
   FdScale.Exposed := false;
   FdScale.ChangesAlways := [chGeometry];
  Fields.Add(FFdScale);
  { X3D specification comment: (0,Inf) }

  FFdSolid := TSFBool.Create(Self, 'solid', true);
   FdSolid.Exposed := false;
   FdSolid.ChangesAlways := [chGeometry];
  Fields.Add(FFdSolid);

  FFdSpine := TMFVec3f.Create(Self, 'spine', [ Vector3Single(0, 0, 0), Vector3Single(0, 1, 0) ]);
   FdSpine.Exposed := false;
   FdSpine.ChangesAlways := [chGeometry];
  Fields.Add(FFdSpine);
  { X3D specification comment: (-Inf,Inf) }

  FFdTexCoord := TSFNode.Create(Self, 'texCoord', [TTextureCoordinateGeneratorNode, TProjectedTextureCoordinateNode, TMultiTextureCoordinateNode]);
   FdTexCoord.ChangesAlways := [chGeometry];
  Fields.Add(FFdTexCoord);
end;

class function TExtrusionNode.ClassNodeTypeName: string;
begin
  Result := 'Extrusion';
end;

class function TExtrusionNode.URNMatching(const URN: string): boolean;
begin
  Result := (inherited URNMatching(URN)) or
    (URN = URNVRML97Nodes + ClassNodeTypeName) or
    (URN = URNX3DNodes + ClassNodeTypeName);
end;

function TExtrusionNode.CrossSection3D(Index: integer): TVector3Single;
begin
  Result[0] := FdCrossSection.Items.L[Index][0];
  Result[1] := 0;
  Result[2] := FdCrossSection.Items.L[Index][1];
end;

function TExtrusionNode.SpineClosed: boolean;
var
  SpinePoints: TVector3SingleList;
begin
  SpinePoints := FdSpine.Items;
  Result := (SpinePoints.Count <> 0) and
    VectorsPerfectlyEqual(SpinePoints.Last,
                          SpinePoints.First);
end;

function TExtrusionNode.CrossSectionClosed: boolean;
var
  CrossSectionPoints: TVector2SingleList;
begin
  CrossSectionPoints := FdCrossSection.Items;
  Result := (CrossSectionPoints.Count <> 0) and
    VectorsPerfectlyEqual(CrossSectionPoints.Last,
                          CrossSectionPoints.First);
end;

function TExtrusionNode.Proxy(var State: TX3DGraphTraverseState;
  const OverTriangulate: boolean): TAbstractGeometryNode;

{ Some of the things that are non-optimal when you will render
  Extrusion using this Proxy instead of directly rendering node:

  - In specialized Extrusion rendering, you now that sides are
    composed of quads, and so you can use QUAD_STRIP to render them.
    This allows vertices sharing. IndexedFaceSet uses vertex arrays,
    that also provides sharing but may be slightly worse.

  - In specialized Extrusion rendering, you know that sides' quads
    are convex. There's never any need to triangulate them carefully.
    For IndexedFaceSet, when caps may be non-convex, you have to set
    convex field to false, so sides will be unnecessarily triangulated
    with the slower algorithm.

  You gain:

  - creaseAngle support, which is really the crucial point why
    I'm implementing this. I already have a specialized Extrusion
    rendering, but adding there creaseAngle support is a major pain.

    You would have to calculate all coordinates first, and then calculate
    their normal vectors using a specialized routine like CreateNormals
    that knows about Extrusion connectivity. This is painful to implement,
    and naive implementation would even suffer from a slowdown not present
    in Proxy approach: calculating all coordinates two times (before
    calculating normals, and later for actual rendering). Sure, smarter
    approach would remember these coordinates. Proxy approach provides
    this automatically, saving coordinates inside Coordinate node,
    for both normals calculation and rendering.

  - A little ease of implementation: 230 lines of Proxy approach
    versus 390 lines of TExtrusionRenderer. And remember that
    actually handling creaseAngle would complicate TExtrusionRenderer
    even more (that's why I didn't dare to do it...), and that
    Proxy approach could also be used to implement things like
    bounding box and triangulate eventually (although for now we use
    our optimized methods).
}

var
  IFS: TIndexedFaceSetNode absolute Result;
  Index: Integer;
  E: TVRMLExtrusion;
  TexCoords: TVector2SingleList;

  procedure NextIndex(SpineIndex, CrossSectionIndex: Integer;
    SpecialTex: boolean; SpecialTexIndex: Integer);
  begin
    if TexCoords <> nil then
    begin
      if SpecialTex then
        IFS.FdTexCoordIndex.Items.L[Index] := SpecialTexIndex else
        IFS.FdTexCoordIndex.Items.L[Index] :=
          SpineIndex * FdCrossSection.Count + CrossSectionIndex;
    end;

    { For closed spine and/or crossSection, specify the same indexes.
      For coordIndex (for texCoordIndex, this is not correct, texture
      may have a seam there).
      This will allow the correct smoothing between spine/crossSection
      edges when calculating normal vectors. }

   if (SpineIndex = FdSpine.Items.Count - 1) and
       E.BeginEndCapsMatching then
      SpineIndex := 0;

    if (CrossSectionIndex = FdCrossSection.Items.Count - 1) and
       E.CrossSectionClosed then
      CrossSectionIndex := 0;

    IFS.FdCoordIndex.Items.L[Index] :=
      SpineIndex * FdCrossSection.Count + CrossSectionIndex;

    Inc(Index);
  end;

  procedure NextIndexFaceDelimiter;
  begin
    IFS.FdCoordIndex.Items.L[Index] := -1;
    if TexCoords <> nil then
      IFS.FdTexCoordIndex.Items.L[Index] := -1;
    Inc(Index);
  end;

  function SideTexCoord(SpineIndex, CrossSectionIndex: Integer): TVector2Single;
  begin
    { Check ranges, to avoid dividing by zero (eventually the
      corresponding coord is set to a predictable zero :) ). }
    if FdCrossSection.Count > 1 then
      Result[0] := CrossSectionIndex / (FdCrossSection.Count - 1) else
      Result[0] := 0;
    if E.High > 0 then
      Result[1] := SpineIndex / E.High else
      Result[1] := 0;
  end;

var
  CoordNode: TCoordinateNode;
  TexCoordNode: TTextureCoordinateNode;
  Transform: TMatrix4Single;
  I, J, Len, SidesCount: Integer;
  LastY, LastZ: TVector3Single;
  TexCapsMin, TexCapsMax: TVector2Single;
  TexCapsTranslation, TexCapsScale: Single;
begin
  IFS := TIndexedFaceSetNode.Create(NodeName, BaseUrl);
  try
    E := TVRMLExtrusion.Create;
    try
      E.Node := Self;

      { calculate SidesCount = number of verts on sides, often used number }
      SidesCount := (E.High + 1) * FdCrossSection.Count;

      { Fill CoordNode }

      CoordNode := TCoordinateNode.Create('', BaseUrl);
      IFS.FdCoord.Value := CoordNode;

      CoordNode.FdPoint.Items.Count := SidesCount;

      for I := 0 to E.High do
      begin
        E.SpineTransformTo1st(I, LastY, LastZ, Transform);
        for J := 0 to FdCrossSection.Count - 1 do
        begin
          CoordNode.FdPoint.Items.L[I * FdCrossSection.Count + J] :=
            MatrixMultPoint(Transform, CrossSection3D(J));
        end;
      end;

      { Fill TexCoordNode }

      if (FdTexCoord.Value <> nil) and FdTexCoord.CurrentChildAllowed then
      begin
        { No need for to create tex coords. }
        IFS.FdTexCoord.Value := FdTexCoord.Value;
        TexCoords := nil;
      end else
      begin
        TexCoordNode := TTextureCoordinateNode.Create('', BaseUrl);
        IFS.FdTexCoord.Value := TexCoordNode;
        TexCoords := TexCoordNode.FdPoint.Items;
      end;

      if TexCoords <> nil then
      begin
        Len := SidesCount;
        if FdBeginCap.Value or FdEndCap.Value then
          Len += FdCrossSection.Count;
        TexCoords.Count := Len;

        for I := 0 to E.High do
          for J := 0 to FdCrossSection.Count - 1 do
            TexCoords.L[I * FdCrossSection.Count + J] := SideTexCoord(I, J);

        { calculate TexCapsTranslation, TexCapsScale }
        if FdBeginCap.Value or FdEndCap.Value then
        begin
          TexCapsTranslation := 0;
          TexCapsScale := 1;
          if FdCrossSection.Items.MinMax(TexCapsMin, TexCapsMax) then
          begin
            if TexCapsMax[0] - TexCapsMin[0] >=
               TexCapsMax[1] - TexCapsMin[1] then
            begin
              if not Zero(TexCapsMax[0] - TexCapsMin[0]) then
              begin
                { scale such that U is along the X of cap.
                  Coord calculation will go like
                    u := (x - TexCapsMin[0]) / (TexCapsMax[0] - TexCapsMin[0]) }
                TexCapsTranslation := -TexCapsMin[0];
                TexCapsScale := 1 /  (TexCapsMax[0] - TexCapsMin[0]);
              end;
            end else
            begin
              if not Zero(TexCapsMax[1] - TexCapsMin[1]) then
              begin
                TexCapsTranslation := -TexCapsMin[1];
                TexCapsScale := 1 /  (TexCapsMax[1] - TexCapsMin[1]);
              end;
            end;
          end;

          for I := 0 to FdCrossSection.Count - 1 do
          begin
            TexCoordNode.FdPoint.Items.L[
              SidesCount + I] :=
              VectorScale(VectorAdd(
                FdCrossSection.Items.L[I],
                Vector2Single(TexCapsTranslation, TexCapsTranslation)),
                TexCapsScale);
          end;
        end;
      end;

      { Fill CoordIndex and TexCoordIndex }

      if E.High > 0 then
        Len := 5 * E.High * (FdCrossSection.Count - 1) else
        Len := 0; { E.High may be -1, then Len should remain 0 }
      if FdBeginCap.Value then
        Len += FdCrossSection.Count + 1 - E.CrossSectionOmit;
      if FdEndCap.Value then
        Len += FdCrossSection.Count + 1 - E.CrossSectionOmit;
      IFS.FdCoordIndex.Items.Count := Len;
      if TexCoords <> nil then
        IFS.FdTexCoordIndex.Items.Count := Len;

      Index := 0;

      for I := 1 to E.High do
      begin
        for J := 1 to FdCrossSection.Count - 1 do
        begin
          { The VRML / X3D specifications say that ordering of sides
            should be
              spine[0](crossSection[0], crossSection[1])
              spine[1](crossSection[1], crossSection[0])
            This is important, because we want faces to be ordered just
            like the specification requires. This makes using "ccw"
            and "solid" fields predictable for VRML author, since he
            knows which side will be CCW and which CW.

            So below we produce the same order. }

          NextIndex(I - 1, J - 1, false, 0);
          NextIndex(I - 1, J    , false, 0);
          NextIndex(I    , J    , false, 0);
          NextIndex(I    , J - 1, false, 0);
          NextIndexFaceDelimiter;
        end;
      end;

      if FdBeginCap.Value then
      begin
        { "downto" order, to match the ordering of sides, this makes
          things such a "solid TRUE" work Ok. }
        for J := FdCrossSection.Count - 1 downto E.CrossSectionOmit do
          NextIndex(0, J, true, SidesCount + J);
        NextIndexFaceDelimiter;
      end;

      if FdEndCap.Value then
      begin
        for J := E.CrossSectionOmit to FdCrossSection.Count - 1 do
          NextIndex(E.High, J, true, SidesCount + J);
        NextIndexFaceDelimiter;
      end;

      { Check that we actually filled all indexes we planned }
      Assert(Index = IFS.FdCoordIndex.Items.Count);

    finally FreeAndNil(E) end;

    IFS.FdSolid.Value := FdSolid.Value;
    IFS.FdCcw.Value := FdCcw.Value;
    IFS.FdCreaseAngle.Value := FdCreaseAngle.Value;
    IFS.FdConvex.Value := FdConvex.Value;
  except FreeAndNil(Result); raise end;
end;

function TExtrusionNode.ProxyUsesOverTriangulate: boolean;
begin
  Result := false;
end;

procedure TExtrusionNode.DirectEnumerateActive(Func: TEnumerateChildrenFunction);
begin
  inherited;
  FdtexCoord.EnumerateValid(Func);
end;

function TExtrusionNode.TexCoordField: TSFNode;
begin
  Result := FdTexCoord;
end;

function TExtrusionNode.AutoGenerate3DTexCoords: boolean;
begin
  Result := (FdTexCoord.Value = nil) or not FdTexCoord.CurrentChildAllowed;
end;

procedure TExtrusionNode.EventSet_crossSectionReceive(
  Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
begin
  FdcrossSection.Assign(Value);
  FdcrossSection.Changed;
end;

procedure TExtrusionNode.EventSet_orientationReceive(
  Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
begin
  Fdorientation.Assign(Value);
  Fdorientation.Changed;
end;

procedure TExtrusionNode.EventSet_scaleReceive(
  Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
begin
  Fdscale.Assign(Value);
  Fdscale.Changed;
end;

procedure TExtrusionNode.EventSet_spineReceive(
  Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
begin
  Fdspine.Assign(Value);
  Fdspine.Changed;
end;

function TExtrusionNode.Convex: boolean;
begin
  Result := FdConvex.Value;
end;

procedure TIndexedFaceSetNode.CreateNode;
begin
  inherited;

  FEventSet_colorIndex := TX3DEvent.Create(Self, 'set_colorIndex', TMFInt32, true);
  Events.Add(FEventSet_colorIndex);
  Eventset_colorIndex.OnReceive.Add(@EventSet_colorIndexReceive);

  FEventSet_coordIndex := TX3DEvent.Create(Self, 'set_coordIndex', TMFInt32, true);
  Events.Add(FEventSet_coordIndex);
  Eventset_coordIndex.OnReceive.Add(@EventSet_coordIndexReceive);

  FEventSet_normalIndex := TX3DEvent.Create(Self, 'set_normalIndex', TMFInt32, true);
  Events.Add(FEventSet_normalIndex);
  Eventset_normalIndex.OnReceive.Add(@EventSet_normalIndexReceive);

  FEventSet_texCoordIndex := TX3DEvent.Create(Self, 'set_texCoordIndex', TMFInt32, true);
  Events.Add(FEventSet_texCoordIndex);
  Eventset_texCoordIndex.OnReceive.Add(@EventSet_texCoordIndexReceive);

  FFdColorIndex := TMFInt32.Create(Self, 'colorIndex', []);
   FdColorIndex.SaveToStreamLineUptoNegative := true;
   FdColorIndex.Exposed := false;
   FdColorIndex.ChangesAlways := [chGeometry];
  Fields.Add(FFdColorIndex);
  { X3D specification comment: [0,Inf) or -1 }

  FFdConvex := TSFBool.Create(Self, 'convex', true);
   FdConvex.Exposed := false;
   FdConvex.ChangesAlways := [chGeometry];
  Fields.Add(FFdConvex);

  FFdCoordIndex := TMFInt32.Create(Self, 'coordIndex', []);
   FdCoordIndex.SaveToStreamLineUptoNegative := true;
   FdCoordIndex.Exposed := false;
   FdCoordIndex.ChangesAlways := [chGeometry];
  Fields.Add(FFdCoordIndex);
  { X3D specification comment: [0,Inf) or -1 }

  FFdCreaseAngle := TSFFloat.Create(Self, 'creaseAngle', 0);
   FdCreaseAngle.Angle := true;
   FdCreaseAngle.Exposed := false;
   FdCreaseAngle.ChangesAlways := [chGeometry];
  Fields.Add(FFdCreaseAngle);
  { X3D specification comment: [0,Inf) }

  FFdNormalIndex := TMFInt32.Create(Self, 'normalIndex', []);
   FdNormalIndex.SaveToStreamLineUptoNegative := true;
   FdNormalIndex.Exposed := false;
   FdNormalIndex.ChangesAlways := [chGeometry];
  Fields.Add(FFdNormalIndex);
  { X3D specification comment: [0,Inf) or -1 }

  FFdTexCoordIndex := TMFInt32.Create(Self, 'texCoordIndex', []);
   FdTexCoordIndex.SaveToStreamLineUptoNegative := true;
   FdTexCoordIndex.Exposed := false;
   FdTexCoordIndex.ChangesAlways := [chGeometry];
  Fields.Add(FFdTexCoordIndex);
  { X3D specification comment: [-1,Inf) }
end;

class function TIndexedFaceSetNode.ClassNodeTypeName: string;
begin
  Result := 'IndexedFaceSet';
end;

class function TIndexedFaceSetNode.URNMatching(const URN: string): boolean;
begin
  Result := (inherited URNMatching(URN)) or
    (URN = URNVRML97Nodes + ClassNodeTypeName) or
    (URN = URNX3DNodes + ClassNodeTypeName);
end;

class function TIndexedFaceSetNode.ForVRMLVersion(const Version: TX3DVersion): boolean;
begin
  Result := Version.Major >= 2;
end;

function TIndexedFaceSetNode.CoordIndex: TMFLong;
begin
  Result := FdCoordIndex;
end;

procedure TIndexedFaceSetNode.EventSet_colorIndexReceive(
  Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
begin
  FdcolorIndex.Assign(Value);
  FdcolorIndex.Changed;
end;

procedure TIndexedFaceSetNode.EventSet_coordIndexReceive(
  Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
begin
  FdcoordIndex.Assign(Value);
  FdcoordIndex.Changed;
end;

procedure TIndexedFaceSetNode.EventSet_normalIndexReceive(
  Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
begin
  FdnormalIndex.Assign(Value);
  FdnormalIndex.Changed;
end;

procedure TIndexedFaceSetNode.EventSet_texCoordIndexReceive(
  Event: TX3DEvent; Value: TX3DField; const Time: TX3DTime);
begin
  FdtexCoordIndex.Assign(Value);
  FdtexCoordIndex.Changed;
end;

function TIndexedFaceSetNode.Convex: boolean;
begin
  Result := FdConvex.Value;
end;

procedure TSphereNode.CreateNode;
begin
  inherited;

  FFdRadius := TSFFloat.Create(Self, 'radius', 1);
   FdRadius.Exposed := false;
   FdRadius.ChangesAlways := [chGeometry];
  Fields.Add(FFdRadius);
  { X3D specification comment: (0,Inf) }

  FFdSolid := TSFBool.Create(Self, 'solid', true);
   FdSolid.Exposed := false;
   FdSolid.ChangesAlways := [chGeometry];
  Fields.Add(FFdSolid);

  FFdTexCoord := TSFNode.Create(Self, 'texCoord', [TTextureCoordinateGeneratorNode, TProjectedTextureCoordinateNode, TMultiTextureCoordinateNode]);
   FdTexCoord.ChangesAlways := [chGeometry];
  Fields.Add(FFdTexCoord);
end;

class function TSphereNode.ClassNodeTypeName: string;
begin
  Result := 'Sphere';
end;

class function TSphereNode.URNMatching(const URN: string): boolean;
begin
  Result := (inherited URNMatching(URN)) or
    (URN = URNVRML97Nodes + ClassNodeTypeName) or
    (URN = URNX3DNodes + ClassNodeTypeName);
end;

class function TSphereNode.ForVRMLVersion(const Version: TX3DVersion): boolean;
begin
  Result := Version.Major >= 2;
end;

function TSphereNode.TexCoordField: TSFNode;
begin
  Result := FdTexCoord;
end;

function TSphereNode.AutoGenerate3DTexCoords: boolean;
begin
  Result := (FdTexCoord.Value = nil) or not FdTexCoord.CurrentChildAllowed;
end;

procedure RegisterGeometry3DNodes;
begin
  NodesManager.RegisterNodeClasses([
    TBoxNode,
    TConeNode,
    TCylinderNode,
    TElevationGridNode,
    TExtrusionNode,
    TIndexedFaceSetNode,
    TSphereNode
  ]);
end;

{$endif read_implementation}
