Rotate Triangle

Blitz3D Forums/Blitz3D Programming/Rotate Triangle

If anyone a simple answer to this, it would save me some time.

Given an arbitrary triangle with it's centre at 0,0,0 and the face normal as a unit vector, how would I rotate it to flat across the Z axis?

Surely some sin/cos would do this? Just rotate each point as you would a point on a circle.

Surely some sin/cos would do this? Just rotate each point as you would a point on a circle.

That makes perfect sense, and I can do that.

What I am looking for is the formula to convert the normal into reliable Pitch / Yaw pair to apply to it.

Ah right. Have you tried rotating it normally then tforming the triangles points?

I may have misunderstood you but ...

Place a pivot at the triangles center, aligntovector the pivot on the z-axis using the face normals.

Place another pivot at the first pivots coords but move it forward on the z-axis a few units.

The functions DeltaYaw( pivot1, pivot2 ) and deltapitch( pivot1, pivot2 ) will give you the rotations which are required to rotate the triangle so that it's facing directly forward on the z-axis.

You want to rotate a 3-point mesh so the normal points along the z-axis? I think the natural approach would be as follows.

The cross product of the normal vector with "z vector" (0,0,1) is perpendicular to both. The cross product is ( ny, -nx, 0 ). This is the desired axis of rotation.

The angle needed is just the angle between the two vectors. The cosine of this angle is the dot product 0*nx + 0*ny + 1*nz.

Here's a bare outline of the code:

; We already have triangle, a mesh with just three points.
; There is also a normal (nx,ny,nz), with length 1.
; And we have a pivot positioned at (0,0,0).

AlignToVector pivot, ny, -nx, 0, 2	; point y-axis at N crossed with (0,0,1)
TurnEntity pivot, 0, ACos(nz), 0	; pivot has orientation desired for triangle

RotateMesh triangle, EntityPitch(pivot), EntityYaw(pivot), EntityRoll(pivot)	


Thanks all for the replies. I have hacked together a cheesy example code with Floyds bit in. The results are not what I expected. I unweld a sphere, centre all the triangles and want them to align. So I make an entity per triangle and rotate them as per the earlier formula, but This is what I get...

Type TRIS
	Field x0#
	Field y0#
	Field z0#

	Field u0#
	Field v0#
	Field U20#
	Field V20#
	
	Field x1#
	Field y1#
	Field z1#

	Field u1#
	Field v1#
	Field U21#
	Field V21#
	
	Field x2#
	Field y2#
	Field z2#

	Field u2#
	Field v2#

	Field U22#
	Field V22#
	
	Field surface
End Type

Dim txv(3)

Global MFNx#, MFNy#, MFNz#, tmp

Graphics3D 800,600,32,2

Local Cam = CreateCamera()
CameraRange cam,0.1,50
PositionEntity cam,0,0,-4
Local Ball = CreateSphere(16)
WireFrame True

Unweld(Ball)
AlignTriangles(Ball)

While Not KeyDown(1)
RenderWorld

If EntityZ(cam) < -0.3 Then MoveEntity cam,0,0,0.01

Flip False
Wend
EndGraphics
End

Function AlignTriangles(Mesh)
	Local X1#,Y1#,Z1#
	Local X2#,Y2#,Z2#
	Local X3#,Y3#,Z3#
	Local X4#,Y4#,Z4#
	
	For nsurf = 1 To CountSurfaces(mesh)
		su=GetSurface(mesh,nsurf)
		For tq = 0 To CountTriangles(su)-1
		X1 = VertexX(Su,TriangleVertex(su,tq,0))
		Y1 = VertexY(Su,TriangleVertex(su,tq,0))
		Z1 = VertexZ(Su,TriangleVertex(su,tq,0))
		
		X2 = VertexX(Su,TriangleVertex(su,tq,1))
		Y2 = VertexY(Su,TriangleVertex(su,tq,1))
		Z2 = VertexZ(Su,TriangleVertex(su,tq,1))
		
		X3 = VertexX(Su,TriangleVertex(su,tq,2))
		Y3 = VertexY(Su,TriangleVertex(su,tq,2))
		Z3 = VertexZ(Su,TriangleVertex(su,tq,2))
		
		X4 = (X1 + X2 + X3) / Float(3)	
		Y4 = (Y1 + Y2 + Y3) / Float(3)	
		Z4 = (Z1 + Z2 + Z3) / Float(3)	
		
		Tmp = CreateMesh()
		Local sfs = CreateSurface(tmp)
		
		AddVertex sfs, X1 - X4, Y1-Y4, Z1 - Z4
		AddVertex sfs, X2 - X4, Y2-Y4, Z2 - Z4
		AddVertex sfs, X3 - X4, Y3-Y4, Z3 - Z4

		AddTriangle sfs,0,1,2

		gfn(sfs,0)

		DebugLog "NX=" + MFNx
		DebugLog "NY=" + MFNy
		DebugLog "NZ=" + MFNz
				
		pvt = CreatePivot()
		AlignToVector pvt, mfny, -mfnx, 0, 2	   						   ; point y-axis at N crossed with (0,0,1)
		TurnEntity pvt, 0, ACos(mfnz), 0	 					           ; pivot has orientation desired for triangle
		RotateMesh tmp, EntityPitch(pvt), EntityYaw(pvt), EntityRoll(pvt)
		
		;RotateMesh tmp, -mfnx*180, -mfny*180,-mfnz*180
		;VertexCoords su,TriangleVertex(su,tq,0),X1 - X4, Y1-Y4, Z1 - Z4
		;VertexCoords su,TriangleVertex(su,tq,1),X2 - X4, Y2-Y4, Z2 - Z4
		;VertexCoords su,TriangleVertex(su,tq,2),X3 - X4, Y3-Y4, Z3 - Z4
		
		Next
	Next	
