Class TVRMLNode

DescriptionHierarchyFieldsMethodsProperties

Unit

Declaration

type TVRMLNode = class(TVRMLFileItem, IVRMLNode)

Description

VRML node.

Descendant implementors note: Each descendant should override constructor to create and add his fields and events. Like Fields.Add(TSFFloat.Create('width', 2, true)). Also, you should define FdXxx properties that allow fast, comfortable and type-secure way to retrieve and set these fields.

Hierarchy

Overview

Fields

Protected fAllowedChildren: boolean;
Protected fParsingAllowedChildren: boolean;

Methods

Protected function DeepCopyCore(CopyState: TVRMLNodeDeepCopyState): TVRMLNode; virtual;
Protected function DeepCopyCreate(CopyState: TVRMLNodeDeepCopyState): TVRMLNode; virtual;
Protected procedure DirectEnumerateActive( Func: TEnumerateChildrenFunction); virtual;
Protected procedure DirectEnumerateAll( Func: TEnumerateChildrenFunction);
Protected procedure DirectEnumerate( Func: TEnumerateChildrenFunction; OnlyActive: boolean);
Protected procedure BeforeTraverse(var State: TVRMLGraphTraverseState); virtual;
Protected procedure MiddleTraverse(State: TVRMLGraphTraverseState); virtual;
Protected procedure AfterTraverse(var State: TVRMLGraphTraverseState); virtual;
Protected function ParseNodeBodyElement(Lexer: TVRMLLexer; const APositionInParent: Integer): boolean; virtual;
Protected procedure SaveContentsToStream(SaveProperties: TVRMLSaveToStreamProperties); virtual;
Public function FieldOrEvent(const Name: string): TVRMLFieldOrEvent;
Public function AnyEvent(const Name: string): TVRMLEvent;
Public function ChildrenCount: integer;
Public procedure AddChild(Index: Integer; child: TVRMLNode); overload;
Public procedure AddChild(child: TVRMLNode); overload;
Public procedure RemoveChild(i: integer);
Public procedure RemoveAllChildren;
Public function ExtractChild(I: Integer): TVRMLNode;
Public function ParentNodesCount: integer;
Public function ParentFieldsCount: Integer;
Public procedure FreeRemovingFromAllParents;
Public procedure FreeIfUnused;
Public function PathFromWWWBasePath(const RelativePath: string): string;
Public procedure Parse(Lexer: TVRMLLexer); virtual;
Public constructor Create(const ANodeName: string; const AWWWBasePath: string); virtual;
Public constructor CreateParse(const ANodeName: string; Lexer: TVRMLLexer);
Public destructor Destroy; override;
Public function NodeTypeName: string; virtual;
Public class function ClassNodeTypeName: string; virtual;
Public procedure Traverse( NodeClass: TVRMLNodeClass; TraversingFunc: TTraversingFunc; TraversingFuncAfter: TTraversingFunc = nil);
Public procedure EnumerateNodes( proc: TVRMLNodeProc; OnlyActive: boolean); overload;
Public procedure EnumerateNodes(nodeClass: TVRMLNodeClass; proc: TVRMLNodeProc; OnlyActive: boolean); overload;
Public procedure EnumerateNodes(nodeClass: TVRMLNodeClass; const SeekNodeName: string; proc: TVRMLNodeProc; OnlyActive: boolean); overload;
Public function TryFindNodeByName(FindClass: TVRMLNodeClass; const FindName: string; OnlyActive: boolean): TVRMLNode;
Public function FindNodeByName(FindClass: TVRMLNodeClass; const FindName: string; OnlyActive: boolean): TVRMLNode;
Public function TryFindNode(FindClass: TVRMLNodeClass; OnlyActive: boolean): TVRMLNode;
Public function FindNode(FindClass: TVRMLNodeClass; OnlyActive: boolean): TVRMLNode;
Public function TryFindNodeState( NodeClass: TVRMLNodeClass; out Node: TVRMLNode; out State: TVRMLGraphTraverseState): boolean;
Public function TryFindNodeTransform( NodeClass: TVRMLNodeClass; out Node: TVRMLNode; out Transform: TMatrix4Single; out AverageScaleTransform: Single): boolean;
Public function TryFindParentByName(const FindName: string): TVRMLNode;
Public function FindParentByName(const FindName: string): TVRMLNode;
Public function HasParent(Node: TVRMLNode): boolean;
Public function TryFindDirectParentByName(const FindName: string): TVRMLNode;
Public function IsNodePresent(Node: TVRMLNode; OnlyActive: boolean): boolean;
Public function NodesCount(NodeClass: TVRMLNodeClass; CountOnlyActiveNodes: boolean): integer;
Public procedure SaveToStream(SaveProperties: TVRMLSaveToStreamProperties); override;
Public class function TraverseStateLastNodesIndex: Integer;
Public class function ForVRMLVersion(const VerMajor, VerMinor: Integer): boolean; virtual;
Public function ChildrenField: TMFNode; virtual;
Public procedure SmartAddChild(Node: TVRMLNode);
Public function SmartChildrenCount: integer;
Public function SmartExtractChild(Index: Integer): TVRMLNode;
Public function SuggestedVRMLVersion( out VerMajor, VerMinor, SuggestionPriority: Integer): boolean; virtual;
Public class function ChildrenSaveToStream: boolean; virtual;
Public function EnumerateReplaceChildren( Func: TEnumerateReplaceNodesFunction): Cardinal;
Public function RemoveChildrenWithMatchingName( const Wildcard: string; IgnoreCase: Boolean): Cardinal;
Public function DeepCopy: TVRMLNode;
Public procedure Bind(NodeNameBinding: TStringList);
Public class function URNMatching(const URN: string): boolean; virtual;
Public procedure TraverseBlenderObjects( TraversingFunc: TBlenderTraversingFunc); overload;

Properties

