Class TVRMLFlatSceneGL

DescriptionHierarchyFieldsMethodsProperties

Unit

Declaration

type TVRMLFlatSceneGL = class(TVRMLFLatScene)

Description

This is a descendant of TVRMLFlatScene that makes it easy to render VRML scene into OpenGL. The point is that this class is the final, comfortable utility to deal with VRML files when you want to be able to render them using OpenGL.

This class uses internal TVRMLOpenGLRenderer instance, thus hiding some "cumbersomness" (is it English?) of the interface of TVRMLOpenGLRenderer class. Also this class provides some functionality (like transparency using OpenGL blending) and some optimizations (like using OpenGL's display lists) that couldn't be achieved inside TVRMLOpenGLRenderer class (because they require looking at rendered VRML model as a whole, not only as a separate ShapeNode+State parts). See Render method for more details.

Also this class can provide comfortable management for TBackgroundGL instance associated with this VRML model, that may be used to render VRML's background. See Background function.

Connection with particular OpenGL context: from the 1st call of [Prepare]Render or Background methods to the next call of CloseGL method or the destructor. Everything between must be called within the same OpenGL context active. In particular: remember that if you called Render method at least once, you must destroy this object or at least call it's CloseGL method before releasing OpenGL context (that was active during Render).

Hierarchy

Overview

Methods

Public constructor Create(ARootNode: TVRMLNode; AOwnsRootNode: boolean; AOptimization: TGLRendererOptimization; ACache: TVRMLOpenGLRendererContextCache = nil);
Public constructor CreateProvidedRenderer( ARootNode: TVRMLNode; AOwnsRootNode: boolean; AOptimization: TGLRendererOptimization; AProvidedRenderer: TVRMLOpenGLRenderer);
Public destructor Destroy; override;
Public procedure CloseGL;
Public procedure PrepareRender( TransparentGroups: TTransparentGroups; Options: TPrepareRenderOptions);
Public procedure Render(TestShapeStateVisibility: TTestShapeStateVisibility; TransparentGroup: TTransparentGroup);
Public procedure RenderFrustum(const Frustum: TFrustum; TransparentGroup: TTransparentGroup);
Public procedure RenderFrustumOctree(const Frustum: TFrustum; Octree: TVRMLShapeStateOctree; TransparentGroup: TTransparentGroup); overload;
Public procedure RenderFrustumOctree(const Frustum: TFrustum; TransparentGroup: TTransparentGroup); overload;
Public procedure ChangedAll; override;
Public procedure ChangedShapeStateFields(ShapeStateNum: integer); override;
Public procedure RenderShadowVolume( const LightPos: TVector4Single; const TransformIsIdentity: boolean; const Transform: TMatrix4Single; const LightCap: boolean; const DarkCap: boolean; const AllowSilhouetteOptimization: boolean = true);
Public procedure RenderShadowVolume( ShadowVolumesHelper: TShadowVolumesHelper; const TransformIsIdentity: boolean; const Transform: TMatrix4Single; const AllowSilhouetteOptimization: boolean = true);
Public procedure InitAndRenderShadowVolume( ShadowVolumesHelper: TShadowVolumesHelper; const TransformIsIdentity: boolean; const Transform: TMatrix4Single; const AllowSilhouetteOptimization: boolean = true);
Public procedure PrepareBackground;
Public function Background: TBackgroundGL;
Public function Attributes: TVRMLSceneRenderingAttributes;
Public function CreateHeadLight: TVRMLGLHeadLight;
Public function BumpMappingMethod: TBumpMappingMethod;

Properties

Public property LastRender_RenderedShapeStatesCount: Cardinal read FLastRender_RenderedShapeStatesCount;
Public property LastRender_AllShapeStatesCount: Cardinal read FLastRender_AllShapeStatesCount;
Public property Optimization: TGLRendererOptimization read FOptimization;
Public property BackgroundSkySphereRadius: Single read FBackgroundSkySphereRadius write SetBackgroundSkySphereRadius;
Public property BumpMappingLightPosition: TVector3Single read GetBumpMappingLightPosition write SetBumpMappingLightPosition;
Public property BumpMappingLightAmbientColor: TVector4Single read GetBumpMappingLightAmbientColor write SetBumpMappingLightAmbientColor;
Public property BumpMappingLightDiffuseColor: TVector4Single read GetBumpMappingLightDiffuseColor write SetBumpMappingLightDiffuseColor;

Description

Methods

Public constructor Create(ARootNode: TVRMLNode; AOwnsRootNode: boolean; AOptimization: TGLRendererOptimization; ACache: TVRMLOpenGLRendererContextCache = nil);
 
Public constructor CreateProvidedRenderer( ARootNode: TVRMLNode; AOwnsRootNode: boolean; AOptimization: TGLRendererOptimization; AProvidedRenderer: TVRMLOpenGLRenderer);

A very special constructor, that forces this class to use provided AProvidedRenderer.

Note that this renderer must be created with AttributesClass = TVRMLSceneRenderingAttributes.

Don't use this unless you really know what you're doing! In all normal circumstances you should use normal Create constructor, that will internally create and use internal renderer object. If you use this constructor you will have to understand how internally this class synchronizes itself with underlying Renderer object.

Once again, if you're not sure, then simply don't use this constructor. It's for internal use — namely it's internally used by TVRMLGLAnimation, this way all scenes of the animation share the same renderer which means that they also share the same information about textures and images loaded into OpenGL. And this is crucial for TVRMLGLAnimation, otherwise animation with 100 scenes would load the same texture to OpenGL 100 times.

Public destructor Destroy; override;
 
Public procedure CloseGL;

Destroy any associations of this object with current OpenGL context. For example, releaseany allocated texture or display list names.

Generally speaking, destroys everything that is allocated by PrepareRender([...], []) call. It's harmless to call this method when there are already no associations with current OpenGL context. This is called automatically from the destructor.

Public procedure PrepareRender( TransparentGroups: TTransparentGroups; Options: TPrepareRenderOptions);

This prepares some internal things in this class, making sure that appropriate methods execute as fast as possible. In most cases, it's not strictly required to call this method — most things will be prepared "as needed" anyway. But this means that some calls may sometimes take a long time, e.g. the first Render call will take a long time because it may have to prepare display lists that will be reused in next Render calls. This may cause a strange behavior of the program: rendering of the first frame takes unusually long time (which confuses user, and also makes things like TGLWindow.FpsCompSpeed strange for a short time). So calling this procedure may be desirable. You may want to show to user that "now we're preparing the VRML scene — please wait".

This method ties this object to current OpenGL context. But it doesn't change any OpenGL state or buffers contents (at most, it allocates some texture and display list names).

Parameters
TransparentGroups
specifies for what TransparentGroup value it should prepare rendering resources (usually you only use [tgAll] or only one of [tgTransparent, tgOpaque] — so it would be a waste of resources and time to prepare for every possible TransparentGroup value).
Options
says what additional features (besides rendering) should be prepared to execute fast. See TPrepareRenderOption, the names should be self-explanatory (they refer to appropriate methods of this class).
Public procedure Render(TestShapeStateVisibility: TTestShapeStateVisibility; TransparentGroup: TTransparentGroup);

Renders this VRML scene for OpenGL. This is probably the most important function in this class, usually it is the very reason why this class is used.

It uses internal TVRMLOpenGLRenderer instance. Although this internal object is not accessible to your code, you can get some detailed info about how rendering into OpenGL works by looking at comments in VRMLOpenGLRenderer unit.

Each call to Render renders the scene, roughly executing the same OpenGL commands as would be done by calling following methods of TVRMLOpenGLRenderer instance:

  • RenderBegin

  •   for S := each item of ShapeStates list,
        if (TestShapeStateVisibility is not assigned) or
          (TestShapeStateVisibility returns true for given ShapeState) then
        call Render(S.ShapeNode, S.State)
    

  • RenderEnd

If Optimization = roSceneAsAWhole, TestShapeStateVisibility is ignored (because then rendering call almost always does not have such detailed control over which shapestates are actually rendered). So generally you should think of TestShapeStateVisibility as a way to optimize rendering, by quickly eliminating whole shapestates that you know are not visible (e.g. you know that their BoundingBox is outside current camera frustum).

Don't try to put Render inside OpenGL's display-list, the point is that Render can internally create such display-list and manage it itself. So you don't have to worry about such things. This also means that code using this class doesn't care about complexity of using VRMLOpenGLRenderer (and care only about complexity of using this class, TVRMLFlatSceneGL :) ).

