Tangent and Binormal for GeoMipTerrain

I still need tangent and binormal vectore for normal mapping for terrain generated by GeoMipTerrain…

Searching the forum didn’t give much - some code by coppertop, that I doesn’t seam to work: geomipterrain and binormals/tangets

If I understand this and Linus is right, then calculating the vectors seams simple:

I got as far as this:

def generateTBN(np):  
    geomNode = np.find('**/+GeomNode').node()
    geom = geomNode.modifyGeom(0)
    vdata = geom.modifyVertexData()
    
    tangent = GeomVertexWriter(vdata, 'tangent')
    binormal = GeomVertexWriter(vdata, 'binormal')
    normal = GeomVertexReader(vdata, 'normal')  
    
    while not normal.isAtEnd():
       N = normal.getData3f()
       tangent.setData3f(float3(N.x, N.z, -N.y))
       binormal.setData3f(float3(N.z, N.y, -N.x))
Assertion failed: has_column() at line 508 of c:\buildslave\release_sdk_win32\bu
ild\panda3d\panda\src\gobj\geomVertexWriter.I
Traceback (most recent call last):
  File "geo.py", line 47, in <module>
    w = World()
  File "geo.py", line 33, in __init__
    generateTBN(self.terrain.getRoot())
  File "geo.py", line 16, in generateTBN
    tangent.setData3f(Vec3(N.x, N.z, -N.y))
AssertionError: has_column() at line 508 of c:\buildslave\release_sdk_win32\buil
d\panda3d\panda\src\gobj\geomVertexWriter.I

Reading the manual on Modifying existing geometry data, GeomVertexFormat and Defining your own GeomVertexFormat only gave me a headache.

Help, tips, solutions or working code would be welcome.

Unfortunately this is where the problem lies I think. It seems that you haven’t setup the extra columns in your GeomVertexFormat needed to handle the binormal and tangent information. I’ve done this quite a bit with procedurally generated geometry but I don’t how it would translate to imported models; the basic idea is:

# Build array for new format.
array = GeomVertexArrayFormat()
array.addColumn(InternalName.make('vertex'), 3, Geom.NTFloat32, Geom.CPoint)
array.addColumn(InternalName.make('color'), 4, Geom.NTFloat32, Geom.Ccolor)
array.addColumn(InternalName.make('texcoord'), 2, Geom.NTFloat32, Geom.CTexcoord)
array.addColumn(InternalName.make('normal'), 3, Geom.NTFloat32, Geom.CVector)
array.addColumn(InternalName.make('binormal'), 3, Geom.NTFloat32, Geom.CVector)
array.addColumn(InternalName.make('tangent'), 3, Geom.NTFloat32, Geom.CVector)

# Create and register format.
format = GeomVertexFormat()
format.addArray(array)
format = GeomVertexFormat.registerFormat(format)

# Create vdata and vertex writers.
vdata = GeomVertexData('name', format, Geom.UHStatic)
vertex = GeomVertexWriter(vdata, 'vertex')
color = GeomVertexWriter(vdata, 'color')
normal = GeomVertexWriter(vdata, 'normal')
texcoord = GeomVertexWriter(vdata, 'texcoord')
binormal = GeomVertexWriter(vdata, 'binormal')
tangent = GeomVertexWriter(vdata, 'tangent')

# Write to verts.
for vert in verts:
    vertex.addData3f(vert.x, vert.y, vert.z)
    ...
    tangent.addData3f(tx, ty, tz)

Like I said I don’t know how this works with imported models but this might be adaptable to your case. I’m not sure whether redefining the vformat means that you would have to rewrite all your columns (like the above code) or whether you could get away with just writing to the binormal and tangent columns.

Thanks.
But how can I set the format if I’m using vdata = geom.modifyVertexData() ? modifyVertexData() won’t take any parameters.

Do I have to make a totally new mesh on the fly that is just a copy of the mesh generated by GeoMipTerrain, add my tangent/binormal to it and finally throw away the original mesh? Seams a bit wasteful and way to complicated … and I don’t even know how to copy the polygons (primitives).

I had a look at the C++ code under GeoMipTerrain (geoMipTerrain.cxx) and it doesn’t look as adding tangent/binormals to it would be a big problem for someone that actually knows C++

I’m giving up on this :confused:

I took the shader dumped by the shader generator and changed the vshader like this:

void vshader(
	 in float4 vtx_texcoord0 : TEXCOORD0,
	 out float4 l_texcoord0 : TEXCOORD0,
	 in float4 vtx_texcoord1 : TEXCOORD1,
	 out float4 l_texcoord1 : TEXCOORD1,
	 in float4 vtx_texcoord2 : TEXCOORD2,
	 out float4 l_texcoord2 : TEXCOORD2,
	 uniform float4x4 trans_model_to_world,
	 out float4 l_world_position : TEXCOORD3,
	 uniform float4x4 trans_model_to_view,
	 out float4 l_eye_position : TEXCOORD4,
	 uniform float4x4 tpose_view_to_model,
	 out float4 l_eye_normal : TEXCOORD5,
	 in float4 vtx_normal : TEXCOORD3,	 
	 out float4 l_tangent : TEXCOORD6,
	 out float4 l_binormal : TEXCOORD7,
	 float4 vtx_position : POSITION,
	 out float4 l_position : POSITION,
	 uniform float4x4 mat_modelproj
) {
     float3 vtx_tangent1= float3(vtx_normal.x, vtx_normal.z, -vtx_normal.y);
	 float3 vtx_binormal1= float3(vtx_normal.z, vtx_normal.y, -vtx_normal.x);
	 l_position = mul(mat_modelproj, vtx_position);
	 l_world_position = mul(trans_model_to_world, vtx_position);
	 l_eye_position = mul(trans_model_to_view, vtx_position);
	 l_eye_normal.xyz = mul((float3x3)tpose_view_to_model, vtx_normal.xyz);
	 l_eye_normal.w = 0;
	 l_tangent.xyz = mul((float3x3)tpose_view_to_model, vtx_tangent1.xyz);
	 l_tangent.w = 0;
	 l_binormal.xyz = mul((float3x3)tpose_view_to_model, -vtx_binormal1.xyz);
	 l_binormal.w = 0;
}

Seams to work.