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

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
  
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
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
  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.
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
  
