Performance difference between DrawLine and DrawLines? - visual-c++

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);
}

Related

How to change the background properties of an mfc application

CURRENT UI
NEW UI
I want to change the background color of a button in MFC application. I have created my user interface(UI) in MFC .I have added every controls from the toolbox.But the problem is that i want to change the background and foreground properties of a button and window.How it is possible?
please help me to change the properties of controls in MFC.In windows application we can directly change the properties in the property window.
But in the case of MFC application that is not possible.
please help me..i have not enough experience in MFC application development.....
Thanks in advance......................
code from dialer.h
class CButtonDialer : public CButton
{
// Construction
public:
CButtonDialer();
// Attributes
public:
CButton m_button;
// CButton IDC_KEY_1;
CBrush m_brush;
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CButtonDialer)
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
// Implementation
public:
virtual ~CButtonDialer();
// Generated message map functions
protected:
//{{AFX_MSG(CButtonDialer)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
CFont m_FontLetters;
CMapStringToString m_map;
HTHEME m_hTheme;
void OpenTheme() { m_hTheme = OpenThemeData(m_hWnd, L"Button"); }
void CloseTheme() {
if (m_hTheme) { CloseThemeData(m_hTheme); m_hTheme = NULL; }
}
DECLARE_MESSAGE_MAP()
virtual void PreSubclassWindow();
afx_msg LRESULT OnThemeChanged();
afx_msg void OnMouseMove(UINT,CPoint);
afx_msg void OnSize(UINT type, int w, int h);
};
code from dialer.cpp file
#include "stdafx.h"
#include "ButtonDialer.h"
#include "Strsafe.h"
#include "const.h"
/////////////////////////////////////////////////////////////////////////////
// CButtonDialer
CButtonDialer::CButtonDialer()
{
//255,255,255
m_brush.CreateSolidBrush(
(173, 41, 41));
m_map.SetAt(_T("1"),_T(""));
m_map.SetAt(_T("2"),_T("ABC"));
m_map.SetAt(_T("3"),_T("DEF"));
m_map.SetAt(_T("4"),_T("GHI"));
m_map.SetAt(_T("5"),_T("JKL"));
m_map.SetAt(_T("6"),_T("MNO"));
m_map.SetAt(_T("7"),_T("PQRS"));
m_map.SetAt(_T("8"),_T("TUV"));
m_map.SetAt(_T("9"),_T("WXYZ"));
m_map.SetAt(_T("0"),_T(""));
m_map.SetAt(_T("*"),_T(""));
m_map.SetAt(_T("#"),_T(""));
}
CButtonDialer::~CButtonDialer()
{
CloseTheme();
}
BEGIN_MESSAGE_MAP(CButtonDialer, CButton)
ON_WM_THEMECHANGED()
ON_WM_MOUSEMOVE()
ON_WM_SIZE()
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CButtonDialer message handlers
void CButtonDialer::PreSubclassWindow()
{
OpenTheme();
HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
LOGFONT lf;
GetObject(hFont, sizeof(LOGFONT), &lf);
lf.lfHeight = 14;
StringCchCopy(lf.lfFaceName,LF_FACESIZE,_T("Microsoft Sans Serif"));
m_FontLetters.CreateFontIndirect(&lf);
DWORD dwStyle = ::GetClassLong(m_hWnd, GCL_STYLE);
dwStyle &= ~CS_DBLCLKS;
::SetClassLong(m_hWnd, GCL_STYLE, dwStyle);
}
LRESULT CButtonDialer::OnThemeChanged()
{
CloseTheme();
OpenTheme();
return 0L;
}
void CButtonDialer::OnSize(UINT type, int w, int h)
{
CButton::OnSize(type, w, h);
}
void CButtonDialer::OnMouseMove(UINT nFlags,CPoint point)
{
CRect rect;
GetClientRect(&rect);
if (rect.PtInRect(point)) {
if (GetCapture() != this) {
SetCapture();
Invalidate();
}
}
else {
ReleaseCapture();
Invalidate();
}
CButton::OnMouseMove(nFlags, point);
}
void CButtonDialer::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC dc;
dc.Attach(lpDrawItemStruct->hDC); //Get device context object
CRect rt;
rt = lpDrawItemStruct->rcItem; //Get button rect
dc.FillSolidRect(rt,dc.GetBkColor());
dc.SetBkMode(TRANSPARENT);
CRect rtl = rt;
UINT state = lpDrawItemStruct->itemState; //Get state of the button
if (!m_hTheme) {
UINT uStyle = DFCS_BUTTONPUSH;
if ( (state & ODS_SELECTED) ) {
uStyle |= DFCS_PUSHED;
rtl.left+=1;
rtl.top+=1;
}
dc.DrawFrameControl(rt, DFC_BUTTON, uStyle);
} else {
UINT uStyleTheme = RBS_NORMAL;
if ( (state & ODS_SELECTED) ) {
uStyleTheme = PBS_PRESSED;
} else if (GetCapture()==this) {
uStyleTheme = PBS_HOT;
}
DrawThemeBackground(m_hTheme, dc.m_hDC,
BP_PUSHBUTTON, uStyleTheme,
rt, NULL);
}
CString strTemp;
GetWindowText(strTemp); // Get the caption which have been set
rtl.top += 4;
CString letters;
COLORREF crOldColor;
if (m_map.Lookup(strTemp,letters)) {
rtl.left+=15;
dc.DrawText(strTemp,rtl,DT_LEFT|DT_TOP|DT_SINGLELINE); // Draw out the caption
HFONT hOldFont = (HFONT)SelectObject(dc.m_hDC, m_FontLetters);
// Do your text drawing
rtl.left += 13;
rtl.top += 4;
rtl.right -=4;
crOldColor = dc.SetTextColor(RGB(148, 167, 70));
dc.DrawText(letters,rtl,DT_LEFT | DT_TOP | DT_SINGLELINE);
dc.SetTextColor(crOldColor);
// Always select the old font back into the DC
SelectObject(dc.m_hDC, hOldFont);
} else {
//127,127,127
crOldColor = dc.SetTextColor(RGB(148, 167, 70));
dc.DrawText(strTemp,rtl,DT_CENTER|DT_TOP|DT_SINGLELINE); // Draw out the caption
dc.SetTextColor(crOldColor);
}
if ( (state & ODS_FOCUS ) ) // If the button is focused
{
int iChange = 3;
rt.top += iChange;
rt.left += iChange;
rt.right -= iChange;
rt.bottom -= iChange;
dc.DrawFocusRect(rt);
}
dc.Detach();
}
BOOL CButtonDialer::OnEraseBkgnd(CDC* pDC)
{
CRect rect;
GetClientRect(&rect);
//255,255,255
CBrush myBrush(RGB(173, 41, 41)); // dialog background color
CBrush *pOld = pDC->SelectObject(&myBrush);
BOOL bRes = pDC->PatBlt(0, 0, rect.Width(), rect.Height(), PATCOPY);
pDC->SelectObject(pOld); // restore old brush
return bRes; // CDialog::OnEraseBkgnd(pDC);
}
You need to provide a message handler for the WM_ERASEBKGND message and paint the background yourself.
Put a normal button on the Resource Editor.
On the .h file of the form where you use it, declare a CMFCButton variable like m_btnRead.
In the DoDataExchange method of your form append a line
DDX_Control(pDX, IDC_BUTTON_READ, m_btnRead);
On the method where you initialize your form (OnInitDialog, Create, etc.) append a line
m_btnRead.SetFaceColor(RGB(0, 255, 255));
and now you are done!

