Freedom of movement - opengl

I am a little stuck with making freedom of movement with the mouse in OpenGL, what I want is to have the controls of a 3D model making program. I am using Windows.
Here is my viewport structure, pretty standard:
struct viewport {
float x;
float y;
float z;
float angle[2];
};
struct viewport viewport;
My gluLookAt call:
glLoadIdentity();
gluLookAt(viewport.x, viewport.y, viewport.z, viewport.x + sin(viewport.angle[0] * M_PI / 180.0f), viewport.y + viewport.angle[1] * M_PI / 180.0f, viewport.z - cos(viewport.angle[0] * M_PI / 180.0f), 0.0f, 1.0f, 0.0f);
My WM_MOVE, to rotate the scene when the user holds down the right mouse button:
case WM_MOUSEMOVE:
if(GetAsyncKeyState(VK_RBUTTON)) {
viewport.angle[0] -= (GET_X_LPARAM(lParam) - lastMouseX) / 16.0f;
viewport.angle[1] += (GET_Y_LPARAM(lParam) - lastMouseY) / 16.0f;
}
lastMouseX = GET_X_LPARAM(lParam);
lastMouseY = GET_Y_LPARAM(lParam);
return 0;
My WM_MOUSEWHEEL, to zoom in and out the scene:
case WM_MOUSEWHEEL:
if(GET_WHEEL_DELTA_WPARAM(wParam) > 0) {
viewport.x += sin(GET_WHEEL_DELTA_WPARAM(wParam) / 120.0f);
viewport.y -= cos(GET_WHEEL_DELTA_WPARAM(wParam) / 120.0f);
}
else {
viewport.x += sin(GET_WHEEL_DELTA_WPARAM(wParam) / 120.0f);
viewport.y -= cos(GET_WHEEL_DELTA_WPARAM(wParam) / 120.0f);
}
return 0;
The zoom code really doesn't work, and I think my code to rotate on the Y axis is wrong, it looks weird.
I was able to get the zoom mostly working, the Y and X and Z aren't linked, but it works kind of. I post my full main.c this time so you can see more of the code:
#include "main.h"
#include <windowsx.h>
#include <math.h>
#define argc __argc
#define argv __argv
#define GRID_SIZE 50
short quit = 0;
short dead = 0;
HWND hWnd;
HDC hDC;
HGLRC hRC;
struct viewport {
float x;
float y[2];
float z;
float angle;
};
struct viewport viewport = { GRID_SIZE / 2.0f, { 1.0f, 1.0f }, GRID_SIZE / 2.0f, 180.0f };
float lastMouseX, lastMouseY = 0;
void die(void) {
if(!dead) {
quit = 1;
dead = 1;
deinitText();
deinitOpenGL();
DestroyWindow(hWnd);
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow) {
if(argc != 2) {
return 0;
}
WNDCLASS wc;
MSG msg;
wc.style = CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = "ObjectViewerC";
if(RegisterClass(&wc) == 0) {
die();
}
hWnd = CreateWindow("ObjectViewerC", argv[1], WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE, 0, 0, 512, 512, NULL, NULL, hInstance, NULL);
if(hWnd == NULL) {
die();
}
if(!initOpenGL()) {
die();
}
if(!initExtensions()) {
die();
}
if(!initTimer()) {
}
float frameStartTime = 0.0f;
float frameEndTime = 0.0f;
initText();
lookNice();
set3D();
while(!quit) {
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if(msg.message == WM_QUIT) {
quit = 1;
}
else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else {
frameStartTime = getTime();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(viewport.x, viewport.y[0], viewport.z, viewport.x + sin(viewport.angle * M_PI / 180.0f), viewport.y[1], viewport.z - cos(viewport.angle * M_PI / 180.0f), 0.0f, 1.0f, 0.0f);
glColor3f(1, 1, 1);
glBegin(GL_LINES);
int i = 0;
for(i = 0; i <= GRID_SIZE; i++) {
glVertex3f(i, 0, 0);
glVertex3f(i, 0, GRID_SIZE);
glVertex3f(0, 0, i);
glVertex3f(GRID_SIZE, 0, i);
};
glEnd();
SwapBuffers(hDC);
do {
frameEndTime = getTime();
} while(frameEndTime < frameStartTime + framerate);
}
}
die();
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch(message) {
case WM_CREATE:
return 0;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
case WM_DESTROY:
die();
return 0;
case WM_KEYDOWN:
switch(wParam) {
case VK_ESCAPE:
PostQuitMessage(0);
return 0;
}
return 0;
case WM_LBUTTONDOWN:
return 0;
case WM_LBUTTONUP:
return 0;
case WM_MOUSEMOVE:
if(GetAsyncKeyState(VK_RBUTTON)) {
viewport.angle -= (GET_X_LPARAM(lParam) - lastMouseX) / 11.2f;
viewport.y[1] += ((GET_Y_LPARAM(lParam) - lastMouseY) / 580.0f);
}
lastMouseX = GET_X_LPARAM(lParam);
lastMouseY = GET_Y_LPARAM(lParam);
return 0;
case WM_MOUSEWHEEL:
if(GET_WHEEL_DELTA_WPARAM(wParam) > 0) {
viewport.x += sin(viewport.angle * M_PI / 180.0f) / 15.0f;
viewport.z -= cos(viewport.angle * M_PI / 180.0f) / 15.0f;
viewport.y[0] -= sin(viewport.y[0] - viewport.y[1]) / 15.0f;
viewport.y[1] -= sin(viewport.y[0] - viewport.y[1]) / 15.0f;
}
else {
viewport.x -= sin(viewport.angle * M_PI / 180.0f) / 15.0f;
viewport.z += cos(viewport.angle * M_PI / 180.0f) / 15.0f;
viewport.y[0] += sin(viewport.y[0] - viewport.y[1]) / 15.0f;
viewport.y[1] += sin(viewport.y[0] - viewport.y[1]) / 15.0f;
}
return 0;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}

For the y Rotation: I think you should use a different vector than viewport.xyz for these arguments.
As you see, the parameters of gluLookAt are:
void gluLookAt(GLdouble eyeX, GLdouble eyeY, GLdouble eyeZ,
GLdouble centerX, GLdouble centerY, GLdouble centerZ,
GLdouble upX, GLdouble upY, GLdouble upZ);
so arguments 4 to 6 define the point where you look at. See this Picture:
So if you want to look around, you have to adjust the at vector, not the eye vector. To do so, try this:
take the vector between eye and at (at - eye)
turn that vector around the y axis (look up vector rotation on the web)
add the vector to eye => you have the new at vector
For the scrolling, I guess what you want is to move the eye vector in the direction the camera is looking. So something like eye += normalized(at - eye) * factor.

Related

Unable to glTranslate() light source

I am having a problem translating the light source.
I have coded a program that accepts up, down, left, and right keys to increase/decrease the x, y, and z values to reposition the light source. But the problem right now is the x, y, and z value does change but the light source remains in the same place. I believe the problem shall be somewhere inside the void lighting() function.
below is my code:
#include <Windows.h>
#include <gl/GL.h>
#include <math.h>
#include <gl/GLU.h>
#pragma comment (lib, "OpenGL32.lib") //link library
#pragma comment(lib, "Winmm.lib")
#define WINDOW_TITLE "Lighting"
bool isOrtho = true;
float ONear = -10.0, OFar = 10.0; //Ortho Near & Far
float PNear = 1.0, PFar = 21.0; //Perspective Near & Far
float exeDif[3] = { 1.0,0.0,0.0 }; //Red diffuse light
float x =6 , y=0, z=0;
float exePosD[3] = { 6,0,0 };
float exeDifM[3] = { 1.0,0.0,0.0 };
bool isLightOn = false; //is light on?
LRESULT WINAPI WindowProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);//quit application
break;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE) PostQuitMessage(0);
else if (wParam == VK_SPACE) {
isLightOn = !isLightOn;
}
else if (wParam == VK_UP) {
y = y + 1.0;
}
else if (wParam == VK_DOWN) {
y = y - 1.0;
}
else if (wParam == VK_LEFT) {
x = x - 1.0;
}
else if (wParam == VK_RIGHT) {
x = x + 1.0;
}
else if (wParam == 'Q') {
z = z - 1.0;
}
else if (wParam == 'E') {
z = z + 1.0;
}
break;
default:
break;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
bool initPixelFormat(HDC hdc)
{
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR));
pfd.cAlphaBits = 8;
pfd.cColorBits = 32;
pfd.cDepthBits = 24;
pfd.cStencilBits = 0;
pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
pfd.iLayerType = PFD_MAIN_PLANE;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
// choose pixel format returns the number most similar pixel format available
int n = ChoosePixelFormat(hdc, &pfd);
// set pixel format returns whether it sucessfully set the pixel format
if (SetPixelFormat(hdc, n, &pfd))
{
return true;
}
else
{
return false;
}
}
void lighting() {
if (isLightOn) {
glEnable(GL_LIGHTING); //Turon on lighting for the whole scene
}
else {
//Turn off lighting for the whole screen
glDisable(GL_LIGHTING);
}
glPushMatrix();
glTranslatef(x, y, z);
glLightfv(GL_LIGHT0, GL_POSITION, exePosD);
glPopMatrix();
glLightfv(GL_LIGHT0, GL_DIFFUSE, exeDif);
glEnable(GL_LIGHT0);
}
void projection() {
glMatrixMode(GL_PROJECTION); //refer to projection matrix
glLoadIdentity(); //reset projection matrix
if (isOrtho) {
//Ortho View
glOrtho(-10.0, 10.0, -10.0, 10.0, ONear, OFar); //Ortho view
}
else {
//Perspective view
gluPerspective(20, 1.0, -1.0, 1.0);
glFrustum(-10.0, 10.0, -10.0, 10.0, PNear, PFar);
}
}
void drawSphere(double r) {
GLUquadricObj* sphere = NULL;
sphere = gluNewQuadric();
gluQuadricDrawStyle(sphere, GLU_FILL);
gluSphere(sphere, r, 30, 30);
gluDeleteQuadric(sphere);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
projection(); //projection
lighting();
glMatrixMode(GL_MODELVIEW); //refer to modelview matrix
glLoadIdentity(); //reset
drawSphere(3.0);
//red color diffuse
glMaterialfv(GL_FRONT, GL_DIFFUSE, exeDifM);
glColor3f(0.0, 0.0, 1.0); //blue
drawSphere(3.0);
}
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int nCmdShow)
{
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.hInstance = GetModuleHandle(NULL);
wc.lpfnWndProc = WindowProcedure;
wc.lpszClassName = WINDOW_TITLE;
wc.style = CS_HREDRAW | CS_VREDRAW;
if (!RegisterClassEx(&wc)) return false;
HWND hWnd = CreateWindow(WINDOW_TITLE, WINDOW_TITLE, WS_OVERLAPPEDWINDOW,
720, 10, 300, 300,
NULL, NULL, wc.hInstance, NULL);
//--------------------------------
// Initialize window for OpenGL
//--------------------------------
HDC hdc = GetDC(hWnd);
// initialize pixel format for the window
initPixelFormat(hdc);
// get an openGL context
HGLRC hglrc = wglCreateContext(hdc);
// make context current
if (!wglMakeCurrent(hdc, hglrc)) return false;
//--------------------------------
// End initialization
//--------------------------------
ShowWindow(hWnd, nCmdShow);
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while (true)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
display();
SwapBuffers(hdc);
}
UnregisterClass(WINDOW_TITLE, wc.hInstance);
return true;
}
This is the output screen:
The light position is transformed with the modelview matrix, but not with the projection matrix. Therefore you need to switch to the modelview matrix after projection() and before lighting():
projection(); //projection
glMatrixMode(GL_MODELVIEW); //refer to modelview matrix
glLoadIdentity();
lighting();

