Tubes , bent tubes, custom pipes

Everything related to 3D programming
applePi
Addict
Addict
Posts: 1404
Joined: Sun Jun 25, 2006 7:28 pm

Tubes , bent tubes, custom pipes

Post by applePi »

the tube are made from circles and every circle (ring) should be perpendicular to the corresponding point on the tube curve, one way to do that is called frenet frame to extrude the circle points from the center point
i will use the frenet sqaure approximation used in a freebasic example by relsoft posted here http://www.dbfinteractive.com/forum/ind ... pic=5993.0
it is implemented the description in http://www.blackpawn.com/texts/pqtorus/
remember: if you want to use other curve equations you must insert it twice in the center point and in the next point For Frenet square ,look code below in lines 132 to 142
the first example is a knot, to change its thickness change #BAND_RAD

Image

you can save the mesh to *.mesh file, and if you want to save to *.x file use the code for MP3D library which i have posted here http://www.purebasic.fr/english/viewtop ... 31#p425131

Code: Select all

                     
#TWOPI = 2 * #PI    ;PI For rotation 

#NUM_RINGS = 360 ; number of rings over all the tube

#NUM_BANDS = 32 ; number of points of every ring (circle)

#BAND_RAD  = 0.5 ;tube thickness
Global.f rot = 1
Global stop = 0

Structure vector3d
  x.f
  y.f
  z.f
EndStructure
	 

Declare DrawTube (Rings.l, Bands.l,BandRadius.f)