Public property Fields: TVRMLFieldsList read FFields;
Public property Events: TVRMLEventsList read FEvents;
Public property Children[i: integer]: TVRMLNode read GetChildrenItem write SetChildrenItem;
Public property ParentNodes[i: integer]: TVRMLNode read GetParentNodesItem;
Public property ParentFields[Index: Integer]: TVRMLField read GetParentFieldsItem;
Public property ParentFieldsNode[Index: Integer]: TVRMLNode read GetParentFieldsNodeItem;
Public property AllowedChildren: boolean read fAllowedChildren default false;
Public property ParsingAllowedChildren: boolean read fParsingAllowedChildren default false;
Public property NodeName: string read fNodeName write FNodeName;
Public property WWWBasePath: string read FWWWBasePath write FWWWBasePath;
Public property SmartChildren[Index: Integer]: TVRMLNode read GetSmartChildren;
Public property Prototypes: TVRMLPrototypeBasesList read FPrototypes;
Public property Routes: TVRMLRoutesList read FRoutes;
Public property PrototypeInstance: boolean read FPrototypeInstance;
Public property PrototypeInstanceSourceNode: TVRMLPrototypeNode read FPrototypeInstanceSourceNode;
Public property PrototypeInstanceHelpers: TVRMLNode read FPrototypeInstanceHelpers;
Public property DefaultContainerField: string read FDefaultContainerField write FDefaultContainerField;
Public property ExplicitContainerField: string read FExplicitContainerField write FExplicitContainerField;
Public property HasInterfaceDeclarations: TVRMLAccessTypes read FHasInterfaceDeclarations write SetHasInterfaceDeclarations default [];
Public property InterfaceDeclarations: TVRMLInterfaceDeclarationsList read FInterfaceDeclarations;
Public property CDataAllowed: boolean read FCDataAllowed write FCDataAllowed;
Public property CDataExists: boolean read FCDataExists write FCDataExists;
Public property CData: string read FCData write FCData;
Public property DestructionNotifications: TDynNodeDestructionNotificationArray read FDestructionNotifications;
Public property ParentEventsProcessor: TObject read FParentEventsProcessor write FParentEventsProcessor;

Description

Fields

Protected fAllowedChildren: boolean;
 
Protected fParsingAllowedChildren: boolean;
 

Methods

Protected function DeepCopyCore(CopyState: TVRMLNodeDeepCopyState): TVRMLNode; virtual;

Does actual DeepCopy work. You can override this to copy some more properties for descendants.

Protected function DeepCopyCreate(CopyState: TVRMLNodeDeepCopyState): TVRMLNode; virtual;

This should be a mere call to constructor of your own class.

In TVRMLNode, this simply calls default virtual constructor, which is Ok for all normal nodes. But we have some special nodes, like TVRMLPrototypeNode or TVRMLUnknownNode, that simply cannot be created by default constructor. They need to override this.

Protected procedure DirectEnumerateActive( Func: TEnumerateChildrenFunction); virtual;

This enumerates all active child nodes of given node.

"Active nodes" are the ones affecting current VRML graph look or collisions, e.g. from Switch node only one child will be enumerated. See Traverse for more precise definition.

"Direct" means that this enumerates only direct descendants, i.e. this is not recursive. See methods like Traverse or EnumerateNodes if you want recursive behavior.

This can enumerate both Children nodes in VRML 1.0 style and nodes within TSFNode and TMFNode fields.

Default implementation in this class returns all Children nodes of VRML 1.0. If you need to remove some children for VRML 1.0 (e.g. for Switch or LOD nodes) or add some children for VRML 2.0 you have to override this. You're not required to call inherited when overriding this.

Protected procedure DirectEnumerateAll( Func: TEnumerateChildrenFunction);

This simply enumerates all direct descendant nodes of this node. I.e. all children in VRML 1.0 style and all nodes in SFNode and MFNode fields. This includes prototype stuff, if this node is expanded from prototype: PrototypeInstanceSourceNode and PrototypeInstanceHelpers.

Protected procedure DirectEnumerate( Func: TEnumerateChildrenFunction; OnlyActive: boolean);

This enumerates direct descendant nodes of this node. This is equivalent to DirectEnumerateActive or DirectEnumerateAll, depending on value of OnlyActive param.

Protected procedure BeforeTraverse(var State: TVRMLGraphTraverseState); virtual;

You can override these methods to determine what happens when given node is traversed during Traverse call. The main use of this is to modify TVRMLGraphTraverseState.

Remember to always call inherited when overriding. In BeforeTraverse and MiddleTraverse you should call inherited at the beginning, in AfterTraverse inherited should be called at the end.

Besides changing State fields, you can even replace the curent State instance in BeforeTraverse. But in this case, you must change it back to original value in AfterTraverse.

Protected procedure MiddleTraverse(State: TVRMLGraphTraverseState); virtual;
 
Protected procedure AfterTraverse(var State: TVRMLGraphTraverseState); virtual;
 
Protected function ParseNodeBodyElement(Lexer: TVRMLLexer; const APositionInParent: Integer): boolean; virtual;

Parse VRML node body element. Usually, this is a field. May also be VRML 1.0 style child node. May also be VRML 2.0 Script node interface declaration, etc. — see VRML 2.0 grammar spec.

This should be overriden to parse special features within particular nodes. While generally VRML is very clean and there's no need to override this, there's one use for this currently:

  1. Since we handle a couple of VRML flavors (at least Inventor, VRML 1.0 and VRML 97), sometimes the same node has different fields to express the same things in various VRML flavors. So it may be useful to parse a field and copy it's value into other fields.

    Example: TNodeShapeHints in Inventor parses "hints" field, and copies it's value to other fields as appropriate. "hints" field is not exposed in TNodeShapeHints interface, so everything is clean in the interface, and under the hood TNodeShapeHints can "magically" handle "hints" field for Inventor.

When overriding, always check inherited result first, and exit if inherited handled successfully. Otherwise either read your stuff and return True (Lexer should advance to the position of next "nodeBodyElement"). Or return False without changing Lexer position.

Protected procedure SaveContentsToStream(SaveProperties: TVRMLSaveToStreamProperties); virtual;

This will be called by SaveToStream within { }. Usually you want to save here what you read in your overridden ParseNodeBodyElement.

