-
DirectX8 FVFs
Just a quick question about D3DFVF_DIFFUSE, D3DFVF_XYZRHW and other definitions like that. I'm trying to make a function to declare a FVF manually, by passing through the necessery boolean flags, and the function will initialize the appropriate FVF. But I'm wondering what to store the combined flags as.
I.e. for "Window Creation" you can set a whole bunch of styles under a DWORD variable.
(DWORD Styles = Style1|Style2|Style3);
And so forth. Is it possible to use a DWORD type variable to hold values for each FVF "style"? Even after looking at the definition of DWORD (unsigned long), it doesn't help me much to figure out if it's useable or not. :)
Any insight's welcome.
Erik
-
The current FVF flags can all be described by 2 bytes. You might want to just use 4 anyways in case they add more later.
-
There are 2 rules for defining FVFs.
1. The FVF declaration must match the order of the members.
For instance:
struct TestVertex
{
float x,y,z;
float nx,ny,nz;
float u,v;
static const DWORD FVF;
};
The FVF declaration for this vertex structure has to be:
const DWORD FVF=D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;
Because you have defined it as position vector, normal vector, texture coords. Any other definition will cause problems.
2. There are certain flags that cannot be combined and certain ones that do not work on all video cards. Take the point size declaration for point sprites. Not all video cards support variable size point sprites and most only support 64x64 pixel point sprites. Anything outside of this range will default back to 64x64 - which is one reason why I rarely use point sprites because they are so limited. However they are nice for particle effects like snow, stars, rain, etc. They are not good for stuff like len's flares, corona's, fire, etc.
Other than that I've always seen FVF's defined as DWORDs.
So I'm not sure what the exact values for the declarations are. I just use a DWORD to be safe and quite honestly I have bigger fish to fry than wondering whether or not FVF is a 2 byte or 4 byte value.
-
Cool, thanks for the info, but I just realized I probably can't do this anyways.....I was being stupid not remembering that I actually have to declare a structure for a vertex....hmm....I'd have to write out all the possible structures (with all the combinations I want to allow), and then create a variable based on the data...but that wouldn't work anyways because the variable would quickly go out of scope....hmm....
Has anyone ever done this before? Or any ideas better than writing out a large number of structures to meet every possible combination of flags? :)
Thanks for the help so far
[EDIT]
Forget it :) I'm giving up, I don't know if this is a cheap way to get around the problem or not, but I'm pretty much just going to declare the structure to use all of the flags I want, which actually don't turn out to be that many, but still a considerable amount. And then I'll just use the values I need when I need them.
Along a somewhat same line, but not quite, I'm just wondering...
Code:
//VALUE DEFINITIONS
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)
//VERTEX STRUCTURE
struct CUSTOMVERTEX
{
float X, Y, Z, RHW;
unsigned long Colour;
float TU, TV;
};
How, oh how, do I store vertices as a private member variable in a class?
The good ol'
Code:
CUSTOMVERTEX m_pVertices[] ={
//INSERT ALL INFO HERE
}
Method does not work, as I can't initialize variables in the class definiton. Which works out sort of okay because I'm planning to initalise all values through code with AddSquareToBuffer() functions and the such, but still I need a member variable to hold my vertices...
Which left me trying:
Code:
CUSTOMVERTEX m_pVertices[];
//OR
CUSTOMVERTEX m_pVertices;
Both of which my compiler does not seem to enjoy very thoroughly.
Another idea was to create the m_pVertices variable at another point, but it doesn't take long to see that a function will quickly go out of scope and leave me without the information anymore. Unless I pass it along from function to function....all the time....without rest.
Is there something simple I'm missing here?
Note: I'm hoping to stay away from the
Code:
#define BE_INEFFICIENT_AS_POSSIBLE 1000000000
CUSTOMVERTEX m_pVertices[BE_INEFFICIENT_AS_POSSIBLE];
Method...
Thanks for any help :)
Erik
[/EDIT]
-
You are incorrect, my friend. If you declare your vertexes as the type of vertex you have declared then the variable will not go out of scope in your class or in your code. Also DirectX provides several default configs of vertexes and is quite useful.
What do you need in your vertex??? Normals? Texture coords? Position? Point size (for point sprites)? ??? You can include all of this information in DirectX.
This is an example of a vertex that has a position vector, normal vector, and texture coordinates. Note that you can also create a constructor inside the vertex structure because in ALL Direct3D SetStreamSource() calls in prep for a DrawPrimitive or DrawIndexedPrimitive you must pass in the stride factor. The stride factor is essentially the size of one vertex. In this way you can store more information than DirectX uses and still be able to send it through the fixed function pipeline.
I'm going to write a fair amount of code here to show you how a lot of this fits together.
CFileLog.h
Code:
#ifndef CFILELOG
#define CFILELOG
#include <fstream.h>
#include <time.h>
class CFileLog
{
char curtime[10];
char curdate[10];
public:
ofstream File;
CFileLog(void) {};
~CFileLog(void) {File.close();};
void UpdateDateTime(void)
{
_strtime(curtime);
_strdate(curdate);
}
char *GetTime(void) {return curtime;};
char *GetDate(void) {return curdate;};
void Create(const char *filename,const char *title)
{
File.open(filename);
//Put header in
File << title << endl;
UpdateDateTime();
File << "Created on " << curdate << " at "<<curtime<<endl;
File << "--------------------" << endl;
File << endl;
}
void Close(void) {File.close();};
};
#endif
Object3D.h
Code:
#ifndef OBJECT3D
#define OBJECT3D
#include "CFileLog.h"
#include <string>
#include "d3dx9.h"
struct CustomVertex
{
D3DXVECTOR3 pos;
D3DXVECTOR3 normal;
float u,v;
static const DWORD FVF;
CustomVertex(D3DXVECTOR3 _pos,D3DXVECTOR3 _normal,float _u,float _v):pos
(_pos),normal(_normal),u(_u),v(_v) {}
};
class Object
{
protected:
CFileLog ObjectLog;
D3DXVECTOR3 World_pos;
D3DXVECTOR3 Model_rotation;
D3DXVECTOR3 World_rotation;
IDirect3DDevice9 *Device;
IDirect3DVertexBuffer9 *VB;
IDirect3DIndexBuffer9 *IB;
IDirect3DTexture9 *Texture;
int NumVertices;
int NumTriangles;
void SetTexture(std::string File)
{
if (FAILED(D3DXCreateTextureFromFile(Device,File.c_str(),&Texture)))
{
Object.Log.File << "Failed to load object texture" << endl;
return;
}
}
public:
Object(void) { ...init all vars to 0 or NULL... };
~Object(void)
{
if (VB) VB->Release();
if (IB) IB->Release();
if (Texture) Texture->Release();
}
void Init(IDirect3DDevice9 *_device,const char *LogFilename,const char *LogTitle,std::string TextureFile)
{
Device=_device;
ObjectLog.Create(LogFilename,LogTitle);
SetTexture(TextureFile);
}
void Create(D3DXVECTOR3 _worldpos,D3DXVECTOR3 _modelrot,D3DXVECTOR3 _worldrot)
{
World_pos=_worldpos;
Model_rotation=_modelrot;
World_rotation=_worldrot;
NumVertices= <...your data goes here...>;
int NumFaces= <...your data goes here...>;
NumTris= <...your data goes here...>
int NumIndices=<...your data goes here...>;
Device->CreateVertexBuffer(NumVertices,D3DUSAGE_WRITEONLY,CustomVertex::FVF,D3DPOOL_MANAGED,&VB,NULL);
CustomVertex *Vertices;
VB->Lock(0,0,(void **)&Vertices,0);
//Fill in your object vertex data here
VB->Unlock();
Device->CreateIndexBuffer(NumIndices,D3DUSAGE_WRITEONLY,D3DFMT_INDEX16,D3DPOOL_MANAGED,&IB,NULL);
WORD *Indices;
IB->Lock(0,0,(void **)&Indices,0);
//Fill in your index data here
IB->Unlock();
}
void Render(float timeDelta)
{
D3DXMATRIX trans;
D3DXMatrixTranslation(&trans,World_pos.x,World_pos.y,World_pos.z);
D3DXMATRIX mrot;
D3DXRotationYawPitchRoll(&mrot,Model_rotation.x,Model_rotation.y,Model_rotation.z);
D3DXMATRIX wrot;
D3DXRotationYawPitchRoll(&wrot,World_rotation.x,World_rotation.y,World_rotation.z)
D3DXMATRIX world;
world=mrot*trans*wrot;
Device->SetTransform(D3DTS_WORLD,&world);
//Set any custom render states applicable to this object only here
Device->SetStreamSource(0,&VB,0,sizeof(CustomVertex));
Device->SetIndices(&IB);
Device->SetFVF(CustomVertex::FVF);
Device->SetTexture(0,Texture);
Device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,NumVertices,0,NumTris);
//Disable custom render states here so they are not applied to other objects
}
};
Lots of code but it shows a good example of how you might fit all of this together. Feel free to use it.
-
Hm, I probably should have mentioned this before, but I'm using DX8. Is there anything in this code that I can't convert to DX8 strictly due to the functions or variable types not existing? (Not that it may be difficult to translate, but is there something that I actually can not physically do under DX8?)
But this is awesome, thanks for the code so far, I've been trying to pick it apart for the last couple of days, and it's slowly coming along.
I've just a question about:
Code:
Device->CreateVertexBuffer(NumVertices,D3DUSAGE_WRITEONLY, CustomVertex::FVF,D3DPOOL_MANAGED,&VB,NULL);
Specifically the:
part. I see you declare FVF as a "static const", but, when in the world does it ever get assigned a value if it's a "const" and not actually initialized right then and there in your structure definition? (From what I can tell anyways)
Also, just a general question about classes I guess, I didn't know I could actually initialize variables within the header file as so:
Code:
Constructor(void) {var1(0), var2(0)};
I'm pretty sure that's the method I saw being used a while back, but there might be some syntax (or just general) problems with what I wrote there, but I think you can get what I mean. The question is, say my variable is a pointer, or another type that I generally initalise as NULL. Will the "(0)" do the same job, or should I put "(NULL)" instead? (I don't know if this is a ridiculous question, all common sense is telling me to put (NULL), but before I start using this new method I want to be absolutely sure)
Thanks again, I really appreciate the help you've given so far (You too MrWizard) :)
-
Heyo. You are right, he didn't put the line of code that assigns the const it's value. You will need something like:
Code:
const DWORD CustomVertex::FVF = D3DFVF_XYZ; // whatever
You can put that in the header after the declaration or in the associated cpp file if you want.
As far as the class constructor assignments. You have to do it as follows:
Code:
// ctor
Foo::Foo(void) : m_myFloat(5.3f), m_myInt(100) { }
You see the syntax? Those assignments do not go within the curly braces. You can put whatever else you want in the body of the constructor but those initializations go before the braces and after the colon.
You can use NULL or 0 for the pointer. I personally use NULL but that's just a preference. Feel free to use 0 if you prefer.
-
That's because all my code was in Object3D.h. The const assignment goes into Object3D.cpp - but I didn't want to write a header and a source file on the board so I merged it into one.
I'm not sure if MSVC will accept assigning the const a value inside of the header. I've had problems with this approach. The problem is that when the header is included it is re-assigned. This should be taken care of by the multiple include check using the #ifndef blocks, but for some reason MSVC still says that the FVF has been redefined. Assigning values to variables in headers will always result in problems. I've never been able to assign any variable to any value inside of a header save for #define's and other macro definitions.
-
Awesome, thanks for clearing that up guys.
Just a quick question about boolean variables. For some reason, I'm getting odd results when passing them as references. I.e.
Code:
int main(void)
{
bool bVar = false;
setBool(&bVar);
while(bVar)
{
MessageBox(NULL, "Should See This", "Blah", MB_OK);
bVar = false;
}
return 0;
}
void setBool(bool *bVar)
{
*bVar = true;
}
This is a very simple version of the code I'm using, and my problem is that once I get to that "while" loop, the program just hangs. It doesn't go into the loop at all, it just sits there and doesn't want to go any further.
I've stepped through and watched the value of my boolean variable, and everything seems to be working fine, I get a 0 when it's false, and a 1 for the boolean variable in the main function. But, when MSVC shows me the value, it looks something like:
bVar = 1 "|"
Now, while the 1 is correct, I don't know what the big straight line is doing in the quotes. To make a short question ridiculously long, it's okay to pass boolean variables as references right?
I started writing this code (not the code you see above) :) about a year ago, and it's awesome for me to see how it's come along, mainly thanks to the help I've gotten here on this board. It's gone from one enormous CPP file with about 30 global variables to a nice mish-mash of a main cpp file and classes and pointers, and as the days go by, it just looks prettier and prettier :) Thanks again for all the help everyone here's given.
-
The code should work fine. Are you using VC6? I tested it out just to double-check and it worked as advertised on my machine.
-
It's times like this I wonder how I've ever learned to program enough to be attempting DirectX. You're right, the code above works perfectly, just as it should...
It wouldn't work, though, if for some reason one was to place a semi-colon at the end of the while(bVar) line....like I had.
Thanks for testing out the code and in turn making me take a second (closer) look at mine. :)
-
Man I've made that same error about a billion times. I'll go searching through line after line of code only to find it was my own stupid fault.
I also love it when I implement some new class in my game and then MSVC proceeds to puke out about 50,000 errors along with 10,000 warnings, when I only added 20 lines of code at the max.
I'm starting to understand MSVC extremely cryptic and extremely long errors. If anyone from MS is reading this which I doubt they are - please write your errors in English and please keep them short enough that I don't have to scroll over 20 times just to read the error.
Borland definitely has a monopoly over MS on one thing - compilers that are easy to use.
-
Braces should be mandatory. It would save a lot of trouble and it wouldn't hurt readability that much.
My program teacher forbade everyone to write compund statements withot braces.
-
Alright, I've gotten through much of your code (Bubba) and translated it into something working, thanks again for the help, it's pushed this project of mine along very nicely (AND it made me learn about Index Buffers and what they are)
Other than the fact that this code is grossly unneccessary and accomplishes absolutely nothing, is there anything wrong with the following code:
Code:
HRESULT cObject2D::InitVBuffer()
{
HRESULT Attempt;
Attempt = m_pD3DDevice->CreateVertexBuffer(4 * sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &m_pVBuffer2D);
if(FAILED(Attempt))
return E_FAIL;
m_pVBuffer->Release();
m_pVBuffer = NULL;
Attempt = m_pD3DDevice->CreateVertexBuffer(10 * sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &m_pVBuffer2D);
if(FAILED(Attempt))
return E_FAIL;
m_pVBuffer->Release();
m_pVBuffer = NULL;
Attempt = m_pD3DDevice->CreateVertexBuffer(1 * sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &m_pVBuffer2D);
if(FAILED(Attempt))
return E_FAIL;
m_pVBuffer->Release();
m_pVBuffer = NULL;
Attempt = m_pD3DDevice->CreateVertexBuffer(1000 * sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &m_pVBuffer2D);
if(FAILED(Attempt))
return E_FAIL;
m_pVBuffer->Release();
m_pVBuffer = NULL;
return S_OK;
}
m_pD3DDevice is a private member variable of my class
I know that there is absolutely no time I would ever use this, but it's the question of Releasing, Nulling, then Re-Initializing a Vertex Buffer that I'm curious whether will work. If so, this may just solve my problem of adding Vertices on the fly...but I'll be more specific about it once I get there :) Until then, I'm curious about the code above...Thanks :)
-
Under the assumption that the above code was going to work, I went ahead and tried out my plan, here's some (somewhat lengthy) code:
Code:
//IN THE HEADER
IDirect3DDevice8 *m_pD3DDevice;
IDirect3DVertexBuffer8 *m_pVBuffer2D;
//THIS CODE INITIALIZES ONE CUSTOM VERTEX TYPE THE FIRST
//TIME BECAUSE I CAN'T WORK WITH AN EMPTY VERTEX BUFFER
HRESULT cObject2D::InitVBuffer()
{
HRESULT Attempt;
CUSTOMVERTEX *VBufferVertices;
Attempt = m_pD3DDevice->CreateVertexBuffer(1 * sizeof(CUSTOMVERTEX), 0,
D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &m_pVBuffer2D);
if(FAILED(Attempt))
return E_FAIL;
Attempt = m_pVBuffer2D->Lock(0, 1 * sizeof(CUSTOMVERTEX), (BYTE**)&VBufferVertices, 0);
if(FAILED(Attempt))
return E_FAIL;
VBufferVertices[0].vPosition = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
VBufferVertices[0].Colour = D3DCOLOR_XRGB(0, 0, 0);
m_pVBuffer2D->Unlock();
m_pNumV += 1;
AddBox(D3DXVECTOR3(50.0f, 0.0f, 1.0f), 40.0f, 40.0f);
AddBox(D3DXVECTOR3(0.0f, 50.0f, 1.0f), 40.0f, 40.0f);
return S_OK;
}
//HERE'S WHERE ALL THE MAGIC HAPPENS
HRESULT cObject2D::AddBox(D3DXVECTOR3 Coords, float fWidth, float fHeight)
{
IDirect3DVertexBuffer8 *VBufferStorage = NULL;
CUSTOMVERTEX *VBufferVertices = NULL;
HRESULT Attempt;
//CREATE TEMP BUFFER TO HOLD OLD DATA
Attempt = m_pD3DDevice->CreateVertexBuffer(m_pNumV * sizeof(CUSTOMVERTEX), 0,
D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT,
&VBufferStorage);
if(FAILED(Attempt))
return E_FAIL;
//COPY OLD DATA TO TEMP BUFFER
*VBufferStorage = *m_pVBuffer2D;
//CLEAR OLD BUFFER AND NULL IT
if(m_pVBuffer2D != NULL)
{
m_pVBuffer2D->Release();
m_pVBuffer2D = NULL;
}
//RE-CREATE OLD BUFFER FOR NEW SHAPE (SIZE OF: 4 NEW VERTICES + TEMP BUFFER)
Attempt = m_pD3DDevice->CreateVertexBuffer(sizeof(CUSTOMVERTEX) * (4 + m_pNumV), 0,
D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT,
&m_pVBuffer2D);
if(FAILED(Attempt))
return E_FAIL;
//COPY THE OLD DATA BACK INTO THE NEW AND IMPROVED BUFFER
*m_pVBuffer2D = *VBufferStorage;
//LOCK THE LAST 4 POSITIONS OF THE NEW AND IMPROVED BUFFER
Attempt = m_pVBuffer2D->Lock(0, 0, (BYTE**)&VBufferVertices, 0);
if(FAILED(Attempt))
return E_FAIL;
VBufferVertices[m_pNumV + 0].vPosition = Coords + (D3DXVECTOR3(-fWidth / 2, +fHeight / 2, 0.0f));
VBufferVertices[m_pNumV + 1].vPosition = Coords + (D3DXVECTOR3(+fWidth / 2, +fHeight / 2, 0.0f));
VBufferVertices[m_pNumV + 2].vPosition = Coords + (D3DXVECTOR3(-fWidth / 2, -fHeight / 2, 0.0f));
VBufferVertices[m_pNumV + 3].vPosition = Coords + (D3DXVECTOR3(+fWidth / 2, -fHeight / 2, 0.0f));
VBufferVertices[m_pNumV + 0].Colour = D3DCOLOR_XRGB(255, 0, 0);
VBufferVertices[m_pNumV + 1].Colour = D3DCOLOR_XRGB(0, 255, 0);
VBufferVertices[m_pNumV + 2].Colour = D3DCOLOR_XRGB(0, 0, 255);
VBufferVertices[m_pNumV + 3].Colour = D3DCOLOR_XRGB(0, 255, 255);
m_pVBuffer2D->Unlock();
m_pNumV += 4;
//CLEAN OUT UNNECCESSARY VARIABLES, JUST FOR KICKS
VBufferVertices = NULL;
VBufferStorage->Release();
VBufferStorage = NULL;
//SUCCESSFULLY TACKED 4 MORE VERTICES TO THE END OF OUR BUFFER, REPORT SUCCESS
return S_OK;
}
//NOT AS IMPORTANT, JUST SHOWING WHAT I'M RENDERING WITH
void cObject2D::RenderAll()
{
m_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
m_pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
m_pD3DDevice->SetRenderState(D3DRS_LIGHTING, false);
m_pD3DDevice->SetTransform(D3DTS_PROJECTION, &m_pMatProj);
m_pD3DDevice->SetTransform(D3DTS_WORLD, &m_pMatView);
m_pD3DDevice->SetTransform(D3DTS_VIEW, &m_pMatView);
m_pD3DDevice->SetStreamSource(0, m_pVBuffer2D, sizeof(CUSTOMVERTEX));
m_pD3DDevice->SetVertexShader(D3DFVF_CUSTOMVERTEX);
m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 1, 2);
}
If you actually read it all, you'll see that I've tried to implement code that will update the Vertex Buffer on the fly with the correct number of vertices, allowing me to add a box simply through a function. Now, the first time that AddBox() gets called, it works just fine, no hitches, I can see the square. But, if I call it a second time (as shown in the code), then my screen is all blank (just the black window shows). Now, there are a few things that've come up when I've tested this code...
1) *VBufferStorage = *m_pVBuffer2D; and *m_pVBuffer2D = *VBufferStorage;
I use those to store the original vertex buffer, then recopy the data over once the new buffer has been initialized, respectively. Assuming I only called this function once (when it actually works), I don't get why VBufferStorage doesn't point to a NULL when I NULL out m_pVBuffer2D a few lines later.
2) Which is what led me to try those very same lines without the "*"s, and let's just say, MSVC is very against that, and I don't see it wise to try and force it on the compiler :)
3) I tried to use the memcpy() function instead of straight assignment "=", and I have problems because the sizeof(VBufferStorage) and sizeof(m_pVBuffer2D) is always 4, as I'm guessing the "sizeof" a IDirect3DVertexBuffer8 type is 4? Is there a way I can figure out their size in bytes depending on how many vertices they're storing?
I know there's a lot to read up there and look through, but any advice on any of the problems, or even suggestions and ideas on how to improve the methods I'm using is always appreciated greatly.
Thanks,
Erik