End Function


Function unWeld(mish)
For nsurf = 1 To CountSurfaces(mish)
su=GetSurface(mish,nsurf)
For tq = 0 To CountTriangles(su)-1
txv(0) = TriangleVertex(su,tq,0)
txv(1) = TriangleVertex(su,tq,1)
txv(2) = TriangleVertex(su,tq,2)
	vq.TRIS = New TRIS
	vq\x0# = VertexX(su,txv(0))
	vq\y0# = VertexY(su,txv(0))
	vq\z0# = VertexZ(su,txv(0))
	vq\u0# = VertexU(su,txv(0),0)
	vq\v0# = VertexV(su,txv(0),0)
	vq\u20# = VertexU(su,txv(0),1)
	vq\v20# = VertexV(su,txv(0),1)
	
	vq\x1# = VertexX(su,txv(1))
	vq\y1# = VertexY(su,txv(1))
	vq\z1# = VertexZ(su,txv(1))
	vq\u1# = VertexU(su,txv(1),0)
	vq\v1# = VertexV(su,txv(1),0)
	vq\u21# = VertexU(su,txv(1),1)
	vq\v21# = VertexV(su,txv(1),1)

	vq\x2# = VertexX(su,txv(2))
	vq\y2# = VertexY(su,txv(2))
	vq\z2# = VertexZ(su,txv(2))
	vq\u2# = VertexU(su,txv(2),0)
	vq\v2# = VertexV(su,txv(2),0)
	vq\u22# = VertexU(su,txv(2),1)
	vq\v22# = VertexV(su,txv(2),1)
Next

ClearSurface su

For vq.tris = Each tris

		AddVertex su,vq\x0#,vq\y0#,vq\z0#,vq\u0#,vq\v0#
		VertexTexCoords su,mycount,vq\u20#,vq\v20#,0,1
		mycount = mycount +1

		AddVertex su,vq\x1#,vq\y1#,vq\z1#,vq\u1#,vq\v1#
		VertexTexCoords su,mycount,vq\u21#,vq\v21#,0,1
		mycount = mycount +1

		AddVertex su,vq\x2#,vq\y2#,vq\z2#,vq\u2#,vq\v2#
		VertexTexCoords su,mycount,vq\u22#,vq\v22#,0,1
		mycount = mycount +1


	AddTriangle su,mycount-3,mycount-2,mycount-1

Next

Delete Each tris
mycount=0
Next
End Function

Function GFN(SUR,TRI)
If sur = 0 Then Return
 Local X1#,Y1#,Z1#
 Local X2#,Y2#,Z2#
 Local X3#,Y3#,Z3#
		
V0 = TriangleVertex(SUR,TRI,0)
V1 = TriangleVertex(SUR,TRI,1)
V2 = TriangleVertex(SUR,TRI,2)

X1# = VertexX(SUR,V0)
Y1# = VertexY(SUR,V0)
Z1# = VertexZ(SUR,V0)

X2# = VertexX(SUR,V1)
Y2# = VertexY(SUR,V1)
Z2# = VertexZ(SUR,V1)