Public function FieldOrEvent(const Name: string): TVRMLFieldOrEvent;

Search by name for given field or event (exposed by some field or not).

Nil if not found.

Public function AnyEvent(const Name: string): TVRMLEvent;

Search by name for given event (exposed by some field or not).

Nil if not found.

Public function ChildrenCount: integer;
 
Public procedure AddChild(Index: Integer; child: TVRMLNode); overload;

AddChild z Index (musi byc w zakresie 0..ChildrenCount) przesuwa elementy o numerach Index i wiekszych w prawo i wstawia child na wskazane Index.

Public procedure AddChild(child: TVRMLNode); overload;

AddChild bez Indexu - dodaje na koniec listy Children.

Public procedure RemoveChild(i: integer);
 
Public procedure RemoveAllChildren;
 
Public function ExtractChild(I: Integer): TVRMLNode;

Remove and return children indexed I, and never free it.

Compare this with RemoveChild, that removes children I and frees it if it's reference count gets 0.

ExtractChild removes children I, appropriately adjusting all parent/children links, but even if reference count of the node will get zero, ExtractChild will not free it. ExtractChild always returns extracted child.

Public function ParentNodesCount: integer;
 
Public function ParentFieldsCount: Integer;
 
Public procedure FreeRemovingFromAllParents;

