[bb] PickedUV / Move texture with mouse by Noobody [ 1+ years ago ]

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

Previous topic - Next topic

BlitzBot

Title : PickedUV / Move texture with mouse
Author : Noobody
Posted : 1+ years ago

Description : Since B3D doesn't have functions for the UV coordinates at the intersection point of CameraPick/LinePick/etc. I wrote a function to do this. It works by first making a local 2D orthonormal coordinate system on the triangle and then converting the picked location to barycentric coordinates, allowing interpolation of UV coordinates within the triangle.

The sample code shows an application of this function by allowing the user to move a texture on a mesh using the mouse. The texel clicked by the user always stays at the mouse position, while the rest of the texture is moved around on the mesh (useful for editors, maybe?)


Code :
Code (blitzbasic) Select
Graphics3D 800, 600, 0, 2
SetBuffer BackBuffer()

Global PickedU#, PickedV#


Local Cam = CreateCamera()
Local Texture = CreateFunkyTexture()
Local Mesh = CreateCone(64)
EntityPickMode Mesh, 2
EntityTexture Mesh, Texture

MoveEntity Cam, 0.0, 0.0, -3.0

Local Timer = CreateTimer(60)

Local OldPickedU#, OldPickedV#, OldPickedSurface

While Not KeyHit(1)
TurnEntity Mesh, KeyDown(200) - KeyDown(208), KeyDown(205) - KeyDown(203), 0.0, True

If MouseDown(1) Then
CameraPick(Cam, MouseX(), MouseY())

If PickedEntity() <> 0 And PickedSurface() <> 0 Then
CalculatePickedUV()

If PickedSurface() = OldPickedSurface Then
DU# = PickedU# - OldPickedU#
DV# = PickedV# - OldPickedV#

Local Surface = PickedSurface()
For Vertex = 0 To CountVertices(Surface) - 1
Local U# = VertexU(Surface, Vertex)
Local V# = VertexV(Surface, Vertex)

VertexTexCoords Surface, Vertex, U# - DU#, V# - DV#
Next

PickedU# = PickedU# - DU#
PickedV# = PickedV# - DV#
Else
OldPickedSurface = PickedSurface()
EndIf

OldPickedU# = PickedU#
OldPickedV# = PickedV#
Else
OldPickedSurface = 0
EndIf
Else
OldPickedSurface = 0
EndIf

RenderWorld

Text 0, 0, "Click and drag the texture!"
Text 0, 15, "Use arrow keys to turn the mesh"

Flip 0
WaitTimer Timer
Wend
End

Function CreateFunkyTexture()
Local Texture = CreateTexture(256, 256, 1)

SetBuffer TextureBuffer(Texture)
LockBuffer()

For X = 0 To 255
For Y = 0 To 255
WritePixelFast X, Y, (X*$010001) Xor (Y*$010101)
Next
Next

UnlockBuffer()
SetBuffer BackBuffer()

Return Texture
End Function

Function CalculatePickedUV()
Local Surface = PickedSurface()
If Surface Then
Local Mesh = PickedEntity()

TFormPoint PickedX(), PickedY(), PickedZ(), 0, Mesh

Local Triangle = PickedTriangle()

Local V1 = TriangleVertex(Surface, Triangle, 0)
Local V2 = TriangleVertex(Surface, Triangle, 1)
Local V3 = TriangleVertex(Surface, Triangle, 2)

Local DX1# = VertexX(Surface, V2) - VertexX(Surface, V1)
Local DY1# = VertexY(Surface, V2) - VertexY(Surface, V1)
Local DZ1# = VertexZ(Surface, V2) - VertexZ(Surface, V1)
Local DX2# = VertexX(Surface, V3) - VertexX(Surface, V1)
Local DY2# = VertexY(Surface, V3) - VertexY(Surface, V1)
Local DZ2# = VertexZ(Surface, V3) - VertexZ(Surface, V1)