X3# = VertexX(SUR,V2)
Y3# = VertexY(SUR,V2)
Z3# = VertexZ(SUR,V2)

         ; Calculate the normal of the plane which this triangle lies in, (A = Nx, B = Ny, C = Nz)
         ; and the distance of the plane (D) from the origin (0,0,0) at it's closest point To the Origin.
         A# = Y1# * (Z2# - Z3#) + Y2# * (Z3# - Z1#) + Y3# * (Z1# - Z2#)
         B# = Z1# * (X2# - X3#) + Z2# * (X3# - X1#) + Z3# * (X1# - X2#)
         C# = X1# * (Y2# - Y3#) + X2# * (Y3# - Y1#) + X3# * (Y1# - Y2#)
         ;D# = -(X1# * (Y2# * Z3# -  Y3# * Z2#) + X2# * (Y3# *  Z1# - Y1# * Z3#) + X3# * (Y1# * Z2# - Y2# * Z1#))


         ; Normalize the plane equation.
         ; (Remove any scaling which will skew our results.  Make the normal's length 1.)
         Length# = Sqr(A#*A# + B#*B# + C#*C#)
         
         A# = A# / Length#
         B# = B# / Length#
         C# = C# / Length#
         ;D# = D# / Length#


         ; Return the normal of the triangle.
         MFNx# = A#
         MFNy# = B#
         MFNz# = C#


End Function


Lee,

I out-clevered myself.

The original idea was to set up the pivot as described, parent the triangle and then turn the pivot. But this affects only the triangle entity, not the underlying mesh. Updating the mesh to match the entity orientation seemed messy. Then I got the "improved" idea to use the pivot orientation to rotate the mesh. That failed because

AlignToVector pivot, ny, -nx, 0, 2

points the y-axis the way I want, but other than that the orientation could be anything.

So it is back to the original idea. I think this works:

Graphics3D 800, 600, 0, 2

tri1 = CreateTriangle()

;SeedRnd MilliSecs()
;RotateMesh tri1, Rnd(50), Rnd(50), Rnd(50)

tri2 = CopyMesh( tri1 )

dumpvertices tri1, "Before"

AlignTriangle tri2

dumpvertices tri2, "After"

EntityColor tri1, 150, 150, 150
EntityColor tri2, 0, 255, 0

cam = CreateCamera()
PositionEntity cam, 0, 0, 30
TurnEntity cam, 0, 180, 0
CameraZoom cam, 7


HideEntity tri2
Repeat
	HideEntity tri1
	ShowEntity tri2
	RenderWorld
	Flip
	Delay 400
	
	HideEntity tri2
	ShowEntity tri1
	RenderWorld
	Flip
	Delay 400
Until KeyHit(1)	
Stop



Function CreateTriangle()
	tMesh=CreateMesh()
	tSurface=CreateSurface( tMesh )

	AddVertex tSurface, 3, 0, 0
	AddVertex tSurface, 0, 3, 0
	AddVertex tSurface, 0, 0, 3
	
	AddTriangle tSurface,0, 1, 2
	UpdateNormals tMesh

	PositionMesh tMesh, -1, -1, -1

	Return tMesh
End Function

Function AlignTriangle( tri )
	s = GetSurface( tri, 1 )
	
	nx# = VertexNX( s, 0 )
	ny# = VertexNY( s, 0 )
	nz# = VertexNZ( s, 0 )
	
	pivot = CreatePivot()
	AlignToVector pivot,  ny, -nx, 0, 2
	EntityParent tri, pivot
	TurnEntity pivot, 0, -ACos(nz), 0   ; Note minus sign. Yaw goes opposite way from what I expect. 
				
	EntityParent tri, 0
	FreeEntity pivot
	
	; Tricky way to change mesh to match entity orientation.
	
	RotateMesh tri, EntityPitch( tri ), EntityYaw( tri ), EntityRoll( tri )
	RotateEntity tri, 0, 0, 0	
	
	
End Function



Function DumpVertices( mesh, message$="Vertex list" )

Local nSurfs, sIndex, surface, lf$, txt$
Local x$,y$,z$, nx$,ny$,nz$

	lf$=Chr$(10)	
	nSurfs=CountSurfaces( mesh )
	DebugLog lf+message+lf

	For sIndex=1 To nSurfs

		surface=GetSurface( mesh, sIndex )

		For v=0 To CountVertices( surface ) - 1

			 x = RSet(  VertexX( surface, v ), 14)
			 y = RSet(  VertexY( surface, v ), 14)
			 z = RSet(  VertexZ( surface, v ), 14)
			nx = RSet( VertexNX( surface, v ), 14)
			ny = RSet( VertexNY( surface, v ), 14)
			nz = RSet( VertexNZ( surface, v ), 14)			
		
			txt="v="+RSet(v,4)+"  "+x+y+z+"     "+nx+ny+nz
			DebugLog txt
			
		Next
				
	Next

End Function


A few notes about this:

AlignTriangle does the actual work. The face normal has been stored in the first vertex.

The triangle is equilateral for ease of visual inspection. The aligned ( green ) triangle is face-on to the camera and should look equilateral.

In theory the aligned triangle has z=0 for all vertices. In practice this is only approximately true.

Notice the minus sign on -ACos(nz). I initially forgot this. The yaw rotation has to be the opposite way from what I visualize. Those left-handed coordinates bite me in the ass every time!

And finally I really did have a clever idea, a simple way to rotate the tri mesh to match the tri entity.

Hi Floyd,

Thanks for that. I'll have a proper look when I get home. :)

Note: I've found it quite difficult to convert my brain back to B3D after using BlitzMax for a while, which is strange, since this kind of stuff used to come so easily. Been away too long I suppose 8D