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