Some additional notes (specific to TVRMLFlatSceneGL.Render, not to the VRMLOpenGLRenderer):

  • glDepthMask, glEnable/Disable(GL_BLEND), glBlendFunc states are controlled in this function. This means that the state of this variables before calling this function does not have any influence on the effect produced by this function - and this means that this function does something like glPushAttrib + initialize those variables to some predetermined values + render everything + glPopAttrib.

    We use these OpenGL variables to implement transparency using OpenGL's blending. Some more notes about blending: what we do here is a standard OpenGL technique : first we render all opaque objects (if TransparentGroup is tgAll or tgOpaque) and then we make depth-buffer read-only and then we render all partially trasparent objects (if TransparentGroup is tgAll or tgTransparent).

    Note that while rendering just everything with tgAll is simple, but it has some important disadvantages if your OnDraw does not consist of only one call to Render. E.g. instead of simple

      Scene.Render(nil, tgAll);

    you have

      Scene1.Render(nil, tgAll);
      Scene2.Render(nil, tgAll);

    The code above it not good if both scenes contain some opaque and some transparent objects. You should always render all opaque objects before all transparent objects. E.g. Scene2 can't have any opaque objects if Scene1 has some of them.

    So that's when TransparentGroups come to use: you can write

      Scene1.Render(nil, tgOpaque);
      Scene2.Render(nil, tgOpaque);
    
      Scene1.Render(nil, tgTransparent);
      Scene2.Render(nil, tgTransparent);

    Note that when Attributes.Blending is False then everything is always opaque, so tgOpaque renders everything and tgTransparent renders nothing.