Quit.b = #False
ExamineDesktops()
OpenWindow(3, 0, 0, DesktopWidth(0), DesktopHeight(0), "tubes , press space to toggle rotation", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

;Initialize environment
InitEngine3D()
InitSprite()
OpenWindowedScreen(WindowID(3), 0, 0, DesktopWidth(0), DesktopHeight(0), 0, 0, 0)
InitKeyboard()
SetFrameRate(60)
Add3DArchive(".", #PB_3DArchive_FileSystem)
Add3DArchive(#PB_Compiler_Home + "Examples/3D/Data/Textures", #PB_3DArchive_FileSystem)

CreateLight(0,RGB(255,255,255),-100,40,30)
AmbientColor(RGB(100,100,100))

CreateCamera(0, 0, 0, 100, 100)
MoveCamera(0, 0, 4, 12)
CameraLookAt(0, 0, 2, 0)

RotateCamera(0, -15, 0, 0)
;CreateMaterial(0, LoadTexture(0, "terrain_texture.jpg"))
CreateMaterial(0, LoadTexture(0, "DosCarte.png"))
;CreateMaterial(0, LoadTexture(0, "MRAMOR6X6.jpg"))
MaterialCullingMode(0, #PB_Material_NoCulling)
;MaterialShadingMode(0, #PB_Material_Wireframe)
DisableMaterialLighting(0, #True)
CreateMesh(1, #PB_Mesh_TriangleList, #PB_Mesh_Static )
SetMeshMaterial(1, MaterialID(0))

DrawTube (#NUM_RINGS, #NUM_BANDS, #BAND_RAD )
NormalizeMesh(1)
FinishMesh(1)

CreateEntity(1, MeshID(1), MaterialID(0))
;ScaleEntity(1,2,2,2)
MoveEntity(1,0,1,-2)
ExamineKeyboard()
y.f
Repeat
  Event = WindowEvent()
  If KeyboardReleased(#PB_Key_Space)  ; rotate left
    stop ! 1 
  EndIf  
   If stop = 0 
    y+rot
   EndIf
   
   RotateEntity(1,0,y,0)
   
   RenderWorld()
   FlipBuffers()
   
   ExamineKeyboard()
   If KeyboardPushed(#PB_Key_Escape)
      Quit = #True
    EndIf
Until Quit = #True Or Event = #PB_Event_CloseWindow

;beginning of frenet approx procedures
Procedure vector_cross(*v1.vector3d, *v2.vector3d, *vout.vector3d)
    *vout\x = (*v1\y * *v2\z) - (*v2\y * *v1\z)
    *vout\y = (*v1\z * *v2\x) - (*v2\z * *v1\x)
    *vout\z = (*v1\x * *v2\y) - (*v2\x * *v1\y)
EndProcedure

Procedure.f vector_magnitude(*v.vector3d)
    mag.f
    mag = Sqr(*v\x * *v\x + *v\y * *v\y + *v\z * *v\z)
    If mag = 0:mag = 1:EndIf
    ProcedureReturn mag
EndProcedure

Procedure vector_normalize (*v.vector3d)
    mag.f
    mag = vector_magnitude(*v)
    *v\x = *v\x / mag
    *v\y = *v\y / mag
    *v\z = *v\z / mag
EndProcedure


Procedure vector_add (*v1.vector3d, *v2.vector3d, *vout.vector3d)
    *vout\x = *v1\x + *v2\x
    *vout\y = *v1\y + *v2\y
    *vout\z = *v1\z + *v2\z
EndProcedure

Procedure vector_sub (*v1.vector3d, *v2.vector3d, *vout.vector3d)
    *vout\x = *v1\x - *v2\x
    *vout\y = *v1\y - *v2\y
    *vout\z = *v1\z - *v2\z
EndProcedure

; main program
Procedure  DrawTube (Rings.l, Bands.l, BandRadius.f)

  x.f: y.f: z.f

	op.f = 1
	    
	txu.f : txv.f
	current_point.vector3d  ; current point on tube curve
  next_point.vector3d     ; next point on tube curve
  T.vector3d
  B.vector3d
  N.vector3d
  p.f
        
	For i = 0 To rings + 1
	    	
        ;center point
        p = op * i * #TWOPI / rings
        current_point\x = Cos(p) + 2.5*Cos(-2*p) 
        current_point\y = Sin(p) + 3.5*Sin(-2*p)
        current_point\z = 2 * Sin(3*p)

        ;next point For Frenet square
        p = op * (i + 1) * #TWOPI / rings
        next_point\x = Cos(p) + 2.5*Cos(-2*p)
        next_point\y = Sin(p) + 3.5*Sin(-2*p)
        next_point\z = 2 * Sin(3*p)

        ;T  = P' - P
        vector_sub(next_point, current_point, T)

        ;N = P' + P
        vector_add(next_point, current_point, N)

        ;B = T x N
        vector_cross(T, N, B)

        ;N = B x T
        vector_cross(B, T, N)

        ;Normalize vectors Or Else it won't work
        vector_normalize(B)
        vector_normalize(N)
        
        For j = 0 To bands - 1
           	new_point_x.f
        		new_point_y.f
        		
        		;rotate around the current point using normal rotation makes bands
            new_point_x = Sin(j * #TWOPI / bands) * #BAND_RAD
            new_point_y = Cos(j * #TWOPI / bands) * #BAND_RAD
            
				    ;this is the coords of our point along the curve
            x = N\x * new_point_x + B\x * new_point_y + current_point\x
            y = N\y * new_point_x + B\y * new_point_y + current_point\y
            z = N\z * new_point_x + B\z * new_point_y + current_point\z
            
                MeshVertexPosition(x, y, z)
                MeshVertexTextureCoordinate(txu, txv)
                MeshVertexNormal(x, y, z)
                txv = txv + 1/bands
                         
			Next 
			txv = 0
			;txu = txu + 1/rings 
			txu = txu + 1/bands 
		Next 
		
		v.l
		 For i = 0 To rings - 1
      For j = 0 To bands - 1
          MeshFace(v,v+1,v + bands+1)
          MeshFace(v + bands+1,v + bands+2,v+1 )
          
          v + 1   
          
     Next
     
   Next  


  EndProcedure
  
the next example is a helix which have physics, and a ball are dropped from its opening following the helical tube, press Right or Left keys to toggle rotation clockwise or anticlockwise, the ball should have friction and when you rotate the helix the same as the ball the ball will fall slower
Image
note that if you want to stretched the helix change the lines 156, 162 to something like
current_point\z = 0.6 * p
next_point\z = 0.6 * p

Image

Code: Select all

; this code depends on Frenet square approximation from Relsoft Rel.Betterwebber.com
                    
#manyPi = 10 * #PI    ;PI For rotation 

#NUM_RINGS = 360
;#NUM_RINGS = 70
#NUM_BANDS = 32
;#RING_RAD  = 2.0
#BAND_RAD  = 0.5 ; thickness of the tube
#plane = 9
#ball = 10
Global stop = 1
Global.f rot = 1

Structure vector3d
  x.f
  y.f
  z.f
EndStructure
	 

Declare DrawTube (Rings.l, Bands.l, BandRadius.f)


Quit.b = #False
ExamineDesktops()
OpenWindow(3, 0, 0, DesktopWidth(0), DesktopHeight(0), "tubes , press right, Left  to toggle rotation clockwise/ anticlockwise ", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

;Initialize environment
InitEngine3D()
InitSprite()
OpenWindowedScreen(WindowID(3), 0, 0, DesktopWidth(0), DesktopHeight(0), 0, 0, 0)
InitKeyboard()
SetFrameRate(60)
Add3DArchive(".", #PB_3DArchive_FileSystem)
Add3DArchive(#PB_Compiler_Home + "Examples/3D/Data/Textures", #PB_3DArchive_FileSystem)

CreateMaterial(1, LoadTexture(1, "clouds.jpg"))
CreatePlane(#plane, 10, 10, 1, 1, 1, 1)
CreateEntity (#plane, MeshID(#plane), MaterialID(1))
MoveEntity(#plane,0,-3,0)
EntityPhysicBody(#plane, #PB_Entity_StaticBody)

CreateLight(0,RGB(255,255,255),-100,40,30)
AmbientColor(RGB(100,100,100))

CreateCamera(0, 0, 0, 100, 100)
MoveCamera(0, 0, 4, 12)
CameraLookAt(0, 0, 2, 0)

RotateCamera(0, -15, 0, 0)
CreateMaterial(0, LoadTexture(0, "terrain_texture.jpg"))
;CreateMaterial(0, LoadTexture(0, "wood.jpg"))
;CreateMaterial(0, LoadTexture(0, "MRAMOR6X6.jpg"))
MaterialCullingMode(0, #PB_Material_NoCulling)
;MaterialBlendingMode(0, #PB_Material_AlphaBlend)

MaterialShadingMode(0, #PB_Material_Wireframe)
DisableMaterialLighting(0, 1)
CreateMesh(1, #PB_Mesh_TriangleList, #PB_Mesh_Static )
SetMeshMaterial(1, MaterialID(0))

DrawTube (#NUM_RINGS, #NUM_BANDS, #BAND_RAD)
NormalizeMesh(1)
FinishMesh(1)

CreateEntity(1, MeshID(1), MaterialID(0))
MoveEntity(1,0,4,-2)
EntityPhysicBody(1, #PB_Entity_StaticBody  ,0.1)
RotateEntity(1,90,250,0)
CreateSphere(#ball,2)
CreateEntity(#ball,MeshID(#ball),#PB_Material_None )
ScaleEntity(#ball, 0.2,0.2,0.2)
MoveEntity(#ball, -0.3,4,-1.1)
EntityPhysicBody(#ball, #PB_Entity_SphereBody   ,1,0.01,2)
y.f

ExamineKeyboard()

Repeat
  Event = WindowEvent()
  If KeyboardReleased(#PB_Key_Left)  ; rotate left
    stop ! 1 : rot = 1
  ElseIf  KeyboardReleased(#PB_Key_Right) 
    stop ! 1 : rot = -1
  EndIf  
   If stop = 0 
    y+rot
    RotateEntity(1,90,-y+250,0)
   EndIf
   RenderWorld()
   FlipBuffers()
   
   ExamineKeyboard()
   If KeyboardPushed(#PB_Key_Escape)
      Quit = #True
    EndIf
Until Quit = #True Or Event = #PB_Event_CloseWindow


Procedure vector_cross(*v1.vector3d, *v2.vector3d, *vout.vector3d)
    *vout\x = (*v1\y * *v2\z) - (*v2\y * *v1\z)
    *vout\y = (*v1\z * *v2\x) - (*v2\z * *v1\x)
    *vout\z = (*v1\x * *v2\y) - (*v2\x * *v1\y)
EndProcedure

Procedure.f vector_magnitude(*v.vector3d)
    mag.f
    mag = Sqr(*v\x * *v\x + *v\y * *v\y + *v\z * *v\z)
    If mag = 0:mag = 1:EndIf
    ProcedureReturn mag
EndProcedure

Procedure vector_normalize (*v.vector3d)
    mag.f
    mag = vector_magnitude(*v)
    *v\x = *v\x / mag
    *v\y = *v\y / mag
    *v\z = *v\z / mag
EndProcedure


Procedure vector_add (*v1.vector3d, *v2.vector3d, *vout.vector3d)
    *vout\x = *v1\x + *v2\x
    *vout\y = *v1\y + *v2\y
    *vout\z = *v1\z + *v2\z
EndProcedure

Procedure vector_sub (*v1.vector3d, *v2.vector3d, *vout.vector3d)
    *vout\x = *v1\x - *v2\x
    *vout\y = *v1\y - *v2\y
    *vout\z = *v1\z - *v2\z
EndProcedure





Procedure  DrawTube (Rings.l, Bands.l, BandRadius.f)

  x.f: y.f: z.f
	op.f = 1
	txu.f : txv.f
	For i = 0 To rings + 1
	  	  current_point.vector3d
        next_point.vector3d
        T.vector3d
        B.vector3d
        N.vector3d
        p.f
        
        ;center point
        p = op * i * #manyPi / rings
        current_point\x = Cos(p) 
        current_point\y = Sin(p)
        current_point\z = 0.2 * p

        ;next point For Frenet square
        p = op * (i + 1) * #manyPi / rings
        next_point\x = Cos(p)
        next_point\y = Sin(p) 
        next_point\z = 0.2 * p

        ;T  = P' - P
        vector_sub(next_point, current_point, T)

        ;N = P' + P
        vector_add(next_point, current_point, N)

        ;B = T x N
        vector_cross(T, N, B)

        ;N = B x T
        vector_cross(B, T, N)

        ;Normalize vectors Or Else it won't work
        vector_normalize(B)
        vector_normalize(N)
        
        For j = 0 To bands - 1
           	new_point_x.f
        		new_point_y.f
        		
        		;rotate around the current point using normal rotation makes bands
            new_point_x = Sin(j * 2*#PI / bands) * #BAND_RAD
            new_point_y = Cos(j * 2*#PI / bands) * #BAND_RAD
            
				    ;this is the coords of our point along the curve
            x = N\x * new_point_x + B\x * new_point_y + current_point\x
            y = N\y * new_point_x + B\y * new_point_y + current_point\y
            z = N\z * new_point_x + B\z * new_point_y + current_point\z
            
                MeshVertexPosition(x, y, z)
                MeshVertexTextureCoordinate(txu, txv)
                MeshVertexNormal(x, y, z)
                txv = txv + 1/bands
                         
			Next 
			txv = 0
			;txu = txu + 1/rings 
			txu = txu + 1/bands 
		Next 
		
		v.l
		 For i = 0 To rings - 1
      For j = 0 To bands - 1
          MeshFace(v,v+1,v + bands+1)
          MeshFace(v + bands+1,v + bands+2,v+1 )
          
          v + 1   
          
     Next
     
   Next  


  EndProcedure
  
you can engineer your tube, ie build a tube from data you draw previously on paper, the following are just a show case only.
if you want to make a city made from custom tubes try to reduce the number of rings and bands. such as a long straight tube can be made only from 2 rings.
Image

Code: Select all

                     
#TWOPI = 2 * #PI    ;PI For rotation 

#NUM_RINGS = 8

#NUM_BANDS = 16

#BAND_RAD  = 1.0 ;tube thickness
Global.f rot = 1
Global stop = 0

Structure vector3d
  x.f
  y.f
  z.f
EndStructure
	 

Declare DrawTube (Rings.l, Bands.l,BandRadius.f)

Quit.b = #False
ExamineDesktops()
OpenWindow(3, 0, 0, DesktopWidth(0), DesktopHeight(0), "tubes , press space to toggle rotation", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

;Initialize environment
InitEngine3D()
InitSprite()
OpenWindowedScreen(WindowID(3), 0, 0, DesktopWidth(0), DesktopHeight(0), 0, 0, 0)
InitKeyboard()
SetFrameRate(60)
Add3DArchive(".", #PB_3DArchive_FileSystem)
Add3DArchive(#PB_Compiler_Home + "Examples/3D/Data/Textures", #PB_3DArchive_FileSystem)

CreateLight(0,RGB(255,255,255),-100,40,30)
AmbientColor(RGB(100,100,100))

CreateCamera(0, 0, 0, 100, 100)
MoveCamera(0, 0, 4, 12)
CameraLookAt(0, 0, 2, 0)

RotateCamera(0, -15, 0, 0)
CreateMaterial(0, LoadTexture(0, "terrain_texture.jpg"))
;CreateMaterial(0, LoadTexture(0, "wood.jpg"))
;CreateMaterial(0, LoadTexture(0, "MRAMOR6X6.jpg"))
MaterialCullingMode(0, #PB_Material_NoCulling)
;MaterialShadingMode(0, #PB_Material_Wireframe)
DisableMaterialLighting(0, #True)
CreateMesh(1, #PB_Mesh_TriangleList, #PB_Mesh_Static )
SetMeshMaterial(1, MaterialID(0))

DrawTube (#NUM_RINGS, #NUM_BANDS, #BAND_RAD )
NormalizeMesh(1)
FinishMesh(1)

CreateEntity(1, MeshID(1), MaterialID(0))
;ScaleEntity(1,2,2,2)
MoveEntity(1,0,0,2)
ExamineKeyboard()
y.f
Repeat
  Event = WindowEvent()
  If KeyboardReleased(#PB_Key_Space)  ; rotate left
    stop ! 1 
  EndIf  
   If stop = 0 
    y+rot
    
   EndIf
   
   RotateEntity(1,0,y,0)
   
   RenderWorld()
   FlipBuffers()
   
   ExamineKeyboard()
   If KeyboardPushed(#PB_Key_Escape)
      Quit = #True
    EndIf
Until Quit = #True Or Event = #PB_Event_CloseWindow


Procedure vector_cross(*v1.vector3d, *v2.vector3d, *vout.vector3d)
    *vout\x = (*v1\y * *v2\z) - (*v2\y * *v1\z)
    *vout\y = (*v1\z * *v2\x) - (*v2\z * *v1\x)
    *vout\z = (*v1\x * *v2\y) - (*v2\x * *v1\y)
EndProcedure

Procedure.f vector_magnitude(*v.vector3d)
    mag.f
    mag = Sqr(*v\x * *v\x + *v\y * *v\y + *v\z * *v\z)
    If mag = 0:mag = 1:EndIf
    ProcedureReturn mag
EndProcedure

Procedure vector_normalize (*v.vector3d)
    mag.f
    mag = vector_magnitude(*v)
    *v\x = *v\x / mag
    *v\y = *v\y / mag
    *v\z = *v\z / mag
EndProcedure


Procedure vector_add (*v1.vector3d, *v2.vector3d, *vout.vector3d)
    *vout\x = *v1\x + *v2\x
    *vout\y = *v1\y + *v2\y
    *vout\z = *v1\z + *v2\z
EndProcedure

Procedure vector_sub (*v1.vector3d, *v2.vector3d, *vout.vector3d)
    *vout\x = *v1\x - *v2\x
    *vout\y = *v1\y - *v2\y
    *vout\z = *v1\z - *v2\z
EndProcedure





Procedure  DrawTube (Rings.l, Bands.l, BandRadius.f)

  x.f: y.f: z.f

	op.f
	max_radius.f
	op = 1
	max_radius = 2.0
	u.f: r.f
      
  txu.f : txv.f
  Dim p.f(32)
  Restore points
	  For i=0 To 32
	    Read.f p(i)
	  ;Debug p(i)  
	  Next 
	  
	  current_point.vector3d
        next_point.vector3d
        T.vector3d
        B.vector3d
        N.vector3d
        p.f
	  For i = 0 To 29 Step 3
	  
    	  
        
        ;center point
                
        current_point\x = p(i) 
        current_point\y = p(i+1)
        current_point\z = p(i+2)

        ;next point For Frenet square
        
        next_point\x = p(i+3) 
        next_point\y = p(i+4)
        next_point\z = p(i+5)

        ;T  = P' - P
        vector_sub(next_point, current_point, T)

        ;N = P' + P
        vector_add(next_point, current_point, N)

        ;B = T x N
        vector_cross(T, N, B)

        ;N = B x T
        vector_cross(B, T, N)

        ;Normalize vectors Or Else it won't work
        vector_normalize(B)
        vector_normalize(N)
        
        For j = 0 To bands - 1
           	new_point_x.f
        		new_point_y.f
        		
        		;rotate around the current point using normal rotation makes bands
            new_point_x = Sin(j * #TWOPI / bands) * #BAND_RAD
            new_point_y = Cos(j * #TWOPI / bands) * #BAND_RAD
            
				    ;this is the coords of our point along the curve
            x = N\x * new_point_x + B\x * new_point_y + current_point\x
            y = N\y * new_point_x + B\y * new_point_y + current_point\y
            z = N\z * new_point_x + B\z * new_point_y + current_point\z
            
                MeshVertexPosition(x, y, z)
                MeshVertexTextureCoordinate(txu, txv)
                MeshVertexNormal(x, y, z)
                txv = txv + 1/bands
                         
			Next 
			txv = 0
			;txu = txu + 1/rings 
			txu = txu + 1/bands 
		Next 
		
		v.l
		 For i = 0 To rings - 1
      For j = 0 To bands - 1
          MeshFace(v,v+1,v + bands+1)
          MeshFace(v + bands+1,v + bands+2,v+1 )
          
          v + 1   
          
     Next
     
   Next  


 EndProcedure
 
 DataSection
    points:    
    Data.f -5,0,2
    Data.f -3,0,2
    Data.f -1,0,2
    Data.f 1,0,2
    Data.f 1.5,0.5,2 ;the bent part of the tube
    Data.f 2,1,2  
    Data.f 3 ,2,2
    Data.f 4, 3, 2
    Data.f  6, 5,2
    Data.f  7, 6,2
    Data.f  2, 10,2
        
  EndDataSection

  
User avatar
DK_PETER
Addict
Addict
Posts: 904
Joined: Sat Feb 19, 2011 10:06 am
Location: Denmark
Contact:

Re: Tubes , bent tubes, custom pipes

Post by DK_PETER »

Hey Applepi.
Nicely done, as usual ;-)
Thank you very much.

Best regards
Peter
Current configurations:
Ubuntu 20.04/64 bit - Window 10 64 bit
Intel 6800K, GeForce Gtx 1060, 32 gb ram.
Amd Ryzen 9 5950X, GeForce 3070, 128 gb ram.
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Tubes , bent tubes, custom pipes

Post by IdeasVacuum »

Nice one ApplePi 8)
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
Post Reply