Local NX# = DY1#*DZ2# - DY2#*DZ1#
Local NY# = DX2#*DZ1# - DX1#*DZ2#
Local NZ# = DX1#*DY2# - DY1#*DX2#
Local UX# = NY #*DZ2# - DY2#*NZ #
Local UY# = DX2#*NZ # - NX #*DZ2#
Local UZ# = NX #*DY2# - NY #*DX2#

Local InvLength1# = 1.0/Sqr(UX#*UX# + UY#*UY# + UZ#*UZ#)
Local Length2# = Sqr(DX2#*DX2# + DY2#*DY2# + DZ2#*DZ2#)
Local InvLength2# = 1.0/Length2#

UX# = UX#*InvLength1#
UY# = UY#*InvLength1#
UZ# = UZ#*InvLength1#
DX2# = DX2#*InvLength2#
DY2# = DY2#*InvLength2#
DZ2# = DZ2#*InvLength2#

Local T1# = 0.0
Local S1# = 0.0
Local T2# = DX1#*UX # + DY1#*UY # + DZ1#*UZ #
Local S2# = DX1#*DX2# + DY1#*DY2# + DZ1#*DZ2#
Local T3# = 0.0
Local S3# = Length2#
Local T4# = (TFormedX() - VertexX(Surface, V1))*UX # + (TFormedY() - VertexY(Surface, V1))*UY # + (TFormedZ() - VertexZ(Surface, V1))*UZ #
Local S4# = (TFormedX() - VertexX(Surface, V1))*DX2# + (TFormedY() - VertexY(Surface, V1))*DY2# + (TFormedZ() - VertexZ(Surface, V1))*DZ2#


Local Denominator# = 1.0/((S2# - S3#)*(T1# - T3#) + (T3# - T2#)*(S1# - S3#))
Local Lambda1# = ((S2# - S3#)*(T4# - T3#) + (T3# - T2#)*(S4# - S3#))*Denominator#
Local Lambda2# = ((S3# - S1#)*(T4# - T3#) + (T1# - T3#)*(S4# - S3#))*Denominator#
Local Lambda3# = 1.0 - Lambda1# - Lambda2#

PickedU# = VertexU(Surface, V1)*Lambda1# + VertexU(Surface, V2)*Lambda2# + VertexU(Surface, V3)*Lambda3#
PickedV# = VertexV(Surface, V1)*Lambda1# + VertexV(Surface, V2)*Lambda2# + VertexV(Surface, V3)*Lambda3#
EndIf
End Function


Comments :


Kryzon(Posted 1+ years ago)

 Thanks for sharing, it looks great!Further references:Fredborg also wrote PickedUVW() style functions: <a href="codearcsc667.html?code=515" target="_blank">http://blitzbasic.com/codearcs/codearcs.php?code=515</a>You can also find something similar inside Blitz3DsamplesirdieTexPaint (if I got the author's name right.)


Noobody(Posted 1+ years ago)

 Oh, I wasn't aware of either of them, thanks for mentioning :)The other PickedU/V/W functions from the codearchives take a bit more code, but seem to work great! Replacing the PickedUV in this example with the code you linked gives the exact same behaviour.The one from the birdie sample does the job as well, although it uses CameraProject, which is something I wanted to avoid; values get very inaccurate when parts of the triangle are off-screen or behind the camera.


BlitzSupport(Posted 1+ years ago)

 Nice one!


Charrua(Posted 1+ years ago)

 thank's so muchJuan


void(Posted 1+ years ago)

 Much thanks


Mikorians(Posted 1+ years ago)

 Oooh yeah...!!!


RemiD

hello,

little addon to what our friend Noobody posted, (PickedU, PickedV)
to demonstrate how to calculate the picked texel position X,Y (on the texture) ( PickedTX, PickedTY )
with a demo