How to add a gripper to a PropertySheet?

I have a class derived from CPropertySheet, and i want to insert a "gripper" on the bottom right of the dialog.
my dialog already is resizable, i just can't insert the gripper.
I don't know if there are any special APIs to do that. One option is to draw it manually, then override ON_WM_NCHITTEST and return HTBOTTOMRIGHT for gripper's position. For example:
void CMyDialog::OnPaint()
{
CPaintDC dc(this);
CRect rc;
GetClientRect();
rc.left = rc.right - ::GetSystemMetrics(SM_CXHSCROLL);
rc.top = rc.bottom - ::GetSystemMetrics(SM_CYVSCROLL);
HTHEME ht = OpenThemeData(m_hWnd, L"STATUS");
if (ht)
{
DrawThemeBackground(ht, dc, SP_GRIPPER, 0, &rc, 0);
CloseThemeData(ht);
}
else
{
dc.DrawFrameControl(rc, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
}
}
LRESULT CMyDialog::OnNcHitTest(CPoint point)
{
CRect rc;
GetWindowRect(rc);
rc.left = rc.right - ::GetSystemMetrics(SM_CXHSCROLL);
rc.top = rc.bottom - ::GetSystemMetrics(SM_CYVSCROLL);
if (rc.PtInRect(point))
return HTBOTTOMRIGHT;
return CDialog::OnNcHitTest(point);
}
void CMyDialog::OnSize(UINT type, int cx, int cy)
{
CDialog::OnSize(type, cx, cy);
Invalidate(TRUE);
}
Add to message map:
ON_WM_PAINT()
ON_WM_NCHITTEST()
ON_WM_SIZE()

Tool tip for disabled check boxes in Check List Box control in MFC

I am working in an MFC windows application. I am using check boxes in Check List Box control. Some of the check boxes are disabled. How can I implement the tool tips for disabled check boxes?
Ran Wainstein was implemented the tool tip for each item in the list box control. This can be extended to the Check List Box control also.
MyCheckListBox.h
class CMyCheckListBox : public CCheckListBox
{
DECLARE_DYNAMIC(CMyCheckListBox)
public:
CMyCheckListBox(){};
virtual ~CMyCheckListBox(){};
afx_msg int OnToolHitTest(CPoint point, TOOLINFO * pTI) const;
UINT ItemFromPoint2(CPoint pt, BOOL& bOutside) const;
BOOL OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult );
protected:
virtual void PreSubclassWindow();
DECLARE_MESSAGE_MAP()
};
MyCheckListBox.cpp
This will work only for Unicode strings.
IMPLEMENT_DYNAMIC(CMyCheckListBox, CCheckListBox)
BEGIN_MESSAGE_MAP(CMyCheckListBox, CCheckListBox)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXT, 0, 0xFFFF, OnToolTipText)
END_MESSAGE_MAP()
void CMyCheckListBox::PreSubclassWindow() {
CCheckListBox::PreSubclassWindow();
EnableToolTips(TRUE);
}
int CMyCheckListBox::OnToolHitTest(CPoint point, TOOLINFO * pTI) const{
int row;
RECT cellrect;
BOOL tmp = FALSE;
row = ItemFromPoint(point,tmp);
if ( row == -1 )
return -1;
GetItemRect(row,&cellrect);
pTI->rect = cellrect;
pTI->hwnd = m_hWnd;
pTI->uId = (UINT)((row));
pTI->lpszText = LPSTR_TEXTCALLBACK;
return pTI->uId;
}
BOOL CMyCheckListBox::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult ){
TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
CString strTipText;
UINT nID = pNMHDR->idFrom;
GetText( nID ,strTipText);
lstrcpyn(pTTTW->szText, strTipText, 80);
*pResult = 0;
return TRUE;
}
UINT CMyCheckListBox::ItemFromPoint2(CPoint pt, BOOL& bOutside) const{
int nFirstIndex, nLastIndex;
nFirstIndex = GetTopIndex();
nLastIndex = nFirstIndex + GetCount();
bOutside = TRUE;
CRect Rect;
int nResult = -1;
for (int i = nFirstIndex; nResult == -1 && i <= nLastIndex; i++){
if (GetItemRect(i, &Rect) != LB_ERR){
if (Rect.PtInRect(pt)){
nResult = i;
bOutside = FALSE;
}
}
}
return nResult;
}
Finally implement Check List Box control in the corresponding dialog box.The output is