Soft edged images in GDI+

Im trying to convert the code found here: soft-edged-images-in-gdi, to c++. Its used to create bitmaps with rounded corners and soft edges.
#include <windows.h>
#include <iostream>
#include <exception>
#include <gdiplus.h>
#include <gdipluspath.h>
#include <gdiplusimaging.h>
#include <gdipluspixelformats.h >
#include <wingdi.h>
#pragma comment (lib, "gdiplus.lib")
using namespace Gdiplus;
GraphicsPath* createRoundRect(int x, int y, int width, int height, int radius)
{
GraphicsPath* gp = new GraphicsPath;
if (radius == 0)
gp->AddRectangle(Rect(x, y, width, height));
else
{
gp->AddLine(x + radius, y, x + width - radius, y);
gp->AddArc(x + width - radius, y, radius, radius, 270, 90);
gp->AddLine(x + width, y + radius, x + width, y + height - radius);
gp->AddArc(x + width - radius, y + height - radius, radius, radius, 0, 90);
gp->AddLine(x + width - radius, y + height, x + radius, y + height);
gp->AddArc(x, y + height - radius, radius, radius, 90, 90);
gp->AddLine(x, y + height - radius, x, y + radius);
gp->AddArc(x, y, radius, radius, 180, 90);
gp->CloseFigure();
}
return gp;
}
Brush* createFluffyBrush(GraphicsPath* gp, float* blendPositions, float* blendFactors, INT count, INT* in_out_count)
{
PathGradientBrush* pgb = new PathGradientBrush(gp);
//Blend blend = new Blend();
//blend.Positions = blendPositions;
//blend.Factors = blendFactors;
//pgb->Blend = blend;
/* count:
Type: INT
Integer that specifies the number of elements in the blendFactors array.
This is the same as the number of elements in the blendPositions array.
*/
pgb->SetBlend(blendFactors, blendPositions, count);
pgb->SetCenterColor(Color::White);
// in_out_count:
/*
Type: INT*
Pointer to an integer that, on input, specifies the number of Color objects in the colors array.
If the method succeeds, this parameter, on output, receives the number of surround colors set.
If the method fails, this parameter does not receive a value.
*/
pgb->SetSurroundColors(new Color(Color::Black), in_out_count);
return pgb;
}
enum ChannelARGB
{
Blue = 0,
Green = 1,
Red = 2,
Alpha = 3
};
void transferOneARGBChannelFromOneBitmapToAnother(Bitmap* source, Bitmap* dest, ChannelARGB sourceChannel, ChannelARGB destChannel)
{
if ((source->GetWidth() != dest->GetWidth())
|| (source->GetHeight() != dest->GetHeight())
)
{
//throw ArgumentException();
};
Rect* r = new Rect(0, 0, source->GetWidth(), source->GetHeight());
BitmapData* bdSrc = new BitmapData;
source->LockBits(r, ImageLockMode::ImageLockModeRead, PixelFormat32bppARGB, bdSrc); //PixelFormat(Format32bppArgb)
BitmapData* bdDst = new BitmapData;
dest->LockBits(r, ImageLockMode::ImageLockModeRead, PixelFormat32bppARGB, bdDst);
try
{
byte* bpSrc = (byte*)bdSrc->Scan0;//ToPointer()
byte* bpDst = (byte*)bdDst->Scan0;
bpSrc += (int)sourceChannel;
bpDst += (int)destChannel;
for (int i = r->Height * r->Width; i > 0; i--)
{
*bpDst = *bpSrc;
bpSrc += 4;
bpDst += 4;
}
}
catch (const std::exception&)
{
}
source->UnlockBits(bdSrc);
dest->UnlockBits(bdDst);
}
//////////////////////////////////////
//////////////////////////////////////
//Bitmap* bmpFluffy = new Bitmap(bmpOriginal);
Gdiplus::Bitmap* bmpFluffy = Gdiplus::Bitmap::FromFile(L"picture.png", false);
Rect r(0, 0, bmpFluffy->GetWidth(), bmpFluffy->GetHeight());
Bitmap* bmpMask = new Bitmap(r.Width, r.Height);
Graphics* g = Graphics::FromImage(bmpMask);
GraphicsPath* path = createRoundRect(
r.X, r.Y,
r.Width, r.Height,
min(r.Width, r.Height) / 5);;
int in_out_count = 1;
Brush brush = createFluffyBrush(
path,
new float[] { 0.0f, 0.1f, 1.0f },
new float[] { 0.0f, 0.95f, 1.0f }, 3, &in_out_count);
{
g->FillRectangle(new SolidBrush(Color::Black), r); //Brush to solidbrush
g->SetSmoothingMode(SmoothingMode::SmoothingModeHighQuality);
g->FillPath(&brush, path);
transferOneARGBChannelFromOneBitmapToAnother(
bmpMask,
bmpFluffy,
ChannelARGB::Blue,
ChannelARGB::Alpha);
}
I have no experience with .net, and im not sure if the code has been converted correctly, i wonder if someone could take a look at it.
Current im getting error only at this line: Brush brush = createFluffyBrush(
no suitable constructor exist to convert from "Gdiplus::Brush *" to "Gdiplus::Brush"
createFluffyBrush return a pointer to Brush,
so you should use :
Brush brush = *createFluffyBrush(...)
or (better here ?)
Brush * brush = createFluffyBrush(...)
Avoid using the new operator in modern C++. Gdiplus is kind of old, so you can keep using pointers and new in some places, for example to declare Gdiplus::Bitmap* object. For structures such as Gdiplus::GraphicsPath you can use pass by reference, that's usually the easiest and safest method.
To draw rounded images, get the rounded path, clip the region, then use anti-aliasing to draw a rounded rectangle over that image. Example:
#include <windows.h>
#include <gdiplus.h>
#pragma comment (lib,"Gdiplus.lib")
void GetRoundRectPath(Gdiplus::GraphicsPath& path, Gdiplus::Rect rc, int diameter)
{
if (diameter > rc.Width) diameter = rc.Width;
if (diameter > rc.Height) diameter = rc.Height;
int dx = rc.Width - diameter;
int dy = rc.Height - diameter;
Gdiplus::Rect tl(rc.X, rc.Y, diameter, diameter); //top-left
Gdiplus::Rect tr = tl; tr.Offset(dx, 0); //top-right
Gdiplus::Rect br = tl; br.Offset(dx, dy); //bottom-right
Gdiplus::Rect bl = tl; bl.Offset(0, dy); //bottom-left
path.Reset();
path.AddArc(br, 0, 90);
path.AddArc(bl, 90, 90);
path.AddArc(tl, 180, 90);
path.AddArc(tr, 270, 90);
path.CloseFigure();
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static Gdiplus::Image* img = nullptr;
switch (message)
{
case WM_CREATE:
img = Gdiplus::Bitmap::FromFile(L"test.jpg");
if (img->GetLastStatus() != 0)
MessageBox(0, L"no image", 0, 0);
return 0;
case WM_PAINT:
{
if (!img)
break;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
Gdiplus::Graphics g(hdc);
Gdiplus::GraphicsPath path;
Gdiplus::Rect rc{ 10, 10, (int)img->GetWidth(), (int)img->GetHeight() };
GetRoundRectPath(path, rc, 32);
Gdiplus::Region rgn(&path);
g.SetClip(&rgn);
g.DrawImage(img, rc);
rgn.MakeInfinite();
g.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
Gdiplus::Pen pen(Gdiplus::Color::White, 2.0F);
g.DrawPath(&pen, &path);
EndPaint(hWnd, &ps);
return 0;
}
case WM_DESTROY:
if(img) delete img;
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hinst, HINSTANCE, PSTR, INT)
{
ULONG_PTR token;
Gdiplus::GdiplusStartupInput tmp;
Gdiplus::GdiplusStartup(&token, &tmp, NULL);
WNDCLASS wndClass {0};
wndClass.lpfnWndProc = WndProc;
wndClass.hInstance = hinst;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.lpszClassName = L"test";
RegisterClass(&wndClass);
CreateWindow(wndClass.lpszClassName, L"Test", WS_VISIBLE|WS_OVERLAPPEDWINDOW,
100, 100, 800, 600, NULL, NULL, hinst, nullptr);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Gdiplus::GdiplusShutdown(token);
return 0;
}

Convert triangle strips to Octagonal?

I'm using the gl.h library and it outputs triangle strips. The example shows this:
#include <windows.h>
#include <gl/gl.h>
#include <math.h>
#include <stdio.h>
#define step 8
#define angle 3.1415926 * 2.f / step
LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
void EnableOpenGL(HWND hwnd, HDC*, HGLRC*);
void DisableOpenGL(HWND, HDC, HGLRC);
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASSEX wcex;
HWND hwnd;
HDC hDC;
HGLRC hRC;
MSG msg;
BOOL bQuit = FALSE;
float theta = 0.0f;
/* register window class */
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_OWNDC;
wcex.lpfnWndProc = WindowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "GLSample";
wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);;
if (!RegisterClassEx(&wcex))
return 0;
/* create main window */
hwnd = CreateWindowEx(0,
"GLSample",
"OpenGL Sample",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
600,
600,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, nCmdShow);
/* enable OpenGL for the window */
EnableOpenGL(hwnd, &hDC, &hRC);
/* program main loop */
while (!bQuit)
{
/* check for messages */
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
/* handle or dispatch messages */
if (msg.message == WM_QUIT)
{
bQuit = TRUE;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
/* OpenGL animation code goes here */
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glPushMatrix();
//glRotatef(theta, 0.0f, 0.0f, 1.0f);
float xPos = 0;
float yPos = 0;
float radius = 0.5f;
float prevX = xPos;
float prevY = yPos - radius;
for (int i=0;i<=step;i++)
{
float newX = radius * sin(angle * i);
float newY = - radius * cos(angle * i);
glBegin(GL_TRIANGLES);
glColor3f(0,0.5f,0);
glVertex3f(0.0f,0.0f,0.0f);
glVertex3f(prevX , prevY , 0.0f);
glVertex3f(newX , newY , 0.0f);
prevX = newX;
prevY = newY;
}
glEnd();
glPopMatrix();
SwapBuffers(hDC);
theta += 1.0f;
Sleep (1);
}
}
/* shutdown OpenGL */
DisableOpenGL(hwnd, hDC, hRC);
/* destroy the window explicitly */
DestroyWindow(hwnd);
return msg.wParam;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
PostQuitMessage(0);
break;
case WM_DESTROY:
return 0;
case WM_KEYDOWN:
{
switch (wParam)
{
case VK_ESCAPE:
PostQuitMessage(0);
break;
}
}
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
void EnableOpenGL(HWND hwnd, HDC* hDC, HGLRC* hRC)
{
PIXELFORMATDESCRIPTOR pfd;
int iFormat;
/* get the device context (DC) */
*hDC = GetDC(hwnd);
/* set the pixel format for the DC */
ZeroMemory(&pfd, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW |
PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 16;
pfd.iLayerType = PFD_MAIN_PLANE;
iFormat = ChoosePixelFormat(*hDC, &pfd);
SetPixelFormat(*hDC, iFormat, &pfd);
/* create and enable the render context (RC) */
*hRC = wglCreateContext(*hDC);
wglMakeCurrent(*hDC, *hRC);
}
void DisableOpenGL (HWND hwnd, HDC hDC, HGLRC hRC)
{
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hRC);
ReleaseDC(hwnd, hDC);
}
float xPos = 0;
float yPos = 0;
float radius = 0.5f;
float prevX = xPos;
float prevY = yPos - radius;
for (int i=0;i<=step;i++)
{
float newX = radius * sin(angle * i);
float newY = - radius * cos(angle * i);
glBegin(GL_TRIANGLES);
glColor3f(0,0.5f,0);
glVertex3f(0.0f,0.0f,0.0f);
glVertex3f(prevX , prevY , 0.0f);
glVertex3f(newX , newY , 0.0f);
prevX = newX;
prevY = newY;
}
glEnd();
The issue is in the fact that this renders multiple triangle strips. This is a problem for me. My application renders with IDE - code Blocks v17.12 and I need a way to modify the above code so that instead it could look something more like this:
Use the polygon rasterization mode GL_LINE to render a wire frame:
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
float xPos = 0.0f;
float yPos = 0.0f;
float radius = 0.5f;
int steps = 8;
float prevX = xPos;
float prevY = yPos - radius;
glBegin(GL_TRIANGLES);
glColor3f(0.0f, 0.5f, 0.0f);
for (int i=0; i <= step; ++i)
{
float angle = 3.1415926f * 2.0f * i / step;
float newX = radius * sin(angle);
float newY = - radius * cos(angle);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(prevX, prevY, 0.0f);
glVertex3f(newX, newY, 0.0f);
prevX = newX;
prevY = newY;
}
glEnd();
Use a GL_TRIANGLE_FAN Triangle primitive to simplify your code:
glBegin(GL_TRIANGLE_FAN);
glColor3f(0.0f, 0.5f, 0.0f);
for (int i=0; i <= step; ++i)
{
float angle = 3.1415926f * 2.0f * i / step;
float newX = radius * sin(angle);
float newY = - radius * cos(angle);
glVertex3f(newX, newY, 0.0f);
}
glEnd();
If you want to use a triangle GL_TRIANGLE_STRIP, you must change the order of the vertices:
glBegin(GL_TRIANGLE_STRIP);
glColor3f(0.0f, 0.5f, 0.0f);
for (int i=0; i <= step; ++i)
{
int strip_i = i % 2 == 0 ? i/2 : step - i/2;
float angle = 3.1415926f * 2.0f * strip_i / step;
float newX = radius * sin(angle);
float newY = - radius * cos(angle);
glVertex3f(newX, newY, 0.0f);
}
glEnd();
If you want to render the wire frame on top of the polygon, you need to enable the depth test and render the polygon first. Note the default depth test function is GL_LESS:
for (int pass = 0; pass < 2; ++pass)
{
if (pass == 0)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glColor3f(1.0f, 1.0f, 1.0f);
}
else
{
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glColor3f(0.0f, 0.5f, 0.0f);
}
glBegin(GL_TRIANGLE_STRIP);
for (int i=0; i <= step; ++i)
{
int strip_i = i % 2 == 0 ? i/2 : step - i/2;
float angle = 3.1415926f * 2.0f * strip_i / step;
float newX = radius * sin(angle);
float newY = - radius * cos(angle);
glVertex3f(newX, newY, 0.0f);
}
glEnd();
}