;PickedU,PickedV with mouse by Noobody
;PickedTX,PickedTY addon by RemiD
;calculates the picked U,V with some math wizardry (by Noobody)
;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$ = "Noobody" : 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 )
   CalculatePickedUVvNoobody() ;by Noobody
   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 CalculatePickedUVvNoobody()
;it works by first making a local 2D orthonormal coordinate system on the triangle, and then converting the picked location to barycentric coordinates, allowing interpolation of UV coordinates within the triangle.
Local Surface = PickedSurface()

If Surface Then

Local Mesh = PickedEntity()

TFormPoint PickedX(), PickedY(), PickedZ(), 0, Mesh

Local Triangle = PickedTriangle()

Local V1 = TriangleVertex(Surface, Triangle, 0)
Local V2 = TriangleVertex(Surface, Triangle, 1)
Local V3 = TriangleVertex(Surface, Triangle, 2)

Local DX1# = VertexX(Surface, V2) - VertexX(Surface, V1)
Local DY1# = VertexY(Surface, V2) - VertexY(Surface, V1)
Local DZ1# = VertexZ(Surface, V2) - VertexZ(Surface, V1)
Local DX2# = VertexX(Surface, V3) - VertexX(Surface, V1)
Local DY2# = VertexY(Surface, V3) - VertexY(Surface, V1)
Local DZ2# = VertexZ(Surface, V3) - VertexZ(Surface, V1)

Local NX# = DY1#*DZ2# - DY2#*DZ1#
Local NY# = DX2#*DZ1# - DX1#*DZ2#
Local NZ# = DX1#*DY2# - DY1#*DX2#
Local UX# = NY #*DZ2# - DY2#*NZ #
Local UY# = DX2#*NZ # - NX #*DZ2#
Local UZ# = NX #*DY2# - NY #*DX2#

Local InvLength1# = 1.0/Sqr(UX#*UX# + UY#*UY# + UZ#*UZ#)
Local Length2# = Sqr(DX2#*DX2# + DY2#*DY2# + DZ2#*DZ2#)
Local InvLength2# = 1.0/Length2#

UX# = UX#*InvLength1#
UY# = UY#*InvLength1#
UZ# = UZ#*InvLength1#
DX2# = DX2#*InvLength2#
DY2# = DY2#*InvLength2#
DZ2# = DZ2#*InvLength2#

Local T1# = 0.0
Local S1# = 0.0
Local T2# = DX1#*UX # + DY1#*UY # + DZ1#*UZ #
Local S2# = DX1#*DX2# + DY1#*DY2# + DZ1#*DZ2#
Local T3# = 0.0
Local S3# = Length2#
Local T4# = (TFormedX() - VertexX(Surface, V1))*UX# + (TFormedY() - VertexY(Surface, V1))*UY# + (TFormedZ() - VertexZ(Surface, V1))*UZ#
Local S4# = (TFormedX() - VertexX(Surface, V1))*DX2# + (TFormedY() - VertexY(Surface, V1))*DY2# + (TFormedZ() - VertexZ(Surface, V1))*DZ2#

Local Denominator# = 1.0/((S2# - S3#)*(T1# - T3#) + (T3# - T2#)*(S1# - S3#))
Local Lambda1# = ((S2# - S3#)*(T4# - T3#) + (T3# - T2#)*(S4# - S3#))*Denominator#
Local Lambda2# = ((S3# - S1#)*(T4# - T3#) + (T1# - T3#)*(S4# - S3#))*Denominator#
Local Lambda3# = 1.0 - Lambda1# - Lambda2#

PickedU# = VertexU(Surface, V1)*Lambda1# + VertexU(Surface, V2)*Lambda2# + VertexU(Surface, V3)*Lambda3#
PickedV# = VertexV(Surface, V1)*Lambda1# + VertexV(Surface, V2)*Lambda2# + VertexV(Surface, V3)*Lambda3#

EndIf

End Function

Thanks again to Noobody