This component defines the basic properties of geometric objects, and nodes to render triangles, lines and points. See Geometry3D component for more comfortable geometric objects like polygons and spheres.
Contents:
Most of the nodes in this section have both indexed (like TIndexedTriangleSetNode
) and non-indexed (like TTriangleSetNode
) versions.
The "indexed" versions allow to refer to the same vertex (on the Coord
list) multiple times, since you specify the vertex once in TCoordinateNode
, and then can use the same index multiple times e.g. in the array you pass to TIndexedTriangleSetNode.SetIndex
.
If you have a large mesh that has vertexes shared by neighboring polygons, it usually makes sense to use "indexed" versions. They will be slightly more optimal — the vertex is specified once, some calculations can run on it just once (even if it is then used by multiple polygons). This is often a natural representation of a triangle set when generated by 3D authoring tools like Blender.
Otherwise, for smaller meshes, and meshes that would not reuse the vertexes anyway (because they specify mostly unconnected triangles), you can use non-indexed versions.
Most of geometries created "by code" usually qualify as "smaller meshes" :) So just use whatever is most natural, which is usually non-indexed versions. It probably doesn't make sense to even think about optimizing when vertex count < 100.
Coordinate
(Pascal API: TCoordinateNode
),
Color
(Pascal API: TColorNode
),
ColorRGBA
(Pascal API: TColorRGBANode
),
Normal
(Pascal API: TNormalNode
)
These nodes provide information about positions/colors/normal vectors
to various geometry nodes. They are used e.g. by various
nodes in this component (like IndexedTriangleSet
(Pascal API: TIndexedTriangleSetNode
)),
and in Geometry3D component
(like IndexedFaceSet
(Pascal API: TIndexedFaceSetNode
)).
Triangles:
IndexedTriangleSet
(Pascal API: TIndexedTriangleSetNode
),
TriangleSet
(Pascal API: TTriangleSetNode
),
IndexedTriangleFanSet
(Pascal API: TIndexedTriangleFanSetNode
),
TriangleFanSet
(Pascal API: TTriangleFanSetNode
),
IndexedTriangleStripSet
(Pascal API: TIndexedTriangleStripSetNode
),
TriangleStripSet
(Pascal API: TTriangleStripSetNode
)
Notes:
If you're looking for quads,
use instead IndexedQuadSet
, QuadSet
from CAD geometry component.
If you're looking for polygons (with arbitrary number of vertexes for each face),
use instead IndexedFaceSet
from
Geometry3D component.
The geometry nodes have many fields to specify
positions, and various per-vertex information (normal vectors,
colors, attrib
with per-vertex attributes for shaders,
fogCoord
with per-vertex fog depth).
The TriangleFanSet
and TriangleStripSet
nodes
have a special limitation: if you will use colors
(colors are always per-vertex on these primitives,
according to X3D spec) and request generation of per-face normals
at the same time, and the shape is lit (has material),
then shading results will be slightly incorrect.
Like this:
#X3D V3.0 utf8 PROFILE Interchange Shape { appearance Appearance { material Material { } } geometry TriangleFanSet { coord Coordinate { point [ 0 0 0, 1 0 0, 1 1 0, 0.5 1.5 0.5 ] } fanCount 4 color Color { color [ 1 0 0, 0 1 0, 0 0 1, 1 1 1 ] } normalPerVertex FALSE } }
Unfortunately, this is unfixable without falling back to
worse rendering methods (instead of using triangle fan/strip on GPU).
Shading has to be smooth to interpolate
per-vertex colors, and at the same time the same vertex may require
different normals on different faces.
One day we will implement a special fallback for this case
(to internally convert fans and strips
to IndexedTriangleSet
in such special case).
X3D specification doesn't specify what to do
for triangle/quad sets when appearance specify a texture but no
texCoord
is given.
Our engine currently takes the IndexedFaceSet
approach for
automatic generation of texture coords in this case.
Lines:
LineSet
(Pascal API: TLineSetNode
),
IndexedLineSet
(Pascal API: TIndexedLineSetNode
)
Points:
PointSet
(Pascal API: TPointSetNode
)
Using PointSet
is the fastest and easiest way to render points.
See an example X3D file. You can write the points in an X3D file or define the nodes using Pascal code, as always, see an example how to build X3D graph in Pascal.
To control the size of the point,
set Scene.RenderOptions.PointSize
.
This uses OpenGL(ES) rendering of points. So:
It's really fast (milions of points are not a problem, when placed in one PointSet).
There are GPU-specific (OpenGL-implementation specific, actually) limits on the possible point sizes. For desktop OpenGL, small sizes like 1-8 are usually supported, but (strictly speaking) only size 1 is absolutely guaranteed to be supported (see GL_POINT_SIZE_RANGE). For mobile OpenGLES, controlling the point size is not possible (so it's always like 1).
Using anti-aliasing affects how the large points look (as a rect or circle). See glPointSize docs.
This is the fastest method to render points (fastest to use, and fastest to render) but you need to watch for the limitations mentioned above.
Note: To display a number of 2D points, you can also use DrawPrimitive2D
procedure with Mode
= pmPoints
. It has a parameter where you specify PointSize
. Note that this also uses native OpenGL(ES) points, so controlling the point size is limited, just like noted above.
ClipPlane
(Pascal API: TClipPlaneNode
)
This node allows to clip the geometry by an arbitrary plane in 3D space. In effect, the geometry is "cut" in the middle, with only one part visible.
Notes:
There is a limit on the number of clipping planes that may be enabled at the same time on a single shape. This limit is at least six (see view3dscene "Help -> OpenGL Information", look at "Max clip planes" line, to know the limit of your GPU). Following X3D spec, we treat the "more global" clipping planes as more important.
Clipping planes are a purely visual effect. The clipped (invisible) geometry is still taken into account for collision detection, mouse picking, bounding volume calculation etc.
Clipping planes don't affect background nodes
(X3DBackgroundNode
).
This is an example how to construct in Pascal a scene with
points in a PointSet
and render them or save to file:
{ Show points using TPointSetNode. } uses CastleVectors, CastleWindow, X3DNodes, X3DLoad, CastleScene, CastleViewport, CastleCameras; var PointSet: TPointSetNode; PointCoordinates: TCoordinateNode; PointShape: TShapeNode; Root: TX3DRootNode; Window: TCastleWindow; Scene: TCastleScene; Viewport: TCastleViewport; begin Window := TCastleWindow.Create(Application); Window.Open; Viewport := TCastleViewport.Create(Application); Viewport.FullSize := true; Viewport.AutoCamera := true; Window.Controls.InsertFront(Viewport); Viewport.InsertFront(TCastleExamineNavigation.Create(Application)); PointCoordinates := TCoordinateNode.Create; PointCoordinates.SetPoint( [Vector3(0, 0, 0), Vector3(0, 1, 0), Vector3(1, 1, 0), Vector3(1, 0, 0), Vector3(0.5, 0.5, 0) ]); PointSet := TPointSetNode.Create; PointSet.Coord := PointCoordinates; PointShape := TShapeNode.Create; PointShape.Geometry := PointSet; Root := TX3DRootNode.Create; Root.AddChildren(PointShape); { You can now save Root to X3D file, or render Root (loading it to Scene, adding Scene to TCastleViewport). Or both, as the example below shows. Notes about memory management: - the PointCoordinates are owned by PointSet, - PointSet is owned by PointShape, - PointShape is owned by Root, - Root is owned by Scene (because we call Scene.Load with 2nd parameter "true"), - and Scene is owned by Application. So in this example, everything will be automatically freed when Application is freed (which happens in CastleWindow unit finalization). If you would not call "Scene.Load(Root, true)", then you should free Root manually at the end, by "FreeAndNil(Root)". } SaveNode(Root, 'my_points.x3d'); Scene := TCastleScene.Create(Application); Scene.Load(Root, true); Scene.PreciseCollisions := true; Scene.RenderOptions.PointSize := 10; Viewport.Items.Add(Scene); Application.Run; end.
This is an example how to construct in Pascal a scene with TriangleSet
:
{ Build shape from triangles using TTriangleSetNode. } uses CastleWindow, CastleViewport, CastleScene, X3DNodes, CastleFilesUtils, CastleColors, CastleVectors, CastleTimeUtils, CastleSceneCore; var Scene: TCastleScene; function BuildScene: TX3DRootNode; var Shape: TShapeNode; Geometry: TTriangleSetNode; Material: TPhysicalMaterialNode; Coord: TCoordinateNode; begin Coord := TCoordinateNode.Create; // for TTriangleSetNode, each 3 points define a triangle Coord.SetPoint([ Vector3(-1, -1, 0), Vector3( 1, -1, 0), Vector3( 1, 1, 0), Vector3(-1, 2, 0), Vector3( 1, 2, 0), Vector3( 1, 4, 0) ]); Geometry := TTriangleSetNode.CreateWithShape(Shape); Geometry.Solid := false; // see them from both sides Geometry.Coord := Coord; Material := TPhysicalMaterialNode.Create; Material.BaseColor := YellowRGB; Shape.Appearance := TAppearanceNode.Create; Shape.Appearance.Material := Material; Result := TX3DRootNode.Create; Result.AddChildren(Shape); end; var Window: TCastleWindow; Viewport: TCastleViewport; Headlight: TCastleDirectionalLight; begin Window := TCastleWindow.Create(Application); Window.Open; Headlight := TCastleDirectionalLight.Create(Application); Headlight.Intensity := 10; Viewport := TCastleViewport.Create(Application); Viewport.FullSize := true; Viewport.Camera.SetView( Vector3(0, 0, 10), // position Vector3(0, 0, -1), // look direction Vector3(0, 1, 0) // up ); // add a headlight Viewport.Camera.Add(Headlight); Window.Controls.InsertFront(Viewport); Scene := TCastleScene.Create(Application); Scene.Load(BuildScene, true); Viewport.Items.Add(Scene); Application.Run; end.
This is an example how to construct in Pascal a scene with TriangleFanSet
:
{ Build shape from triangles using TTriangleFanSetNode. } uses CastleWindow, CastleViewport, CastleScene, X3DNodes, CastleFilesUtils, CastleColors, CastleVectors, CastleTimeUtils, CastleSceneCore; var Scene: TCastleScene; function BuildScene: TX3DRootNode; var Shape: TShapeNode; Geometry: TTriangleFanSetNode; Material: TPhysicalMaterialNode; Coord: TCoordinateNode; begin Coord := TCoordinateNode.Create; Coord.SetPoint([ // 1st fan Vector3(-1, -1, 0), Vector3( 1, -1, 0), Vector3( 1, 1, 0), // 1st triangle of 1st fan ends here Vector3(0.6, 1, 0), // 2nd triangle of 1st fan ends here Vector3(0.2, 1, 0), // 3rd triangle of 1st fan ends here Vector3(-0.2, 1, 0), // 4th triangle of 1st fan ends here // 2nd fan Vector3(-1, 2, 0), Vector3( 1, 2, 0), Vector3( 1, 4, 0), // 1st triangle of 2nd fan ends here Vector3(0.6, 4, 0), // 2nd triangle of 2nd fan ends here Vector3(0.2, 4, 0), // 3rd triangle of 2nd fan ends here Vector3(-0.2, 4, 0) // 4th triangle of 2nd fan ends here ]); Geometry := TTriangleFanSetNode.CreateWithShape(Shape); Geometry.Solid := false; // see them from both sides Geometry.Coord := Coord; // define 2 fans Geometry.SetFanCount([6, 6]); Material := TPhysicalMaterialNode.Create; Material.BaseColor := YellowRGB; Shape.Appearance := TAppearanceNode.Create; Shape.Appearance.Material := Material; Result := TX3DRootNode.Create; Result.AddChildren(Shape); end; var Window: TCastleWindow; Viewport: TCastleViewport; Headlight: TCastleDirectionalLight; begin Window := TCastleWindow.Create(Application); Window.Open; Headlight := TCastleDirectionalLight.Create(Application); Headlight.Intensity := 10; Viewport := TCastleViewport.Create(Application); Viewport.FullSize := true; Viewport.Camera.SetView( Vector3(0, 0, 10), // position Vector3(0, 0, -1), // look direction Vector3(0, 1, 0) // up ); // add a headlight Viewport.Camera.Add(Headlight); Window.Controls.InsertFront(Viewport); Scene := TCastleScene.Create(Application); Scene.Load(BuildScene, true); // makes it easier to see triangle Scene.RenderOptions.WireframeEffect := weSolidWireframe; Viewport.Items.Add(Scene); Application.Run; end.