Public procedure RenderFrustum(const Frustum: TFrustum; TransparentGroup: TTransparentGroup);

This calls Render passing TestShapeStateVisibility that tries to quickly eliminate ShapeStates that are entirely not within Frustum. In other words, this does so-called "frustum culling".

Public procedure RenderFrustumOctree(const Frustum: TFrustum; Octree: TVRMLShapeStateOctree; TransparentGroup: TTransparentGroup); overload;

This is like RenderFrustum but it tries to enumerate visible ShapeStates using given Octree (instead of just testing each ShapeState separately).

This way it may work much faster when you have many ShapeStates.

Note that if Optimization = roSceneAsAWhole this doesn't use Octree, but simply calls Render(nil). That's because when Optimization = roSceneAsAWhole Render always renders the whole scene, ignores TestShapeStateVisibility function, so it's useless (and would waste some time) to analyze the scene with Octree.

Public procedure RenderFrustumOctree(const Frustum: TFrustum; TransparentGroup: TTransparentGroup); overload;

This simply calls RenderFrustumOctree(Frustum, DefaultShapeStareOctree). Be sure that you assigned DefaultShapeStareOctree property before calling this.

Public procedure ChangedAll; override;
 
Public procedure ChangedShapeStateFields(ShapeStateNum: integer); override;
 
Public procedure RenderShadowVolume( const LightPos: TVector4Single; const TransformIsIdentity: boolean; const Transform: TMatrix4Single; const LightCap: boolean; const DarkCap: boolean; const AllowSilhouetteOptimization: boolean = true);

Render shadow volume (sides and caps) of this scene, for shadow volume algorithm.

