[bb] PickedU(), PickedV(), PickedW() by fredborg [ 1+ years ago ]

Started by BlitzBot, June 29, 2017, 00:28:39

Previous topic - Next topic

BlitzBot

Title : PickedU(), PickedV(), PickedW()
Author : fredborg
Posted : 1+ years ago

Description : A nice way to retrieve the texture coordinates of a pick (ex. CameraPick and LinePick).

Usage:
PickedU([coordset]) - Returns the U component of the last successful pick command. Coordset can be either 0 or 1 depending on which texture coord set you wish to get (defaults to 0).
PickedV([coordset]) - Returns the V component
PickedW([coordset]) - Returns the W component

Includes a small test program located at the bottom of the code, so you can see how it's used (if you hadn't figured it out by now ;)

Update: Example now shows how to retrieve a pixel color from a texture!

Limitation: This set of commands are not affected by the use of 'ScaleTexture', 'PositionTexture', or 'RotateTexture'. You have to make your own calculations, if you have used any of these commands!!!

Use as you please, but please include a mention/thanks to me :)


Code :
Code (blitzbasic) Select
;
; PickedU(), PickedV(), PickedW() commands
;
; Created by Mikkel Fredborg
;
; Use as you please, but please include a thank you :)
;

;
; PickedTri type
; Necessary for the PickedU(), PickedV(), and PickedW() commands
Type PickedTri
Field ent,surf,tri ;picked entity, surface and triangle
Field px#,py#,pz#    ;picked xyz
Field pu#[1],  pv#[1]  ,pw#[1]  ;picked uvw x 2

Field vx#[2],  vy#[2]  ,vz#[2]  ;vertex xyz
Field vnx#[2], vny#[2] ,vnz#[2] ;vertex normals
Field vu#[5],  vv#[5]  ,vw#[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#(coordset = 0)

; if something new has been picked then calculate the new uvw coordinates
If (PickedX()<>ptripx) Or (PickedY()<>ptripy) Or (PickedZ()<>ptripz) Or (PickedSurface()<>ptrisurf)
PickedUVW()
End If

