Looking for code samples for Direct3D tessellation - direct3d
I am trying to learn how to use the Direct3D function D3DXTessellateRectPatch:
msdn.microsoft.com/en-us/library/bb205471(v=VS.85).aspx
I have found the MSDN documentation quite useful and have been able to implement tessellation of a
single rectangle patch.
I am now trying to tesselate a mesh that consists of thirty two bicubic Bezier 3D patches (the Utah teapot).
I have tried a simple approach - tesselate each Bezier curve individually, then join the vertices and
indices appropriately, taking into account vertex offsets, to create a tessellated merged mesh.
However, this does not quite seem to have the desired result.
If anyone has hints on this problem or, even better, code samples, much appreciated.
Specifically, I have checked:
Www.directxtutorial.com
http://www.amazon.com/Introduction-Game-Programming-Direct-9-0c/dp/1598220160/
And another Direct3D reference, as well as Google.
Thank you and look forward to your advice/pointers.
Yours
Misha
Tim C Schroeder has been a huge help and suggested I use ID3DXPatchMesh. Here is some sample code that generates a tessellated teapot (place in file tester.cpp):
// Main D3DX framework from www.directxtutorial.com (free section)
#include <assert.h>
#include <stdio.h>
// include the basic windows header files and the Direct3D header file
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
#include <d3dx9.h>
// define the screen resolution
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
// include the Direct3D Library files
#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")
// global declarations
LPDIRECT3D9 d3d;
LPDIRECT3DDEVICE9 d3ddev;
LPD3DXMESH mesh = NULL; // define the mesh pointer
// function prototypes
void initD3D(HWND hWnd);
void render_frame(void);
void cleanD3D(void);
void init_graphics(void);
struct vertex_data
{
D3DXVECTOR3 position;
DWORD color;
};
#define FVF_VERTEX_DATA (D3DFVF_XYZ | D3DFVF_DIFFUSE)
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HWND hWnd;
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = "WindowClass";
RegisterClassEx(&wc);
hWnd = CreateWindowEx(NULL, "WindowClass", "Our Direct3D Program",
WS_OVERLAPPEDWINDOW, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT,
NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
initD3D(hWnd);
MSG msg;
while(TRUE)
{
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(msg.message == WM_QUIT)
break;
render_frame();
}
cleanD3D();
return msg.wParam;
}
// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc (hWnd, message, wParam, lParam);
}
// this function initializes and prepares Direct3D for use
void initD3D(HWND hWnd)
{
d3d = Direct3DCreate9(D3D_SDK_VERSION);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hWnd;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dpp.BackBufferWidth = SCREEN_WIDTH;
d3dpp.BackBufferHeight = SCREEN_HEIGHT;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
d3d->CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp,
&d3ddev);
init_graphics();
d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE); // turn off the 3D lighting
d3ddev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); // turn off culling
d3ddev->SetRenderState(D3DRS_ZENABLE, TRUE); // turn on the z-buffer
}
// this is the function used to render a single frame
void render_frame(void)
{
d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
d3ddev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
d3ddev->BeginScene();
d3ddev->SetFVF(FVF_VERTEX_DATA);
// set the view transform
D3DXMATRIX matView; // the view transform matrix
D3DXMatrixLookAtLH(&matView,
&D3DXVECTOR3 (0.0f, 8.0f, 25.0f), // the camera position
&D3DXVECTOR3 (0.0f, 0.0f, 0.0f), // the look-at position
&D3DXVECTOR3 (0.0f, 1.0f, 0.0f)); // the up direction
d3ddev->SetTransform(D3DTS_VIEW, &matView); // set the view transform to matView
// set the projection transform
D3DXMATRIX matProjection; // the projection transform matrix
D3DXMatrixPerspectiveFovLH(&matProjection,
D3DXToRadian(45), // the horizontal field of view
(FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT, // aspect ratio
1.0f, // the near view-plane
100.0f); // the far view-plane
d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection); // set the projection
// set the world transform
static float index = 0.0f; index+=0.03f; // an ever-increasing float value
D3DXMATRIX matRotateY; // a matrix to store the rotation for each triangle
D3DXMatrixRotationY(&matRotateY, index); // the rotation matrix
d3ddev->SetTransform(D3DTS_WORLD, &(matRotateY)); // set the world transform
if (mesh) mesh->DrawSubset(0);
d3ddev->EndScene();
d3ddev->Present(NULL, NULL, NULL, NULL);
}
// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
if (mesh) mesh->Release();
d3ddev->Release();
d3d->Release();
}
#define MAX_PATCHES 1000
#define POINTS_PER_PATCH 16
int num_patches = -1;
int patches[MAX_PATCHES][POINTS_PER_PATCH];
void B_patch(int ii, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n, int o, int p)
{
assert(ii < MAX_PATCHES);
patches[ii][0] = a-1;
patches[ii][1] = b-1;
patches[ii][2] = c-1;
patches[ii][3] = d-1;
patches[ii][4] = e-1;
patches[ii][5] = f-1;
patches[ii][6] = g-1;
patches[ii][7] = h-1;
patches[ii][8] = i-1;
patches[ii][9] = j-1;
patches[ii][10] = k-1;
patches[ii][11] = l-1;
patches[ii][12] = m-1;
patches[ii][13] = n-1;
patches[ii][14] = o-1;
patches[ii][15] = p-1;
assert(POINTS_PER_PATCH==16);
}
#define MAX_POINTS 1000
int num_points = -1;
D3DXVECTOR3 points[MAX_POINTS];
void B_point(int ii, double x, double y, double z)
{
ii--;
assert(ii < MAX_POINTS);
points[ii].x = x;
/*** Y AND Z FLIPPED ***/
points[ii].y = z;
points[ii].z = y;
}
// BEGIN http://www.sjbaker.org/teapot/teaset.tgz
/*
* The file input.c -- Juhana Kouhia, jk87377#cs.tut.fi, Oct. 25, 1991
*
* Load_patch(filename, patches, vertices);
* char *filename; int *patches, *vertices;
* A sample program to read Bezier patches in.
* Returns count of patches and vertices.
* User defined subroutines:
* B_patch(ii, a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p);
* int ii, a, b, ..., p;
* Defines one Bezier patch with index number ii,
* indexes to points are in a, b, c, ..., p.
* B_point(ii, x, y, z);
* int ii; double x, y, z;
* Defines one point with index number ii.
*/
#include <stdio.h>
// Modified to work with g++
void Load_patch(char *filename, int *patches, int *vertices)
{
int ii;
float x,y,z;
int a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p;
FILE *fp;
if (!(fp = fopen(filename,"r"))) {
fprintf(stderr,"Load_patch: Can't open %s\n",filename);
exit(1);
}
(void)fscanf(fp,"%i\n",patches);
for (ii = 0; ii < *patches; ii++) {
(void)fscanf(fp,"%i, %i, %i, %i,",&a,&b,&c,&d);
(void)fscanf(fp,"%i, %i, %i, %i,",&e,&f,&g,&h);
(void)fscanf(fp,"%i, %i, %i, %i,",&i,&j,&k,&l);
(void)fscanf(fp,"%i, %i, %i, %i\n",&m,&n,&o,&p);
B_patch(ii, a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p);
}
(void)fscanf(fp,"%i\n",vertices);
for (ii = 1; ii <= *vertices; ii++) {
(void)fscanf(fp,"%f, %f, %f\n",&x,&y,&z);
B_point(ii, (double)x,(double)y,(double)z);
}
}
// END http://www.sjbaker.org/teapot/teaset.tgz
// this is the function that puts the 3D models into video RAM
void init_graphics(void)
{
// load patch
char filename[255];
sprintf(filename,"teapot");
Load_patch(filename,&num_patches,&num_points);
printf("Loaded patch %s with %d patches and %d vertices.\n",
filename,num_patches,num_points);
// create declarator from FVF
D3DVERTEXELEMENT9 inDecl[MAX_FVF_DECL_SIZE];
if (!SUCCEEDED(D3DXDeclaratorFromFVF(FVF_VERTEX_DATA,inDecl)))
assert(FALSE);
// create patch mesh
LPD3DXPATCHMESH p_mesh;
D3DXPATCHINFO info;
info.PatchType = D3DXPATCHMESH_RECT;
info.Degree = D3DDEGREE_CUBIC;
info.Basis = D3DBASIS_BEZIER;
if (!SUCCEEDED(D3DXCreatePatchMesh(&info,num_patches,num_points,0,inDecl,d3ddev,&p_mesh)))
assert(FALSE);
assert(p_mesh->GetControlVerticesPerPatch()==POINTS_PER_PATCH);
// copy vertices
LPDIRECT3DVERTEXBUFFER9 v_buffer = NULL;
if (!SUCCEEDED(p_mesh->GetVertexBuffer(&v_buffer)))
assert(FALSE);
struct vertex_data* vertex_data = NULL;
DWORD number_of_vertices=p_mesh->GetNumVertices();
assert(number_of_vertices==num_points);
if (!SUCCEEDED(v_buffer->Lock(0,number_of_vertices*sizeof(struct vertex_data),(void **)&vertex_data,D3DLOCK_DISCARD)))
assert(FALSE);
for (int i=0; i<num_points; i++)
{
vertex_data[i].position.x = points[i].x;
vertex_data[i].position.y = points[i].y;
vertex_data[i].position.z = points[i].z;
vertex_data[i].color = D3DCOLOR_XRGB(255,0,0);
}
v_buffer->Unlock();
v_buffer->Release();
// copy indices
LPDIRECT3DINDEXBUFFER9 i_buffer = NULL;
if (!SUCCEEDED(p_mesh->GetIndexBuffer(&i_buffer)))
assert(FALSE);
D3DINDEXBUFFER_DESC i_buffer_desc;
if (!SUCCEEDED(i_buffer->GetDesc(&i_buffer_desc)))
assert(FALSE);
assert(i_buffer_desc.Size==num_patches*POINTS_PER_PATCH*sizeof(WORD));
WORD* index_data = NULL;
if (!SUCCEEDED(i_buffer->Lock(0,0,(void **)&index_data,D3DLOCK_DISCARD)))
assert(FALSE);
int idx=0;
for (int i=0; i<num_patches; i++)
{
for (int j=0; j<POINTS_PER_PATCH; j++)
{
index_data[idx] = patches[i][j];
idx++;
}
}
i_buffer->Unlock();
i_buffer->Release();
// create mesh for tesselation
FLOAT fTessLevel=1.0f;
DWORD Adaptive=FALSE;
DWORD NumTriangles,NumVertices;
if (!SUCCEEDED(p_mesh->GetTessSize(fTessLevel,Adaptive,&NumTriangles,&NumVertices)))
assert(FALSE);
if (!SUCCEEDED(D3DXCreateMeshFVF(NumTriangles,NumVertices,D3DXMESH_MANAGED,FVF_VERTEX_DATA,d3ddev,&mesh)))
assert(FALSE);
// tesselate
assert(Adaptive==FALSE);
if (!SUCCEEDED(p_mesh->Tessellate(fTessLevel,mesh)))
assert(FALSE);
printf("Generated tesselated mesh with %d triangles, %d vertices\n",NumTriangles,NumVertices);
p_mesh->Release();
}
The teapot data (place in file teapot) is (from http://www.sjbaker.org/teapot/teaset.tgz):
32
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
4,17,18,19,8,20,21,22,12,23,24,25,16,26,27,28
19,29,30,31,22,32,33,34,25,35,36,37,28,38,39,40
31,41,42,1,34,43,44,5,37,45,46,9,40,47,48,13
13,14,15,16,49,50,51,52,53,54,55,56,57,58,59,60
16,26,27,28,52,61,62,63,56,64,65,66,60,67,68,69
28,38,39,40,63,70,71,72,66,73,74,75,69,76,77,78
40,47,48,13,72,79,80,49,75,81,82,53,78,83,84,57
57,58,59,60,85,86,87,88,89,90,91,92,93,94,95,96
60,67,68,69,88,97,98,99,92,100,101,102,96,103,104,105
69,76,77,78,99,106,107,108,102,109,110,111,105,112,113,114
78,83,84,57,108,115,116,85,111,117,118,89,114,119,120,93
121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136
124,137,138,121,128,139,140,125,132,141,142,129,136,143,144,133
133,134,135,136,145,146,147,148,149,150,151,152,69,153,154,155
136,143,144,133,148,156,157,145,152,158,159,149,155,160,161,69
162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177
165,178,179,162,169,180,181,166,173,182,183,170,177,184,185,174
174,175,176,177,186,187,188,189,190,191,192,193,194,195,196,197
177,184,185,174,189,198,199,186,193,200,201,190,197,202,203,194
204,204,204,204,207,208,209,210,211,211,211,211,212,213,214,215
204,204,204,204,210,217,218,219,211,211,211,211,215,220,221,222
204,204,204,204,219,224,225,226,211,211,211,211,222,227,228,229
204,204,204,204,226,230,231,207,211,211,211,211,229,232,233,212
212,213,214,215,234,235,236,237,238,239,240,241,242,243,244,245
215,220,221,222,237,246,247,248,241,249,250,251,245,252,253,254
222,227,228,229,248,255,256,257,251,258,259,260,254,261,262,263
229,232,233,212,257,264,265,234,260,266,267,238,263,268,269,242
270,270,270,270,279,280,281,282,275,276,277,278,271,272,273,274
270,270,270,270,282,289,290,291,278,286,287,288,274,283,284,285
270,270,270,270,291,298,299,300,288,295,296,297,285,292,293,294
270,270,270,270,300,305,306,279,297,303,304,275,294,301,302,271
306
1.4,0.0,2.4
1.4,-0.784,2.4
0.784,-1.4,2.4
0.0,-1.4,2.4
1.3375,0.0,2.53125
1.3375,-0.749,2.53125
0.749,-1.3375,2.53125
0.0,-1.3375,2.53125
1.4375,0.0,2.53125
1.4375,-0.805,2.53125
0.805,-1.4375,2.53125
0.0,-1.4375,2.53125
1.5,0.0,2.4
1.5,-0.84,2.4
0.84,-1.5,2.4
0.0,-1.5,2.4
-0.784,-1.4,2.4
-1.4,-0.784,2.4
-1.4,0.0,2.4
-0.749,-1.3375,2.53125
-1.3375,-0.749,2.53125
-1.3375,0.0,2.53125
-0.805,-1.4375,2.53125
-1.4375,-0.805,2.53125
-1.4375,0.0,2.53125
-0.84,-1.5,2.4
-1.5,-0.84,2.4
-1.5,0.0,2.4
-1.4,0.784,2.4
-0.784,1.4,2.4
0.0,1.4,2.4
-1.3375,0.749,2.53125
-0.749,1.3375,2.53125
0.0,1.3375,2.53125
-1.4375,0.805,2.53125
-0.805,1.4375,2.53125
0.0,1.4375,2.53125
-1.5,0.84,2.4
-0.84,1.5,2.4
0.0,1.5,2.4
0.784,1.4,2.4
1.4,0.784,2.4
0.749,1.3375,2.53125
1.3375,0.749,2.53125
0.805,1.4375,2.53125
1.4375,0.805,2.53125
0.84,1.5,2.4
1.5,0.84,2.4
1.75,0.0,1.875
1.75,-0.98,1.875
0.98,-1.75,1.875
0.0,-1.75,1.875
2.0,0.0,1.35
2.0,-1.12,1.35
1.12,-2.0,1.35
0.0,-2.0,1.35
2.0,0.0,0.9
2.0,-1.12,0.9
1.12,-2.0,0.9
0.0,-2.0,0.9
-0.98,-1.75,1.875
-1.75,-0.98,1.875
-1.75,0.0,1.875
-1.12,-2.0,1.35
-2.0,-1.12,1.35
-2.0,0.0,1.35
-1.12,-2.0,0.9
-2.0,-1.12,0.9
-2.0,0.0,0.9
-1.75,0.98,1.875
-0.98,1.75,1.875
0.0,1.75,1.875
-2.0,1.12,1.35
-1.12,2.0,1.35
0.0,2.0,1.35
-2.0,1.12,0.9
-1.12,2.0,0.9
0.0,2.0,0.9
0.98,1.75,1.875
1.75,0.98,1.875
1.12,2.0,1.35
2.0,1.12,1.35
1.12,2.0,0.9
2.0,1.12,0.9
2.0,0.0,0.45
2.0,-1.12,0.45
1.12,-2.0,0.45
0.0,-2.0,0.45
1.5,0.0,0.225
1.5,-0.84,0.225
0.84,-1.5,0.225
0.0,-1.5,0.225
1.5,0.0,0.15
1.5,-0.84,0.15
0.84,-1.5,0.15
0.0,-1.5,0.15
-1.12,-2.0,0.45
-2.0,-1.12,0.45
-2.0,0.0,0.45
-0.84,-1.5,0.225
-1.5,-0.84,0.225
-1.5,0.0,0.225
-0.84,-1.5,0.15
-1.5,-0.84,0.15
-1.5,0.0,0.15
-2.0,1.12,0.45
-1.12,2.0,0.45
0.0,2.0,0.45
-1.5,0.84,0.225
-0.84,1.5,0.225
0.0,1.5,0.225
-1.5,0.84,0.15
-0.84,1.5,0.15
0.0,1.5,0.15
1.12,2.0,0.45
2.0,1.12,0.45
0.84,1.5,0.225
1.5,0.84,0.225
0.84,1.5,0.15
1.5,0.84,0.15
-1.6,0.0,2.025
-1.6,-0.3,2.025
-1.5,-0.3,2.25
-1.5,0.0,2.25
-2.3,0.0,2.025
-2.3,-0.3,2.025
-2.5,-0.3,2.25
-2.5,0.0,2.25
-2.7,0.0,2.025
-2.7,-0.3,2.025
-3.0,-0.3,2.25
-3.0,0.0,2.25
-2.7,0.0,1.8
-2.7,-0.3,1.8
-3.0,-0.3,1.8
-3.0,0.0,1.8
-1.5,0.3,2.25
-1.6,0.3,2.025
-2.5,0.3,2.25
-2.3,0.3,2.025
-3.0,0.3,2.25
-2.7,0.3,2.025
-3.0,0.3,1.8
-2.7,0.3,1.8
-2.7,0.0,1.575
-2.7,-0.3,1.575
-3.0,-0.3,1.35
-3.0,0.0,1.35
-2.5,0.0,1.125
-2.5,-0.3,1.125
-2.65,-0.3,0.9375
-2.65,0.0,0.9375
-2.0,-0.3,0.9
-1.9,-0.3,0.6
-1.9,0.0,0.6
-3.0,0.3,1.35
-2.7,0.3,1.575
-2.65,0.3,0.9375
-2.5,0.3,1.125
-1.9,0.3,0.6
-2.0,0.3,0.9
1.7,0.0,1.425
1.7,-0.66,1.425
1.7,-0.66,0.6
1.7,0.0,0.6
2.6,0.0,1.425
2.6,-0.66,1.425
3.1,-0.66,0.825
3.1,0.0,0.825
2.3,0.0,2.1
2.3,-0.25,2.1
2.4,-0.25,2.025
2.4,0.0,2.025
2.7,0.0,2.4
2.7,-0.25,2.4
3.3,-0.25,2.4
3.3,0.0,2.4
1.7,0.66,0.6
1.7,0.66,1.425
3.1,0.66,0.825
2.6,0.66,1.425
2.4,0.25,2.025
2.3,0.25,2.1
3.3,0.25,2.4
2.7,0.25,2.4
2.8,0.0,2.475
2.8,-0.25,2.475
3.525,-0.25,2.49375
3.525,0.0,2.49375
2.9,0.0,2.475
2.9,-0.15,2.475
3.45,-0.15,2.5125
3.45,0.0,2.5125
2.8,0.0,2.4
2.8,-0.15,2.4
3.2,-0.15,2.4
3.2,0.0,2.4
3.525,0.25,2.49375
2.8,0.25,2.475
3.45,0.15,2.5125
2.9,0.15,2.475
3.2,0.15,2.4
2.8,0.15,2.4
0.0,0.0,3.15
0.0,-0.002,3.15
0.002,0.0,3.15
0.8,0.0,3.15
0.8,-0.45,3.15
0.45,-0.8,3.15
0.0,-0.8,3.15
0.0,0.0,2.85
0.2,0.0,2.7
0.2,-0.112,2.7
0.112,-0.2,2.7
0.0,-0.2,2.7
-0.002,0.0,3.15
-0.45,-0.8,3.15
-0.8,-0.45,3.15
-0.8,0.0,3.15
-0.112,-0.2,2.7
-0.2,-0.112,2.7
-0.2,0.0,2.7
0.0,0.002,3.15
-0.8,0.45,3.15
-0.45,0.8,3.15
0.0,0.8,3.15
-0.2,0.112,2.7
-0.112,0.2,2.7
0.0,0.2,2.7
0.45,0.8,3.15
0.8,0.45,3.15
0.112,0.2,2.7
0.2,0.112,2.7
0.4,0.0,2.55
0.4,-0.224,2.55
0.224,-0.4,2.55
0.0,-0.4,2.55
1.3,0.0,2.55
1.3,-0.728,2.55
0.728,-1.3,2.55
0.0,-1.3,2.55
1.3,0.0,2.4
1.3,-0.728,2.4
0.728,-1.3,2.4
0.0,-1.3,2.4
-0.224,-0.4,2.55
-0.4,-0.224,2.55
-0.4,0.0,2.55
-0.728,-1.3,2.55
-1.3,-0.728,2.55
-1.3,0.0,2.55
-0.728,-1.3,2.4
-1.3,-0.728,2.4
-1.3,0.0,2.4
-0.4,0.224,2.55
-0.224,0.4,2.55
0.0,0.4,2.55
-1.3,0.728,2.55
-0.728,1.3,2.55
0.0,1.3,2.55
-1.3,0.728,2.4
-0.728,1.3,2.4
0.0,1.3,2.4
0.224,0.4,2.55
0.4,0.224,2.55
0.728,1.3,2.55
1.3,0.728,2.55
0.728,1.3,2.4
1.3,0.728,2.4
0.0,0.0,0.0
1.5,0.0,0.15
1.5,0.84,0.15
0.84,1.5,0.15
0.0,1.5,0.15
1.5,0.0,0.075
1.5,0.84,0.075
0.84,1.5,0.075
0.0,1.5,0.075
1.425,0.0,0.0
1.425,0.798,0.0
0.798,1.425,0.0
0.0,1.425,0.0
-0.84,1.5,0.15
-1.5,0.84,0.15
-1.5,0.0,0.15
-0.84,1.5,0.075
-1.5,0.84,0.075
-1.5,0.0,0.075
-0.798,1.425,0.0
-1.425,0.798,0.0
-1.425,0.0,0.0
-1.5,-0.84,0.15
-0.84,-1.5,0.15
0.0,-1.5,0.15
-1.5,-0.84,0.075
-0.84,-1.5,0.075
0.0,-1.5,0.075
-1.425,-0.798,0.0
-0.798,-1.425,0.0
0.0,-1.425,0.0
0.84,-1.5,0.15
1.5,-0.84,0.15
0.84,-1.5,0.075
1.5,-0.84,0.075
0.798,-1.425,0.0
1.425,-0.798,0.0
Finally, to compile using mingw on Ubuntu 10.04 amd64 with proper software installed:
#!/bin/bash
rm tester.exe > /dev/null 2>&1
i586-mingw32msvc-g++ tester.cpp -o tester.exe -fcheck-new -Idxsdk/DXSDK/Include -ld3d9 dxsdk/DXSDK/Lib/x86/d3dx9.lib
Related
WCHAR* not compatible with "LPSTR" parameter. In wsprintf C++
Can't compile the following code in C++ with Visual Studio 2017 (Got it from: Source Code): #include <Wincodec.h> // we use WIC for saving images #include <d3d9.h> // DirectX 9 header #pragma comment(lib, "d3d9.lib") // link to DirectX 9 library #include <stdio.h> #include <Windows.h> #include <wchar.h> #define WIDEN2(x) L ## x #define WIDEN(x) WIDEN2(x) #define __WFILE__ WIDEN(__FILE__) #define HRCHECK(__expr) {hr=(__expr);if(FAILED(hr)){wprintf(L"FAILURE 0x%08X (%i)\n\tline: %u file: '%s'\n\texpr: '" WIDEN(#__expr) L"'\n",hr, hr, __LINE__,__WFILE__);goto cleanup;}} #define RELEASE(__p) {if(__p!=nullptr){__p->Release();__p=nullptr;}} HRESULT SavePixelsToFile32bppPBGRA(UINT width, UINT height, UINT stride, LPBYTE pixels, LPWSTR filePath, const GUID &format) { if (!filePath || !pixels) return E_INVALIDARG; HRESULT hr = S_OK; IWICImagingFactory *factory = nullptr; IWICBitmapEncoder *encoder = nullptr; IWICBitmapFrameEncode *frame = nullptr; IWICStream *stream = nullptr; GUID pf = GUID_WICPixelFormat32bppPBGRA; BOOL coInit = CoInitialize(nullptr); HRCHECK(CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory))); HRCHECK(factory->CreateStream(&stream)); HRCHECK(stream->InitializeFromFilename(filePath, GENERIC_WRITE)); HRCHECK(factory->CreateEncoder(format, nullptr, &encoder)); HRCHECK(encoder->Initialize(stream, WICBitmapEncoderNoCache)); HRCHECK(encoder->CreateNewFrame(&frame, nullptr)); // we don't use options here HRCHECK(frame->Initialize(nullptr)); // we dont' use any options here HRCHECK(frame->SetSize(width, height)); HRCHECK(frame->SetPixelFormat(&pf)); HRCHECK(frame->WritePixels(height, stride, stride * height, pixels)); HRCHECK(frame->Commit()); HRCHECK(encoder->Commit()); cleanup: RELEASE(stream); RELEASE(frame); RELEASE(encoder); RELEASE(factory); if (coInit) CoUninitialize(); return hr; } HRESULT Direct3D9TakeScreenshots(UINT adapter, UINT count) { HRESULT hr = S_OK; IDirect3D9 *d3d = nullptr; IDirect3DDevice9 *device = nullptr; IDirect3DSurface9 *surface = nullptr; D3DPRESENT_PARAMETERS parameters = { 0 }; D3DDISPLAYMODE mode; D3DLOCKED_RECT rc; UINT pitch; SYSTEMTIME st; LPBYTE *shots = nullptr; // init D3D and get screen size d3d = Direct3DCreate9(D3D_SDK_VERSION); HRCHECK(d3d->GetAdapterDisplayMode(adapter, &mode)); parameters.Windowed = TRUE; parameters.BackBufferCount = 1; parameters.BackBufferHeight = mode.Height; parameters.BackBufferWidth = mode.Width; parameters.SwapEffect = D3DSWAPEFFECT_DISCARD; parameters.hDeviceWindow = NULL; // create device & capture surface HRCHECK(d3d->CreateDevice(adapter, D3DDEVTYPE_HAL, NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, ¶meters, &device)); HRCHECK(device->CreateOffscreenPlainSurface(mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface, nullptr)); // compute the required buffer size HRCHECK(surface->LockRect(&rc, NULL, 0)); pitch = rc.Pitch; HRCHECK(surface->UnlockRect()); // allocate screenshots buffers shots = new LPBYTE[count]; for (UINT i = 0; i < count; i++) { shots[i] = new BYTE[pitch * mode.Height]; } GetSystemTime(&st); // measure the time we spend doing <count> captures wprintf(L"%i:%i:%i.%i\n", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); for (UINT i = 0; i < count; i++) { // get the data HRCHECK(device->GetFrontBufferData(0, surface)); // copy it into our buffers HRCHECK(surface->LockRect(&rc, NULL, 0)); CopyMemory(shots[i], rc.pBits, rc.Pitch * mode.Height); HRCHECK(surface->UnlockRect()); } GetSystemTime(&st); wprintf(L"%i:%i:%i.%i\n", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); // save all screenshots for (UINT i = 0; i < count; i++) { WCHAR file[100]; wsprintf(file, L"cap%i.png", i); HRCHECK(SavePixelsToFile32bppPBGRA(mode.Width, mode.Height, pitch, shots[i], file, GUID_ContainerFormatPng)); } cleanup: if (shots != nullptr) { for (UINT i = 0; i < count; i++) { delete shots[i]; } delete[] shots; } RELEASE(surface); RELEASE(device); RELEASE(d3d); return hr; } int _tmain(int argc, _TCHAR* argv[]) { HRESULT hr = Direct3D9TakeScreenshots(D3DADAPTER_DEFAULT, 10); return 0; } Gives error at: // save all screenshots for (UINT i = 0; i < count; i++) { WCHAR file[100]; wsprintf(file, L"cap%i.png", i); HRCHECK(SavePixelsToFile32bppPBGRA(mode.Width, mode.Height, pitch, shots[i], file, GUID_ContainerFormatPng)); } Where: "WCHAR file[100]" is not compatible with "LSPTR" on wsprintf. L"cap%i.png" (const wchar_t) is not compatible with LPCSTR on wsprintf. And in: int _tmain(int argc, _TCHAR* argv[]) { HRESULT hr = Direct3D9TakeScreenshots(D3DADAPTER_DEFAULT, 10); return 0; } Where _TCHAR is not defined. ¡I'm new to C++ so please give me some hints!
Import FBX Vertex And Index Buffer To DirectX 11
Ok, I'm still trying to figure out how to correctly import FBX vertex and index buffer into DirectX 11. I wrote a controller for doing that and passing the vertex and index buffer to the DX11 renderer, the output should look like a cube but it is not, I only see triangles that don't make sense. The code is shown below. I did multiply the Z values by -1, though. What do I need to modify to get the render right? #pragma once #include "Array.h" #include "Vector.h" #include "fbxsdk.h" #include <assert.h> #include "constants.h" class FbxController { public: FbxController(); ~FbxController(); void Import(const char* lFilename) { lImporter = FbxImporter::Create(lSdkManager, ""); bool lImportStatus = lImporter->Initialize(lFilename, -1, lSdkManager->GetIOSettings()); if (!lImportStatus) { printf("Call to FbxImporter::Initialize() failed.\n"); printf("Error returned: %s\n\n", lImporter->GetStatus().GetErrorString()); exit(-1); } lScene = FbxScene::Create(lSdkManager, "myScene"); lImporter->Import(lScene); FbxNode* lRootNode = lScene->GetRootNode(); int childCount = lRootNode->GetChildCount(); FbxNode *node1 = lRootNode->GetChild(0); const char* nodeName1 = node1->GetName(); fbxsdk::FbxMesh *mesh = node1->GetMesh(); int cpCount1 = mesh->GetControlPointsCount(); fbxsdk::FbxVector4 *controlPoints = mesh->GetControlPoints(); for (int i = 0; i < cpCount1; i++) { fbxsdk::FbxVector4 cpitem = controlPoints[i]; printf("%d, %d, %d, %d", cpitem[0], cpitem[1], cpitem[2], cpitem[3] ); VERTEXPOSCOLOR vpc; vpc.Color.x = 0.5f; vpc.Color.y = 0.5f; vpc.Color.z = 0.5f; vpc.Position.x = cpitem[0]; vpc.Position.y = cpitem[1]; vpc.Position.z = cpitem[2] * -1.0f; m_vertices.add(vpc); } int pvCount = mesh->GetPolygonVertexCount(); int polyCount = mesh->GetPolygonCount(); for (int i = 0; i < polyCount; i++) { int polyItemSize = mesh->GetPolygonSize(i); assert(polyItemSize == 3); for (int j = 0; j < polyItemSize; j++) { int cpIndex = mesh->GetPolygonVertex(i, j); m_indices.add(cpIndex); float x = controlPoints[cpIndex].mData[0]; float y = controlPoints[cpIndex].mData[1]; float z = controlPoints[cpIndex].mData[2]; } } fbxsdk::FbxMesh *mesh2; bool isT = mesh->IsTriangleMesh(); FbxNode *node2 = lRootNode->GetChild(1); FbxNode *node3 = lRootNode->GetChild(2); //lImporter->Destroy(); } Array<VERTEXPOSCOLOR> GetVertexPosColors() { return m_vertices; } Array<unsigned int> getIndexBuffer() { return m_indices; } protected: FbxManager *lSdkManager; FbxIOSettings *ios; FbxImporter *lImporter; bool lImportStatu; FbxScene *lScene; private: Array<VERTEXPOSCOLOR> m_vertices; Array<unsigned int> m_indices; };
I think you have some problems in your index buffer creation. You simply gives an index for each vertex, and index buffer not working that way. let me know if you solve this.
Problems with SDL Audio (No output)
I'm facing some problems with understanding how the SDL audio callback works. I have this simple code, which should generate a simple square wave: #include "SDL.h" #include "SDL_audio.h" #include <stdlib.h> #include <math.h> SDL_Surface *screen; SDL_AudioSpec spec; Uint32 sound_len=512; Uint8 *sound_buffer; int sound_pos = 0; int counter; unsigned int phase_delta=600; unsigned int phase; unsigned char out; //Initialization void init_sdl (void) { if (SDL_Init (SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0) exit (-1); atexit (SDL_Quit); screen = SDL_SetVideoMode (640, 480, 16, SDL_HWSURFACE); if (screen == NULL) exit (-1); } //Generates a new sample and outputs it to the audio card void Callback (void *userdata, Uint8 *stream, int len) { Uint8 *waveptr; //Generates a new sample phase+=phase_delta; if ((phase>>8)<127) out=255; else out=0; //End //Output the current sample to the audio card waveptr = sound_buffer; SDL_MixAudio(stream, waveptr, 1, SDL_MIX_MAXVOLUME); } void play (void) { sound_buffer = new Uint8[512]; sound_len= 512; spec.freq = 22050; spec.format = AUDIO_S16SYS; spec.channels = 1; spec.silence = 0; spec.samples = 512; spec.padding = 0; spec.size = 0; spec.userdata = 0; spec.callback = Callback; if (SDL_OpenAudio (&spec, NULL) < 0) { //Throw an error printf ("I don't think you like this: %s\n", SDL_GetError ()); exit (-1); } SDL_PauseAudio (0);//Start the audio } int main(int argc, char* argv[]) { init_sdl (); play (); SDL_Delay (250); return 0; } I know that the callback is not done right, because I have no idea how to output to the buffer. Each time the callback is called, the first part of the callback function code generates the new sample, and stores it in the variabile Out. Can anyone here modify this code so that the new samples go from Out to the correct position in the audio buffer? Also, I don't want to have the code modified in a very super-complex way just to generate the square wave - I have already taken care of that. The wave is generated correctly, each new sample appearing in the variable Out. I just need these samples to be routed correctly to the audio buffer.
You need to cast stream to a actual.format-appropriate datatype and then overwrite the values in stream with len / sizeof( <format's datatype> ) samples. The square-wave will be kinda hard to hear because the given algorithm will only generate a brief high pulse every ~7.1 million samples (~5 minutes #22050Hz) when phase wraps around. Try something like this: #include <SDL.h> #include <SDL_audio.h> #include <iostream> using namespace std; //Generates new samples and outputs them to the audio card void Callback( void* userdata, Uint8* stream, int len ) { // the format of stream depends on actual.format in main() // we're assuming it's AUDIO_S16SYS short* samples = reinterpret_cast< short* >( stream ); size_t numSamples = len / sizeof( short ); const unsigned int phase_delta = 600; static unsigned int phase = 0; // loop over all our samples for( size_t i = 0; i < numSamples; ++i ) { phase+=phase_delta; short out = 0; if ((phase>>8)<127) out=SHRT_MAX; else out=0; samples[i] = out; } } int main( int argc, char* argv[] ) { if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO ) < 0 ) return -1; atexit( SDL_Quit ); SDL_Surface* screen = SDL_SetVideoMode( 640, 480, 16, SDL_ANYFORMAT ); if( screen == NULL) return -1; SDL_AudioSpec spec; spec.freq = 22050; spec.format = AUDIO_S16SYS; spec.channels = 1; spec.samples = 4096; spec.callback = Callback; spec.userdata = NULL; SDL_AudioSpec actual; if( SDL_OpenAudio( &spec, &actual ) < 0 ) { cerr << "I don't think you like this: " << SDL_GetError() << endl; return -1; } if( spec.format != actual.format ) { cerr << "format mismatch!" << endl; return -1; } SDL_PauseAudio( 0 ); SDL_Event ev; while( SDL_WaitEvent( &ev ) ) { if( ev.type == SDL_QUIT ) break; } SDL_CloseAudio(); SDL_Quit(); return 0; }
Visual c++ DirectX compilation error
Can any one help me with this error ? And I have Linked to d3d10.lib and d3dx10.lib. I am new to directX stuff and I was following wendy jones DirectX 10 toturial 1>t1.obj : error LNK2019: unresolved external symbol _D3D10CreateDeviceAndSwapChain#32 referenced in function "bool cdecl InitDirect3D(struct HWND *,int,int)" (?InitDirect3D##YA_NPAUHWND__##HH#Z) 1>C:\Users\Ehsan\Documents\Visual Studio 2010\Projects\DirectX\t1\Debug\t1.exe : fatal error LNK1120: 1 unresolved externals source code : // t1.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "windows.h" #include "tchar.h" #include <d3d10.h> #include <d3dx10.h> // Global Variables: HINSTANCE hInst; // global handle to hold the application instance HWND wndHandle; // global variable to hold the window handle int width = 640; int height = 480; // Direct3D global vars ID3D10Device * pD3DDevice = NULL; IDXGISwapChain * pSwapChain = NULL; ID3D10RenderTargetView * pRenderTargetView = NULL; // Forward declarations of functions included in this code module: LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); bool InitWindow( HINSTANCE hInstance, int width, int height ); void Render(); void ShutDownDirect3D(); bool InitDirect3D(HWND hWnd, int width, int height); int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // TODO: Place code here. MSG msg = {0}; // Perform application initialization: if ( !InitWindow( hInstance, width, height ) ) { return FALSE; } // called after creating the window if(!InitDirect3D(wndHandle, width, height)) { return FALSE; } //hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_T1)); // Main message loop: while (WM_QUIT != msg.message) { while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE) { TranslateMessage(&msg); DispatchMessage(&msg); } // Call the render function Render(); } ShutDownDirect3D(); return (int) msg.wParam; } bool InitWindow(HINSTANCE hInstance, int width, int height) { WNDCLASSEX wcex; // Fill in the WNDCLASSEX structure. This describes how the window // will look to the system wcex.cbSize = sizeof(WNDCLASSEX); // the size of the structure wcex.style = CS_HREDRAW | CS_VREDRAW; // the class style wcex.lpfnWndProc = (WNDPROC)WndProc; // the window procedure callback wcex.cbClsExtra = 0; // extra bytes to allocate for this class wcex.cbWndExtra = 0; // extra bytes to allocate for this instance wcex.hInstance = hInstance; // handle to the application instance wcex.hIcon = 0; // icon to associate with the application wcex.hCursor = LoadCursor(NULL, IDC_ARROW); // the default cursor to use wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); // the background color wcex.lpszMenuName = NULL; // the resource name for the menu wcex.lpszClassName = TEXT("DirectXExample"); // the class name being created wcex.hIconSm = 0; // the handle to the small icon RegisterClassEx(&wcex); // Resize the window RECT rect = { 0, 0, width, height }; AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); // create the window from the class above wndHandle = CreateWindow(TEXT("DirectXExample"), TEXT("DirectXExample"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, hInstance, NULL); if (!wndHandle) { return false; } // Display the window on the screen ShowWindow(wndHandle, SW_SHOW); UpdateWindow(wndHandle); return true; } // // FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) // // PURPOSE: Processes messages for the main window. // // WM_COMMAND - process the application menu // WM_PAINT - Paint the main window // WM_DESTROY - post a quit message and return // // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { // Check any available messages from the queue switch (message) { // Allow the user to press the Escape key to end the application case WM_KEYDOWN: switch(wParam) { // Check if the user hit the Escape key case VK_ESCAPE: PostQuitMessage(0); break; } break; // The user hit the close button, close the application case WM_DESTROY: PostQuitMessage(0); break; } // Always return the message to the default window procedure for furtherprocessing return DefWindowProc(hWnd, message, wParam, lParam); } /******************************************************************* * InitDirect3D * Initializes Direct3D * Inputs - Parent window handle - HWND, Window width - int Window height - int Updating the Code 31 * Outputs - true if successful, false if failed - bool *******************************************************************/ bool InitDirect3D(HWND hWnd, int width, int height) { // Create the clear the DXGI_SWAP_CHAIN_DESC structure DXGI_SWAP_CHAIN_DESC swapChainDesc; ZeroMemory(&swapChainDesc, sizeof(swapChainDesc)); // Fill in the needed values swapChainDesc.BufferCount = 1; swapChainDesc.BufferDesc.Width = width; swapChainDesc.BufferDesc.Height = height; swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swapChainDesc.BufferDesc.RefreshRate.Numerator = 60; swapChainDesc.BufferDesc.RefreshRate.Denominator = 1; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.OutputWindow = hWnd; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.Windowed = TRUE; // Create the D3D device and the swap chain HRESULT hr = D3D10CreateDeviceAndSwapChain(NULL, D3D10_DRIVER_TYPE_REFERENCE, NULL, 0, D3D10_SDK_VERSION, &swapChainDesc, &pSwapChain, &pD3DDevice); // Error checking. Make sure the device was created if (hr != S_OK) { return false; } // Get the back buffer from the swapchain ID3D10Texture2D * pBackBuffer; hr = pSwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*) &pBackBuffer); if (hr != S_OK) { return false; } // create the render target view hr = pD3DDevice->CreateRenderTargetView(pBackBuffer, NULL, &pRenderTargetView); // release the back buffer pBackBuffer->Release(); // Make sure the render target view was created successfully if (hr != S_OK) { return false; } // set the render target pD3DDevice->OMSetRenderTargets(1, &pRenderTargetView, NULL); // create and set the viewport D3D10_VIEWPORT viewport; viewport.Width = width; viewport.Height = height; viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; viewport.TopLeftX = 0; viewport.TopLeftY = 0; pD3DDevice->RSSetViewports(1, &viewport); return true; } /******************************************************************* * ShutdownDirect3D * Closes down and releases the resources for Direct3D 34 Chapter 2 n Your First DirectX Program * Inputs - void * Outputs - void *******************************************************************/ void ShutDownDirect3D() { // release the rendertarget if(pRenderTargetView) { pRenderTargetView->Release(); } // release the swapchain if(pSwapChain) { pSwapChain->Release(); } // release the D3D Device if(pD3DDevice) { pD3DDevice->Release(); } } /******************************************************************* * Render * All drawing happens in the Render function * Inputs - void * Outputs - void *******************************************************************/ void Render() { if (pD3DDevice != NULL) { // clear the target buffer pD3DDevice->ClearRenderTargetView(pRenderTargetView, D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f)); // All drawing will go here. // display the next item in the swap chain pSwapChain->Present(0, 0); } }
It's a problem of linkage, did you add the right library in your visual studio ?
Add D3D10.lib to the list of linker dependencies.
What about your include folder in your hard drive? You need to go to that through your includes and also added in lib/86 to library dependances. Follow the steps in this link: http://www.rastertek.com/dx10tut01.html This will take you through the process of setting up DirectX. Worst happens and you do it and you still get the same problem, you know its not a linker issue. Which 4 people have now told you it is. Also this site is great for taking you through DirectX as well. I highly advise.
Performance difference between DrawLine and DrawLines?
Im using GDI+ in C++ to draw a chart control. I want to know if there is any performance difference between the above 2 functions. I am not lazy to write code for DrawLines() but it is that doing so makes my code complex. So im weighin the chances of whether to make code execution faster at the expense of reducing readability and potentially increasing errors and bugs. Any help wud be appreciated. Eraj.
There shouldn't be a significant difference between the two for most drawing activities, but to be sure, I wrote up a test project to compare the difference between them (well, actually 3 of them). For a very large number of lines (x25000) on my machine, DrawLines() (640ms) was about 50% faster over DrawLine() (420ms). To be honest here, I also misread the question the first time around and wrote my initial test in C#. Performance was about the same between the two, which is to be expected as .NET Graphics are based upon GDI+. Just out of curiosity, I tried regular GDI, which I expect would be faster. Using the win32 PolyLine() (530ms) function was about 20% faster, with 45000 lines. This is 116% faster than using GDI+ DrawLines(). Even more stunning, perhaps, is that using win32 LineTo() instead of GDI+ DrawLine() results in times under 125ms. With an assumed time of 125ms and 45000 lines, this method is at least 800% faster. (Timer resolution and thread timing make it difficult to measure performance in this threshold without resorting to QueryPerformanceCounter and other timing methods of higher frequency.) However, I should caution against making the assumption that this is a significant bottleneck in drawing code. Many of the performance improvements that can be made will have nothing to do with what objects have to be drawn. I would guess that your requirements will probably dictate that a few hundred items may need to be drawn in normal operation for your control. In that case, I would recommend you write your drawing code to be as straightforward and bug-free as you can, as debugging drawing issues can be an expensive use of time and potentially less beneficial as improving the rest of your control or your application. Also, if you need to actively update thousands of items, you will see much higher performance gains by moving to a back-buffered solution. This should also make it easier to develop code to draw your control, aside from managing the off-screen buffer. Here are my source code examples. Each of them handles mouse clicks to alternate between using bulk drawing versus itemized drawing. GDI+, hosted in a barebones MFC SDI App This assumes that someone has already declared GDI+ headers and written code to initialize/teardown GDI+. In ChildView.h // Attributes public: bool m_bCompositeMode; // Operations public: void RedrawScene(Graphics &g, int lineCount, int width, int height); PointF *CreatePoints(int lineCount, int width, int height); void ReportTime(Graphics &g, int lineCount, DWORD tickSpan); public: afx_msg void OnLButtonUp(UINT nFlags, CPoint point); In ChildView.cpp, added to PreCreateWindow() m_bCompositeMode = false; Remainder of ChildView.cpp, including OnPaint() and Message Map changes. BEGIN_MESSAGE_MAP(CChildView, CWnd) ON_WM_PAINT() ON_WM_LBUTTONUP() END_MESSAGE_MAP() void CChildView::OnPaint() { CPaintDC dc(this); // device context for painting RECT rcClient; ::GetClientRect(this->GetSafeHwnd(), &rcClient); Graphics g(dc.GetSafeHdc()); g.Clear(Color(0, 0, 0)); RedrawScene(g, 25000, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); } void CChildView::RedrawScene(Graphics &g, int lineCount, int width, int height) { DWORD tickStart = 0; DWORD tickEnd = 0; Pen p(Color(0, 0, 0x7F)); PointF *pts = CreatePoints(lineCount, width, height); tickStart = GetTickCount(); if (m_bCompositeMode) { g.DrawLines(&p, pts, lineCount); } else { int i = 0; int imax = lineCount - 1; for (i = 0; i < imax; i++) { g.DrawLine(&p, pts[i], pts[i + 1]); } } tickEnd = GetTickCount(); delete[] pts; ReportTime(g, lineCount, tickEnd - tickStart); } void CChildView::ReportTime(Graphics &g, int lineCount, DWORD tickSpan) { CString strDisp; if(m_bCompositeMode) { strDisp.Format(_T("Graphics::DrawLines(Pen *, PointF *, INT) x%d took %dms"), lineCount, tickSpan); } else { strDisp.Format(_T("Graphics::DrawLine(Pen *, PointF, PointF) x%d took %dms"), lineCount, tickSpan); } // Note: sloppy, but simple. Font font(L"Arial", 14.0f); PointF ptOrigin(0.0f, 0.0f); SolidBrush br(Color(255, 255, 255)); Status s = g.DrawString(strDisp, -1, &font, ptOrigin, &br); } PointF* CChildView::CreatePoints(int lineCount, int width, int height) { if(lineCount <= 0) { PointF *ptEmpty = new PointF[2]; ptEmpty[0].X = 0; ptEmpty[0].Y = 0; ptEmpty[1].X = 0; ptEmpty[1].Y = 0; return ptEmpty; } PointF *pts = new PointF[lineCount + 1]; int i = 1; while(i < lineCount) { pts[i].X = (float)(rand() % width); pts[i].Y = (float)(rand() % height); i++; } return pts; } void CChildView::OnLButtonUp(UINT nFlags, CPoint point) { m_bCompositeMode = !m_bCompositeMode; this->Invalidate(); CWnd::OnLButtonUp(nFlags, point); } C#.NET, hosted in a basebones WinForms App, with default class Form1 Set a default size for the form, equal to the size of the MFC version if you are comparing the two. A size-change handler could be added as well. public Form1() { InitializeComponent(); bCompositeMode = false; } bool bCompositeMode; private void Form1_Paint(object sender, PaintEventArgs e) { e.Graphics.Clear(Color.Black); RedrawScene(e.Graphics, 25000, this.ClientRectangle.Width, this.ClientRectangle.Height); } private void RedrawScene(Graphics g, int lineCount, int width, int height) { DateTime dtStart = DateTime.MinValue; DateTime dtEnd = DateTime.MinValue; using (Pen p = new Pen(Color.Navy)) { Point[] pts = CreatePoints(lineCount, width, height); dtStart = DateTime.Now; if (bCompositeMode) { g.DrawLines(p, pts); } else { int i = 0; int imax = pts.Length - 1; for (i = 0; i < imax; i++) { g.DrawLine(p, pts[i], pts[i + 1]); } } dtEnd = DateTime.Now; } ReportTime(g, lineCount, dtEnd - dtStart); } private void ReportTime(Graphics g, int lineCount, TimeSpan ts) { string strDisp = null; if (bCompositeMode) { strDisp = string.Format("DrawLines(Pen, Point[]) x{0} took {1}ms", lineCount, ts.Milliseconds); } else { strDisp = string.Format("DrawLine(Pen, Point, Point) x{0} took {1}ms", lineCount, ts.Milliseconds); } // Note: sloppy, but simple. using (Font font = new Font(FontFamily.GenericSansSerif, 14.0f, FontStyle.Regular)) { g.DrawString(strDisp, font, Brushes.White, 0.0f, 0.0f); } } private Point[] CreatePoints(int count, int width, int height) { Random rnd = new Random(); if (count <= 0) { return new Point[] { new Point(0,0), new Point(0,0)}; } Point[] pts = new Point[count + 1]; pts[0] = new Point(0, 0); int i = 1; while (i <= count) { pts[i] = new Point(rnd.Next(width), rnd.Next(height)); i++; } return pts; } private void Form1_Click(object sender, EventArgs e) { bCompositeMode = !bCompositeMode; Invalidate(); } Regular GDI, hosted in a barebones MFC SDI App In ChildView.h // Attributes public: bool m_bCompositeMode; // Operations public: void RedrawScene(HDC hdc, int lineCount, int width, int height); POINT *CreatePoints(int lineCount, int width, int height); void ReportTime(HDC hdc, int lineCount, DWORD tickSpan); public: afx_msg void OnLButtonUp(UINT nFlags, CPoint point); In ChildView.cpp Update PreCreateWindow() just as in the GDI+ sample. BEGIN_MESSAGE_MAP(CChildView, CWnd) ON_WM_PAINT() ON_WM_LBUTTONUP() END_MESSAGE_MAP() void CChildView::OnPaint() { CPaintDC dc(this); // device context for painting HDC hdc = dc.GetSafeHdc(); HBRUSH brClear = (HBRUSH)::GetStockObject(BLACK_BRUSH); RECT rcClient; ::GetClientRect(this->m_hWnd, &rcClient); ::FillRect(hdc, &rcClient, brClear); ::DeleteObject(brClear); RedrawScene(hdc, 45000, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); } void CChildView::RedrawScene(HDC hdc, int lineCount, int width, int height) { DWORD tickStart = 0; DWORD tickEnd = 0; HPEN p = ::CreatePen(PS_SOLID, 1, RGB(0, 0, 0x7F)); POINT *pts = CreatePoints(lineCount, width, height); HGDIOBJ prevPen = SelectObject(hdc, p); tickStart = GetTickCount(); if(m_bCompositeMode) { ::Polyline(hdc, pts, lineCount); } else { ::MoveToEx(hdc, pts[0].x, pts[0].y, &(pts[0])); int i = 0; int imax = lineCount; for(i = 1; i < imax; i++) { ::LineTo(hdc, pts[i].x, pts[i].y); } } tickEnd = GetTickCount(); ::SelectObject(hdc, prevPen); delete pts; ::DeleteObject(p); ReportTime(hdc, lineCount, tickEnd - tickStart); } POINT *CChildView::CreatePoints(int lineCount, int width, int height) { if(lineCount <= 0) { POINT *ptEmpty = new POINT[2]; memset(&ptEmpty, 0, sizeof(POINT) * 2); return ptEmpty; } POINT *pts = new POINT[lineCount + 1]; int i = 1; while(i < lineCount) { pts[i].x = rand() % width; pts[i].y = rand() % height; i++; } return pts; } void CChildView::ReportTime(HDC hdc, int lineCount, DWORD tickSpan) { CString strDisp; if(m_bCompositeMode) { strDisp.Format(_T("PolyLine(HDC, POINT *, int) x%d took %dms"), lineCount, tickSpan); } else { strDisp.Format(_T("LineTo(HDC, HPEN, int, int) x%d took %dms"), lineCount, tickSpan); } HFONT font = (HFONT)::GetStockObject(SYSTEM_FONT); HFONT fontPrev = (HFONT)::SelectObject(hdc, font); RECT rcClient; ::GetClientRect(this->m_hWnd, &rcClient); ::ExtTextOut(hdc, 0, 0, ETO_CLIPPED, &rcClient, strDisp.GetString(), strDisp.GetLength(), NULL); ::SelectObject(hdc, fontPrev); ::DeleteObject(font); } void CChildView::OnLButtonUp(UINT nFlags, CPoint point) { m_bCompositeMode = !m_bCompositeMode; this->Invalidate(); CWnd::OnLButtonUp(nFlags, point); }