Nodes and other important bulding blocks of VRML/X3D (prototypes, routes and so on).
This is the central unit for VRML/X3D processing, as VRML/X3D file is basically just a graph of nodes. We represent whole VRML/X3D file by it's root node. This is what we load, save and process in this unit.
Nodes can be loaded or saved from the stream in a classic or XML encoding. For classic encoding we use a lexer in X3DLexer unit. For XML encoding, we use standard FPC DOM unit. Loading and saving of fields (in both encodings) is inside X3DFields unit.
When reading VRML/X3D files, we generally do not change the VRML/X3D graph. So we're able to save exactly the same VRML/X3D graph back to another file. See also [http://castle-engine.sourceforge.net/vrml_engine_doc/output/xsl/html/section.writing_vrml.html#section.vrml_preserving]. This allows writing various VRML/X3D processing tools, that can simply read the file, change whatever they want, and write the file back — knowing that the "untouched" parts of graph are preserved perfectly.
TX3DNode class offers a lot of methods to process VRML/X3D graph. See TX3DNode.Traverse, TX3DNode.EnumerateNodes and TX3DNode.FindNode. TX3DNode.Traverse is especially important, as it walks through VRML/X3D graph just as the specification says (accumulating transformation, visiting only active children of nodes like Switch or LOD), gathering some state (useful especially for VRML 1.0, but also used for various things in later VRML/X3D versions).
When you want to render VRML/X3D graph, you can just traverse the graph and render each geometry node (TAbstractGeometryNode instance) knowing it's state (that will contain transformation and such). Alternatively, simple renderer can also use TAbstractGeometryNode.Triangulate.
TAbstractGeometryNode is an important descendant of TX3DNode, as it defines stuff actually visible in the 3D world. It has useful routines for calculating bounding volumes, triangulating and such.
This unit doesn't depend on OpenGL, or any other particular rendering method. So it's suitable also for CastleRayTracer, and every other possible renderer that will ever get implemented.
Your own units can define new VRML/X3D nodes, by declaring new classes descending from TX3DNode (or other, more specialized, descendant). You should register your new classes by calling NodesManager.RegisterNodeClasses.
Examples of defining your own VRML/X3D node types (without modifying sources of this unit, or any other unit) are for example in the X3DBezierCurve unit in bezier_curves demo, and LevelUnit in malfunction.
Node class names, and inheritance:
Normal VRML/X3D nodes are defined by classses named like TXxxNode. These nodes can be specified inside the VRML/X3D files. See VRML/X3D specifications, and also our extensions specification, on [http://castle-engine.sourceforge.net/vrml_x3d.php].
There are also abstract node classes. Their definitions are helpful for handling some functionality common to many descendants, and to declare allowed children in SFNode/MFNode fields. Abstract node classes are named like TAbstractXxxNode. Some of the abstract nodes are also defined by X3D specification, and some of them are just our own inventions.
Finally, there are some special-purpose node classes that play important role in our VRML/X3D organization. They are not abstract, but also their exact instances are not created under normal circumstances. These are named like TX3DXxxNode, currently these are only: TX3DNode, TX3DRootNode, TX3DUnknownNode, TX3DPrototypeNode.
All node classes descend from the base TX3DNode class.
Some abstract nodes have also Pascal interfaces, like IAbstractXxxNode. Some ideas of X3D specification (although not many) need multiple inheritance, so interfaces have to be used. They all descend from IX3DNode.
Optional suffix _1 or _2 at the node class name indicates that this is only for a specific VRML/X3D standard version. Suffix _1 indicates nodes specific to VRML 1.0. Suffix _2 indicates nodes specific to VRML 2.0 (aka 97), that are not available in X3D. Latest X3D nodes do not have any suffix (to not clutter the source code that simply wants to use the latest and best version of the standard).
Note that structures in this unit are not focused on either VRML 1.0 or VRML >= 2.0. On the contrary: we try to handle the sum of all VRML and X3D. When reading VRML 1.0, many VRML 2.0 constructs (that do not conflict with anything in VRML 1.0) are allowed, and the other way around too.
Files organization: X3D nodes are inside x3d_COMPONET_NAME.inc files. This way X3D specification components provide a natural way to group the vast number of nodes into files. Some remaining nodes that are not part of X3D are in other x3dnodes_xxx.inc files, for example x3dnodes_1.inc contains only VRML 1.0-specific nodes.
Load VRML/X3D file in classic encoding from stream. Return it's root node.
Note that you must pass here TPeekCharStream class, not just any generic TStream class. But it's not a problem, really, because you can wrap any class inside TPeekCharStream descendant. E.g. do
Note that this function can't handle compressed data (VRML files are sometimes compressed with gzip). You should already pass here a stream with uncompressed text data.
Load VRML/X3D file in classic encoding, return it's root node.
Automatically handles VRML/X3D files compressed with gzip, uncompressing them on the fly.
URL without any protocol is treated as a filename (absolute or relative to current dir). We can also load network URLs using CastleDownload.
If AllowStdIn then URL = '-' is understood as "standard input" (StdInStream). Base for resolving relative URLs in this case is the current working directory.
If Gzipped then we can be relatively sure that stream is gzipped. Otherwise, it can still be gzipped (.wrl files are sometimes gzipped without extension indicating it).
function LoadX3DXml(const URL: string; Gzipped: boolean): TX3DRootNode;
Read X3D encoded in XML, and convert it to VRML/X3D nodes graph.
Overloaded version that takes Stream as a parameter expects that reading the stream returns the uncompressed content (no longer gzip compressed). This version also takes URL as a parameter, but it is not used to load contents (these are inside Stream), it it only used to resolve relative URLs inside content and for error messages.
function LoadX3DXml(Stream: TStream; const URL: string): TX3DRootNode;
Write VRML/X3D model to a file. Generates a complete file, with header lines (XML headers, or #VRML / #X3D in classic encoding) and everything.
Overloaded version that takes an URL will automatically compress the file with gzip if an extension indicating gzip compression is present (like .x3dv.gz, or .x3d.gz).
Generator and Source, if not empty, will be used to set appropriate META values of X3D root file node. This way you can indicate inside X3D file the generator (your program) name and source (original 3D model file name). If this is not an X3D root file node, we will record it inside a comment.
When ForceConvertingToX3D or when Encoding <> xeClassic, the node graph will be converted to X3D (if it isn't X3D already). This can be used to convert VRML 2.0 to X3D. For VRML 1.0 or Inventor, this will just save them in X3D (XML or classic) encoding, which is not really usable. We simply don't implement VRML 1.0/Inventor convertion (it's difficult, and not worth the trouble, VRML 1.0/Inventor are really old).
Which VRML/X3D specification version should be used to encode. It should be calculated by Save3DVersion(Node). You often want to calculate it yourself, before calling Save3D, this way you can propose a sensible saved model extension for user (.wrl for VRML <= 2.0 in classic encoding, .x3dv for X3D in classic encoding).
Note that this parameter is not a mechanism to convert between various VRML/X3D versions. This procedure does not convert VRML/X3D nodes/fields inside. For example, you can't just change Version.Major value from 1 to 3 to convert VRML 1.0 to X3D. It would cause some information to be encoded in X3D style (e.g. generated file will have X3D header and PROFILE information), but the nodes would stil have VRML 1.0 names and fields. Which means that you would not generate correct X3D file.
So this should really be calculated based on model contents, usually by Save3DVersion(Node).
A limited VRML 2.0 convertion is possible by ForceConvertingToX3D = true. In this case, Version will be automatically changed to X3D anyway.
Free all unused VRML/X3D nodes on the list, then free and Nil the list itself.
function KeyRange(Key: TSingleList; const Fraction: Single; out T: Single): Integer;
Find a range within "key" field corresponding to given Fraction. Returns the index of right range delimiter. So for normal ranges (between two values of "key" field) it's always between 1 and FdKey.Count - 1. Result 0 indicates we're before the left limit, and result equal FdKey.Count indicates we're after right limit.
Result is always between 0 and FdKey.Count.
Output T is the value between 0..1 indicating where within the range we are. It's undefined when Result is 0 or Key.Count (indicating we're outside limits).
Free TX3DNode if it is unused (see TX3DNode.FreeIfUnused), setting reference to Nil. Analogous to standard FreeAndNil, but checks if node is used first.
Font family that can be specified by FontStyle node in family field. First three fields are equal (after casting by Ord) to three values of FSFAMILY_* constants.
Values
ffSerif:
ffSans:
ffTypeWriter:
TX3DFontJustify = (...);
Font justification that can be specified by FontStyle in justify/justification field. First three fields are equal (after casting by Ord) to JUSTIFICATION_* constants.
I constructed URNs below looking at examples in the RFC, annotated by a funny line "The following examples are not guaranteed to be real. They are presented for pedagogical reasons only."
Nodes manager instance. In normal circumstances, this is the only instance of TNodesManager class ever created. It is created / destroyed in this unit's initialization / finalization.
WarnAboutAbsoluteFilenames: boolean = true;
Should be make a warning (using OnWarning) when loading data from an URI with absolute path. This is quite useful if you want to be able to move/copy the files to some other system/location, as absolute paths prevent it.
Detail_QuadricSlices: Cardinal = 30;
Quadric triangulation settings.
Slices divide the circumference of the circle, like a slices of pizza. Stacks divide the height of the object, like stacks of a cake or tower. The precise meaning of slices and stacks parameters follows exactly the OpenGL Utility (GLU) functions (although our implementation doesn't use GLU).
Note that the cylinder, cone, sphere and disk slices must match, otherwise artifacts will appear when you try to connect a sphere with a cylinder cap. Stacks and RectDivisions do not really have to match, but still it's sensible.
Rectangles (used for Cube sides) are also subdivided, for better Gouraud shading. We use Detail_RectDivisions + 1 columns and rows, so we render (Detail_RectDivisions + 1)ˆ2 quads for each cube side.