There are two underlying algorithms here, and their speed difference is very noticeable:

  1. Rendering with AllowSilhouetteOptimization. This is the usual, fast method of rendering shadow volumes.

    This renders shadow quads of silhouette edge. Edges from ManifoldEdges list are used to find silhouette edge. Additionally edges from BorderEdges always produce shadow quads, i.e. we treat them like they would always be silhouette edges.

    The very idea of this optimization is that most edges are in ManifoldEdges and so only real silhouette edges produce shadow quads. In other words, BorderEdges list should not contain too many items. When BorderEdges contains all edges (ManifoldEdges is empty), then this method degenerates to a naive rendering without silhouette optimization. So you should try to make your models as much as possible resembling nice 2-manifolds. Ideally, if your mesh is a number of perfectly closed manifolds, and vertex ordering is consistent, then BorderEdges is empty, and this works perfect.

    Usually, most models are mostly 2-manifold (only the real border edges are, well, in BorderEdges), and this works great.

  2. Without silhouette optimization. If you pass AllowSilhouetteOptimization = False, you explicitly want to use the naive approach that just renders 3 shadow quads for each triangle. This is much slower, and is not adviced... read below for exceptions.

    The only good reason to use this is that silhouette optimization for models that are not perfect 2-manifold (i.e., have some BorderEdges) may show some artifacts. See "VRML engine documentation" on [http://vrmlengine.sourceforge.net/vrml_engine_doc.php], chapter "Shadows", for description and pictures of these artifacts. They are quite unavoidable in any shadow volumes implementation, just like normal ghost shadows.

    While you can avoid these artifacts by turning off AllowSilhouetteOptimization, still it's usually much better to fix your 3D model to be correct 2-manifold.

All shadow quads are generated from scene triangles transformed by Transform. This must be able to correctly detect front and back facing triangles with respect to LightPos, so "LightPos" and "scene transformed by Transform" must be in the same coordinate system. (That's why explicit Transform parameter is needed, you can't get away with simply doing glPush/PopMatrix and glMustMatrix around RenderShadowVolume call.) If TransformIsIdentity then Transform value is ignored and everything works like Transform = identity matrix (and is a little faster in this special case).

This uses TrianglesList(false) and ManifoldEdges and BorderEdges (so you may prefer to prepare it before, e.g. by calling PrepareRender with prShadowVolume included).

LightPos is the light position. LightPos[3] must be 1 (to indicate positional light) or 0 (a directional light).

LightCap and DarkCap say whether you want to cap your shadow volume. LightCap is the cap at the caster position, DarkCap is the cap in infinity. This is needed by z-fail method, you should set them both to True. To be more optimal, you can request LightCap only if z-fail and the caster is inside camera frustum. For directional lights, DarkCap is ignored, since the volume is always closed by a single point in infinity.

For ShadowVolumesHelper version, LightPos, LightCap and DarkCap are already available in ShadowVolumesHelper properties (set by ShadowVolumesHelper.InitFrustumAndLight and ShadowVolumesHelper.InitScene calls).

Faces (both shadow quads and caps) are rendered such that CCW <=> you're looking at it from outside (i.e. it's considered front face of this shadow volume).

All the commands passed to OpenGL by this methods are: glBegin, sequence of glVertex, then glEnd.

Public procedure RenderShadowVolume( ShadowVolumesHelper: TShadowVolumesHelper; const TransformIsIdentity: boolean; const Transform: TMatrix4Single; const AllowSilhouetteOptimization: boolean = true);
 
Public procedure InitAndRenderShadowVolume( ShadowVolumesHelper: TShadowVolumesHelper; const TransformIsIdentity: boolean; const Transform: TMatrix4Single; const AllowSilhouetteOptimization: boolean = true);

A shortcut for ShadowVolumesHelper.InitScene and then RenderShadowVolume. It will calculate current bounding box using Transform, TransformIsIdentity and BoundingBox method.

Public procedure PrepareBackground;

= 1

Public function Background: TBackgroundGL;

Returns TBackgroundGL instance for this scene. Background's properties are based on the attributes of first "Background" VRML node in the RootNode scene (and on his place in scene transformations). They are also based on current value of BackgroundSkySphereRadius. And on the values of Attributes.ColorModulatorSingle/Byte. If there is no "Background" node in VRML scene this function returns nil.

Note: this Background object is managed (automatically created/freed etc.) by this TVRMLFlatSceneGL object but it is NOT used anywhere in this class, e.g. Render does not call Background.Render. If you want to use this Background somehow, you have to do this yourself.

The results of this function are internally cached. Cache is invalidated on such situations as change in RootNode scene, changes to BackgroundSkySphereRadius, CloseGL, Attributes.ColorModulatorSingle/Byte.

PrepareBackground (and PrepareRender(true, ...)) automatically validate this cache.

Remember that this cache is connected with the current OpenGL context. So you HAVE to call CloseGL to disconnent this object from current OpenGL context after you used this function.

Public function Attributes: TVRMLSceneRenderingAttributes;

Rendering attributes.

You are free to change them all at any time. Although note that changing some attributes (the ones defined in base TVRMLRenderingAttributes class) may be a costly operation (next PrepareRender or Render call may need to recalculate some things, some display lists need to be rebuild etc.). So don't change them e.g. every frame. You should use Optimization = roNone if you really have to change attributes every frame.

