Title: Introduction to DirectX Programming
1Introduction toDirectX Programming
2References
- DirectX Complete (DirectX 6) (1999)
- Michael Root and James Boer McGraw Hill (C)
- Tricks of the Windows Game Programming Gurus
(1999) - Andre LaMothe SAMS (DirectX 6.0) - (C)
- 3D Game Programming with DirectX 8.0 (2002)
- Clayton E. Crooks Game Development Series-(VB)
- Game Programming All in one (2002)
- Bruno Miguel Teixera De Sousa Game Development
Series (SDK 8.1 in CD included) - (C)
3What is DirectX
- DirectX is a set of components, referred to as
the Foundation, developed to provide
Windows-based programs with high-performance,
real-time access to available hardware on current
computer systems. - DirectX was primarily developed for Windows 95,
but Windows NT 4.0 currently supports a subset of
DirectX. - 2D/3D graphics / multimedia API
- Designed for Game and Multimedia Programming
- Works only on MS Windows platform
- 95/98/ME/2000/XP
- Old version for Windows NT
4Versions of DirectX
- First introduction in 1996
- Goal Easy game programming under MS Windows
- Backward Compatible
- Applications complied with older version should
work with newer version - Current versions
- DirectX 9
- DirectX 8.1b Windows 95/98/ME/2000 (2001)
- Built-in in Windows XP
- DirectX 3 Windows NT 4.0
5Components of DirectX 8.x
- Direct3D
- DirectDraw integrated into Direct3D in DX 8
- DirectShow integrated into Direct3D in DX 8
- DirectSound / DirectMusic
- DirectInput
- DirectPlay
- DirectSetup
6Direct3D 8
- There is no more DirectDraw--only Direct3D
- Historically, graphics in DirectX has been
divided into two parts DirectDraw for 2D
graphics and Direct3D for taking care of the 3D
graphics pipeline. DirectX 8.0 is to merge
DirectDraw and Direct3D into a single entity - Direct3D 8 provides hardware-independent way for
using 3D capabilities of videocards. - Standard 3D transformation pipeline is supported
world matrix, view matrix, projection matrix. - Support for rasterising geometric primitives
points, lines, triangles. high-order primitives
are also supported, but only in hardware way - no
software emulation. - Powerful lighting subsystem materials and
lights. - 3D texturing,
- For details look DirectX 8.1 SDK
7Direct3D 8
- Before DirectX 8.0 Graphics in DirectX were
handled through two differing libraries.
DirectDraw and Direct3D( version 6.0 onwards).
The rendering of graphics in the 2D plane were
the responsibility of the DirectDraw. It
supported bitmaps, lines, pixels, polygons. - After DirectX 8.0 Graphics is now handled in one
lib DirectGraphics. This was many due to the
complaints of the hardware industry about some of
function allowed under the DirectDraw library. - Basic Principles within a window the Basic way
to use DirectX is to create a instance of the
surface object and render directly to this
surface.
8DirectX 8.1 SDK
http//www.microsoft.com/directx
- First of all for Direct3D to work you need to
download the most recent SDK (Software
Development Kit) from Microsoft. - For the creation of a Direct3D 8 application you
must include all necessary header files into your
program and link your program with all necessary
libraries. - There are two useful header files and two
necessary libraries - d3d8.h     - Header file with core Direct3D8
interfaces declarations. - d3d8.lib   - Library file for linking your
program with Direct3D8 DLL. - d3dx8.h    - Header with some very useful tool
functions and interfaces. -
- d3dx8.lib  - Library for d3dx8.h.
9Demo DirectX 8.1 Samples
- Some of the Advanced features are shown here
Fog
Vertex-Shader
Stencil-Mirror
Lighting
Dolphin
10How DirectX work?
- Almost hardware-level control of all devices
- Through a technology called Component Object
Model (COM) - With a set of drivers and libraries written by
- MS conventions of functions, variable, data
structure - Hardware vendors implement their drivers using
the convention.
11Architecture of DirectX
12The HEL and HAL
- HAL (Hardware Abstraction Layer)
- Device driver from the vendor
- HAL is directly used when an operation is
supported directly by the hardware. - Ex) bitmap rotation (supported by hardware)
- HEL (Hardware Emulation Layer)
- Emulate the operations by software algorithm when
the operation is not supported by the hardware. - Slower than directly using HAL but it works!
- DirectX ? HEL ? HAL ? H/W or
- DirectX ? HAL ? H/W
- Using HEL and HAL is transparent to programmers.
13DirectX v.s. GDI/MCI
Graphic Device interface/Media controller
Interface
14COM(Component Object Model)
- DirectX libraries are implemented as COM
- COM object is a black box performing one or more
tasks - Similar to C objects
- Implemented as DLL
- Strict encapsulation
- Not explicitly loaded
- Supports many programming languages
- C, Delphi, VB, etc
15COM - 1
- COM (Component Object Model)
- New software paradigm
- Similar to how computer chips or Lego blocks
work Plug them together - When a component object is changed
- COM objects can be changed without recompiling
the original program. - Usually COM objects are DLLs (Dynamic Link
Libraries) - Can be downloaded or supplied with the program
16The Component Object Model (COM)
- Most of the DirectX API is composed of objects
and interfaces based on COM. - COM is the foundation of an object-based system
that focuses on reuse of interfaces. It is also
an interface specification from which any number
of interfaces can be built. -
- A DirectX application is built from instances of
COM objects. You can consider an object to be a
black box that represents hardware or data that
you access through interfaces. Commands are sent
to the object through methods of the COM
interface. - For example, the IDirectDraw7GetDisplayMode
method of the IDirectDraw7 interface is called to
get the current display mode of the display
adapter from the DirectDraw object. - Objects can bind to other objects at run time,
and they can use the implementation of interfaces
provided by the other object. If you know that an
object is a COM object and you know which
interfaces that object supports, your application
can determine which services the first object can
perform. One of the methods that all COM objects
inherit, the QueryInterface method, lets you
determine which interfaces an object supports and
creates pointers to these interfaces.
17COM - 2
18COM Object - 1
- A COM Object
- A C class or a set of C classes that
implement a number of interfaces - An interface
- Set of functions
- Used to communicate with the COM object
- Single COM object can have one or more
interfaces. - We may have one or more COM objects.
- All interfaces must be derived from a special
base class called IUnknown.
19The Component Object Model (COM)
- IUnknown interface
- All COM objects support an interface called
IUnknown. This interface provides DirectX with
control of the object's lifetime and the ability
to retrieve other interfaces implemented by the
object. IUnknown has three methods - AddRef increments the object's reference count by
1 when an interface or another application binds
itself to the object. - QueryInterface queries the object about the
features that it supports by requesting pointers
to a specific interface. - Release decrements the object's reference count
by 1. When the count reaches 0, the object is
deallocated. - The AddRef and Release methods maintain an
object's reference count. - For example, if you create a DirectDrawSurface
object, the object's reference count is set to 1.
- Every time a function returns a pointer to an
interface for that object, the function then must
call AddRef through that pointer to increment the
reference count. Match each AddRef call with a
call to Release. Before the pointer can be
destroyed, you must call Release through that
pointer. After an object's reference count
reaches 0, the object is destroyed, and all
interfaces to it become invalid. - The QueryInterface method determines whether an
object supports a specific interface. If an
object supports an interface, QueryInterface
returns a pointer to that interface. You then can
use the methods of that interface to communicate
with the object. If QueryInterface successfully
returns a pointer to an interface, it implicitly
calls AddRef to increment the reference count, so
your application must call Release to decrement
the reference count before destroying the pointer
to the interface.
20COM Object - 2
21QueryInterface( ) - 1
- Used to request a pointer to the interface
functions that you desire. - Request using an interface ID
- Unique number
- 128 bits long 2128 unique numbers
- An interface can request any other interface as
long as they are from the same COM object.
22Retrieving Newer Interfaces
- COM dictates that objects update their
functionality, not by changing the methods within
existing interfaces, but by extending new
interfaces that encompass new features. By
keeping existing interfaces static, an object
built on COM can freely extend its services,
while maintaining compatibility with older
applications. - DirectX components follow this practice. For
example, the DirectDraw component supports
several interfaces to access a DirectDrawSurface
object IDirectDrawSurface, IDirectDrawSurface2,
IDirectDrawSurface3, IDirectDrawSurface4, and
IDirectDrawSurface7. Each version of the
interface supports the methods provided by its
ancestor but adds new methods to support new
features. - If your application does not need these new
features, it does not need to retrieve newer
interfaces. To take advantage of features
provided by a new interface, you must call the
object's IUnknownQueryInterface method,
specifying the globally unique identifier (GUID)
of the interface that you want to retrieve.
Interface GUIDs are declared in the corresponding
header file.
23Retrieving Newer Interfaces
The following example shows how to query for a
new interface // C LPDIRECTDRAW
lpDD1 LPDIRECTDRAW2 lpDD2 ddrval
DirectDrawCreate( NULL, lpDD1, NULL ) if(
FAILED(ddrval)) goto ERROROUT // Query for the
IDirectDraw2 interface. ddrval
lpDD1-gtQueryInterface(IID_IDirectDraw2, (void
)lpDD2) if( FAILED(ddrval)) goto
ERROROUT // Now that you have an IDirectDraw2,
release the original interface. lpDD1-gtRelease()
24QueryInterface( ) - 2
25DirectX7 COM Object
- Many COM objects make up DirectX
- DLLs
- DirectX hides many details about handling COM
objects. - Wrapper functions in libraries.
- DDRAW.LIB, DSOUND.LIB, DINPUT.LIB, DSETUP.LIB,
DPLAYX.LIB, D3DIM.LIB, D3DRM.LIB
26DirectDraw (DX7)
- Rendering and 2D bitmap engine
- Controlling the video display
- All graphics must go through it
- Most important of all DirectX components
27DirectDraw - 2D graphics
- DirectDraw is the component of the DirectX
application programming interface (API) that
allows you to directly manipulate display memory,
the hardware blitter, hardware overlay support,
and flipping surface support. - DirectDraw is a software interface that provides
direct access to display devices while
maintaining compatibility with the Windows
graphics device interface (GDI). It is not a
high-level application programming interface
(API) for graphics. DirectDraw provides a
device-independent way for games and Windows
subsystem software, such as three-dimensional
(3-D) graphics packages and digital video codecs,
to gain access to the features of specific 2D
display devices. - DirectDraw works with a wide variety of display
hardware, ranging from simple SVGA monitors to
advanced hardware implementations that provide
clipping, stretching, and non-RGB colour format
support. The interface is designed so that your
applications can enumerate the capabilities of
the underlying hardware and then use any
supported hardware-accelerated features. Features
that are not implemented in hardware are emulated
by DirectX.
28Architectural Overview of DirectDraw
- Multimedia software requires high-performance
graphics. DirectDraw enables a much higher level
of efficiency and speed in graphics-intensive
applications for Windows than is possible with
GDI, while maintaining device independence.
DirectDraw provides tools to perform such key
tasks as - Manipulating multiple display surfaces
- Accessing the video memory directly
- Page flipping
- Back buffering
- Managing the palette
- Clipping
- The DirectDraw component provides services
through COM-based interfaces. In the most recent
iteration, these interfaces are IDirectDraw7,
IDirectDrawSurface7, IDirectDrawPalette,
IDirectDrawClipper, and IDirectDrawVideoPort.
29What are surfaces
- A surface, or DirectDrawSurface object,
represents a linear area of display memory. A
surface usually resides in the display memory of
the display card, although surfaces can exist in
system memory. Unless specifically instructed
otherwise during the creation of the
DirectDrawSurface object, DirectDraw object will
put the DirectDrawSurface object wherever the
best performance can be achieved given the
requested capabilities. - DirectDrawSurface objects can take advantage of
specialized processors on display cards, not only
to perform certain tasks faster, but to perform
some tasks in parallel with the system CPU. - The IDirectDrawSurface7 interface enables you to
indirectly access memory through blit methods,
such as IDirectDrawSurface7BltFast. The surface
object can provide a device context to the
display that you can use with GDI functions.
Additionally, you can use IDirectDrawSurface7
methods to directly access display memory. For
example, you can use the IDirectDrawSurface7Lock
method to lock the display memory and retrieve
the address corresponding to that surface.
Addresses of display memory might point to
visible frame buffer memory (primary surface) or
to nonvisible buffers (off-screen or overlay
surfaces). Nonvisible buffers usually reside in
display memory, but can be created in system
memory if required by hardware limitations or if
DirectDraw is performing software emulation. In
addition, the IDirectDrawSurface7 interface
extends other methods that you can use to set or
retrieve palettes, or to work with specific types
or surfaces, like flipping chains or overlays.
30Width vs. Pitch
- Although the terms width and pitch are often used
informally, they have very important (and
distinctly different) meanings. As a result, you
should understand the meanings for each, and how
to interpret the values that DirectDraw uses to
describe them.
Pitch values are useful when you are directly
accessing surface memory. For example, after
calling the IDirectDrawSurface7Lock method, the
lpSurface member of the associated DDSURFACEDESC2
structure contains the address of the top-left
pixel of the locked area of the surface, and the
lPitch member is the surface pitch. You access
pixels horizontally by incrementing or
decrementing the surface pointer by the number of
bytes per pixel, and you move up or down by
adding the pitch value to, or subtracting it
from, the current surface pointer.
31Flipping Surfaces
- Any surface in DirectDraw can be constructed as a
flipping surface. A flipping surface is any piece
of memory that can be swapped between a front
buffer and a back buffer. (This construct is
commonly referred to as a flipping chain). Often,
the front buffer is the primary surface, but it
doesn't have to be.
Typically, when you use the DirectDrawSurface7.
Flip method to request a surface flip operation,
the pointers to surface memory for the primary
surface and back buffers are swapped. Flipping is
performed by switching pointers that the display
device uses for referencing memory, not by
copying surface memory. When a flipping chain
contains a primary surface and more than one back
buffer, the pointers are switched in a circular
pattern, as shown in the illustration.
32Blitting to Surfaces
- ... copying pixels from one DirectDraw surface to
another, or from one part of a surface to
another.
When using IDirectDrawSurface7BltFast, you
supply a valid rectangle in the source surface
from which the pixels are to be copied (or NULL
to specify the entire surface), and an
x-coordinate and y-coordinate in the destination
surface. The source rectangle must be able to fit
in the destination surface with its top left
corner at that point, or the call will fail with
a return value of DDERR_INVALIDRECT. BltFast
cannot be used on surfaces that have an attached
clipper. No stretching, mirroring, or other
effects can be performed when using
BltFast. DDSPrimary.BltFast(100, 200, //
Upper left xy of destination DDSOffOne,
// Source surface nil, // Source
rectangle entire surface DDBLTFAST_WAIT
or DDBLTFAST_SRCCOLORKEY )
33Blitting with Scaling
- The Blt method automatically re-scales the source
image to fit the destination rectangle. If
resizing is not your intention, for best
performance you should make sure that your source
and destination rectangles are exactly the same
size - Other Effects
- If you do not require any special effects other
than scaling when using Blt, you can pass NULL
(nil) as the DDBltFx parameter. Otherwise you
can choose among a variety of effects specified
in a DDBLTFX structure. Among these, colour fills
and mirroring are supported by the HEL, so they
are always available. Most other effects depend
on hardware support. - DDBLTFX_ARITHSTRETCHY - Uses arithmetic
stretching along the y-axis for this blit. - DDBLTFX_MIRRORLEFTRIGHT - Turns the surface on
its y-axis. - DDBLTFX_MIRRORUPDOWN - Turns the surface on its
x-axis. - DDBLTFX_ROTATE180/270/90 - Rotates the surface
180/270/90 degrees clockwise. - .. etc.
34Example Create a Device with D3D
- To use Direct3D, you first create an application
window, then you create and initialise Direct3D
objects. You use the COM interfaces that these
objects implement to manipulate them and to
create other objects required to render a scene. - The CreateDevice example illustrates these tasks
by creating a Direct3D device and rendering a
blue screen. - We apply the following steps to initialise
Direct3D, render a scene, and eventually shut
down. - Step 1 Creating a Window
- Step 2 Initialising Direct3D
- Step 3 Handling System Messages
- Step 4 Rendering and Displaying a Scene
- Step 5 Shutting Down
35Create a Window
- The first thing any Windows application must do
when it is executed is create an application
window to display to the user. - The following sample code performs window
initialisation. - INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE,
LPSTR, INT ) - // Register the window class.
- WNDCLASSEX wc sizeof(WNDCLASSEX), CS_CLASSDC,
MsgProc, 0L, 0L, GetModuleHandle(NULL), NULL,
NULL, NULL, NULL, "D3D Example", NULL - RegisterClassEx( wc )
- // Create the application's window.
- HWND hWnd CreateWindow( "D3D Example", "D3D
Example 01 CreateDevice", WS_OVERLAPPEDWINDOW,
100, 100, 300, 300, GetDesktopWindow(), NULL,
wc.hInstance, NULL ) - The code is standard Windows programming. The
example starts by defining and registering a
window class called "D3D Example." After the
class is registered, the sample code creates a
basic top-level window that uses the registered
class, with a client area of 300 pixels wide by
300 pixels tall. This window has no menu or child
windows. - The example uses the WS_OVERLAPPEDWINDOW window
style to create a window that includes Minimize,
Maximize, and Close buttons common to windowed
applications. -
- Once the window is created, the code sample calls
standard Microsoft Win32 functions to display and
update the window.
36Initialising Direct3D
- After you create an application window, you are
ready to initialise the Direct3D object that you
will use to render the scene. - This process includes creating a Direct3D object,
setting the presentation parameters, and finally
creating the Direct3D device. - After creating a Direct3D object, you can use the
IDirect3D8CreateDevice method to create a
Direct3D device. You can also use the Direct3D
object to enumerate devices, types, modes, and so
on. - The code fragment below creates a Direct3D object
with the Direct3DCreate8 function. - if( NULL ( g_pD3D Direct3DCreate8(
D3D_SDK_VERSION ) ) ) return E_FAIL - The only parameter passed to Direct3DCreate8
should always be D3D_SDK_VERSION. - This informs Direct3D that the correct header
files are being used. This value is incremented
whenever a header or other change would require
applications to be rebuilt. If the version does
not match, Direct3DCreate8 will fail.
37Initialising Direct3D
- The next step is to retrieve the current display
mode by using the IDirect3D8GetAdapterDisplayMod
e method as shown in the code fragment below. - D3DDISPLAYMODE d3ddm
- if( FAILED( g_pD3D-gtGetAdapterDisplayMode(
D3DADAPTER_DEFAULT, d3ddm ) ) ) return E_FAIL - The Format member of the D3DDISPLAYMODE structure
will be used when creating the Direct3D device.
To run in windowed mode, the Format member is
used to create a back buffer that matches the
adapter's current mode. - By filling in the fields of the
D3DPRESENT_PARAMETERS you can specify how you
want your 3-D application to behave. - D3DPRESENT_PARAMETERS d3dpp
- ZeroMemory( d3dpp, sizeof(d3dpp) )
- d3dpp.Windowed TRUE
- d3dpp.SwapEffect D3DSWAPEFFECT_DISCARD
- d3dpp.BackBufferFormat d3ddm.Format
- The CreateDevice example sets its Windowed member
to TRUE, its SwapEffect member to
D3DSWAPEFFECT_DISCARD, and its BackBufferFormat
member to d3ddm.Format.
38Initialising Direct3D
- The final step is to use the IDirect3D8CreateDev
ice method to create the Direct3D device, as
illustrated in the following code example. - if( FAILED( g_pD3D-gtCreateDevice(
D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING, d3dpp,
g_pd3dDevice ) ) ) - The code example creates the device with the
default adapter by using the D3DADAPTER_DEFAULT
flag. - In most cases, the system will have only a single
adapter, unless it has multiple graphics hardware
cards installed. Indicate that you prefer a
hardware device over a software device by
specifying D3DDEVTYPE_HAL for the DeviceType
parameter. - This code sample uses D3DCREATE_SOFTWARE_VERTEXPRO
CESSING to tell the system to use software vertex
processing. - Note that if you tell the system to use hardware
vertex processing by specifying
D3DCREATE_HARDWARE_VERTEXPROCESSING, you will see
a significant performance gain on video cards
that support hardware vertex processing.
39Handling System Messages
- After you have created the application window and
initialised Direct3D, you are ready to render
the scene. - In most cases, Windows applications monitor
system messages in their message loop, and they
render frames whenever no messages are in the
queue. - However, the CreateDevice example waits until a
WM_PAINT message is in the queue, telling the
application that it needs to redraw all or part
of its window. - // The message loop.
- MSG msg while( GetMessage( msg, NULL, 0, 0 ) )
TranslateMessage( msg ) DispatchMessage( msg
) - Each time the loop runs, DispatchMessage calls
MsgProc, which handles messages in the queue.
When WM_PAINT is queued, the application calls
Render, the application-defined function that
will redraw the window. Then the Microsoft Win32
function ValidateRect is called to validate the
entire client area. - The sample code for the message-handling function
is shown below. - LRESULT WINAPI MsgProc( HWND hWnd, UINT msg,
WPARAM wParam, LPARAM lParam ) - switch( msg )
- case WM_DESTROY PostQuitMessage( 0 )
- return 0
- case WM_PAINT Render()
- ValidateRect( hWnd, NULL ) return 0
- return DefWindowProc( hWnd, msg, wParam, lParam
)
40Rendering and Displaying a Scene
- To render and display the scene, the sample code
in this step clears the back buffer to a blue
colour, transfers the contents of the back buffer
to the front buffer, and presents the front
buffer to the screen. - To clear a scene, call the IDirect3DDevice8Clear
method. - // Clear the back buffer to a blue
- color g_pd3dDevice-gtClear( 0, NULL,
D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0
) - The first two parameters accepted by Clear inform
Direct3D of the size and address of the array of
rectangles to be cleared. The array of rectangles
describes the areas on the render target surface
to be cleared. - In most cases, you use a single rectangle that
covers the entire rendering target. - You do this by setting the first parameter to 0
and the second parameter to NULL. - The third parameter determines the method's
behaviour. You can specify a flag to clear a
render-target surface, an associated depth
buffer, the stencil buffer, or any combination of
the three. - This example does not use a depth buffer, so
D3DCLEAR_TARGET is the only flag used. - The last three parameters are set to reflect
clearing values for the render target, depth
buffer, and stencil buffer. - The CreateDevice example sets the clear colour
for the render target surface to blue
(D3DCOLOR_XRGB(0,0,255)). - The final two parameters are ignored by the Clear
method because the corresponding flags are not
present.
41Rendering and Displaying a Scene
- After clearing the viewport, the CreateDevice
sample project informs Direct3D that rendering
will begin, then it signals that rendering is
complete, as shown in the following code
fragment. - // Begin the scene.
- g_pd3dDevice-gtBeginScene()
- // Rendering of scene objects happens here.
- // End the scene.
- g_pd3dDevice-gtEndScene()
- The IDirect3DDevice8BeginScene and
IDirect3DDevice8EndScene methods signal to the
system when rendering is beginning or is
complete. - You can call rendering methods only between calls
to these methods. Even if rendering methods fail,
you must call EndScene before calling BeginScene
again. - After rendering the scene, you display it by
using the IDirect3DDevice8Present method. - g_pd3dDevice-gtPresent( NULL, NULL, NULL, NULL )
- The first two parameters accepted by Present are
a source rectangle and destination rectangle. The
sample code in this step presents the entire back
buffer to the front buffer by setting these two
parameters to NULL. - The third parameter sets the destination window
for this presentation. Because this parameter is
set to NULL, the hWndDeviceWindow member of
D3DPRESENT_PARAMETERS is used. The fourth
parameter is the DirtyRegion parameter and in
most cases should be set to NULL.
42Shutting Down
- Shutting down a DirectX application not only
means that you destroy the application window,
but you also deallocate any DirectX objects your
application uses, and you invalidate the pointers
to them. - The CreateDevice example calls Cleanup, an
application-defined function to handle this when
it receives a WM_DESTROY message. - VOID Cleanup() if( g_pd3dDevice ! NULL)
g_pd3dDevice-gtRelease() - if( g_pD3D ! NULL) g_pD3D-gtRelease()
- The preceding function deallocates the DirectX
objects it uses by calling the IUnknownRelease
methods for each object. Because this example
follows COM rules, the reference count for most
objects should become zero and should be
automatically removed from memory. - In addition to shutdown, there are times during
normal executionsuch as when the user changes
the desktop resolution or colour depthwhen you
might need to destroy and re-create the Direct3D
objects in use. - Therefore it is a good idea to keep your
application's cleanup code in one place, which
can be called when the need arises.
Game
Example