TSprite with alpha and Picks

BlitzMax Forums/MiniB3D Module/TSprite with alpha and Picks

ima747(Posted 2008) [#1]
I probably need to go about this in a totally different way but I thought I'd ask in hopes there's some sort of a hack.

I need to load a bunch of 2d sprites with alpha transparency and lay them on top of each other which works fine and looks right, but I'm trying to use CameraPick(Camera, MouseX(), MouseY()) to identify what is being clicked on, but the camera pick can't see through the empty spots on the sprites to the ones below.

For example, if I have a ring and a square. I put the ring in front of the square. You can see the square around the edge of the ring, as well a through the center. If you click anywhere however it selects the ring, since the transparent parts of the picture still count as part of the 3D object.

I'm using pick mode 2 on everything.

I assume this is just a limitation of the sprite system, it's just a 2d plane with the loaded texture applied, and doesn't identify where the edges are, but I'm hoping someone know a way to make it work.

Any thoughts would be much appreciated.

I could do it easy enough in 2D but I'd really like to create some 3D effects.


Beaker(Posted 2008) [#2]
There are probably a few solutions to this. But here is mine:

1) Convert the PickedUVW() code from the code archive to BlitzMax.

2) Do your camerapick.
3) If you hit a transparent pixel with your pick then hide the PickedEntity and repeat from (2) again.

Should be do-able.