Note for ColorModulatorSingle/Byte properties: In addition to effects described at TVRMLOpenGLRenderer, they also affect what the TVRMLFlatSceneGL.Background function returns.

Public function CreateHeadLight: TVRMLGLHeadLight;

Creates a headlight, using (if present) KambiHeadLight node defined in this VRML file. You're responsible for freeing this node.

Note that this is not concerned whether you actually should use this headlight (this information usually comes from NavigationInfo.headlight value).

Public function BumpMappingMethod: TBumpMappingMethod;

Which bump mapping method will be used ?

This is decided and controlled internally, based on Attributes.BumpMappingMaximum, Attributes.EnableTextures, and current OpenGL capabilities. So the only use of this function is when you want to report this to user, or for debug purposes etc.

Note that calling this ties us to current OpenGL context.

See also
TVRMLOpenGLRenderer.BumpMappingMethod
This checks Attributes (mainly Attributes.BumpMappingMaximum) and OpenGL context capabilities to check which bump mapping method (if any) should be used.

Properties

Public property LastRender_RenderedShapeStatesCount: Cardinal read FLastRender_RenderedShapeStatesCount;

LastRender_ properties provide you read-only statistics about what happened during last render. For now you can see how many ShapeStates were rendered (i.e. send to OpenGL pipeline) versus all ShapeStates that were available (this is simply copied from ShapeStates.Count).

This way you can see how effective was frustum culling (for RenderFrustum or RenderFrustumOctree) or how effective was your function TestShapeStateVisibility (if you used directly Render). "Effective" in the meaning "effective at eliminating invisible ShapeStates from rendering pipeline".

These are initially equal to zeros. Then they are updated each time you called RenderFrustumOctree or RenderFrustum or Render.

Public property LastRender_AllShapeStatesCount: Cardinal read FLastRender_AllShapeStatesCount;
 
Public property Optimization: TGLRendererOptimization read FOptimization;

Optimization method used to render this model.

This is the only way how you can control internal behavior of this class with regards to OpenGL display lists. You have to decide which method is best, based on expected usage of this model: Are you going to (often) change the model structure at runtime ? Is user going to see the scene usually as a whole, or only small part of it (more precisely, is frustum culling sensible in this case) ?

See VRMLRendererOptimization.TGLRendererOptimization for discussion about various values you can set here.

Currently this is read-only after you created TVRMLFlatSceneGL instance, possibly this will be changed in the future. (But changing this at run-time will remain costly operation anyway.)

Public property BackgroundSkySphereRadius: Single read FBackgroundSkySphereRadius write SetBackgroundSkySphereRadius;
 
Public property BumpMappingLightPosition: TVector3Single read GetBumpMappingLightPosition write SetBumpMappingLightPosition;

Light position used for bump mapping.

This is meaningful only if you enabled bump mapping and we are actually able to use bump mapping (BumpMappingMethod <> bmNone, and BumpMappingMethod is capped by Attributes.BumpMappingMaximum).

You can change this at any time, and we will automatically do everything needed to properly update this on next render. But note that when BumpMappingMethod = one of bmMultiTexAll values, changing BumpMappingLightPosition means that we have to rebuild some resources (display lists etc.). So changing BumpMappingLightPosition becomes really costly operation, unless Optimization = roNone.

In other words: if you plan to change BumpMappingLightPosition really often (I mean, like every frame or such) then make sure that either

But roNone means that you lose some other optimizations, so it may be not desirable... in pratice, it's usually best decision to not update BumpMappingLightPosition too often if BumpMappingMethod = one of bmMultiTexAll.

Public property BumpMappingLightAmbientColor: TVector4Single read GetBumpMappingLightAmbientColor write SetBumpMappingLightAmbientColor;

Ambient color of light used for bump mapping. This property simply controls corresponding property of underlying Renderer instance, see TVRMLOpenGLRenderer.BumpMappingLightAmbientColor.

Public property BumpMappingLightDiffuseColor: TVector4Single read GetBumpMappingLightDiffuseColor write SetBumpMappingLightDiffuseColor;

Diffuse color of light used for bump mapping. This property simply controls corresponding property of underlying Renderer instance, see TVRMLOpenGLRenderer.BumpMappingLightDiffuseColor.


Generated by PasDoc 0.10.0 on 2008-02-25 00:00:41