Return ptripu[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#(coordset = 0)

; if something new has been picked then calculate the new uvw coordinates
If (PickedX()<>ptripx) Or (PickedY()<>ptripy) Or (PickedZ()<>ptripz) Or (PickedSurface()<>ptrisurf)
PickedUVW()
End If

Return ptripv[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#(coordset = 0)

; if something new has been picked then calculate the new uvw coordinates
If (PickedX()<>ptripx) Or (PickedY()<>ptripy) Or (PickedZ()<>ptripz) Or (PickedSurface()<>ptrisurf)
PickedUVW()
End If

Return ptripw[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()

If PickedSurface()
ptrient  = PickedEntity()
ptrisurf = PickedSurface()
ptri ri  = PickedTriangle()

ptripx = PickedX()
ptripy = PickedY()
ptripz = PickedZ()

For i = 0 To 2
TFormPoint VertexX(ptrisurf,TriangleVertex(ptrisurf,ptri ri,i)),VertexY(ptrisurf,TriangleVertex(ptrisurf,ptri ri,i)),VertexZ(ptrisurf,TriangleVertex(ptrisurf,ptri ri,i)),ptrient,0

ptrivx[i] = TFormedX()
ptrivy[i] = TFormedY()
ptrivz[i] = TFormedZ()

ptrivnx[i] = VertexNX(ptrisurf,TriangleVertex(ptrisurf,ptri ri,i))
ptrivny[i] = VertexNY(ptrisurf,TriangleVertex(ptrisurf,ptri ri,i))
ptrivnz[i] = VertexNZ(ptrisurf,TriangleVertex(ptrisurf,ptri ri,i))

ptrivu[i+0] = VertexU(ptrisurf,TriangleVertex(ptrisurf,ptri ri,i),0)
ptrivv[i+0] = VertexV(ptrisurf,TriangleVertex(ptrisurf,ptri ri,i),0)
ptrivw[i+0] = VertexW(ptrisurf,TriangleVertex(ptrisurf,ptri ri,i),0)

ptrivu[i+3] = VertexU(ptrisurf,TriangleVertex(ptrisurf,ptri ri,i),1)
ptrivv[i+3] = VertexV(ptrisurf,TriangleVertex(ptrisurf,ptri ri,i),1)
ptrivw[i+3] = VertexW(ptrisurf,TriangleVertex(ptrisurf,ptri ri,i),1)
Next

; Select which component of xyz coordinates to ignore
Local coords = 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#,a1#,b0#,b1#,c0#,c1#

; xy components
If (coords = 3)
; edge 0
a0# = ptrivx[1] - ptrivx[0]
a1# = ptrivy[1] - ptrivy[0]

; edge 1
b0# = ptrivx[2] - ptrivx[0]
b1# = ptrivy[2] - ptrivy[0]

; picked offset from triangle vertex 0
c0# = PickedX() - ptrivx[0]
c1# = PickedY() - ptrivy[0]
Else
; xz components
If (coords = 2)
; edge 0
a0# = ptrivx[1] - ptrivx[0]
a1# = ptrivz[1] - ptrivz[0]

; edge 1
b0# = ptrivx[2] - ptrivx[0]
b1# = ptrivz[2] - ptrivz[0]

; picked offset from triangle vertex 0
c0# = PickedX() - ptrivx[0]
c1# = PickedZ() - ptrivz[0]
Else
; yz components

; edge 0
a0# = ptrivy[1] - ptrivy[0]
a1# = ptrivz[1] - ptrivz[0]

; edge 1
b0# = ptrivy[2] - ptrivy[0]
b1# = ptrivz[2] - ptrivz[0]

; picked offset from triangle vertex 0
c0# = PickedY() - ptrivy[0]
c1# = PickedZ() - ptrivz[0]
End If
End If

;
; 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
;
Local u# = (c0*b1 - b0*c1) / (a0*b1 - b0*a1)
Local v# = (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)
ptripu[0] = (ptrivu[0] + ((ptrivu[1] - ptrivu[0]) * u) + ((ptrivu[2] - ptrivu[0]) * v)) Mod 1
ptripv[0] = (ptrivv[0] + ((ptrivv[1] - ptrivv[0]) * u) + ((ptrivv[2] - ptrivv[0]) * v)) Mod 1
ptripw[0] = (ptrivw[0] + ((ptrivw[1] - ptrivw[0]) * u) + ((ptrivw[2] - ptrivw[0]) * v)) Mod 1

; If any of the coords are negative
If ptripu[0]<0.0 Then ptripu[0] = 1.0 + ptripu[0]
If ptripv[0]<0.0 Then ptripv[0] = 1.0 + ptripv[0]
If ptripw[0]<0.0 Then ptripw[0] = 1.0 + ptripw[0]

; Calculate picked uvw's for coordset 1 (and modulate them to be in the range of 0-1 nescessary)
ptripu[1] = (ptrivu[3] + ((ptrivu[4] - ptrivu[3]) * u) + ((ptrivu[5] - ptrivu[3]) * v)) Mod 1
ptripv[1] = (ptrivv[3] + ((ptrivv[4] - ptrivv[3]) * u) + ((ptrivv[5] - ptrivv[3]) * v)) Mod 1
ptripw[1] = (ptrivw[3] + ((ptrivw[4] - ptrivw[3]) * u) + ((ptrivw[5] - ptrivw[3]) * v)) Mod 1

; If any of the coords are negative
If ptripu[1]<0.0 Then ptripu[1] = 1.0 + ptripu[1]
If ptripv[1]<0.0 Then ptripv[1] = 1.0 + ptripv[1]
If ptripw[1]<0.0 Then ptripw[1] = 1.0 + ptripw[1]
End If

End Function




;
; Test example for
; PickedU(),PickedV(),PickedW() commands
;
; Created by Mikkel Fredborg
; Inspired by David Bird
;
Graphics3D 640,480
SetBuffer BackBuffer()

lit=CreateLight()
LightColor lit,60,60,60
PositionEntity lit,0,10,0
RotateEntity lit,90,0,0

cam=CreateCamera()
CameraRange cam,.1,1000
PositionEntity cam,0,0,-3
CameraClsColor cam,100,100,100

cubetex=CreateTexture(64,64)
; make some squares on the texture
SetBuffer TextureBuffer(cubetex)
Color 128,128,128
Rect 0,0,TextureWidth(cubetex)/2.0,TextureHeight(cubetex)/2.0,True
Rect TextureWidth(cubetex)/2.0,TextureHeight(cubetex)/2.0,(TextureWidth(cubetex)/2.0)-1,(TextureHeight(cubetex)/2.0)-1,True
SetBuffer BackBuffer()

cube = CreateCube()
PositionEntity cube,1,0,0
EntityTexture cube,cubetex,0,0
EntityPickMode cube,2

; Adjust uv coordinates of the cube to tile the texture
surf_n = CountSurfaces(cube)
For i = 1 To surf_n
surf = GetSurface(cube,i)
n_verts = CountVertices(surf)-1
For j = 0 To n_verts
VertexTexCoords surf,j,VertexU(surf,j)*5.0,VertexV(surf,j)*5.0,VertexW(surf,j)*5.0
Next
Next

spheretex=CreateTexture(256,256)
sphere = CreateSphere()
PositionEntity sphere,-1,0,0
EntityTexture sphere,spheretex,0,0
EntityPickMode sphere,2

plane = CreatePlane()
PositionEntity plane,0,-2,0
EntityPickMode plane,2

Global dotred# = 0.0
Global dotgrn# = 0.0
Global dotblu# = 0.0

ang# = 0
While Not KeyDown(1)

xm = MouseX()
ym = MouseY()

If MouseDown(1) Then
Select CameraPick(cam,xm,ym)
Case cube:    PaintRedDot(cubetex  ,PickedU()*(TextureWidth(cubetex)-1)  ,PickedV()*(TextureHeight(cubetex)-1))
Case sphere:  PaintRedDot(spheretex,PickedU()*(TextureWidth(spheretex)-1),PickedV()*(TextureHeight(spheretex)-1))
End Select
Else
If MouseDown(2) Then
Select CameraPick(cam,xm,ym)
Case cube:    ReadDot(cubetex  ,PickedU()*(TextureWidth(cubetex)-1)  ,PickedV()*(TextureHeight(cubetex)-1))
Case sphere:  ReadDot(spheretex,PickedU()*(TextureWidth(spheretex)-1),PickedV()*(TextureHeight(spheretex)-1))
End Select
End If
End If

ang = (ang + 0.1) Mod 360
TurnEntity cube,-Sin(ang)*0.3,-Cos(ang)*0.3,0
TurnEntity sphere,Sin(ang)*0.1,Cos(ang)*0.1,0

UpdateWorld
RenderWorld

; draw mouse cursor
Line xm-8,ym,xm-4,ym
Line xm+8,ym,xm+4,ym
Line xm,ym-8,xm,ym-4
Line xm,ym+8,xm,ym+4
Oval xm-4,ym-4,9,9,False

; write some info
Text 0,  0,"Use left mousebutton to paint the cube and the sphere"
Text 0, 20,"ent = "+PickedEntity()
Text 0, 35,"surf = "+PickedSurface()
Text 0, 50,"tri = "+PickedTriangle()

Text 0, 80,"x = "+PickedX()
Text 0, 95,"y = "+PickedY()
Text 0,110,"z = "+PickedZ()

Text 0,140,"u = "+PickedU()
Text 0,155,"v = "+PickedV()
Text 0,170,"w = "+PickedW()

Text 0,200,"nx= "+PickedNX()
Text 0,215,"ny= "+PickedNY()
Text 0,230,"nz= "+PickedNZ()

Text 14,460,"Use right mousebutton to pick up a texture color"
Color dotred,dotgrn,dotblu
Rect 0,460,12,12,True
Color 255,255,255
Flip
Wend

ClearWorld
End

;
; Plots a red dot :)
Function PaintRedDot(tex,x,y)
SetBuffer TextureBuffer(tex)

maxwidth  = TextureWidth(tex)
maxheight = TextureHeight(tex)

red# = 255
grn# = 0
blu# = 0
WritePixel x,y,blu Or (grn Shl 8) Or (red Shl 16)

For xx = -1 To 1
For yy = -1 To 1
If (xx=0) Xor (yy=0)
If (xx<0 And x>0) Or (xx>0 And x<maxwidth) Or (yy<0 And y>0) Or (yy>0 And y<maxheight)
argb = ReadPixel(x+xx,y+yy)
red# =  64 + (argb Shr 16 And %11111111)
grn# =   0 + (argb Shr 8 And %11111111)
blu# =   0 + (argb Shr 8 And %11111111)

If red>255 Then red = 255
If grn>255 Then grn = 255
If blu>255 Then blu = 255

WritePixel x+xx,y+yy,blu Or (grn Shl 8) Or (red Shl 16)
End If
End If
Next
Next

SetBuffer BackBuffer()
End Function

;
; Reads a pixel in a texture
Function ReadDot(tex,x,y)
SetBuffer TextureBuffer(tex)

maxwidth  = TextureWidth(tex)
maxheight = TextureHeight(tex)

If x<maxwidth And y<maxheight
argb = ReadPixel(x,y)
dotred# = (argb Shr 16 And %11111111)
dotgrn# = (argb Shr 8 And %11111111)
dotblu# = (argb Shr 8 And %11111111)
End If

SetBuffer BackBuffer()
End Function


Comments :


Kryzon(Posted 1+ years ago)

 Looks nice.


RemiD

Hello,

I have rewritten the function by our friend Fredborg so that you don't need custom types and the (useless) functions, only one function ( returns PickedU, PickedV )
also i have added a little addon to calculate the picked texel position X,Y ( PickedTX, PickedTY )
with a demo :

;PickedU,PickedV by Fredborg
;PickedTX,PickedTY addon by RemiD
;calculates picked U,V with some math wizardry (by Fredborg)
;once the U,V ( PickedU PickedV ) of the picked point is determined, we can calculate the position X,Y of the picked texel ( PickedTX, PickedTY )
Graphics3D(800,600,32,2)

SeedRnd(MilliSecs())

Global PickedU#, PickedV# ;picked U,V
Global PickedTX%, PickedTY% ;picked texel X,Y

Local Cam = CreateCamera()
CameraRange(Cam,0.1,100)
CameraClsColor(Cam,025,025,025)

Local Texture = CreateTexture(64,64,1)
SetBuffer(TextureBuffer(Texture))
ClsColor(250,250,250) : Cls()
;outline of the texture
Color(125,125,125) : Line(0,0,64-1,0)
Color(125,125,125) : Line(0,64-1,64-1,64-1)
Color(125,125,125) : Line(0,0,0,64-1)
Color(125,125,125) : Line(64-1,0,64-1,64-1)
;corner top left (red texel)
Color(255,000,000) : Plot(0,0)
;corner top right (green texel)
Color(000,255,000) : Plot(64-1,0)
;corner bottom left (blue texel)
Color(000,000,255) : Plot(0,64-1)
;corner bottom right (white texel)
Color(255,255,255) : Plot(64-1,64-1)
;some text
Color(025,025,025) : TStr$ = "Fredborg" : TX% = TextureWidth(Texture)/2-StringWidth(TStr)/2 : TY% = TextureHeight(Texture)/2-StringHeight(TStr)/2 : Text(TX,TY,TStr)

Local Mesh = CreateCube()
EntityTexture(Mesh,Texture)
RotateEntity(Mesh,0,Rand(-180,180),0,True)
EntityPickMode(Mesh,2)

PositionEntity(Cam,0,1.65,-3.0)
RotateEntity(Cam,22.5,0,0)

While Not KeyDown(1)

If( KeyDown(200)=1 )
  TurnEntity(Mesh,+1,0,0)
Else If( KeyDown(208)=1 )
  TurnEntity(Mesh,-1,0,0)
EndIf
If( KeyDown(203)=1 )
  TurnEntity(Mesh,0,-1,0)
Else If( KeyDown(205)=1 )
  TurnEntity(Mesh,0,+1,0)
EndIf

If( MouseDown(1)=1 )
  CameraPick(Cam, MouseX(), MouseY())
  If( PickedEntity() <> 0 And PickedSurface() <> 0 )
   CalculatePickedUVvFredborg() ;by Fredborg
   PickedTX% = TextureWidth(Texture)*PickedU : PickedTY% = TextureHeight(Texture)*PickedV
   DebugLog(PickedU+","+PickedV+"->"+PickedTX+","+PickedTY)
   SetBuffer(TextureBuffer(Texture))
   Color(255,000,255) : Plot(PickedTX,PickedTY)
  EndIf
EndIf

SetBuffer(BackBuffer())
RenderWorld()

Color(255,255,255)
Text(0,0,"Mouse Left to pick / color texels on the texture")
Text(0,16,"Arrows to turn the shape up, down, left, right")

Flip(True)

Wend

End()

Function CalculatePickedUVvFredborg()

If( PickedSurface() )

picent = PickedEntity()
picsurf = PickedSurface()
pictri  = PickedTriangle()

picpx# = PickedX()
picpy# = PickedY()
picpz# = PickedZ()

Local picvx#[3], picvy#[3], picvz#[3]
;Local picvnx#[3], picvny#[3], picvnz#[3]
Local picvu#[3], picvv#[3] ; picvw#[3]
Local picpu#[1], picpv#[1] ; picpw#[1]

For i = 0 To 2

TFormPoint(VertexX(picsurf,TriangleVertex(picsurf,pictri,i)),VertexY(picsurf,TriangleVertex(picsurf,pictri,i)),VertexZ(picsurf,TriangleVertex(picsurf,pictri,i)),picent,0)

picvx[i] = TFormedX()
picvy[i] = TFormedY()
picvz[i] = TFormedZ()

;picvnx[i] = VertexNX(picsurf,TriangleVertex(picsurf,pictri,i))
;picvny[i] = VertexNY(picsurf,TriangleVertex(picsurf,pictri,i))
;picvnz[i] = VertexNZ(picsurf,TriangleVertex(picsurf,pictri,i))

picvu[i+0] = VertexU(picsurf,TriangleVertex(picsurf,pictri,i),0)
picvv[i+0] = VertexV(picsurf,TriangleVertex(picsurf,pictri,i),0)
;picvw[i+0] = VertexW(picsurf,TriangleVertex(picsurf,pictri,i),0)

Next

; Select which component of xyz coordinates to ignore
Local coords = 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#,a1#,b0#,b1#,c0#,c1#

; xy components
If (coords = 3)
; edge 0
a0# = picvx[1] - picvx[0]
a1# = picvy[1] - picvy[0]

; edge 1
b0# = picvx[2] - picvx[0]
b1# = picvy[2] - picvy[0]

; picked offset from triangle vertex 0
c0# = PickedX() - picvx[0]
c1# = PickedY() - picvy[0]
Else
; xz components
If (coords = 2)
; edge 0
a0# = picvx[1] - picvx[0]
a1# = picvz[1] - picvz[0]

; edge 1
b0# = picvx[2] - picvx[0]
b1# = picvz[2] - picvz[0]

; picked offset from triangle vertex 0
c0# = PickedX() - picvx[0]
c1# = PickedZ() - picvz[0]
Else
; yz components

; edge 0
a0# = picvy[1] - picvy[0]
a1# = picvz[1] - picvz[0]

; edge 1
b0# = picvy[2] - picvy[0]
b1# = picvz[2] - picvz[0]

; picked offset from triangle vertex 0
c0# = PickedY() - picvy[0]
c1# = PickedZ() - picvz[0]
End If
End If

;
; 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
;
Local u# = (c0*b1 - b0*c1) / (a0*b1 - b0*a1)
Local v# = (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)
picpu[0] = (picvu[0] + ((picvu[1] - picvu[0]) * u) + ((picvu[2] - picvu[0]) * v)) Mod 1
picpv[0] = (picvv[0] + ((picvv[1] - picvv[0]) * u) + ((picvv[2] - picvv[0]) * v)) Mod 1
;picpw[0] = (picvw[0] + ((picvw[1] - picvw[0]) * u) + ((picvw[2] - picvw[0]) * v)) Mod 1

; If any of the coords are negative
If( picpu[0] < 0.0 ) Then picpu[0] = 1.0 + picpu[0]
If( picpv[0] < 0.0 ) Then picpv[0] = 1.0 + picpv[0]
        ;If( picpw[0] < 0.0 ) Then picpw[0] = 1.0 + picpw[0]

PickedU = picpu[0] : PickedV = picpv[0]

End If

End Function

Thanks again Fredborg

peteswansen

As usual Remi's coding is really good....so I have been modifying this to load a model in and then "painting" the model in larger blocks of color...nice bit of fun!!

_PJ_

What an amazingly useful set of functions!!!
These really belonged in B3D from the start.

Thanks very much RemiD!