Double buffering in Direct2D?

I'm very new to Direct2D programming and have been following a tutorial. I've adapted the example given in the tutorial to a slightly more complicated program that bounces a ball off the boundaries of the window.
My main program (main.cpp):
#include "Graphics.h"
Graphics* graphics;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// Exit handler
if (uMsg == WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPWSTR cmd, int nCmdShow)
{
WNDCLASSEX windowClass;
SecureZeroMemory(&windowClass, sizeof(WNDCLASSEX));
// Set up window
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
windowClass.hInstance = hInstance;
windowClass.lpfnWndProc = WindowProc;
windowClass.lpszClassName = "MainWindow";
windowClass.style = CS_HREDRAW | CS_VREDRAW;
// Register window class and handle
RegisterClassEx(&windowClass);
RECT rect = { 0, 0, 800, 600 };
AdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, false, WS_EX_OVERLAPPEDWINDOW);
HWND windowHandle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, "MainWindow", "Test Window", WS_OVERLAPPEDWINDOW, 100, 100,
rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, hInstance, 0);
if (!windowHandle)
return -1;
graphics = new Graphics();
if (!graphics->Init(windowHandle))
{
delete graphics;
return -1;
}
ShowWindow(windowHandle, nCmdShow);
// Message loop
float x = 51.0, xSpeed = 5.0f, y = 0.0, ySpeed = 5.0f;
MSG message;
message.message = WM_NULL;
while (message.message != WM_QUIT)
{
if (PeekMessage(&message, NULL, 0, 0, PM_REMOVE))
DispatchMessage(&message);
else
{
// Ball physics
//xSpeed += 0.6f;
x += xSpeed;
ySpeed += 0.2f;
y += ySpeed;
if (y > rect.bottom - 50)
{
ySpeed = -ySpeed;
}
if (x > rect.right - 50)
{
xSpeed = -xSpeed;
}
else if (x < 50)
{
xSpeed = -xSpeed;
}
// Redraw ball
graphics->beginDraw();
graphics->clearScreen(0.0f, 0.0f, 0.5f);
graphics->drawCircle(x, y, 50.0f, 1.0f, 1.0f, 1.0f, 1.0f);
graphics->endDraw();
}
}
delete graphics;
return 0;
}
My header file (Graphics.h):
#pragma once
#include <Windows.h>
#include <d2d1.h>
class Graphics
{
ID2D1Factory* factory;
ID2D1HwndRenderTarget* renderTarget;
ID2D1SolidColorBrush* brush;
public:
Graphics();
~Graphics();
bool Init(HWND windowHandle);
void beginDraw() { renderTarget->BeginDraw(); }
void endDraw() { renderTarget->EndDraw(); }
void clearScreen(float r, float g, float b);
void drawCircle(float x, float y, float radius, float r, float g, float b, float a);
};
My graphics functions (Graphics.cpp):
#include "Graphics.h"
#define CHECKRES if (res != S_OK) return false
Graphics::Graphics()
{
factory = NULL;
renderTarget = NULL;
brush = NULL;
}
Graphics::~Graphics()
{
if (factory)
factory->Release();
if (renderTarget)
renderTarget->Release();
if (brush)
brush->Release();
}
bool Graphics::Init(HWND windowHandle)
{
HRESULT res = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory);
CHECKRES;
RECT rect;
GetClientRect(windowHandle, &rect);
res = factory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(windowHandle, D2D1::SizeU(rect.right, rect.bottom)),
&renderTarget
);
CHECKRES;
res = renderTarget->CreateSolidColorBrush(D2D1::ColorF(0, 0, 0, 0), &brush);
CHECKRES;
return true;
}
void Graphics::clearScreen(float r, float g, float b)
{
renderTarget->Clear(D2D1::ColorF(r, g, b));
}
void Graphics::drawCircle(float x, float y, float radius, float r, float g, float b, float a)
{
brush->SetColor(D2D1::ColorF(r, g, b, a));
renderTarget->DrawEllipse(D2D1::Ellipse(D2D1::Point2F(x, y), radius, radius), brush, 3.0f);
}
While this program does work fine, there is some minor tearing on the ball's bounce. I have seen this question which lead me to this MSDN article. Despite reading the article, I do still not fully understand how to implement double buffering, to hopefully reduce tearing. Can someone provide a concise example and explanation of the ID2D1RenderTarget::CreateCompatibleRenderTarget, as this high level Windows programming is quite different from what I'm used to?
Check article here. ID2D1HwndRenderTarget objects are double buffered by nature and drawing is done to the offscreen buffer first and when drawing ends it will be blitted to the screen.