Free this object (if it's not Nil) also removing it from all parent nodes and fields.

By design, normal destructor (Destroy called by Free) doesn't care about removing references to this object from it's parents. That's because it's the parents that usually initialize freeing of their children, and they free child when it's reference count is 0. So this freeing method is special in this regard.

Use this if you really want to remove all node occurences from the middle of VRML hierarchy.

Public procedure FreeIfUnused;

Free if this node is not referenced by any VRML 1.0 parent node or VRML 2.0 SFNode or MFNode fields. This is a safe way of removing a node that may, but doesn't have to, be part of VRML graph. The idea is that if node is a part of the graph, we will do nothing (assuming you have a reference to the entine graph somewhere), otherwise node is considered unused and freed.

For safety, I advice to usually set reference to Nil after calling FreeIfUnused, like

  Child.FreeIfUnused;
  Child := nil;

Public function PathFromWWWBasePath(const RelativePath: string): string;

This returns absolute path, assuming that RelativePath is relative path from WWWBasePath or that RelativePath is already absolute.

Public procedure Parse(Lexer: TVRMLLexer); virtual;

Parse node. This should set values of your fields, VRML 1.0 Children list, WWWBasePath.

In special cases like TVRMLUnknownNode this may actually initialize whole Fields list (by VRML 1.0 "fields" extensibility feature).

Public constructor Create(const ANodeName: string; const AWWWBasePath: string); virtual;

Konstruktor. Inicjuje wszystko (jak to konstruktor), w szczegolnosci :

  • inicjuje NodeName, WWWBasePath na podstawie podanych tu parametrow

  • inicjuje tablice Fields ustawiajac polom ich defaultowe wartosci (dla implementatorow podklas TVRMLNode: w klasie TVRMLNode inicjujemy Fields na tablice o 0 elementach; w konstruktorze podklasy musisz wywolac Fields.Add(...) aby dodac sobie odpowiednie pola)

  • [Parsing]AllowedChildren (dla implementatorow podklas TVRMLNode: w klasie TVRMLNode inicjujemy je na zbiory puste (tzn. chwilowo po prostu na false) po prostu dlatego ze wydaje sie to byc najczestsza wartoscia. W konstruktorze podklas mozesz swobodnie zmienic wartosci tych pol.)

Public constructor CreateParse(const ANodeName: string; Lexer: TVRMLLexer);

CreateParse simply does Create and then calls Parse.

Public destructor Destroy; override;
 
Public function NodeTypeName: string; virtual;

NodeTypeName zwraca nazwe klasy w VRML'u. Zawsze jest <>''. To ma byc normalna nazwa node'a, taka ktora odczytujemy i zapisujemy bezposrednio z/do pliku VRMLa (wobec tego jest ona tez case-sensitive, jak caly VRML).

Nie zmienia sie przez caly czas zycia obiektu, tzn. raz zainicjowana w konstruktorze juz taka pozostaje. Even for special nodes, like TVRMLUnknownNode and TVRMLPrototypeNode (where this is determined at runtime, since these special nodes are used to instantiate special nodes that are not built-in) — but still even for these special nodes, NodeTypeName is constant for the life of this object.

W tej klasie NodeTypeName zwraca ClassNodeTypeName. Uwagi do implementacji podklas TVRMLNode dotyczace tej funkcji - patrz ClassNodeTypeName.

Public class function ClassNodeTypeName: string; virtual;

ClassNodeTypeName zwraca nazwe klasy VRMLa zwiazanej z tym node'm lub '' w przypadku klas ktore nie maja zwiazanej ze soba 1-znacznej nazwy typu wezla VRMLa ktory reprezentuja (a poniewaz kazda klasa wezla VRMLa musi miec NodeTypeName <> '' wiec oznacza to ze te wyjatkowe klasy ustalaja sobie NodeTypeName w jakis inny, specjalny sposob - jedyny dostepny w tej chwili przyklad tego to TVRMLUnknownNode (chociaz nie wykluczam sobie w tym momencie czy nie pojawi sie kiedys jeszcze jakis inny tego typu node))

Jezeli masz do dyspozycji instancje obiektu to nie powinienes uzywac tej funkcji. Jedyna jej zaleta ponad NodeTypeName jest ze jest funkcja klasy. Jezeli masz do dyspozycji tylko klase obiektu to uzywajac tej funkcji musisz sie zdecydowac co zrobic jesli dostaniesz w odpowiedzi '' (albo jakos sie zabezpieczyc zeby nigdy w danym kontekscie takich klas nie miec...)

Uwagi do implementacji podklas TVRMLNode: W tej klasie ClassNodeTypeName zwraca ''. Wszystkie niesbtrakcyjne klasy wezlow VRMLa musza pokrywac albo ClassNodeTypeName (i w ten sposob staja sie normalnymi klasami ktore maja zawszetaka sama wartosc NodeTypeName dla swojej klasy) albo NodeClassTypeName (i w ten sposob staja sie klasami specjalnymi, jak TVRMLUnknownNode, ktore nie maja stalej wartosci NodeTypeName).

Public procedure Traverse( NodeClass: TVRMLNodeClass; TraversingFunc: TTraversingFunc; TraversingFuncAfter: TTraversingFunc = nil);

Traverse enumerates all nodes of VRML graph that are active,

An "active" part of the VRML graph are the nodes that actually change what the VRML file represents, in terms of geometry, collision detection etc. For example, the Switch node has only one child usually active. Nodes that merely influence the active graph by some events and routes do not have to be active (these nodes may change what the VRML file actually represents, but only by changing other nodes).

For all nodes of NodeClass TraversingFunc will be called.

Traverse not only enumerates these nodes, it also collects all state (transformation, etc — see TVRMLGraphTraverseState) that affects how given node should be presented.

Also, TraversingInfo is passed to each TraversingFunc call. This allows you to investigate, during TraversingFunc call, the parents hierarchy (you can't use ParentNodes / ParentFields of the current node, since a node may have many parents). Traverse calls are naturally recursive, and so the stack of TraversingInfo structures is naturally build and destroyed by recursive calls. For the root node (the one where you called Traverse without specifying initial TraversingInfo), ParentInfo is simply Nil.

The scheme of how Traverse works:

  BeforeTraverse;
  if Self is NodeClass then TraversingFunc (Self, State)
  MiddleTraverse
  for all children returned by DirectEnumerateActive
    call their Traverse(State)
  AfterTraverse,
  if Self is NodeClass then TraversingFuncAfter (Self, State)
  dodaj Self do stanu State do LastNode (o ile Self wsrod
    TraverseStateLastNodesClasses)

Note: I didn't decide yet whether TraversingFuncAfter should be before or after AfterTraverse call. Report if you have any good reason for any setting.

Jezeli zostalo wykonane BeforeTraverse, na pewno zostanie wykonane tez AfterTraverse (wywolanie AfterTraverse jest w finally..end).

Kolejnosc w jakiej przechodzi graf jest naturalnie istotna. W czasie wykonywania Traverse mozesz modyfikowac tylko node'y dzieci (bezposrednie i niebezposrednie) node'a na ktorym wlasnie stoisz.

Public procedure EnumerateNodes( proc: TVRMLNodeProc; OnlyActive: boolean); overload;

Enumerate all our children of some class. Recursively. Zwroci do proc() takze sam obiekt na ktorym EnumerateNodes zostalo wywolane, jezeli tylko ten obiekt jest klasy nodeClass.

This enumerates both VRML 1.0 Children as well as nodes in TSFNode and TMFNode fields.

If OnlyActive then it will enumerate only active parts of the graph ("active" as defined by Traverse), so it will work as a simpler version of Traverse (simpler, because it doesn't track any state).

If not OnlyActive then it will simply enumerate all nodes. This will include then also prototype helpers, if this node was expanded from prototype: see PrototypeInstanceSourceNode and PrototypeInstanceHelpers.

Wersja z argumentem SeekNodeName wymaga ponadto aby node mial NodeName= SeekNodeName (gdy SeekNodeName = '' to znajduje nienazwane node'y, wiec wartosc '' nie jest tu traktowana specjalnie).

Zaczyna przegladac dzieci dopiero jak przegladnie Self. Jezeli np. w proc. zmodyfikowales (np. dodales) wlasne Children to EnumerateNodes will enumerate these new children. To ma znaczenie np. w TVRMLScene.LoadAllInlined gdzie w proc robimy LoadInlined. Poniewaz EnumerateNodes przeglada dzieci po wywolaniu proc., wiadomo ze przegladnie tez nowo zaladowane dziecko.

BTW modyfikowanie dzieci node'a ktory wlasnie dostales do proc() to jedyna dozwolona modyfikacja na hierarchii VRMLa ktora mozesz wykonywac w czasie EnumerateNodes.

Public procedure EnumerateNodes(nodeClass: TVRMLNodeClass; proc: TVRMLNodeProc; OnlyActive: boolean); overload;
 
Public procedure EnumerateNodes(nodeClass: TVRMLNodeClass; const SeekNodeName: string; proc: TVRMLNodeProc; OnlyActive: boolean); overload;
 
Public function TryFindNodeByName(FindClass: TVRMLNodeClass; const FindName: string; OnlyActive: boolean): TVRMLNode;

TryFindNodeByName and TryFindNode seek for a node with given class (and node name, in case of TryFindNodeByName). If OnlyActive then they seek among only active nodes ("active" as defined by Traverse), otherwise all nodes.

These functions are quite like EnumerateNodes, except they stop at the first occurence and return it.

TryFindNodeByName and TryFindNode return Nil if such node is not found. FindNodeByName and FindNode raise exception in this case.

Public function FindNodeByName(FindClass: TVRMLNodeClass; const FindName: string; OnlyActive: boolean): TVRMLNode;
 
Public function TryFindNode(FindClass: TVRMLNodeClass; OnlyActive: boolean): TVRMLNode;
 
Public function FindNode(FindClass: TVRMLNodeClass; OnlyActive: boolean): TVRMLNode;
 
Public function TryFindNodeState( NodeClass: TVRMLNodeClass; out Node: TVRMLNode; out State: TVRMLGraphTraverseState): boolean;

Znajdz pierwszy Node (zadanej klasy NodeClass) razem ze State (lub tylko z Transform). Dziala jak TraverseFromDefaultState ktore zatrzymuje sie po pierwszej udanej probie. W przypadku TryFindNodeTransform nie musisz o tym pamietac, no i TryFindNodeTransform dziala nieco szybciej.

Zwraca false and sets Node, State and Transform to undefined (because they are "out" params) if not found.

Public function TryFindNodeTransform( NodeClass: TVRMLNodeClass; out Node: TVRMLNode; out Transform: TMatrix4Single; out AverageScaleTransform: Single): boolean;
 
Public function TryFindParentByName(const FindName: string): TVRMLNode;

This seeks Self and parent nodes (from ParentNodes and ParentFields, recursively), for given node name.

In other words, this is similar to TryNodeByName or NodeByName, but it goes "upward" in graph hierarchy. Note that this never restricts itself only to "active" graph part ("active" as defined by Traverse) because you really can't detect what is the "active" part of the graph when going upward.

Public function FindParentByName(const FindName: string): TVRMLNode;
 
Public function HasParent(Node: TVRMLNode): boolean;

Przeszukuje podobnie jak powyzsze FindParentByName. Zwraca true jesli znalazl tam gdzies node Node.

Public function TryFindDirectParentByName(const FindName: string): TVRMLNode;

Searches immediate parents of this node for a node with given FindName. Returns Nil if not found.

Public function IsNodePresent(Node: TVRMLNode; OnlyActive: boolean): boolean;

sprawdza czy istnieje w grafie VRML'a zaczepionym w danym punkcie node Node.

If OnlyActive, then only active parts are searched ("active" as defined by Traverse).

Public function NodesCount(NodeClass: TVRMLNodeClass; CountOnlyActiveNodes: boolean): integer;

policz ile jest node'ow danej klasy. Uzywajac np. TVRMLLightNode mozesz sprawdzic czy na scenie zostalo zdefiniowane jakiekolwiek swiato.

If CountOnlyActiveNodes, then only active parts are searched ("active" as defined by Traverse).

This traverses both VRML 1.0 children nodes and VRML 2.0 nodes inside SFNode and MFNode fields.

Public procedure SaveToStream(SaveProperties: TVRMLSaveToStreamProperties); override;

Save node to stream. This saves everything, including node name, node type, then node contents within { }.

We use SaveProperties.NodeNameBinding, pretty much like when parsing. If a node name is already bound with this node, then we know we have to write only USE ... statement. Otherwise we write full node contents, with eventual DEF ... statement.

Note that if ChildrenSaveToStream returns False we don't write our Children. Currently this is used by various inline nodes (WWWInline, Inline, etc.).

Public class function TraverseStateLastNodesIndex: Integer;

szuka tej klasy node'a (rzeczywistej koncowej klasy, z ClassType) w TraverseStateLastNodesClasses. Zwraca indeks lub -1 jesli nie znalazl.

Public class function ForVRMLVersion(const VerMajor, VerMinor: Integer): boolean; virtual;

Some of the nodes are meant to be handled only for specific VRML versions. This functions says whether this node is supposed to be present in given VRML version. VerMajor and VerMinor arguments are expected in the same form as TVRMLLexer.VRMLVerMajor, TVRMLLexer.VRMLVerMinor.

For example some nodes can only work in VRML < 2.0, some others only in VRML >= 2.0. There are even some pairs of nodes: for example TNodeCone_1 works with VRML < 2.0, TNodeCone_2 works with VRML >= 2.0.

NodesManager will use this.

Default implementation of this function returns always True. Generally, I don't try to set this too aggresively — in other words, for all cases when it's sensible, I allow nodes to be used in every VRML version, even when official specification doesn't. This means that when reading VRML 1.0 files actually a large part of VRML 2.0 is allowed too, and also while reading VRML 2.0 many constructs from VRML 1.0 (officially no longer present in VRML 2.0) are allowed too. I'm trying to support what I call a "sum of VRML 1.0 and 2.0".

In practice I only use this function when both VRML 1.0 and 2.0 specify the same node name but

  • With different fields.

    For example Cone and Cylinder have slightly different fields, due to the fact that VRML 2.0 resigned from using TSFBitMask fields.

  • With different behavior.

    For example definitions of Sphere for VRML 1.0 and 2.0 are practically equal. However, the behavior from where to take texture and material info is different — in VRML 1.0 we take last Texture2, Material etc. nodes, while in VRML 2.0 we look in parent Shape's "appearance" field. So once again two different Sphere classes are needed.

Public function ChildrenField: TMFNode; virtual;

MFNode field of this node that should be treated as general "children" field of this node. This is used in some places, like SmartAddChild.

Should return nil if this node doesn't have such field (that's the default implementation in this class). This should always return the same value for given class instance (in other words, don't implement this to sometimes return one field, sometimes the other, sometimes nil, etc.).

Public procedure SmartAddChild(Node: TVRMLNode);

These operate on children nodes, in either VRML 2.0 style (if ChildrenField is non-nil, then these get/set ChildrenField.Items) or in VRML 1.0 style (if ChildrenField is nil, then these get/set our Children).

This is useful to operate on grouping nodes both in VRML 1.0 and VRML 2.0 style using the same code.

Public function SmartChildrenCount: integer;
 
Public function SmartExtractChild(Index: Integer): TVRMLNode;
 
Public function SuggestedVRMLVersion( out VerMajor, VerMinor, SuggestionPriority: Integer): boolean; virtual;

SuggestedVRMLVersion determines what VRML header to use when saving the node to file. Returns True and sets out arguments if some version is preferred, otherwise returns False.

SuggestionPriority should be used to indicate the "strongness" of this suggestion. The idea is that if there are two nodes that have different VRML version suggestions, then the one with greater SuggestionPriority "wins".

Currently used priorities:

  • 1000 for nodes that are only in VRML <= 1.0, or only in VRML >= 2.0. This applies to majority of nodes.

  • 100 for nodes that are only for VRML >= 2.0 according to specifications, but in our engine they were often used also in VRML 1.0 files. That is possible thanks to our implementation, see [http://vrmlengine.sourceforge.net/kambi_vrml_extensions.php#ext_mix_vrml_1_2]. This concerns Fog, Background and a few other nodes.

    By using weaker priority for such nodes, VRML 1.0 files with "bits" of VRML 2.0 will be retained as VRML 1.0. (They are readable only by our engine anyway.) On the other hand, only Background node will be correctly saved as VRML 2.0 file.

  • 2000 for nodes that are only in X3D, that is VRML >= 3.0.

    X3D nodes suggest version 3.2 (not 3.0, because some features (shaders) are only in ammendment 1 I think, so 3.1 is safer and for simplicity (since I looked mostly at 3.2) 3.2 is even better).

    They use priority 2000, so they are slightly stronger than VRML 97 nodes. This way a model with mixed VRML 97 and X3D nodes will be judged as X3D. And that's Ok, because almost everything (incompatible exceptions are some NURBS and geo changes) valid in VRML 97 is also valid in X3D.

  • 10 * 1000 is used by TVRMLRootNode_1 and TVRMLRootNode_2 ForceVersion mechanism. This way we can always force VRML version using this node, which is useful to write files with the same VRML version as was read from disk.

Default implementation in this class enumerates all SFNode and MFNoden fields and Children nodes and determines their suggested VRML version.

Public class function ChildrenSaveToStream: boolean; virtual;

Returns should SaveToStream save our Children. In this class default implementation returns True, this is what you will want in 99% of cases. It's useful to set this to false if you use Children internally, e.g. *Inline nodes.

Public function EnumerateReplaceChildren( Func: TEnumerateReplaceNodesFunction): Cardinal;

Enumerates all children nodes (recursively), allowing you to decide for each node to replace or remove it.

So this is something like EnumerateNodes, except that it allows you to remove the nodes. It always enumerates all nodes, not only active (e.g. it enumerates all Switch node children, not only the chosen one).

Note that (unlike regular EnumerateNodes) this doesn't report Self to Func !. Which is natural, since this may remove nodes by normal RemoveChild calls, so it needs to know ParentNode of the removed node.

For each node Func will be called, with ParentNode and Node set. If you change the Node to something else, then the old node will be removed and new Node inserted in the same place. If new Node is Nil, then only the old node will be removed.

Nodes are traversed in depth-first search. Node is first reported to Func, and then (if it's not replaced) we descend into this Node.

Returns

The number of removed nodes.

Public function RemoveChildrenWithMatchingName( const Wildcard: string; IgnoreCase: Boolean): Cardinal;

Removes all children (and their children, recursively) with node names matchig Wildcard. You can use * and ? special chars in the Wildcard.

Returns

The number of removed nodes.

Public function DeepCopy: TVRMLNode;

Create a deep copy of this node and all it's children.

New copy is completely independent from original, having all children nodes (in both VRML 1.0 sense (Children) and VRML >= 2.0 (inside SFNode and MFNode fields)) also duplicated. New copy has protypes, routes, interface declarations and generally everything established like in the original, using copied nodes.

Doesn't copy things which are dependent on container hierarchy. (So copying them would be more dangerous than useful.) This means: DestructionNotifications, ParentEventsProcessor, ParentNodes, ParentFields. ParentNodes and ParentFields will be set for children anyway (to appropriate copies).

Caller owns this newly created copy — as returned by this method, it's not linked anywhere.

Public procedure Bind(NodeNameBinding: TStringList);

Add Self (NodeName must be initialized) to nodes namespace. Doesn't do anything if NodeName = ''.

Public class function URNMatching(const URN: string): boolean; virtual;

Should we use this node when URN is required by EXTERNPROTO ?

Implementors note: in this class, this returns False. You can use constants like URNVRML97Nodes and URNKambiNodes to help implementing this.

Public procedure TraverseBlenderObjects( TraversingFunc: TBlenderTraversingFunc); overload;

Traverses all Blender objects/meshes instances in this model, assuming that this VRML node was created by Blender VRML 1.0 or 2.0 exporter.

For each Blender object (which means, for each Blender mesh instantiation), this calls TraversingFunc. Since each Blender object is unique in file, you can be sure that each BlenderObjectNode will be enumerated only once by TraversingFunc, as long as this file was really made by Blender exporter. As for BlenderObjectName, Blender VRML 1.0 exporter doesn't write object names (only meshes), so it's always '' for VRML 1.0.

Mesh may occur many times in the file, and both Blender exporters correctly use VRML DEF/USE mechanism, so the same BlenderMeshNode and BlenderMeshName may be enumerated many times by TraversingFunc.

Implementation of this follows the logic of Blender VRML 1.0 and 2.0 standard exporters, there's no other way to implement this. If you wrote in Python your own Blender exporter for VRML, this method may obviously not work. But it's guaranteed that this method will not crash or anything on any VRML model. The worst thing that can happen on all VRML models is simply that TraversingFunc will enumerate something that doesn't correspond to any Blender object...

Properties

Public property Fields: TVRMLFieldsList read FFields;

Node fields.

For normal nodes, all Fields are created and added to Fields list in constructor. Fields default values are set, and of course current field values are set to these defaults. Later, we only modify these fields current values (e.g. when parsing).

However, there are special node classes that set their Fields differently. TVRMLPrototypeNode has their fields set according to it's VRML 2.0 prototype. TVRMLUnknownNode may have it's fields set by VRML 1.0 "fields" feature (so it's Fields are initialized by parsing it).

Nodes with HasInterfaceDeclarations have some Fields and Events added when reading node.

All fields on this list are owned by this object.

Public property Events: TVRMLEventsList read FEvents;

Explicit events (that is, not exposed by some field) of this node. For exposed events, see each field's property ExposedEvents.

Public property Children[i: integer]: TVRMLNode read GetChildrenItem write SetChildrenItem;

Children property lists children VRML nodes, in the sense of VRML 1.0. In VRML 2.0, nodes never have any Children nodes expressed on this list (however, their children nodes may be expressed as items of TMFNode / TSFNode fields).

Kazdy VRML nodes moze miec dowolnie wiele Children. Kiedy jakis node jest na liscie Children jednego node'a to ma swojego rodzica na swojej liscie ParentNodes. Wiec w ten sposob mozemy podrozowac po grafie w obie strony. (pamietaj ze graf VRML'a nie ma cykli gdy na niego patrzec jak na graf skierowany (a takim wlasnie jest) ale kazdy node moze miec wiele rodzicow wiec jezeli potraktujemy go jako graf nieskierowany to mozemy otrzymac cykle; wszystko przez to ze node moze miec wiele ParentNodes bo moze uzywac mechanizmu USE).

Kiedy jakis node jest na liscie Children innego node'a to gdy ten inny node bedzie go kasowal ze swojej listy Children (a w destruktorze kazdy node kasuje wszystkich ze swojej listy Children) to wywola jego destruktora. Innymi slowy, gdy jakis node jest czyims dzieckiem to jest reference-counted i automatycznie zwalniany. Actually, nodes can be children of both nodes (VRML 1.0 style, then Children and ParentNodes is used) or fields (TMFNode or TSFNode, in VRML 2.0 style; then ParentFields is used). So the node is freed only when it's not referenced by any node and not referenced by any field.

Wazna konwencja : jak widac, rodzic automatycznie martwi sie o swoje dzieci. Natomiast dziecko w swoim Free nie martwi sie o uaktualnienie swoich rodzicow.

Zwracam tez uwage ze RemoveChild wymaga indeksu. Okreslanie dziecka jako children: TVRMLNode jest nie-1-znaczne bo przeciez jeden node moze miec kilka razy to samo dziecko (i w rezultacie, nawiasem mowiac, kazde dziecko moze miec wiele razy tego samego Parenta). A nie chcemy przeciez pomieszac sobie kolejnosci w Children (ona determinuje przeciez kolejnosc przegladania grafu, a wiec Renderowania itp.) (Natomiast mozemy sobie pozwolic i nieraz pozwalamy na ew. pomieszanie kolejnosci w ParentNodes; inaczej musielibysmy z kazdym ParentNodes pamietac swoj index na jego liscie). Tak wiec na listach Children i ParentNodes moga byc duplikaty i zdecydowanie nie powinnismy nigdzie niefrasobliwie "czyscic" tych list przez DeleteDuplicates;

You can also replace one children with another by writing to this property, like Children[I] := NewChildren;. This works like a shortcut for RemoveChild(I); AddChild(I, NewChildren);. But 1. it's more efficient; 2. it's safer — if Children[I] is already equal to NewChildren, then first RemoveChild(I); would free this children and following AddChild would be totally wrong.

Public property ParentNodes[i: integer]: TVRMLNode read GetParentNodesItem;

All nodes where this node is referenced as a child. This counts "parents" in the VRML 1.0 / Inventor sense.

Public property ParentFields[Index: Integer]: TVRMLField read GetParentFieldsItem;

This lists all SFNode and MFNode fields where this node is referenced. This is somewhat analogous for ParentNodes, but for VRML 2.0.

ParentFieldsNode is just for your comfort, it returns always appropriate field's ParentNode property value (i.e. (ParentField[Index] as TSFNode).ParentNode or (ParentField[Index] as TMFNode).ParentNode).

Public property ParentFieldsNode[Index: Integer]: TVRMLNode read GetParentFieldsNodeItem;
 
Public property AllowedChildren: boolean read fAllowedChildren default false;

AllowedChildren okresla jakie dzieci moga byc dziecmi tego node'a. Warunek ten bedzie sprawdzany w AddChild wiec nigdy nie uda ci sie dodac node'a ktory nie jest tutaj dozwolony.

ParsingAllowedChildren okresla jakie dzieci moga byc odczytane ze strumienia jako dzieci tego node'a. Chwilowo ma to zastosowanie tylko dla wezlow *Inline ktore w strumieniu nie moze miec zapisanych zadnych dzieci ale laduja swoje inline jako swoje Child. Wiec musza miec ParsingAllowedChildren=[] i AllowedChildren = All.

TODO: jak bedzie mi to potrzebne to zaimplementuje te pola jako tablice TDynVRMLNodeClassArray z dodatkowym polem Any. Taka tablica bedzie pasowala do wszystkiego gdy Any = true, wpp. tylko do wymienionych na niej elementow. Wartosc *AllowedChildren = true tutaj odpowiadac bedzie Any = true tam, *AllowedChildren = false oznacza Any = false i Items=[]. Wartosc Any jest potrzebna zeby na zapas powiedziec : wszystkie node'y sa tu dozwolone, nawet takie o jakich jeszcze nie wie NodesManager - - a jest to przeciez czesta sytuacja.

Naturalnie ParsingAllowedChildren musi sie zawierac w AllowedChildren bo inaczej parsowanie moze wygenerowac wyjatek (wywolujac AddChild z niedozwolonym argumentem). W tej chwili oznacza to tylko ze nie moze byc ParsingAllowedChildren = true i AllowedChildren = false.

Note that in some special cases AllowedChildren and ParsingAllowedChildren values may be changed during object lifetime. Currently, this may concern TVRMLUnknownNode.

Public property ParsingAllowedChildren: boolean read fParsingAllowedChildren default false;
 
Public property NodeName: string read fNodeName write FNodeName;

Name of this node, as defined by VRML "DEF" construct.

NodeName = '' oznacza ze obiekt nie mial zdefiniowanej nazwy.

It's named NodeName, to not confuse this with TVRMLField.Name. (Even though TVRMLField and TVRMLNode classes have nothing in common. TSFNode descends from TVRMLField and contains TVRMLNode instance in it's Value field. Once I wanted to just make TSFNode = TVRMLNode and TVRMLNode descendant of TVRMLField, but this wasn't a good idea: TSFNode may be NULL, but still it has a field name, so it should be nicely represented as TSFNode instance with Value = nil.)

Note that this is writeable property, so you can change NodeName at runtime. Beware that some operations depend that node names don't change during their work: loading and saving nodes from stream (since these operations keep current collection names to read / write VRML DEF / USE statements), searching for nodes by name.

Public property WWWBasePath: string read FWWWBasePath write FWWWBasePath;

WWWBasePath is the base URL path for all URLs in node's fields. This is used by all nodes that get some url (like Texture2 and WWWInline in VRML 1.0, ImageTexture and Inline in VRML 2.0 etc.).

This way URL's in node's fields may contain relative names. If WWWBasePath doesn't begin with <proto>:// it is understood to be a file:// base path.

TODO: chwilowo, poniewaz tylko odwolania do lokalnych plikow sa zaimplementowane, cale to bajanie o URL'ach to tylko mowa "jak kiedys bedzie". Chwilowo WWWBasePath musi byc lokalna sciezka (i to absolutna, bezwzgledna sciezka).

WWWBasePath is set in constructor, and eventually adjusted by various parsing/converting routines (TVRMLNode.Parse, but also potentially other things from Object3dAsVRML). This way we could, if only we would like to, resolve nodes like Inline or ImageTexture immediately after parsing them.

Public property SmartChildren[Index: Integer]: TVRMLNode read GetSmartChildren;
 
Public property Prototypes: TVRMLPrototypeBasesList read FPrototypes;
 
Public property Routes: TVRMLRoutesList read FRoutes;
 
Public property PrototypeInstance: boolean read FPrototypeInstance;

PrototypeInstance = True indicates that this node was created from a non-external prototype instantiation.

Then PrototypeInstanceSourceNode is non-nil and indicates parsed prototype node (and PrototypeInstanceSourceNode.Prototype gives you even a link to the actual prototype specification).

PrototypeInstanceSourceNode is used for events: any ROUTEs specified outside of prototype and leading to/from instantiated prototype should actually lead to PrototypeInstanceSourceNode events (not to events of Self). Reason: prototype events may be different than actual expanded node events, and ROUTEs want to lead to prototype events. This is implemented when expanding prototype (TVRMLPrototypeNode.Instantiate) and when linking ROUTE (TVRMLRoute.SetSource, TVRMLRoute.SetDestination).

PrototypeInstanceHelpers may be Nil if empty, or may contain a list of other nodes duplicated along with the main prototype node. From VRML spec:

        Any other nodes and accompanying scene graphs
        are not part of the transformation hierarchy, but may be referenced
        by ROUTE statements or Script nodes in the prototype definition.

TODO: memory leaks are known to be possible in some difficult cases with PrototypeInstanceHelpers. See e.g. ../../../kambi_vrml_test_suite/vrml_2/warnings/errors/proto_leak.wrl and ../../../kambi_vrml_test_suite/vrml_2/warnings/errors/proto_leak_2.wrl for simple testcases. Reason: PrototypeInstanceHelpers may contain, by DEF statements, links to Self. This causes circular dependency (Self is child of some node on PrototypeInstanceHelpers, but PrototypeInstanceHelpers will be freed only if Self is freed) causing some memory to be left always allocated.

Note that for TVRMLPrototypeNode (within PrototypeInstanceSourceNode) these have a little different meaning: they describe the nested prototype, if any, that was used to create this node. This may happen if the node was expanded from one prototype within another. (Usually, you shouldn't be concerned about this; see TVRMLPrototypeNode.Instantiate implementation comments for gory details about this.)

Public property PrototypeInstanceSourceNode: TVRMLPrototypeNode read FPrototypeInstanceSourceNode;
 
Public property PrototypeInstanceHelpers: TVRMLNode read FPrototypeInstanceHelpers;
 
Public property DefaultContainerField: string read FDefaultContainerField write FDefaultContainerField;

Default value of "containerField" attribute for this node in X3D XML encoding.

Public property ExplicitContainerField: string read FExplicitContainerField write FExplicitContainerField;

Value of "containerField" attribute specified explicitly for this node in X3D XML encoding. This is practically usable (read/write) only by X3D XML reader.

Public property HasInterfaceDeclarations: TVRMLAccessTypes read FHasInterfaceDeclarations write SetHasInterfaceDeclarations default [];

For some special VRML / X3D nodes (like Script, ComposedShader) that allow the definition of additional fields/events within.

In X3D specification this is marked like

  # And any number of:
  fieldType [in]     fieldName
  fieldType [in,out] fieldName    initialValue
  fieldType [out]    fieldName
  fieldType []       fieldName    initialValue

If HasInterfaceDeclarations is not [], then InterfaceDeclarations will be non-nil and parser (classic VRML parser in this unit, X3D XML reader too) will read this from VRML files. Moreover, for each interface declaration, also appropriate field/event will be added to the list of Fields or Events, so fields/events created by interface declarations will function just like other standard fields everywhere.

Public property InterfaceDeclarations: TVRMLInterfaceDeclarationsList read FInterfaceDeclarations;
 
Public property CDataAllowed: boolean read FCDataAllowed write FCDataAllowed;

Does this node allow CDATA section when encoded in XML. See X3D XML encoding specification about "Encapsulating Script node code", instantreality also uses CDATA to encode shader source code within XML file and this seems sensible (following the intention of the spec?).

This is only used to produce eventual warnings when CDATA is encountered. Whether or not CDataAllowed is True, we will parse CDATA anyway into CData value.

This should be set in descendants constructor.

Public property CDataExists: boolean read FCDataExists write FCDataExists;

CDATA section when this node is encoded in XML. See X3D XML encoding specification. When CDataExists = False, CData is always empty.

Public property CData: string read FCData write FCData;
 
Public property DestructionNotifications: TDynNodeDestructionNotificationArray read FDestructionNotifications;

Functions registered here will be called when this TVRMLNode descendant will be destroyed.

Public property ParentEventsProcessor: TObject read FParentEventsProcessor write FParentEventsProcessor;

Events processing object for this node, or Nil if none.

Currently this must always be an instance of TVRMLScene class (although it cannot be declared as such, since TVRMLScene is not known in this unit). It must have ProcessEvents = True while it's set as ParentEventsProcessor.

Note: While it is possble and perfectly fine to have the same VRML node included in more than one TVRMLScene instance, only one of such scenes may have ProcessEvents = True. Otherwise, some things could get unsynchronized, like WorldTime that is also recorded in TVRMLRoute to avoid loops. So a node may inside at most one TVRMLScene with events processing at the same time. So this simple property is enough (no need to change it to something like TVRMLScenesList). This is also the reason why this shouldn't be treated as a "parent TVRMLScene" for arbritrary purposes, it's only for events processing things!


Generated by PasDoc 0.11.0 on 2008-09-12 11:58:37