I have created a simple particle simulation for a fountain from this source. I also wanted to implement this using C++ classes. So, tried to put the above part of the logic into classes at this source (This is a very nice framework). I just have to inherit the class and redefine the functionalities that I would like.
I defined the functions for display() in which I call my DrawObjects().
I am running into the following issues:
I am initializing the number of particles to as low as 50 or 100 or 500. Nothing visible.
I am trying to load a texture(BMP) for my particles. It is failing for some reason. I tried both relative and absolute paths. Then tried to load a RAW texture(using IrfanViewer) with no luck. Also tried to remove the texture code to make them simple OpenGL primitives. Again no luck.
I wrote a simple DrawAxis() function which actually needs to draw cyan colored axis with red colored strings. However, gray colored primitives are showing up. I checked the code for usage of grayscale configuration, but no such thing.
Note: Working on Windows VC++ 2010. Code can be found at NippyZip.
Minimal Code
Main.cpp
#include "ParticleSimulation.h"
int main(int argc, char *argv[]) {
ParticleSimulation particleSimulation(50);
particleSimulation.InitParticles();
particleSimulation.setLookAt(80.0, 80.0, 80.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
particleSimulation.startFramework(argc, argv);
// **Note** No code below startFramework() will get executed
return 0;
}
GlutFramework.cpp
#include "GlutFramework.h"
namespace glutFramework {
// Set constants
const double GlutFramework::FRAME_TIME = 1.0 / GlutFramework::FPS * 1000.0; // Milliseconds
GlutFramework *GlutFramework::instance = NULL;
GlutFramework::GlutFramework() {
elapsedTimeInSeconds = 0;
frameTimeElapsed = 0;
title = "GLUT Framework: Paul Solt 2010";
eyeVector = Vector<float>(0.0, 0.0, -10.0); // move the eye position back
position = 0.0f;
direction = 1.0 / FRAME_TIME;
}
GlutFramework::~GlutFramework() {
}
void GlutFramework::startFramework(int argc, char *argv[]) {
setInstance(); // Sets the instance to self, used in the callback wrapper functions
// Initialize GLUT
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowPosition(WINDOW_X_POSITION, WINDOW_Y_POSITION);
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutCreateWindow(title.c_str());
// Function callbacks with wrapper functions
glutReshapeFunc(reshapeWrapper);
glutMouseFunc(mouseButtonPressWrapper);
glutMotionFunc(mouseMoveWrapper);
glutDisplayFunc(displayWrapper);
glutKeyboardFunc(keyboardDownWrapper);
glutKeyboardUpFunc(keyboardUpWrapper);
glutSpecialFunc(specialKeyboardDownWrapper);
glutSpecialUpFunc(specialKeyboardUpWrapper);
init(); // Initialize
glutIdleFunc(runWrapper); // The program run loop
glutMainLoop(); // Start the main GLUT thread
}
void GlutFramework::load() {
// Subclass and override this method
}
void GlutFramework::display(float dTime) {
// Subclass and override this method
static int frame = 0;
std::cout << "GlutFramework Display: Frame: " << frame << ", dt(sec): " << dTime << ", Position: " << position << std::endl;
++frame;
// DEMO: Create a teapot and move it back and forth on the x-axis
glTranslatef(position, 0.0f, 0.0f);
glutSolidTeapot(2.5);
if(position > 4 && direction > 0) {
direction = -1.0 / FRAME_TIME;
} else if(position < -4 && direction < 0) {
direction = 1.0 / FRAME_TIME;
}
position += direction;
}
void GlutFramework::reshape(int width, int height) {
glViewport(0,0,(GLsizei)width,(GLsizei)height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, (GLdouble) width, 0.0, (GLdouble) height);
}
void GlutFramework::mouseButtonPress(int button, int state, int x, int y) {
printf("MouseButtonPress: x: %d y: %d\n", x, y);
}
void GlutFramework::mouseMove(int x, int y) {
printf("MouseMove: x: %d y: %d\n", x, y);
}
void GlutFramework::keyboardDown( unsigned char key, int x, int y )
{
// Subclass and override this method
printf( "KeyboardDown: %c = %d\n", key, (int)key );
if (key==27) { //27 =- ESC key
exit (0);
}
keyStates.keyDown( (int)key );
}
void GlutFramework::keyboardUp( unsigned char key, int x, int y )
{
// Subclass and override this method
printf( "KeyboardUp: %c \n", key );
keyStates.keyUp( (int)key );
}
void GlutFramework::specialKeyboardDown( int key, int x, int y )
{
// Subclass and override this method
printf( "SpecialKeyboardDown: %d\n", key );
}
void GlutFramework::specialKeyboardUp( int key, int x, int y )
{
// Subclass and override this method
printf( "SpecialKeyboardUp: %d \n", key );
}
// ******************************
// ** Graphics helper routines **
// ******************************
// Initialize the projection/view matricies.
void GlutFramework::setDisplayMatricies() {
/* Setup the projection and model view matricies */
int width = glutGet( GLUT_WINDOW_WIDTH );
int height = glutGet( GLUT_WINDOW_HEIGHT );
float aspectRatio = width/height;
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( 60, aspectRatio, 1, 500.0 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
gluLookAt(eyeVector.x, eyeVector.y, eyeVector.z,
centerVector.x, centerVector.y, centerVector.z,
upVector.x, upVector.y, upVector.z);
}
void GlutFramework::setupLights() {
GLfloat light1_position[] = { 0.0, 1.0, 1.0, 0.0 };
GLfloat white_light[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat lmodel_ambient[] = { 0.4, 0.4, 0.4, 1.0 };
GLfloat ambient_light[] = { 0.8, 0.8, 0.8, 1.0 };
glLightfv( GL_LIGHT0, GL_POSITION, light1_position );
glLightfv( GL_LIGHT0, GL_AMBIENT, ambient_light );
glLightfv( GL_LIGHT0, GL_DIFFUSE, white_light );
glLightfv( GL_LIGHT0, GL_SPECULAR, white_light );
glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient );
}
void GlutFramework::setLookAt(float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ, float upX, float upY, float upZ) {
eyeVector = Vector<float>(eyeX, eyeY, eyeZ);
centerVector = Vector<float>(centerX, centerY, centerZ);
upVector = Vector<float>(upX, upY, upZ);
}
Vector<float> GlutFramework::getEyeVector() const {
return eyeVector;
}
Vector<float> GlutFramework::getCenterVector() const {
return centerVector;
}
Vector<float> GlutFramework::getUpVector() const {
return upVector;
}
void GlutFramework::setTitle(std::string theTitle) {
title = theTitle;
}
// **************************
// ** GLUT Setup functions **
// **************************
void GlutFramework::init() {
glClearColor(0.0, 0.0, 0.0, 1.0);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
load();
}
void GlutFramework::setInstance() {
//std::cout << "GlutFramework::setInstance()" << std::endl;
instance = this;
}
void GlutFramework::run() {
if(frameRateTimer.isStopped()) { // The initial frame has the timer stopped, start it once
frameRateTimer.start();
}
frameRateTimer.stop(); // stop the timer and calculate time since last frame
double milliseconds = frameRateTimer.getElapsedMilliseconds();
frameTimeElapsed += milliseconds;
if( frameTimeElapsed >= FRAME_TIME ) { // If the time exceeds a certain "frame rate" then show the next frame
glutPostRedisplay();
frameTimeElapsed -= FRAME_TIME; // remove a "frame" and start counting up again
}
frameRateTimer.start(); // start the timer
}
void GlutFramework::displayFramework() {
if(displayTimer.isStopped()) { // Start the timer on the initial frame
displayTimer.start();
}
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // Clear once
displayTimer.stop(); // Stop the timer and get the elapsed time in seconds
elapsedTimeInSeconds = displayTimer.getElapsedSeconds(); // seconds
setupLights();
setDisplayMatricies();
display(elapsedTimeInSeconds);
glutSwapBuffers();
displayTimer.start(); // reset the timer to calculate the time for the next frame
}
// ******************************************************************
// ** Static functions which are passed to Glut function callbacks **
// ******************************************************************
void GlutFramework::displayWrapper() {
instance->displayFramework();
}
void GlutFramework::reshapeWrapper(int width, int height) {
instance->reshape(width, height);
}
void GlutFramework::runWrapper() {
instance->run();
}
void GlutFramework::mouseButtonPressWrapper(int button, int state, int x, int y) {
instance->mouseButtonPress(button, state, x, y);
}
void GlutFramework::mouseMoveWrapper(int x, int y) {
instance->mouseMove(x, y);
}
void GlutFramework::keyboardDownWrapper(unsigned char key, int x, int y) {
instance->keyboardDown(key,x,y);
}
void GlutFramework::keyboardUpWrapper(unsigned char key, int x, int y) {
instance->keyboardUp(key,x,y);
}
void GlutFramework::specialKeyboardDownWrapper(int key, int x, int y) {
instance->specialKeyboardDown(key,x,y);
}
void GlutFramework::specialKeyboardUpWrapper(int key, int x, int y) {
instance->specialKeyboardUp(key,x,y);
}
} // namespace
ParticleSimulation.h
#include "preheader.h"
#include "Particle.h"
#include "GlutFramework.h"
#ifndef ___PARTICLESIMULATION_H___
#define ___PARTICLESIMULATION_H___
using namespace glutFramework;
#ifndef ABS_IMAGE_LOCATION
#define ABS_IMAGE_LOCATION "E:/IIIT B/College/Sem 3/CG/GraphicAssignment3/images/particle.bmp"
#endif
class ParticleSimulation : virtual public GlutFramework{
private:
GLuint numParticles; // total number of particles in the system
Particle *particles;
GLuint textureParticle;
//static GLint textureCount;
//functions
void AllocateObjects(void );
void DeAllocateObjects(void );
public:
ParticleSimulation(void );
ParticleSimulation(GLuint numParticles);
~ParticleSimulation(void );
void InitParticles(void );
void EvolveParticle(void );
void DisplayObjects(void );
void LoadTextureRAW(const char * filename, int wrap);
void LoadTextureBMP(const char * filename, int wrap);
void FreeTexture(void );
void DrawAxis();
void RenderBitmapString(GLfloat x, GLfloat y, GLfloat z, void *font, char *string);
//virtual functions
void display(float dTime);
};
#endif //___PARTICLESIMULATION_H___
ParticleSimulation.cpp
#include "ParticleSimulation.h"
using namespace std;
. . .
void ParticleSimulation::DisplayObjects(){
// rendering functions
glLoadIdentity();
//glRotatef(20.0, 1.0, 0.0, 0.0); // show scene from top front
//glBindTexture(GL_TEXTURE_2D, this->textureParticle); // choose particle texture
for (int i = 0; i <= this->numParticles; i++){
GLfloat xpos = 0.0f, ypos = 0.0f, zpos = 0.0f;
particles[i].getPosition(xpos, ypos, zpos);
if(ypos < 0.0)
particles[i].setLifeTime(0.0);
if(particles[i].getActiveStatus() && particles[i].getLifeTime() > 0.0) {
GLfloat red = 0.0f, green = 0.0f, blue = 0.0f;
particles[i].getColor(red, green, blue);
glColor3f(red, green, blue);
glBegin(GL_TRIANGLE_STRIP);
glVertex2f(0.0,1.0); glVertex3f(xpos + 0.002, ypos + 0.002, zpos + 0.0); // top right
glVertex2f(0.0,0.0); glVertex3f(xpos - 0.002, ypos + 0.002, zpos + 0.0); // top left
glVertex2f(1.0,1.0); glVertex3f(xpos + 0.002, ypos - 0.002, zpos + 0.0); // bottom right
glVertex2f(1.0,0.0); glVertex3f(xpos - 0.002, ypos - 0.002, zpos + 0.0); // bottom left
//glTexCoord2f(1.0,0.0); glVertex3f(xpos - 0.002, ypos - 0.002, zpos + 0.0); // bottom left
glEnd();
} else {
particles[i].CreateParticle();
}
}
EvolveParticle();
}
void ParticleSimulation::EvolveParticle()
{
for(int i = 0; i <= this->numParticles; i++){ // evolve the particle parameters
GLfloat xpos = 0.0f, ypos = 0.0f, zpos = 0.0f;
GLfloat green = 0.0f, red = 0.0f, blue = 0.0f;
GLfloat xVel = 0.0f, yVel = 0.0f, zVel = 0.0f;
particles[i].setLifeTime( particles[i].getLifeTime() - particles[i].getDecay() );
particles[i].getPosition(xpos, ypos, zpos);
particles[i].getSpeed(xVel, yVel, zVel);
particles[i].getPosition(xpos += xVel, ypos += yVel, zpos += zVel);
particles[i].getSpeed(xVel, yVel -= 0.00007, zVel);
}
}
// Texture ///////////////////////////////////////////////////////////
// load a 256x256 RGB .RAW file as a texture
void ParticleSimulation::LoadTextureBMP(const char * filename, int wrap)
{
int width = 0, height = 0;
BYTE *data = NULL;
FILE *file = NULL;
// open texture data
file = fopen( filename, "rb" );
if ( file == NULL ) {
cout << "\nFile could not be opened." << endl;
return;
}
// allocate buffer
width = 256;
height = 256;
data = (BYTE *) malloc( width * height * 3 );
// read texture data
fread( data, width * height * 3, 1, file );
fclose(file);
// allocate a texture name
this->textureParticle = 0;
glGenTextures( 1, &this->textureParticle );
// select our current texture
glBindTexture( GL_TEXTURE_2D, this->textureParticle );
// select modulate to mix texture with color for shading
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
// when texture area is small, bilinear filter the closest MIP map
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_NEAREST );
// when texture area is large, bilinear filter the first MIP map
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
// if wrap is true, the texture wraps over at the edges (repeat)
// ... false, the texture ends at the edges (clamp)
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
wrap ? GL_REPEAT : GL_CLAMP );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
wrap ? GL_REPEAT : GL_CLAMP );
// build our texture MIP maps
gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width,
height, GL_RGB, GL_UNSIGNED_BYTE, data );
// free buffer
free( data );
return;
}
void ParticleSimulation::FreeTexture(void )
{
glDeleteTextures(1, &this->textureParticle);
}
void ParticleSimulation::DrawAxis() {
char s1[10];
glPushMatrix();
glColor3f(1.0f, 0.0f, 0.0f);
sprintf(s1, "X-axis");
RenderBitmapString(50.0f, 0.0f, 0.0f, GLUT_BITMAP_HELVETICA_12, s1);
sprintf(s1, "Y-axis");
RenderBitmapString(0.0f, 50.0f, 0.0f, GLUT_BITMAP_HELVETICA_12, s1);
sprintf(s1, "Z-axis");
RenderBitmapString(0.0f, 0.0f, 50.0f, GLUT_BITMAP_HELVETICA_12, s1);
sprintf(s1, "Origin");
RenderBitmapString(0.0f, 0.0f, 0.0f, GLUT_BITMAP_HELVETICA_12, s1);
glPointSize(4.0f);
glColor3f(0.0f, 1.0f, 1.0f);
glBegin(GL_LINE_STRIP);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 50.0f, 0.0f);
glEnd();
glBegin(GL_LINE_STRIP);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(50.0f, 0.0f, 0.0f);
glEnd();
glBegin(GL_LINE_STRIP);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 50.0f);
glEnd();
glPopMatrix();
}
void ParticleSimulation::display(float dTime)
{
static int frame = 0;
//cout << "GlutFramework Display: Frame: " << frame << ", dt(sec): " << dTime << ", Position: " << position << endl;
++frame;
//cout << "Calling the DisplayObject Function" << endl;
//glTranslatef(0.0f, 0.0f, 0.0f);
//glutSolidTeapot(2.5);
DrawAxis(); //Aditya: Working with grayscale though
DisplayObjects(); //Aditya: Nothing is drawn
}
LoadTextureBMP() is being invoked (with its bevy of OpenGL calls) before you have an OpenGL context (via startFramework()).
You need a current context before issuing GL commands.
EDIT:
for(int i = 0; i <= this->numParticles; i++)
Stop that. You're accessing off the end of your allocated memory.
Do this:
for(int i = 0; i < this->numParticles; i++)
Notice the less-than instead of the less-than-or-equal. For C/C++ arrays of size N the only valid indexes are in the range [0, N-1]. Your <= will cause the for-loop to try to access element N.
That's bad.
EDIT2:
GlutFramework::init() enables lighting. This is altering the colors in your axis rendering in ParticleSimulation::DrawAxis(). Disable it in ParticleSimulation::display() via glDisable(GL_LIGHTING):
EDIT3:
This will do something but probably not what you expect:
glBegin(GL_TRIANGLE_STRIP);
glVertex2f(0.0,1.0); glVertex3f(xpos + 0.002, ypos + 0.002, zpos + 0.0); // top right
glVertex2f(0.0,0.0); glVertex3f(xpos - 0.002, ypos + 0.002, zpos + 0.0); // top left
glVertex2f(1.0,1.0); glVertex3f(xpos + 0.002, ypos - 0.002, zpos + 0.0); // bottom right
glVertex2f(1.0,0.0); glVertex3f(xpos - 0.002, ypos - 0.002, zpos + 0.0); // bottom left
glEnd();
You seem to think that glVertex2f() will somehow generate texture coordinates. It will not. You're looking for glTexCoord2f().
For drawing view-aligned triangles you'll want to look into billboarding.
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