Faster way to draw huge amount of triangle / height map (Direct 3D, C++)

I'm currently writting a 3D visualization tool for a scientific application and I am facing a performance problem.
I have a relatively large grid (~ 1000 rows x 1000 columns) and for each point of this grid I have a physical value that I want to represent as height (for instance: temperature).
Here is an example of what I am trying to draw with white gaussian noise:
http://i.stack.imgur.com/KM23m.jpg
I am using DirectX 9 to draw my scene. I basiclaly draw a bunch of triangles with the (X,Y) coordinate being a point on the grid, and the Z coordinate being the physical measurement at that point.
Here are the operations that I do on each frame:
I create a vertex buffer (CreateVertexBuffer()) and an index buffer (CreateIndexBuffer()) (the data changes on each frame, but the size of the data does not)
I lock them
I fill the two buffers properly, including assigning a color depending on the value (high value are red, low value are blue)
I unlock them
I set the stream source (SetStreamSource()), set the indices (SetIndices()) and draw the triangles (as a triangle strip)
I release the two buffers
My problem is that the frame rate is not as high as expected.
I achieve ~30fps for ~2 millions triangles drawn on a Nvidia GTX 770 (& Intel Core i7 4770k if that matters) when I want to have at least 60fps.
Is there a better way to do what I am doing or my problem is that the number of triangle is too large ?
Will I get better performance if I use DirectX 11 ?
Thank you for your help.
Edit here is a stand-alone simplified code:
#include <windows.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <windowsx.h>
#include <d3d9.h>
#include <vector>
#include <random>
#include <fcntl.h>
#include <io.h>
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
#define COLORMAPSIZE 256
#pragma comment (lib, "d3d9.lib")
#define DEG2RAD(x) (x* (float)M_PI/180.0f)
#define CUSTOMFVF (D3DFVF_XYZ | D3DFVF_DIFFUSE)
std::default_random_engine randGenerator;
// global declarations
LPDIRECT3D9 d3d;
LPDIRECT3DDEVICE9 d3ddev;
const int Nrows = 1000, Ncols = 2000;
float indexAz=DEG2RAD(90), indexEl = DEG2RAD(60), distance=80;
const float dataAmplitude = 5.f;
std::vector<float> dataBuffer;
typedef struct D3DXVECTOR3 : public D3DVECTOR
{
public:
FLOAT x,y,z;
D3DXVECTOR3() {};
D3DXVECTOR3( FLOAT xx, FLOAT yy, FLOAT zz ) : x(xx), y(yy), z(zz) {};
} D3DXVECTOR3, *LPD3DXVECTOR3;
typedef struct {
float x, y, z;
D3DCOLOR color;
} Vertex;
void initD3D(HWND hWnd);
void resetD3D(HWND hWnd);
void render_frame(void);
void cleanD3D(void);
void draw_graphics(void);
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void CreateConsole() {
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
int consoleHandleR, consoleHandleW ;
long stdioHandle;
FILE *fptr;
AllocConsole();
std::wstring strW = L"Dev Console";
SetConsoleTitleW( strW.c_str() );
EnableMenuItem(GetSystemMenu(GetConsoleWindow(), FALSE), SC_CLOSE , MF_GRAYED);
DrawMenuBar(GetConsoleWindow());
GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), &consoleInfo );
stdioHandle = (long)GetStdHandle( STD_INPUT_HANDLE );
consoleHandleR = _open_osfhandle( stdioHandle, _O_TEXT );
fptr = _fdopen( consoleHandleR, "r" );
*stdin = *fptr;
setvbuf( stdin, NULL, _IONBF, 0 );
stdioHandle = (long) GetStdHandle( STD_OUTPUT_HANDLE );
consoleHandleW = _open_osfhandle( stdioHandle, _O_TEXT );
fptr = _fdopen( consoleHandleW, "w" );
*stdout = *fptr;
setvbuf( stdout, NULL, _IONBF, 0 );
stdioHandle = (long)GetStdHandle( STD_ERROR_HANDLE );
*stderr = *fptr;
setvbuf( stderr, NULL, _IONBF, 0 );
}
// Generate a random number following a uniform distribution
double rand(const double inf=0, const double sup=1) {
std::uniform_real_distribution<double> distribution(inf,sup);
return distribution(randGenerator);
}
// Update the buffer with new data
void UpdateDataBuffer()
{
static bool firstCall = true;
if (firstCall) //fill the whole buffer
{
for(unsigned k = 0 ; k < Nrows*Ncols ; k++)
dataBuffer[k] = (float)rand(0,dataAmplitude);
firstCall = false;
} else { // remove the first column, shift the whole buffer and update the last column
memmove(&dataBuffer[0], &dataBuffer[Nrows], (Ncols-1)*Nrows*sizeof(float));
for(unsigned k= Nrows*(Ncols-1) ; k < Nrows*Ncols ; k++)
dataBuffer[k] = (float)rand(0,dataAmplitude);
}
}
// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
CreateConsole();
randGenerator.seed( GetTickCount() );
dataBuffer.resize(Nrows * Ncols);
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;
LARGE_INTEGER frequency;
LARGE_INTEGER t1, t2;
float fps = 0.f;
float NFramAvg = 1.0f/10.0f, totalElapsedTime = 0.0f;
QueryPerformanceFrequency(&frequency);
while(TRUE)
{
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(msg.message == WM_QUIT)
break;
UpdateDataBuffer();
QueryPerformanceCounter(&t1);
render_frame();
QueryPerformanceCounter(&t2);
fps = fps - NFramAvg*fps + NFramAvg* frequency.QuadPart / (t2.QuadPart - t1.QuadPart);
totalElapsedTime += (t2.QuadPart - t1.QuadPart)*1000.0f / frequency.QuadPart;;
if (totalElapsedTime > 1000) {
printf("FPS = %g\n", fps);
totalElapsedTime = 0.0;
}
}
cleanD3D();
return msg.wParam;
}
// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int clickX, clickY;
static bool down = false;
switch(message)
{
case WM_LBUTTONDOWN:
if (!down)
{
clickX = GET_X_LPARAM(lParam);
clickY = GET_Y_LPARAM(lParam);
}
down = true;
break;
case WM_LBUTTONUP:
down = false;
break;
case WM_MOUSEMOVE:
if (down)
{
int dx = GET_X_LPARAM(lParam) - clickX;
int dy = GET_Y_LPARAM(lParam) - clickY;
indexAz += dx*DEG2RAD(0.5f);
if (indexEl + dy*DEG2RAD(0.5f) < M_PI_2 && indexEl + dy*DEG2RAD(0.5f) > -M_PI_2)
indexEl += dy*DEG2RAD(0.5f);
clickX += dx;
clickY += dy;
}
break;
case WM_MOUSEWHEEL:
{
int zDelta = GET_WHEEL_DELTA_WPARAM(wParam); //scroll power
distance -= 2*zDelta/120.f;
distance = max(1.0f, distance);
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
} break;
case WM_SIZE:
if (d3ddev)
resetD3D(hWnd);
break;
}
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;
RECT rect;
GetClientRect(hWnd, &rect);
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hWnd;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dpp.BackBufferWidth = rect.right - rect.left;
d3dpp.BackBufferHeight = rect.bottom -rect.top;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
d3d->CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp,
&d3ddev);
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
}
void resetD3D(HWND hWnd)
{
D3DPRESENT_PARAMETERS d3dpp;
RECT rect;
GetClientRect(hWnd, &rect);
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hWnd;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dpp.BackBufferWidth = rect.right - rect.left;
d3dpp.BackBufferHeight = rect.bottom -rect.top;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
if (d3dpp.BackBufferWidth && d3dpp.BackBufferHeight)
{
d3ddev->Reset(&d3dpp);
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
InvalidateRect(hWnd, NULL, FALSE);
}
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
}
D3DXVECTOR3 *D3DVec3Subtract(D3DXVECTOR3 *pOut, const D3DXVECTOR3 *pV1,const D3DXVECTOR3 *pV2)
{
pOut->x = pV1->x - pV2->x;
pOut->y = pV1->y - pV2->y;
pOut->z = pV1->z - pV2->z;
return pOut;
}
D3DXVECTOR3* D3DVec3Normalize(D3DXVECTOR3 *pOut,const D3DXVECTOR3 *pV)
{
FLOAT norm = sqrt( pV->x * pV->x + pV->y * pV->y + pV->z * pV->z );
pOut->x = pV->x / norm;
pOut->y = pV->y / norm;
pOut->z = pV->z / norm;
return pOut;
}
D3DXVECTOR3* D3DVec3Cross(D3DXVECTOR3 *pOut,const D3DXVECTOR3 *pV1,const D3DXVECTOR3 *pV2)
{
pOut->x = pV1->y*pV2->z - pV1->z*pV2->y;
pOut->y = pV1->z*pV2->x - pV1->x*pV2->z;
pOut->z = pV1->x*pV2->y - pV1->y*pV2->x;
return pOut;
}
FLOAT D3DVec3Dot(const D3DXVECTOR3 *pV1,const D3DXVECTOR3 *pV2)
{
return pV1->x * pV2->x + pV1->y * pV2->y + pV1->z * pV2->z;
}
D3DMATRIX* D3DMatrixLookAtLH(D3DMATRIX *pOut,const D3DXVECTOR3 *pEye,const D3DXVECTOR3 *pAt,const D3DXVECTOR3 *pUp)
{
D3DXVECTOR3 right, rightn, up, upn, vec, vec2;
D3DVec3Subtract(&vec2, pAt, pEye);
D3DVec3Normalize(&vec, &vec2);
D3DVec3Cross(&right, pUp, &vec);
D3DVec3Cross(&up, &vec, &right);
D3DVec3Normalize(&rightn, &right);
D3DVec3Normalize(&upn, &up);
pOut->m[0][0] = rightn.x;
pOut->m[1][0] = rightn.y;
pOut->m[2][0] = rightn.z;
pOut->m[3][0] = -D3DVec3Dot(&rightn,pEye);
pOut->m[0][1] = upn.x;
pOut->m[1][1] = upn.y;
pOut->m[2][1] = upn.z;
pOut->m[3][1] = -D3DVec3Dot(&upn, pEye);
pOut->m[0][2] = vec.x;
pOut->m[1][2] = vec.y;
pOut->m[2][2] = vec.z;
pOut->m[3][2] = -D3DVec3Dot(&vec, pEye);
pOut->m[0][3] = 0.0f;
pOut->m[1][3] = 0.0f;
pOut->m[2][3] = 0.0f;
pOut->m[3][3] = 1.0f;
return pOut;
}
D3DMATRIX* D3DXMatrixPerspectiveFovLH(D3DMATRIX *pOut, const FLOAT fovy, const FLOAT Aspect, const FLOAT zn, const FLOAT zf)
{
FLOAT yScale = tanf((float)M_PI_2 - fovy/2);
FLOAT xScale = yScale /Aspect;
memset(pOut,0, sizeof(*pOut));
pOut->_11 = xScale;
pOut->_22 = yScale;
pOut->_33 = zf/(zf-zn);
pOut->_34 = 1;
pOut->_43 = -zn*zf/(zf-zn);
return pOut;
}
long GetColor(const float x, const float inf, const float sup)
{
BYTE c =(BYTE)( 255 * (x-inf)/(sup-inf) );
return D3DCOLOR_XRGB(c,c,c);
}
// this is the function that puts the 3D models into video RAM
void draw_graphics(void)
{
static long colorTab[COLORMAPSIZE] = {0,};
static std::vector<Vertex> tmp;
static LPDIRECT3DVERTEXBUFFER9 v_buffer = NULL;
static LPDIRECT3DINDEXBUFFER9 i_buffer = NULL;
static unsigned NVertex = 0;
// Create empty IDirect3DTexture9
const unsigned MN = Nrows*Ncols;
unsigned k=0;
if (MN > tmp.size())
tmp.resize( MN );
if (colorTab[0] == 0) // if colortab empty, fill it
{
for(int i=0 ; i < COLORMAPSIZE ;i++)
colorTab[i] = GetColor((float)i, (float)0, (float)(COLORMAPSIZE-1));
}
if (!v_buffer)
d3ddev->CreateVertexBuffer(MN*sizeof(Vertex), 0,D3DFVF_XYZ | D3DFVF_DIFFUSE,D3DPOOL_MANAGED,&v_buffer,NULL);
float factor = (COLORMAPSIZE-1.0f)/dataAmplitude;
for (k=0 ; k < MN ; k++)
{
if (dataBuffer[k] >= dataAmplitude)
tmp[k].color = colorTab[COLORMAPSIZE-1];
else if (dataBuffer[k] <= 0)
tmp[k].color = colorTab[0];
else
tmp[k].color = colorTab[(int)( ( dataBuffer[k])*factor )];
}
float M_2 = Nrows/2.0f, N_2 = Ncols/2.0f;
k=0;
for (unsigned n=0 ; n < Ncols ; n++)
{
for (unsigned m=0 ; m < Nrows ; m++, k++)
{
tmp[k].x = M_2 - m;
tmp[k].z = n - N_2;
tmp[k].y = dataBuffer[k];
}
}
Vertex* pVoid;
v_buffer->Lock(0, 0, (void**)&pVoid, 0);
memcpy(pVoid, &tmp[0], MN*sizeof(Vertex));
v_buffer->Unlock();
if (!i_buffer)
d3ddev->CreateIndexBuffer(3*2*(Nrows-1)*(Ncols-1)*sizeof(DWORD), 0, D3DFMT_INDEX32, D3DPOOL_MANAGED,&i_buffer,NULL);
DWORD *indices;
i_buffer->Lock(0, 0, (void**)&indices, 0);
k=0;
for (unsigned n=0 ; n < Ncols-1 ; n++)
{
if (n!=0)
indices[k++] = n*Nrows;
for (unsigned m=0 ; m < Nrows-1 ; m++)
{
indices[k++] = m + n*Nrows;
indices[k++] = m + (n+1)*Nrows;
}
indices[k++] = Nrows-2 + (n+1)*Nrows;
}
NVertex = k;
i_buffer->Unlock();
d3ddev->SetStreamSource(0, v_buffer, 0, sizeof(Vertex));
d3ddev->SetIndices(i_buffer);
d3ddev->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, MN, 0, NVertex-2);
//printf("%d triangle drawn\n", NVertex-2);
//i_buffer->Release();
//v_buffer->Release();
}
// 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(CUSTOMFVF);
// set the view transform
D3DMATRIX matView; // the view transform matrix
float R = distance*25;
D3DMatrixLookAtLH(&matView,
&D3DXVECTOR3 (R*sin(indexAz)*cos(indexEl), R*sin(indexEl),R*cos(indexAz)*cos(indexEl)), // 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
D3DMATRIX matProjection; // the projection transform matrix
D3DXMatrixPerspectiveFovLH(&matProjection,
DEG2RAD(45), // the horizontal field of view
(FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT, // aspect ratio
0.001f, // the near view-plane
100000.f); // the far view-plane
d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection); // set the projection
indexAz += DEG2RAD(0.1f);
draw_graphics();
d3ddev->EndScene();
d3ddev->Present(NULL, NULL, NULL, NULL);
}
// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
d3ddev->Release();
d3d->Release();
}
Well, because you say you're IO bound, the best thing would be to reduce the amount of data you send to the GPU every update.
If you're targeting SM 3.0 or higher you can use vertex texture fetching to sample the height from a texture that you update every frame. This is a lot easier on the input assembler and on BW.
To do this most easily, you might want to change your vertex data, instead of supplying a float3 with xyz, you might want to supply a uint2 with just xz, this way you can directly fetch from the texture without doing a float to int conversion in your shader.
as a side note, there is very little reason for you to write a vertex's color to your VB if it can be trivially derived from its position, it would be less bw to calculate it in the shader with a lerp or some other blending operation.
I've done this very thing for a project of mine, and the speedup is massive on my GTX 480. Brought me from spending 50ms on rendering heightmaps to around 1ms.
Unless I am missing something you don't need to lock your index buffer and update it, you are setting it to the same values anyways. That will get rid of that 1000x2000 loop.
Also I wouldn't update the vertex buffer everyframe if the data isn't changing everyframe. Just update it whenever the data is actually updated.