Notifications
Clear all

[Closed] Tangent Space Per Geom Vertex

Hi, I want to calculate the tangent space pe geom vertex.
As I see gutil has a function ComputeTangentAndBinormal as stated here
http://area.autodesk.com/blogs/chris/how_the_3ds_max_scanline_renderer_computes_tangent_and_binormal_vectors_for_normal_mapping

But, it computes the tangent space per TVFace vertex.
I need to convert it to Geom Vertex.

As I understand I can liniar interpolate the bitangent and tanget and the normalize of the neighbour TVFace vert.

If somebody has done this, can he share some ideas about implemeting this optimal.

Also how to convert the TVFace vertex index to Geom vertex index?

I’m working with MNMesh.

Thanks

16 Replies

Also how to convert the TVFace vertex index to Geom vertex index?

you do it on a face by face basis, as shown in the second function you
linked to…

for( int f=0; f<numFaces; f++ ) 
  {
 	Face& geomFace = geomFaces[f];
 	geomTri[0] = geomVerts[ geomFace.v[0] ];
 	geomTri[1] = geomVerts[ geomFace.v[1] ];
 	geomTri[2] = geomVerts[ geomFace.v[2] ];
 
 	TVFace& mapFace = mapFaces[f];
 	mapTri[0] = mapVerts[ mapFace.t[0] ];
 	mapTri[1] = mapVerts[ mapFace.t[1] ];
 	mapTri[2] = mapVerts[ mapFace.t[2] ];
 
 	ComputeTangentAndBinormal( mapTri, geomTri, basisVec );

btw is this for an exporter or something internal ?

This is for something internal for some deformation in tangent space

one more problem is that calculation uses 3 verts, 3 tverts, … but I’m doing it for poly, so its normal to assume that if face deg > 3 the first 3 vertex will make the correct tangent space?

i don’t think you can make that assumption at all, what about this case ?

you could use MNMesh::OutToTri(Mesh &tmesh) to create a temp mesh and process that to generate the Tangents and binormals. Or process the Polys based on their triangulation. Personally I would go for outtotri method and use the prewritten code directly though you may want to ask theArea OP about the FindIndex function call.

Ah, ok, thanks.
Btw, do you know a method to convert from tvert index to geom vert? I see that in NormalMap sample its using some “magic” FindIndex that I’m not sure if its what I need

i think you’ll get most your answers from maxsdk/samples/materials/NormalBump/ in the sdk

from VNormal.cpp 
[b]
     // VNormalChannel
     //
     // Holds a normal vector and related data at each vertex & smoothing group;
     // that is, one piece of data per vertex per smoothing group at the vertex.

[/b]after a quick browse of the code, you should just be able to drop VNormalMgr (VNormal.h & VNormal.cpp) straight into your plugin as is. Call it via the interface mechanism as per the normal render version.

might need to add an additional VNormalMgr::InitTangentBasis to initialize from your MNMesh but should be pretty straightforward ;).

I though about it, but I don’t understand the
GetTangentBasis( int faceIndex, int vertIndex, int faceVertIndex ) method…

its needs a faceIndex, vertIndex and faceVertIndex.
So I need somehow to convert the vertIndex to all this data?
or for each neighbour face, I need to find the vertex index on the face of my vertex, get the tangent, add it to the sum, then normalize it?

If its like this then there is still this question: “Is there a method to get all TVerts indexes from a VertexIndex?”

you just need to convert VNormal.h & VNormal.cpp to work from your MNMesh as opposed to a ShadeContext/RenderInstance method it current uses in the NormalShader example. 99% of the code is written for you…

I understood that VNormal has the calculations for tangents,
But I don’t see any method here to get the tangets per vertex… I only see a method to get the tanget given a Geom VertexIndex, Face Index, TVertex Index.

I didn’t found any method to get the TVerts indices from Vertex Indices…

If you still say that VNormal has it all, can you give me a simple exemple how to acces the tangent from VNormal classes (doesn’t mater which class, I will convert it for my needs if it will work) using only a vertex index?

I see that in this loop:


for( int v=0; v<numVerts; v++ ) {
  if( (v&0xFF)==0 )
   vertBase[v>>8] = itemBase = itemCount;
  WORD offset = (WORD)(itemCount-itemBase);
  vertOffset[v] = offset;
  itemCount += grouping.GetGroupCount(v);
 }
 

ItemCount can be greater than vertex count…

ItemCount is used as TangentArray count

this->numItems = itemCount;
tangentBasisSet.SetCount( numItems );

void VNormalMgr::InitTangentBasis( ShadeContext& sc, int mapChannel ) {
	int nodeID = sc.NodeID();
	RenderInstance* inst = sc.globContext->GetRenderInstance(nodeID);
	if( (inst==NULL) || (inst->mesh==NULL) ) return;

	lock.EnterWrite();

	InitChannel_Internal( *(inst->mesh), nodeID, mapChannel );
	VNormalChannel* channel = GetChannel_Internal( nodeID, mapChannel );
	if( !channel->ValidTangentBasis() ) // initialized while waiting on the semaphore?
		 channel->InitTangentBasis( *(inst->mesh), inst->objToCam, mapChannel );

	lock.ExitWrite();
}


void VNormalMgr::GetTangentBasis( ShadeContext& sc, int mapChannel, TangentBasis& tangentBasis ) {
	DbgAssert( sc.globContext!=NULL );
	int nodeID = sc.NodeID();
	RenderInstance* inst = sc.globContext->GetRenderInstance(nodeID);
	if( (inst==NULL) || 
		 (inst->mesh==NULL) ||
		 NULL == inst->mesh->faces ||
		 inst->mesh->getNumFaces() <= sc.FaceNumber() )
	{
		return;
	} 

	lock.EnterRead();

	VNormalChannel* channel = GetChannel_Internal( nodeID, mapChannel );
	while( (channel==NULL) || (!channel->ValidTangentBasis()) ) {
		lock.ExitRead(); //release lock while initializing
		InitTangentBasis( sc, mapChannel );
		lock.EnterRead(); //regain lock after initializing
		channel = GetChannel_Internal( nodeID, mapChannel );
	}


	int faceIndex = sc.FaceNumber();
	Face& f = inst->mesh->faces[faceIndex];
	DWORD smGroup = f.smGroup;
	if( smGroup==0 )
		tangentBasis = channel->GetTangentBasis( faceIndex, 0, 0 );
	else {
		DWORD *v = f.v;
		TangentBasis& b0 = channel->GetTangentBasis( faceIndex, v[0], 0 ); //returned in camera space
		TangentBasis& b1 = channel->GetTangentBasis( faceIndex, v[1], 1 ); //returned in camera space
		TangentBasis& b2 = channel->GetTangentBasis( faceIndex, v[2], 2 ); //returned in camera space

		Point3 bary = sc.BarycentricCoords();
		tangentBasis.uBasis = ((bary.x*b0.uBasis) + (bary.y*b1.uBasis) + (bary.z*b2.uBasis)).Normalize();
		tangentBasis.vBasis = ((bary.x*b0.vBasis) + (bary.y*b1.vBasis) + (bary.z*b2.vBasis)).Normalize();
	}

	lock.ExitRead();
}

these are the functions you change, the first one initialize the VNormalMgr from a mesh, the second one returns the tangents of a face depending where on the face you want them (if you don’t know BarycentricCoords there plenty online) In your case you don’t need that the values will be b0,b1,b2.

Page 1 / 2