Painting Ownerdrawn CStatic with Calculated HRGN

I have a class which inherits from CStatic, which is owner drawn and calculates the region the window should have using SetWindowRgn.
In my DrawItem override I attempt to use FillRgn passing in the same region as was calculated but the method returns FALSE. The Documentation says
Return Value: Nonzero if the function is successful; otherwise 0.
But when I GetLastError the message is "The operation completed successfully".
The window's region is definitely getting the set fine as I have managed to receive WM_LBUTTONUP messages.
Here is my class:
.h file
class Country : public CStatic
{
DECLARE_DYNAMIC(Country)
public:
Country(HBITMAP hBm, CPoint point);
virtual ~Country();
protected:
DECLARE_MESSAGE_MAP()
CPoint m_point;
HBITMAP m_hBm;
BITMAP m_bm;
HDC m_dcBm;
HRGN m_hRgn;
void CreateWindowRgn();
public:
void CreateCountryWindow(CWnd * pParent);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
};
.cpp
void Country::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CRgn * clientRgn = CRgn::FromHandle(m_hRgn);
CBrush blackBrush(RGB(0,0,0));
if(!(pDC->FillRgn(clientRgn, &blackBrush)))
{
AfxMessageBox(GetLastErrorMessage());
}
}
void Country::CreateCountryWindow(CWnd * pParent)
{
if(Create(
_T(""),
WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_OWNERDRAW,
CRect(m_point.x, QUESTION_PANEL_HEIGHT + m_point.y, m_bm.bmWidth, m_bm.bmHeight),
pParent))
{
ShowWindow(SW_SHOW);
CreateWindowRgn();
}
}
void Country::CreateWindowRgn()
{
HRGN hTmpRgn;
m_hRgn = CreateRectRgn(0,0,m_bm.bmWidth, m_bm.bmHeight);
for(int i = 0; i < m_bm.bmWidth; i++)
{
for(int j = 0; j < m_bm.bmHeight; j++)
{
COLORREF c = GetPixel(m_dcBm,i,j);
if(GetRValue(c) < 10 && GetGValue(c) < 10 && GetBValue(c) < 10)
{
hTmpRgn = CreateRectRgn(i, j, i+1, j+1);
CombineRgn(m_hRgn, m_hRgn, hTmpRgn, RGN_XOR);
DeleteObject(hTmpRgn);
}
}
}
SetWindowRgn(m_hRgn, TRUE);
}
void Country::OnLButtonUp(UINT nFlags, CPoint point)
{
AfxMessageBox(_T("Hello World"));
CStatic::OnLButtonUp(nFlags, point);
}
As with most MFC problems the answer lies in the documentation:
The system does not make a copy of the region. Thus, do not make
further function calls with this region handle.
Documentation

Looking for code samples for Direct3D tessellation

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

Resources