ima747(Posted 2008) [#3]
Wonderful! I will begin my code digging and report results, thank you very much!

Klepto, I know you're busy at the moment, but maybe a function like this can make it into extended at some point? Camera pick with an option for texture transparency handling? just a thought


ima747(Posted 2008) [#4]
If there's a better place to post this for posterity please let me know.

This is just a conversion of Mikkel Fredborg's PickedUVW code, without the example code. It isn't greatly tested yet, but so far it looks like it's doing the core of what it's supposed to (find the U/V coord of the texture on a picked entity, from which one can find the X/Y coord of the texture itself and then do any further figuring easily, i.e. transparency).

A note about use. You must use a pick command (CameraPick, LinePick, etc.) BEFORE calling any of the other commands as it uses the last picked item as it's target. Additionally you should only use PickedU(), PickedV() and PickedW() as it stores all the values whenever you make the first call to any of them after changing your picked elements, so it doesn't need to run all the point finding every time you call any of the functions. Quite a nice optimization in Mikkel's part I think.

SuperStrict

Import sidesign.minib3d

Rem 
; Created by Mikkel Fredborg
; Adapted for MiniB3D by Logan Chittenden
; 
; Use as you please, but please Include a thank you :)
;
End Rem

'; PickedTri Type
'; Necessary For the PickedU(), PickedV(), And PickedW() commands
Type PickedTri
	Field ent:TEntity,surf:TSurface,tri:Float		';picked entity, surface And triangle
	Field px:Float,py:Float,pz:Float				';picked xyz
	Field pu:Float[1],  pv:Float[1]  ,pw:Float[1]  	';picked uvw x 2
	
	Field vx:Float[2],  vy:Float[2]  ,vz:Float[2]  	';vertex xyz
	Field vnx:Float[2], vny:Float[2] ,vnz:Float[2] 	';vertex normals
	Field vu:Float[5],  vv:Float[5]  ,vw:Float[5]  	';vertex uvw x 2
End Type

Global ptri:pickedtri = New pickedtri


'; Returns the Texture U coordinate of the last successful pick command
'; coordset may be set To either 0 Or 1
Function PickedU:Float(coordset:Byte = 0)
	
	'; If something New has been picked Then calculate the New uvw coordinates
	If (PickedX()<>ptri.px) Or (PickedY()<>ptri.py) Or (PickedZ()<>ptri.pz) Or (PickedSurface()<>ptri.surf)
		PickedUVW()
	End If
	
	Return ptri.pu[coordset]
	
End Function


'; Returns the Texture U coordinate of the last successful pick command
'; coordset may be set To either 0 Or 1
Function PickedV:Float(coordset:Byte = 0)
	
	'; If something New has been picked Then calculate the New uvw coordinates
	If (PickedX()<>ptri.px) Or (PickedY()<>ptri.py) Or (PickedZ()<>ptri.pz) Or (PickedSurface()<>ptri.surf)
		PickedUVW()
	End If
	
	Return ptri.pv[coordset]
	
End Function


'; Returns the Texture U coordinate of the last successful pick command
'; coordset may be set To either 0 Or 1
Function PickedW:Float(coordset:Byte = 0)
	
	'; If something New has been picked Then calculate the New uvw coordinates
	If (PickedX()<>ptri.px) Or (PickedY()<>ptri.py) Or (PickedZ()<>ptri.pz) Or (PickedSurface()<>ptri.surf)
		PickedUVW()
	End If
	
	Return ptri.pw[coordset]
	
End Function


'; Calculates the UVW coordinates of a pick
'; Do Not call this by yourself, as PickedU(), PickedV(), And PickedW()
'; takes care of calling it when nescessary
Function PickedUVW()
	Local i:Byte

	If PickedSurface()
		ptri.ent  = PickedEntity()
		ptri.surf = PickedSurface()
		ptri.tri  = PickedTriangle()
			
		ptri.px = PickedX()
		ptri.py = PickedY()
		ptri.pz = PickedZ()
		
		For i = 0 To 2
			TFormPoint VertexX(ptri.surf,TriangleVertex(ptri.surf,ptri.tri,i)),VertexY(ptri.surf,TriangleVertex(ptri.surf,ptri.tri,i)),VertexZ(ptri.surf,TriangleVertex(ptri.surf,ptri.tri,i)),ptri.ent,Null

			ptri.vx[i] = TFormedX()
			ptri.vy[i] = TFormedY()
			ptri.vz[i] = TFormedZ()

			ptri.vnx[i] = VertexNX(ptri.surf,TriangleVertex(ptri.surf,ptri.tri,i))
			ptri.vny[i] = VertexNY(ptri.surf,TriangleVertex(ptri.surf,ptri.tri,i))
			ptri.vnz[i] = VertexNZ(ptri.surf,TriangleVertex(ptri.surf,ptri.tri,i))
					
			ptri.vu[i+0] = VertexU(ptri.surf,TriangleVertex(ptri.surf,ptri.tri,i),0)
			ptri.vv[i+0] = VertexV(ptri.surf,TriangleVertex(ptri.surf,ptri.tri,i),0)
			ptri.vw[i+0] = VertexW(ptri.surf,TriangleVertex(ptri.surf,ptri.tri,i),0)

			ptri.vu[i+3] = VertexU(ptri.surf,TriangleVertex(ptri.surf,ptri.tri,i),1)
			ptri.vv[i+3] = VertexV(ptri.surf,TriangleVertex(ptri.surf,ptri.tri,i),1)
			ptri.vw[i+3] = VertexW(ptri.surf,TriangleVertex(ptri.surf,ptri.tri,i),1)
		Next

		'; Select which component of xyz coordinates To ignore
		Local coords:Byte = 3

		If Abs(PickedNX()) > Abs(PickedNY())
			If Abs(PickedNX())>Abs(PickedNZ()) Then coords = 1
		Else
			If Abs(PickedNY())>Abs(PickedNZ()) Then coords = 2
		EndIf
		
		Local a0:Float,a1:Float,b0:Float,b1:Float,c0:Float,c1:Float
		
		'; xy components
		If (coords = 3)
			'; edge 0
			a0 = ptri.vx[1] - ptri.vx[0]
			a1 = ptri.vy[1] - ptri.vy[0]
		
			'; edge 1
			b0 = ptri.vx[2] - ptri.vx[0]
			b1 = ptri.vy[2] - ptri.vy[0]

			'; picked offset from triangle vertex 0
			c0 = PickedX() - ptri.vx[0]
			c1 = PickedY() - ptri.vy[0]
		Else		
			'; xz components
			If (coords = 2)
				'; edge 0
				a0 = ptri.vx[1] - ptri.vx[0]
				a1 = ptri.vz[1] - ptri.vz[0]
		
				'; edge 1
				b0 = ptri.vx[2] - ptri.vx[0]
				b1 = ptri.vz[2] - ptri.vz[0]

				'; picked offset from triangle vertex 0
				c0 = PickedX() - ptri.vx[0]
				c1 = PickedZ() - ptri.vz[0]
			Else
				'; yz components

				'; edge 0
				a0 = ptri.vy[1] - ptri.vy[0]
				a1 = ptri.vz[1] - ptri.vz[0]
		
				'; edge 1
				b0 = ptri.vy[2] - ptri.vy[0]
				b1 = ptri.vz[2] - ptri.vz[0]

				'; picked offset from triangle vertex 0
				c0 = PickedY() - ptri.vy[0]
				c1 = PickedZ() - ptri.vz[0]
			End If
		End If
						
		Rem
		; u And v are offsets from vertex 0 along edge 0 And edge 1
		; using these it is possible To calculate the Texture UVW coordinates
		; of the picked XYZ location
		;
		; a0*u + b0*v = c0
		; a1*u + b1*v = c1
		;
		; solve equation (standard equation with 2 unknown quantities)
		; check a math book To see why the following is True
		;
		End Rem
		Local u:Float = (c0*b1 - b0*c1) / (a0*b1 - b0*a1)
		Local v:Float = (a0*c1 - c0*a1) / (a0*b1 - b0*a1)
		
		'; If either u Or v is out of range Then the
		'; picked entity was Not a mesh, And therefore
		'; the uvw coordinates cannot be calculated
		If (u<0.0 Or u>1.0) Or (v<0.0 Or v>1.0)
			Return 
		End If
		
		'; Calculate picked uvw's for coordset 0 (and modulate them to be in the range of 0-1 nescessary)
		ptri.pu[0] = (ptri.vu[0] + ((ptri.vu[1] - ptri.vu[0]) * u) + ((ptri.vu[2] - ptri.vu[0]) * v)) Mod 1
		ptri.pv[0] = (ptri.vv[0] + ((ptri.vv[1] - ptri.vv[0]) * u) + ((ptri.vv[2] - ptri.vv[0]) * v)) Mod 1
		ptri.pw[0] = (ptri.vw[0] + ((ptri.vw[1] - ptri.vw[0]) * u) + ((ptri.vw[2] - ptri.vw[0]) * v)) Mod 1
		
		'; If any of the coords are negative
		If ptri.pu[0]<0.0 Then ptri.pu[0] = 1.0 + ptri.pu[0]
		If ptri.pv[0]<0.0 Then ptri.pv[0] = 1.0 + ptri.pv[0]
		If ptri.pw[0]<0.0 Then ptri.pw[0] = 1.0 + ptri.pw[0]
		
		'; Calculate picked uvw's for coordset 1 (and modulate them to be in the range of 0-1 nescessary)
		ptri.pu[1] = (ptri.vu[3] + ((ptri.vu[4] - ptri.vu[3]) * u) + ((ptri.vu[5] - ptri.vu[3]) * v)) Mod 1
		ptri.pv[1] = (ptri.vv[3] + ((ptri.vv[4] - ptri.vv[3]) * u) + ((ptri.vv[5] - ptri.vv[3]) * v)) Mod 1
		ptri.pw[1] = (ptri.vw[3] + ((ptri.vw[4] - ptri.vw[3]) * u) + ((ptri.vw[5] - ptri.vw[3]) * v)) Mod 1

		'; If any of the coords are negative
		If ptri.pu[1]<0.0 Then ptri.pu[1] = 1.0 + ptri.pu[1]
		If ptri.pv[1]<0.0 Then ptri.pv[1] = 1.0 + ptri.pv[1]
		If ptri.pw[1]<0.0 Then ptri.pw[1] = 1.0 + ptri.pw[1]
	End If

End Function