Skip to main content
gamedev.net
🔒 Locked

Vertex Normals

Started by VanillaSnake21 Jan 2, 2007 at 1:19 PM 6 replies 1.4k views
Original Post
VanillaSnake21
VanillaSnake21
How do I find the vertex normals for a mesh. I know the equations and I know that you have o average the face normals. But how do I write a function that does that. I tried using D3DXComputeNormals() but for some reasons all the normals I get are poinintg in the positive direction. My main obstacle is that to compute the face normal, I need to have three vertecies that define a riangle. If I have a mesh of 4000 something verts, I don't know whih vertecies make up a triangle. In the vertex buffer, are the vertecies put in a liniar order? Meaning thet (lets say p_vert is a pointer to a locked mem of vertex buffer) p_vert[0], p_vert[1], p_vert[2], does that define a first triangle and p_vert[2], p_vert[3], p_vert[4] define a second etc? Im lost please help.
You didn't come into this world. You came out of it, like a wave from the ocean. You are not a stranger here. -Alan Watts
Xrystal
Xrystal
I have a book I bought recently : Programing an RTS with Direct3D which contained generating normals for terrains.

At quick glance it looks like this might be what you are looking for:

D3DXVECTOR3 GetNormal(int x, int y){   // neighbouring map nodes   D3DXVECTOR2 mapnodes[6];   mapnodes[0] = D3DXVECTOR2(x-1,y);   mapnodes[1] = D3DXVECTOR2(x,y-1);   mapnodes[2] = D3DXVECTOR2(x+1,y-1);   mapnodes[3] = D3DXVECTOR2(x+1,y);   mapnodes[4] = D3DXVECTOR2(x,y+1);   mapnodes[5] = D3DXVECTOR2(x-1,y+1);    // If an invalid node return (0,1,0)   if (!Within(mapnodes[0]) || !Within(mapnodes[1]) || !Within(mapnodes[2]) ||       !Within(mapnodes[3]) || !Within(mapnodes[4]) || !Within(mapnodes[5]))   {      return D3DXVECTOR3(0,1,0);   }   // Calculate normals for surrounding planes   D3DXVECTOR3 normal = D3DXVECTOR3(0,0,0);   for (int i = 0; i < 6; i++)   {      D3DXPLANE plane;      D3DXPlaneFromPoints(&plane,                          &GetWorldPos(D3DXVECTOR2(x,y)),                          &GetWorldPos(D3DXVECTOR2(mapnode),                          &GetWorldPos(D3DXVECTOR2(mapnode[(i+1) % 6]));      normal += D3DXVECTOR3(plane.a,plane.b,plane.c);   }   D3DXVec3Normalize(&normal,&normal);   return normal;}


Thats the code copied out of the books pages as is but I'm assuming GetWorldPos and Within are created functions.

I've not got that far into the book yet to understand the effects of it yet myself. Hopefully it will help you understand what they are talking about in connection to your own coding.
Thr33d
Thr33d
Quote:
Original post by VanillaSnake21
How do I find the vertex normals for a mesh. I know the equations and I know that you have o average the face normals. But how do I write a function that does that. I tried using D3DXComputeNormals() but for some reasons all the normals I get are poinintg in the positive direction. My main obstacle is that to compute the face normal, I need to have three vertecies that define a riangle. If I have a mesh of 4000 something verts, I don't know whih vertecies make up a triangle. In the vertex buffer, are the vertecies put in a liniar order? Meaning thet (lets say p_vert is a pointer to a locked mem of vertex buffer) p_vert[0], p_vert[1], p_vert[2], does that define a first triangle and p_vert[2], p_vert[3], p_vert[4] define a second etc? Im lost please help.


Direct3D and your video card need to know about the triangles - and not just the vertices, so triangle information is "encoded" in one way or another.

The most basic way to include that information is, as you've described, order your vertices so that they create a triangle.
Also, you'll always want to order them the same way (triangles are one-sided and they only have one normal, generally speaking)
So go Clockwise (CW) or Counter-clockwise (CCW) - I can't remember what's default for Direct3d.

Anyway, yes - vertex 1, 2, and 3 are your first triangle. 4, 5, and 6 are your second, and so on.

Triangle Strips and Triangle Fans work differently, if you're using them. also, if you're using Indexed primitives, the triangle list will hold indices (indexes?) of the vertices which make up the triangles, instead of the actual (x,y,z) data itself.

I think you can take it from here - questions?

-M
VanillaSnake21
VanillaSnake21
Quote:
Original post by Thr33d
Quote:
Original post by VanillaSnake21
How do I find the vertex normals for a mesh. I know the equations and I know that you have o average the face normals. But how do I write a function that does that. I tried using D3DXComputeNormals() but for some reasons all the normals I get are poinintg in the positive direction. My main obstacle is that to compute the face normal, I need to have three vertecies that define a riangle. If I have a mesh of 4000 something verts, I don't know whih vertecies make up a triangle. In the vertex buffer, are the vertecies put in a liniar order? Meaning thet (lets say p_vert is a pointer to a locked mem of vertex buffer) p_vert[0], p_vert[1], p_vert[2], does that define a first triangle and p_vert[2], p_vert[3], p_vert[4] define a second etc? Im lost please help.


Direct3D and your video card need to know about the triangles - and not just the vertices, so triangle information is "encoded" in one way or another.

The most basic way to include that information is, as you've described, order your vertices so that they create a triangle.
Also, you'll always want to order them the same way (triangles are one-sided and they only have one normal, generally speaking)
So go Clockwise (CW) or Counter-clockwise (CCW) - I can't remember what's default for Direct3d.

Anyway, yes - vertex 1, 2, and 3 are your first triangle. 4, 5, and 6 are your second, and so on.

Triangle Strips and Triangle Fans work differently, if you're using them. also, if you're using Indexed primitives, the triangle list will hold indices (indexes?) of the vertices which make up the triangles, instead of the actual (x,y,z) data itself.

I think you can take it from here - questions?

-M



but how do I order them? I basically load my mesh with D3DXLoadMeshFromX() functions, get the vertex buffer of the mesh and work with that but how will I be able to change how the vertecies are listed?
You didn't come into this world. You came out of it, like a wave from the ocean. You are not a stranger here. -Alan Watts
inezdias
inezdias
If you have a mesh you'll have an index buffer, as Thr33d explains above. Getting the index buffer is similar to getting the vertex buffer (consult the docs), and form there you get the index into the vertex buffer (like, if the first three numbers in the index buffer are 2, 8 15 the first triangle will be made up of the vertices 2, 8 and 15 in the vertex buffer).
renman29
renman29
It's really strange that D3DXComputeNormals isn't working. If you really need to brute-force recalculate the normals and reapply them, it can be somewhat complicated doing this from a d3dxmesh - at least more complicated than one would expect. If your mesh contains subobjects - you'll probably want to create a subobject list - each containing a list from a polygon struct and create a list of polygons from the mesh data(this alone is complicated - need to mess around with attribute tables). For each polygon you'd calculate its normal. Then for each vertex you'd find polygons that share that vertex and add their normal value to some variable - ie normal_total - and then average that based on the final quantity. It would look something like this:
#define VEC D3DXVECTOR3//....void CalculateFaceNormal(VEC &n,VEC v1,VEC v2,VEC v3,bool make_unit){		    float Ux=0.0f, Uy=0.0f, Uz=0.0f;    float Vx=0.0f, Vy=0.0f, Vz=0.0f;    VEC vec,nvec;    Ux= v3.x-v1.x;   Uy= v3.y-v1.y;   Uz= v3.z-v1.z;    Vx= v2.x-v1.x;   Vy= v2.y-v1.y;   Vz= v2.z-v1.z;				    if (make_unit) {        vec.x=Ux;vec.y=Uy;vec.z=Uz;    	D3DXVec3Normalize(&nvec,&vec);//overflow precaution		Ux=nvec.x;Uy=nvec.y;Uz=nvec.z;		vec.x=Vx;vec.y=Vy;vec.z=Vz;	D3DXVec3Normalize(&nvec,&vec);//overflow precaution		Vx=nvec.x;Vy=nvec.y;Vz=nvec.z;		      }    float NormalX =((Vy*Uz)-(Vz*Uy));    float NormalY =((Vx*Uz)-(Vz*Ux))*-1; //corrected    float NormalZ =((Vx*Uy)-(Vy*Ux));    vec.x=NormalX;vec.y=NormalY;vec.z=NormalZ;    if (make_unit) D3DXVec3Normalize(&n,&vec);	else {n.x=vec.x;n.y=vec.y;n.z=vec.z;}}//END CalculateFaceNormal// bla bla bla .....//VERTEX NORMAL CALCULATIONS:float sumx=0.0f,sumy=0.0f,sumz=0.0f;a=0;  //CALCULATE_NORMALS:do {  tt=0;  do{    m=0;    do{        CalculateFaceNormal(*sys[w]->ob1[a]->MeshList->FaceNormal[m],   *sys[w]->ob1[a]->MeshList->VertexList[sys[w]->ob1[a]->FaceList[m]->a],   *sys[w]->ob1[a]->MeshList->VertexList[sys[w]->ob1[a]->FaceList[m]->b],   *sys[w]->ob1[a]->MeshList->VertexList[sys[w]->ob1[a]->FaceList[m]->c],   false);        m++;    }while(m<sys[w]->ob1[a]->NumFaces);    m=0;    do{ 	            //CALCULATE_GOURAUD_VARIABLES				sumx=sumy=sumz=0.0f;numsum=o=0;	do { 	    if ((sys[w]->ob1[a]->FaceList[o]->a==m)||                (sys[w]->ob1[a]->FaceList[o]->b==m)||                (sys[w]->ob1[a]->FaceList[o]->c==m)) {		sumx+=sys[w]->ob1[a]->MeshList->FaceNormal[o]->x;		sumy+=sys[w]->ob1[a]->MeshList->FaceNormal[o]->y;		sumz+=sys[w]->ob1[a]->MeshList->FaceNormal[o]->z;		numsum++;	    }	    o++;	}while(o<sys[w]->ob1[a]->NumFaces);	sys[w]->ob1[a]->MeshList->VertexNormal[m]->x=sumx/(float)numsum;	sys[w]->ob1[a]->MeshList->VertexNormal[m]->y=sumy/(float)numsum;	sys[w]->ob1[a]->MeshList->VertexNormal[m]->z=sumz/(float)numsum;				D3DXVec3Normalize(sys[w]->ob1[a]->MeshList->VertexNormal[m],                          sys[w]->ob1[a]->MeshList->VertexNormal[m]);	m++;    }while(m<sys[w]->ob1[a]->NumVertices);  tt++;  }while(tt<sys[w]->num_frames);a++;}while(a<sys[w]->num_objects);


This is just old code of mine - *copy-paste*. Kind of bulky and sloppy(especially the pointers), but I think you can get the picture from this. :)
renman29
renman29
If you do want to store mesh data in your own structs you can do something like this:
//How to extract sub-meshes into your mesh structstruct stMyVertex{	FLOAT x, y, z;   	FLOAT nx,ny,nz;	DWORD colour; 	FLOAT tu, tv;    };//#define ALLOC(p1,p2) {p1 = new p2;   \//  if (p1==NULL)   \//  FatalError(L"Unable to allocate memory for " L#p1 L"(type: " L#p2 L")\n");}//#define GET_ATTRIBUTE_TABLE(p1,p2)     \//  if (mesh->GetAttributeTable(p1, &p2)!=D3D_OK)   \//  R_ER("Get Attribute Table failed.");//#define GET_MESH_VERTEX_BUFFER(p1)     \//  if(mesh->GetVertexBuffer(&p1)!=D3D_OK)   \//  R_ER("Can't get vertex buffer.");//#define GET_MESH_INDEX_BUFFER(p1)     \//  if(mesh->GetIndexBuffer( &p1 )!=D3D_OK)   \//  R_ER("Can't get index buffer.");//#define LOCK_MESH_VERTEX_BUFFER(p1,p2) \//  if (FAILED(p1->LockVertexBuffer(0, 	(LPVOID *)&p2))) \//  R_ER("Error locking mesh vertex buffer");//#define LOCK_MESH_INDEX_BUFFER(p1,p2) \ //  if (FAILED(p1->LockIndexBuffer(0, 	(LPVOID *)&p2))) \//  R_ER("Error locking mesh index buffer");// S T O R E  D 3 D X  M E S H  G E O M E T R Y  D A T Avoid StoreD3DXMeshGeometryData() {if (!mesh) R_ER("No mesh to store.");	D3DXATTRIBUTERANGE* AttributeTable;D3DXATTRIBUTERANGE* AttributeTableStart;	BYTE				*vb=NULL;BYTE				*ib=NULL;BYTE				*ptr=NULL;LPDIRECT3DINDEXBUFFER9 IndexBuffer;		D3DINDEXBUFFER_DESC IndexDesc; 	D3DXVECTOR3			v0,v1,v2;DWORD				AttributeTableSize;DWORD                           MemSize    = 0;DWORD				i0=0,i1=0,i2=0; DWORD               VertexSize = mesh->GetNumBytesPerVertex();DWORD               IndexSize=0,FaceSize=0;DWORD				a=0,b=0,c=0;stFace *face;stObject *object;DWORD n_objects;DWORD n_faces;	VEC *vec1; VEC *vec2; VEC *vec3;sys.GrandFaceCount=0; sys.GrandVertexCount=0;sys.pos.x=sys.pos.y=sys.pos.z=0.0f;GET_ATTRIBUTE_TABLE(0,AttributeTableSize)ALLOC(AttributeTableStart,D3DXATTRIBUTERANGE[AttributeTableSize]) AttributeTable = AttributeTableStart; //point it to our allocated memoryGET_ATTRIBUTE_TABLE(AttributeTable,AttributeTableSize)	LOCK_MESH_VERTEX_BUFFER(mesh,vb)	LOCK_MESH_INDEX_BUFFER(mesh,ib)	if (FAILED(mesh->GetIndexBuffer(&IndexBuffer)))   PROBLEM(L"Unable to get index buffer");if (FAILED(IndexBuffer->GetDesc(&IndexDesc)))   PROBLEM(L"Unable to get index description");SAFE_RELEASE(IndexBuffer)if (IndexDesc.Format!=D3DFMT_INDEX32) {REPORT("Not 32 bit index format.");}//1) - find offset into the vertex buffer for this sub-mesh//2) - allocate enough memory for this vertex list//3) - loop through until vertex count for sub-mesh is met	//4) - find offset into index buffer for sub-mesh//5) - allocate memory for index list//6) - collect and remap indices until index count is metsys.num_objects=AttributeTableSize;		ALLOC(sys.ob1,stObject*[sys.num_objects])g_TotalNumVertices=0;g_TotalNumFaces=0;n_objects=sys.num_objects;a=0; do {	ALLOC(sys.ob1[a],stObject)		object=sys.ob1[a];	object->MaterialRef  = AttributeTable->AttribId; 	object->NumFaces     = AttributeTable->FaceCount;	object->NumVertices  = AttributeTable->VertexCount;        // 1] get vertex offset	object->start_vertex = AttributeTable->VertexStart;	// 4] get index offset	object->start_index  = AttributeTable->FaceStart*3;	// 2] allocate memory for vertex list	ptr=vb+VertexSize*object->start_vertex;	ALLOC(object->VertexList,VEC*[object->NumVertices])	object->NumTexturedVertices=object->NumVertices;	ALLOC(object->UV,stTexCoord*[object->NumTexturedVertices])	ALLOC(object->VertexNormal,VEC*[object->NumVertices])	b=0;	do {		ALLOC(object->VertexList,VEC)		ALLOC(object->UV,stTexCoord)		ALLOC(object->VertexNormal,VEC)	        // 3] loop - get vertex data from mesh's vertex buffer				stMyVertex *vPtr = (stMyVertex *)ptr;	        vec1=object->VertexList;		vec1->x=vPtr->x;		vec1->y=vPtr->y;		vec1->z=vPtr->z;		object->UV->U=vPtr->tu;		object->UV->V=vPtr->tv;		vec1=object->VertexNormal;		vec1->x=vPtr->nx;		vec1->y=vPtr->ny;		vec1->z=vPtr->nz;		ptr+=VertexSize;			b++;	}while(b<sys.ob1[a]->NumVertices);	// 5] allocate memory for face list	ptr=ib+(sizeof(DWORD)*(size_t)object->start_index);	ALLOC(object->FaceList,stFace*[object->NumFaces])	b=0;c=0;	do {		ALLOC(object->FaceList,stFace)		// 6] collect and remap indices	                i0 = ((DWORD*)ptr)[c]; c++; 		i1 = ((DWORD*)ptr)[c]; c++;		i2 = ((DWORD*)ptr)[c]; c++;	        // remap indices		face=sys.ob1[a]->FaceList;					face->a=i0-AttributeTable->VertexStart;		face->b=i1-AttributeTable->VertexStart;		face->c=i2-AttributeTable->VertexStart;			b++;	}while(b<sys.ob1[a]->NumFaces);	AttributeTable++;	g_TotalNumFaces+=object->NumFaces;	g_TotalNumVertices+=object->NumVertices;	a++;}while(a<n_objects);mesh->UnlockVertexBuffer();mesh->UnlockIndexBuffer();	SAFE_DELETE_ARRAY(AttributeTableStart)	//Create an independent vertex buffer containing all polygons of sub-object.//VertexEntry and VertexSpan will be used to depict sub-objectsDWORD face_point_count=0;face_point_count=g_TotalNumFaces*3;stMyVertex *pVertices=NULL;    //ptr to data for main vb	DWORD *pIndex=NULL;	CREATE_VERTEX_BUFFER(face_point_count*sizeof(stMyVertex),MY_CUSTOM_VERTEX,&vb1)LOCK_VERTEX_BUFFER(vb1,0,0,(void **)&pVertices) //lock all of it (0,0)  //1) loop through mesh subsets:DWORD mem_pos=0;		DWORD clr=0;a=0;do {	//2) loop through polygons of this mesh:	b=0;	object=sys.ob1[a];			n_faces=object->NumFaces;			object->vb_offset=mem_pos;	do {		//3) get this polygon's data:		face=object->FaceList;		i0=face->a;		i1=face->b;		i2=face->c;		//4) get the vertices of this polygon from the indices:		vec1=object->VertexList[i0];		vec2=object->VertexList[i1];		vec3=object->VertexList[i2];		pVertices[mem_pos].x=vec1->x;		pVertices[mem_pos].y=vec1->y;		pVertices[mem_pos].z=vec1->z;		pVertices[mem_pos].tu=object->UV[i0]->U;		pVertices[mem_pos].tv=object->UV[i0]->V;		pVertices[mem_pos].nx=object->VertexNormal[i0]->x;		pVertices[mem_pos].ny=object->VertexNormal[i0]->y;		pVertices[mem_pos].nz=object->VertexNormal[i0]->z;		//pVertices[mem_pos].colour=clr;				if ((mem_pos+1)>(face_point_count))                     PROBLEM(L"mepos>g_TotalNumFaces*3 @ 1")		mem_pos++;		pVertices[mem_pos].x=vec2->x;		pVertices[mem_pos].y=vec2->y;		pVertices[mem_pos].z=vec2->z;		pVertices[mem_pos].tu=object->UV[i1]->U;		pVertices[mem_pos].tv=object->UV[i1]->V;		pVertices[mem_pos].nx=object->VertexNormal[i1]->x;	        pVertices[mem_pos].ny=object->VertexNormal[i1]->y;		pVertices[mem_pos].nz=object->VertexNormal[i1]->z;		///pVertices[mem_pos].colour=clr;		if ((mem_pos+1)>(face_point_count))                     PROBLEM(L"mepos>g_TotalNumFaces*3 @ 2")		mem_pos++;		pVertices[mem_pos].x=vec3->x;		pVertices[mem_pos].y=vec3->y;		pVertices[mem_pos].z=vec3->z;				pVertices[mem_pos].tu=object->UV[i2]->U;		pVertices[mem_pos].tv=object->UV[i2]->V;		pVertices[mem_pos].nx=object->VertexNormal[i2]->x;		pVertices[mem_pos].ny=object->VertexNormal[i2]->y;		pVertices[mem_pos].nz=object->VertexNormal[i2]->z;		//pVertices[mem_pos].colour=clr; clr++;				if ((mem_pos+1)>(face_point_count))                     PROBLEM(L"mepos>g_TotalNumFaces*3 @ 3")			mem_pos++;		b++;	}while(b<n_faces);	a++;}while(a<n_objects);UNLOCK_VERTEX_BUFFER(vb1)}

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.