It is a vehicle within a track or road, which is in the plane (2D) or in space (3D), and this road path can be designed and modified at run time.
A single algorithm is shown here, perhaps the simplest imaginable for this task. I have another slightly more complex algorithm, theoretically more efficient, but in practice similar.
I challenge anyone to create any strategy to make algorithmic races

NOTE that it is a sketch, a draft program, not free of bugs, which serves only as a sample so that if you dare and if you feel like it you can improve it, modify it as you wish, decorate it, put other "capsules" with other algorithms to run, put pretty images, make it more visually attractive, put more or less speed, share ideas, etc.
By the way, there is even much more fun to play with the code by changing things than with the "game" itself. By the way, there is hardly anything commented, to make it even more fun

Tested only in PB6.04 and PB6.12 in Windows, for other versions it may need to be adapted.
LeftCTRL+mouse move key allows you to orbit the camera view,
LeftCTRL+'+/-' on numeric keyboard allows camera to get closer or farer,
RightCTRL key starts or stops the capsule,
left mouse botton onto capsule allows to move it,
middle mouse button onto capsule allows to rotate it,
The tube road can be modified in a "wysiwyg" way using cursor keys and keys Repag, Prevpag, Home and End keys.
etc (look at the code)
Code: Select all
;/ inits
#ventana=1
Global pantallacompleta.b=0,Titulo$="My clever capsule"
If ExamineDesktops()=0:End:EndIf
Global bitplanes.a=DesktopDepth(0),FRX.u=DesktopWidth(0),FRY.u=DesktopHeight(0),RX.u=FRX,RY.u=FRY,FrecuenciadeMuestreo.u=60
If pantallacompleta=0
If FRX<1280 Or FRY<720:RX=FRX*2/3:RY=FRY*2/3:Else:RX=1280:RY=720:EndIf
EndIf
If InitEngine3D(#PB_Engine3D_NoLog,#PB_Compiler_Home+"Compilers\Engine3d.dll")=0
MessageRequester("Error","The 3D Engine can't be initialized",0):End
EndIf
If InitEngine3D(#PB_Engine3D_NoLog,#PB_Compiler_Home+"Compilers\Engine3d.dll")=0
MessageRequester("Error","The 3D Engine can't be initialized",0):End
EndIf
;AntialiasingMode(#PB_AntialiasingMode_x4)
If InitMouse()=0 Or InitSprite()=0 Or InitKeyboard()=0:MessageRequester("Error","Can't open DirectX",0):End:EndIf
If pantallacompleta.b=2
OpenScreen(RX,RY,bitplanes.a,Titulo$,#PB_Screen_WaitSynchronization,FrecuenciadeMuestreo.u)
Else
OpenWindow(#ventana,0,0,RX,RY,Titulo$,#PB_Window_BorderLess|#PB_Window_ScreenCentered)
OpenWindowedScreen(WindowID(#ventana),0,0,RX,RY,1,0,0,#PB_Screen_WaitSynchronization)
EndIf
Add3DArchive(#PB_Compiler_Home+"examples\3D\Data\GUI",#PB_3DArchive_FileSystem)
Enumeration Ventanas3D
#TextoInfoGeneral
#TextoInfoObjeto
EndEnumeration
Enumeration Luces
#Luz
EndEnumeration
Enumeration Camaras
#Camara
EndEnumeration
Enumeration Texturas
#texturacubo1
EndEnumeration
Enumeration Materiales
#materialrojo
#materialverde
#materialazul
#materialamarillo
#materialcapsula
#materialvectorescapsula
#materialtuberia
EndEnumeration
Enumeration Mallas
#mallacapsula
#mallavectorescapsula
#MallaCoordenadaTuberia
#mallatuberia
EndEnumeration
Enumeration Nodos
#pivotcamara
#pivottuberia
EndEnumeration
Enumeration Entidades
#capsula
#CoordenadaTuberia0; <- 0 a 255 entidades
#tuberia=300
EndEnumeration
Enumeration Texto3D
#texto1
EndEnumeration
Enumeration Splines
#splinetuberia
EndEnumeration
CreateMaterial(#materialrojo,#Null,$3333F7):CreateMaterial(#materialverde,#Null,$33F733):CreateMaterial(#materialazul,#Null,$F93333):CreateMaterial(#materialamarillo,#Null,$33F7F9)
CreateLight(#luz,$EEEEEE,4,4,2,#PB_Light_Point):SetLightColor(#luz,#PB_Light_DiffuseColor,$EEEEEE):MoveLight(#luz,4,4,2,#PB_Absolute)
CreateCamera(#camara,0,0,100,100):CreateNode(#pivotcamara,0,0,0):AttachNodeObject(#pivotcamara,CameraID(#camara)):CameraRange(#camara,0.1,1E4):CameraBackColor(#camara,$181911)
;\
Structure D3DXVECTOR3
x.f
y.f
z.f
EndStructure
Structure Vector3D Extends D3DXVECTOR3
m.f;<-length(modulo)
EndStructure
;/ Globales:
Global.Vector3D DELTA,THETA; para puntero ratón en su uso 2D
Global.D3DXVECTOR3 pick,pickv,pos; <- para usar puntero del ratón para referir elementos 3D
Global EntidadSeleccionada.i,wireframe.a
Global.Vector3D vector,var.f ; comodin para todo
Global Origenhaz.Vector3D,haz0.Vector3D,haznormal.Vector3D,sector.Vector3D,KF.f=0.1,KL.f=0.1,capsulacel.Vector3D,medialat.Vector3D
Global capsula.a,radiocapsula.f=0.323,alturacapsula.f=1.5622,longhaz0.f=4,inclhazlat.f=1,nhacescapsula.a=6,Dim hacescapsula.Vector3D(nhacescapsula)
Global tuberia.a,nverticestuberia.a=188,nverticescirctuberia.a=10,npuntosbeziertuberia.a=10,grosortuberia.f=4,NewList puntosbeziertuberia.D3DXVECTOR3(); <- para tuberia 3D
DataSection; <- preset road for npuntosbeziertuberia.a=30
tuberia1:
Data.f 7.64,-1.38,-9.77
Data.f 6.00,9.31,-14.45
Data.f 4.23,20.10,-12.13
Data.f 3.28,29.38,-5.68
Data.f -0.98,37.11,-1.15
Data.f -7.91,43.51,-2.10
Data.f -16.62,49.39,-1.72
Data.f -27.34,48.01,-1.87
Data.f -38.58,46.44,-0.69
Data.f -47.58,51.46,2.49
Data.f -55.33,47.38,0.14
Data.f -60.13,34.85,-11.40
Data.f -70.14,24.43,-25.07
Data.f -79.20,17.72,-23.31
Data.f -79.44,13.33,-10.98
Data.f -72.61,8.54,4.78
Data.f -73.81,-0.76,11.38
Data.f -83.02,-10.47,10.23
Data.f -78.25,-22.32,12.36
Data.f -73.13,-30.18,2.19
Data.f -64.98,-26.57,-6.82
Data.f -60.64,-5.78,-17.13
Data.f -50.06,2.83,-20.78
Data.f -35.62,-4.51,-20.46
Data.f -27.07,-24.66,-8.24
Data.f -17.43,-34.73,-8.13
Data.f -7.87,-35.69,-4.52
Data.f -1.80,-27.64,1.69
Data.f 4.20,-19.07,3.42
Data.f 8.12,-14.34,-3.74
Data.f 9.23,-7.61,-8.11
EndDataSection
DataSection; <- preset road for npuntosbeziertuberia.a=30
tuberia2:
Data.f 7.22,0.32,-1.77
Data.f 6.64,6.91,-12.05
Data.f 2.43,13.98,-13.40
Data.f -2.25,18.04,1.25
Data.f -6.02,22.77,3.17
Data.f -7.96,27.81,1.70
Data.f -10.91,30.75,-1.21
Data.f -15.83,31.32,-2.40
Data.f -22.19,33.61,-3.71
Data.f -27.63,33.59,-3.61
Data.f -33.81,31.02,-0.85
Data.f -39.11,27.28,2.81
Data.f -42.38,21.69,-0.32
Data.f -47.28,17.40,-0.48
Data.f -49.58,10.89,0.67
Data.f -49.60,5.06,-0.27
Data.f -50.73,1.02,-1.17
Data.f -50.18,-5.54,-1.88
Data.f -47.92,-10.74,-2.03
Data.f -45.42,-18.02,-9.10
Data.f -38.48,-22.97,-9.57
Data.f -33.43,-24.51,-4.04
Data.f -28.51,-25.07,0.00
Data.f -22.54,-25.70,0.00
Data.f -17.32,-24.18,0.62
Data.f -14.34,-21.00,1.30
Data.f -13.11,-16.40,3.31
Data.f -15.52,-9.47,6.04
Data.f -7.59,-4.41,8.77
Data.f 4.50,-4.66,4.10
Data.f 7.02,-1.60,0.27
EndDataSection
DataSection; <- preset road for puntosbeziertuberia.a=10
tuberia3:
Data.f 10.76,3.27,0.98
Data.f 9.80,7.16,-0.82
Data.f 4.46,16.57,-1.64
Data.f 2.32,11.02,-18.46
Data.f -19.11,26.56,-6.86
Data.f -20.15,8.99,6.54
Data.f -11.07,-3.39,8.87
Data.f -18.19,-8.89,-3.96
Data.f -5.57,-16.64,-5.79
Data.f 5.39,-22.60,17.67
Data.f 9.27,-2.52,1.69
EndDataSection
;\
Macro RND(v1,v2,ndecimales=3); maximo 'ndecimales' = 9
;genera un número aleatorio entre los valores v1 y v2
;NOTE: usar con variables de punto flotante para que esta macro funcione correctamente
(Random(1E#ndecimales#)*(v2#-v1#)/1E#ndecimales#+v1#)
EndMacro
Procedure.b MouseButtonEdgeDetection(boton.b,estado.b)
Static mb.b:Protected i.b=1<<boton
If estado;<- if current key status is PUSHED
If mb&i=0:mb|i:ProcedureReturn 1:EndIf;<- if previous key status was NOT PUSHED, then assign previous state to current one, and EXIT.
ElseIf mb&i;<- else (if previous key status was PUSHED and current key status is NOT PUSHED):
mb!i:ProcedureReturn -1;<- set previous key status to NOT PUSHED.
EndIf
ProcedureReturn 0
EndProcedure
Procedure.b KeyEdgeDetection(tecla.a,estado.b)
Static pka.a
If estado;<-if current key status is PUSHED
If pka=0:pka=tecla:ProcedureReturn 1:EndIf;<-if previous key status was NOT PUSHED, then assign previous state to current one, and EXIT.
ElseIf pka=tecla;<-else (if previous key status was PUSHED and current key status is NOT PUSHED):
pka=0:ProcedureReturn -1;<-set previous key status to NOT PUSHED.
EndIf
ProcedureReturn 0
EndProcedure
Macro ProductoEscalar(a,b,ax=x,ay=y,az=z,bx=x,by=y,bz=z)
(a#\ax#*b#\bx#+a#\ay#*b#\by#+a#\az#*b#\bz#)
EndMacro
Macro getmodulo(v,vx=x,vy=y,vz=z)
(Sqr#ProductoEscalar(v#,v#,vx#,vy#,vz#,vx#,vy#,vz#))
EndMacro
Macro ProductoVectorial(in1,in2,out,in1x=x,in1y=y,in1z=z,in2x=x,in2y=y,in2z=z,outx=x,outy=y,outz=z); <- Calculates the vectorial product of two 3D vectors. Just modify this procedure to get the vectorial product for 4D, 5D, 6D or any dimension you need.
out#\outx#=in1#\in1y#*in2#\in2z#-in1#\in1z#*in2#\in2y#
out#\outy#=in1#\in1z#*in2#\in2x#-in1#\in1x#*in2#\in2z#
out#\outz#=in1#\in1x#*in2#\in2y#-in1#\in1y#*in2#\in2x#
EndMacro
Macro ProyeccionVectorial(A,B,P,Ax=x,Ay=y,Az=z,Bx=x,By=y,Bz=z,Px=x,Py=y,Pz=z)
P#\Pz#=ProductoEscalar(A#,B#,Ax#,Ay#,Az#,Bx#,By#,Bz#)/ProductoEscalar(B#,B#,Bx#,By#,Bz#,Bx#,By#,Bz#)
P#\Px#=P#\Pz#*B#\Bx#
P#\Py#=P#\Pz#*B#\By#
P#\Pz#*B#\Bz#
EndMacro
Macro ProyeccionVectorialOrtogonal(A,B,P,Ax=x,Ay=y,Az=z,Bx=x,By=y,Bz=z,Px=x,Py=y,Pz=z)
ProyeccionVectorial(A#,B#,P#,Ax#,Ay#,Az#,Bx#,By#,Bz#,Px#,Py#,Pz#)
P#\Px#=A#\Ax#-P#\Px#
P#\Py#=A#\Ay#-P#\Py#
P#\Pz#=A#\Az#-P#\Pz#
EndMacro
Procedure AnguloFormadoPor2Vectores(*in1.Vector3D,*in2.Vector3D,*out.Vector3D)
Protected.Vector3D v1,v2:CopyMemory(*in1,@v1,SizeOf(Vector3D)):CopyMemory(*in2,@v2,SizeOf(Vector3D))
ProductoVectorial(*in1,*in2,*out):*out\m=getmodulo(*out)
v1\m=getmodulo(v1):v2\m=getmodulo(v2)
If *out\m=0.0
If v1\m=0.0 Or v2\m=0.0:FillMemory(*out,SizeOf(Vector3D),0,#PB_Byte):ProcedureReturn:EndIf
*out\m=1.0
EndIf
v1\x/v1\m:v1\y/v1\m:v1\z/v1\m
v2\x/v2\m:v2\y/v2\m:v2\z/v2\m
v1\x-v2\x:v1\y-v2\y:v1\z-v2\z:v1\m=getmodulo(v1)
v2\m=2*ASin(v1\m/2)
*out\x*v2\m/*out\m:*out\y*v2\m/*out\m:*out\z*v2\m/*out\m
*out\m=v2\m
EndProcedure
Procedure.b Rotar_Vector3D_por_adicion_Angular(*fi.Vector3D,*R0.Vector3D)
Protected Rt.Vector3D,u.Vector3D,P0.Vector3D
*fi\m=ProductoEscalar(*fi,*fi)
If *fi\m
u\m=ProductoEscalar(*R0,*fi)/*fi\m; <- aqui lo uso como variable comodin
*fi\m=Sqr(*fi\m)
;Rt-> = Proyeccion de *R0-> sobre *fi->:
Rt\x=u\m**fi\x
Rt\y=u\m**fi\y
Rt\z=u\m**fi\z
;P0-> = proyeccion ortogonal de *R0-> sobre *fi->:
P0\x=*R0\x-Rt\x
P0\y=*R0\y-Rt\y
P0\z=*R0\z-Rt\z
P0\m=getmodulo(P0)
If P0\m<1E-5; <= no hay giro ya que *R0-> y *fi-> son colineales:
ProcedureReturn -1
EndIf
;Calcular el producto vectorial: u-> = *fi-> X *R0-> (o por P0-> daría igual)
u\x=*fi\y**R0\z-*fi\z**R0\y
u\y=*fi\z**R0\x-*fi\x**R0\z
u\z=*fi\x**R0\y-*fi\y**R0\x
;ahora obtener *R0-> = (Proyeccion de *R0-> sobre *fi->)-> + (cos(|*fi->|)·P0-> + |P0->|/|u->|·sin(|*fi->|)·u->)->:
*R0\x=Rt\x
*R0\y=Rt\y
*R0\z=Rt\z
Rt\x=Cos(*fi\m):Rt\y=Sin(*fi\m)
u\m=getmodulo(u)
P0\m*Rt\y/u\m
*R0\x+Rt\x*P0\x+P0\m*u\x
*R0\y+Rt\x*P0\y+P0\m*u\y
*R0\z+Rt\x*P0\z+P0\m*u\z
ProcedureReturn 1
EndIf
ProcedureReturn 0
EndProcedure
Macro AnimarGiroenPropioEjeXYZ(rot,tipoobjeto,objeto,grados,veloc=5)
seno2.d=Pow(Sin(Animaciondiferencial.d),2)
; seno2.d=1-Abs(Cos(Animaciondiferencial.d)); con este va pero el paso no es de #PI/(grados#*2/veloc#). Interesante estudiar esto matemáticamente.
Animaciondiferencial.d+#PI/(grados#*2/veloc#)
If Abs(Animaciondiferencial.d)>=#PI:Animaciondiferencial.d=0:AnimacionPivotCamara.b=0
Else:rot#(tipoobjeto#ID(objeto#),Sign(grados#)*veloc#*seno2.d,#PB_Local|#PB_Relative)
EndIf
EndMacro
Macro OrbitarObjeto1SobreObjeto0(tecla=LeftControl,tipo1=Camera,objeto1=#Camara,tipo0=Node,objeto0=#Pivotcamara); <- el 'objeto1' orbita sobre el 'objeto0', y este debe ser nodo de 'objeto1'
If AnimacionPivotCamara.b
Select TipodeGiro.b
Case 1; pitch 90 grados
AnimarGiroenPropioEjeXYZ(Pitch,tipo0#,objeto0#,Gradosdegiro.d)
Case 2; pitch -90 grados
AnimarGiroenPropioEjeXYZ(Pitch,tipo0#,objeto0#,Gradosdegiro.d)
Case 3; yaw 90 grados
AnimarGiroenPropioEjeXYZ(Yaw,tipo0#,objeto0#,Gradosdegiro.d)
Case 4; yaw -90 grados
AnimarGiroenPropioEjeXYZ(Yaw,tipo0#,objeto0#,Gradosdegiro.d)
Case 5; roll 90 grados
AnimarGiroenPropioEjeXYZ(Roll,tipo0#,objeto0#,Gradosdegiro.d)
Case 6; roll -90 grados
AnimarGiroenPropioEjeXYZ(Roll,tipo0#,objeto0#,Gradosdegiro.d)
EndSelect
Else
If KeyboardPushed(#PB_Key_Pad0)
Rotate#tipo0#(objeto0#,0,0,0,#PB_Absolute)
Move#tipo1#(objeto1#,0,0,9,#PB_Absolute)
EndIf
estadotecla.b=KeyboardPushed(#PB_Key_#tecla#):estadoteclaf.b=KeyEdgeDetection(#PB_Key_#tecla#,estadotecla.b)
If estadoteclaf.b>0; <- inicia control camara
pasocam.f=0.01:pasocamincr.f=0.001
ShowGUI(180,0,#camara,1):CursorX0.f=CursorX.f:CursorY0.f=CursorY.f
ElseIf estadoteclaf.b<0; <- termina control camara
ShowGUI(180,1,#camara,1)
ElseIf estadotecla.b; <- mover el punto de vista
MouseLocate(CursorX0.f,CursorY0.f)
;para desplazar la camara hacia delante, atras, arriba, abajo, izq o der
If DELTA\m
If mmb.b
Move#tipo1#(objeto1#,DELTA\x/40,-DELTA\y/40,0,#PB_Local)
Else
Rotate#tipo0#(objeto0#,-DELTA\y/10,-DELTA\x/10,0,#PB_Relative)
If DELTA\z
Move#tipo1#(objeto1#,0,0,-DELTA\z,#PB_Local)
EndIf
EndIf
ElseIf KeyboardPushed(#PB_Key_Add)
Move#tipo1#(objeto1#,0,0,-pasocam,#PB_Local)
pasocam+pasocamincr
ElseIf KeyboardPushed(#PB_Key_Subtract)
Move#tipo1#(objeto1#,0,0,pasocam,#PB_Local)
pasocam+pasocamincr
EndIf
ElseIf KeyboardPushed(#PB_Key_Pad8)
If KeyboardPushed(#PB_Key_Pad5)
Gradosdegiro.d=-90
AnimacionPivotCamara.b=1:TipodeGiro.b=1
Else
Pitch(tipo0#ID(objeto0#),-0.5,#PB_Local|#PB_Relative)
EndIf
ElseIf KeyboardPushed(#PB_Key_Pad2)
If KeyboardPushed(#PB_Key_Pad5)
Gradosdegiro.d=90
AnimacionPivotCamara.b=1:TipodeGiro.b=2
Else
Pitch(tipo0#ID(objeto0#),0.5,#PB_Local|#PB_Relative)
EndIf
ElseIf KeyboardPushed(#PB_Key_Pad4)
If KeyboardPushed(#PB_Key_Pad5)
Gradosdegiro.d=-90
AnimacionPivotCamara.b=1:TipodeGiro.b=3
Else
Yaw(tipo0#ID(objeto0#),-0.5,#PB_Local|#PB_Relative)
EndIf
ElseIf KeyboardPushed(#PB_Key_Pad6)
If KeyboardPushed(#PB_Key_Pad5)
Gradosdegiro.d=90
AnimacionPivotCamara.b=1:TipodeGiro.b=4
Else
Yaw(tipo0#ID(objeto0#),0.5,#PB_Local|#PB_Relative)
EndIf
ElseIf KeyboardPushed(#PB_Key_Pad7)
If KeyboardPushed(#PB_Key_Pad5)
Gradosdegiro.d=-90
AnimacionPivotCamara.b=1:TipodeGiro.b=5
Else
Roll(tipo0#ID(objeto0#),-0.5,#PB_Local|#PB_Relative)
EndIf
ElseIf KeyboardPushed(#PB_Key_Pad9)
If KeyboardPushed(#PB_Key_Pad5)
Gradosdegiro.d=90
AnimacionPivotCamara.b=1:TipodeGiro.b=6
Else
Roll(tipo0#ID(objeto0#),0.5,#PB_Local|#PB_Relative)
EndIf
EndIf
EndIf
EndMacro
Macro desplazardesdepuntodevista(entidad)
ConvertLocalToWorldPosition(CameraID(#camara),THETA\x,-THETA\y,THETA\z)
THETA\x=GetX():THETA\y=GetY():THETA\z=GetZ():THETA\m=getmodulo(THETA)
MoveEntity(entidad#,THETA\x,THETA\y,THETA\z,#PB_World|#PB_Relative)
EndMacro
Macro rotardesdepuntodevista(entidad)
Swap THETA\x,THETA\y
ConvertLocalToWorldPosition(CameraID(#camara),THETA\x,THETA\y,THETA\z)
THETA\x=GetX():THETA\y=GetY():THETA\z=GetZ():THETA\m=getmodulo(THETA)
EntityFixedYawAxis(entidad#,1,THETA\x/THETA\m,THETA\y/THETA\m,THETA\z/THETA\m); <- esta funcion interpreta el eje como local
Yaw(EntityID(entidad#),Degree(THETA\m),#PB_World|#PB_Relative)
;EntityFixedYawAxis(entidad#,0)
EndMacro
Macro ponequitacapsula(capx=4.68,capy=10.81,capz=-14.74)
If capsula.a
;capsula:
CreateMaterial(#materialcapsula,0,$90A8D4):SetMaterialColor(#materialcapsula,#PB_Material_SelfIlluminationColor,$ABF81F)
CreateCapsule(#mallacapsula,radiocapsula,alturacapsula,2,8,1):TransformMesh(#mallacapsula,0,0,0,1,1,1,-90,0,0,0)
CreateEntity(#capsula,MeshID(#mallacapsula),MaterialID(#materialcapsula),capx#,capy#,capz#)
Else
FreeEntity(#capsula)
FreeMesh(#mallacapsula)
FreeMaterial(#materialcapsula)
EndIf
EndMacro
Procedure TuberiaBezier(actualizar.b=1,color.l=$43D4E2)
Protected sp1.Vector3D,sp0.Vector3D,sp.Vector3D,vector0.Vector3D,compas.Vector3D,radio.Vector3D,i.a,j.a,texcoordu.f,texcoordv.f,ncara.u=0,Dim primercirculo.D3DXVECTOR3(nverticescirctuberia)
If actualizar
ForEach puntosbeziertuberia()
UpdateSplinePoint(#splinetuberia,ListIndex(puntosbeziertuberia()),puntosbeziertuberia()\x,puntosbeziertuberia()\y,puntosbeziertuberia()\z)
Next
If IsEntity(#tuberia):FreeEntity(#tuberia):EndIf; o bien si la malla va con 'node' en lugar de con 'entity': ;UpdateMesh(#mallatuberia,0)
Else
CreateSpline(#splinetuberia)
CreateSphere(#MallaCoordenadaTuberia,0.05)
CreateMaterial(#materialtuberia,0,color):DisableMaterialLighting(#materialtuberia,1)
ForEach puntosbeziertuberia()
AddSplinePoint(#splinetuberia,puntosbeziertuberia()\x,puntosbeziertuberia()\y,puntosbeziertuberia()\z)
CreateEntity(#CoordenadaTuberia0+ListIndex(puntosbeziertuberia()),MeshID(#MallaCoordenadaTuberia),#PB_Material_None,puntosbeziertuberia()\x,puntosbeziertuberia()\y,puntosbeziertuberia()\z,0)
Next
EndIf
CreateMesh(#mallatuberia,#PB_Mesh_TriangleList,#PB_Mesh_Static); <- quitar si la malla va con 'node' en lugar de con 'entity'
ComputeSpline(#splinetuberia,0.0):sp\x=SplineX(#splinetuberia):sp\y=SplineY(#splinetuberia):sp\z=SplineZ(#splinetuberia); <- tiempo 0
sp0=sp; <- inicio nulo
FillMemory(@vector,SizeOf(Vector3D)); <- inicio nulo
For i=0 To nverticestuberia; <- recorrer toda la linea spline; desde tiempo=0 hasta tiempo=1 en la spline
sp1=sp0
sp0=sp
ComputeSpline(#splinetuberia,(i+1.0)/(nverticestuberia+1.0))
sp\x=SplineX(#splinetuberia):sp\y=SplineY(#splinetuberia):sp\z=SplineZ(#splinetuberia)
vector0=vector
vector\x=sp\x-sp0\x:vector\y=sp\y-sp0\y:vector\z=sp\z-sp0\z:vector\m=getmodulo(vector); <- vector sentido del tiempo spline
vector\x*2*#PI/vector\m/nverticescirctuberia:vector\y*2*#PI/vector\m/nverticescirctuberia:vector\z*2*#PI/vector\m/nverticescirctuberia; <- vector sentido del tiempo spline modulado
compas\x=0:compas\y=grosortuberia/2:compas\z=0:compas\m=compas\y
If i=0
ProyeccionVectorialOrtogonal(compas,vector,radio):radio\m=getmodulo(radio)
If radio\m<1E-2
Swap compas\y,compas\z
ProyeccionVectorialOrtogonal(compas,vector,radio):radio\m=getmodulo(radio)
EndIf
Else
ProyeccionVectorialOrtogonal(radio,vector,compas):compas\m=getmodulo(compas)
vector0\x*grosortuberia/2/vector0\m:vector0\y*grosortuberia/2/vector0\m:vector0\z*grosortuberia/2/vector0\m
ProyeccionVectorialOrtogonal(vector0,vector,radio):radio\m=getmodulo(radio)
If radio\m<compas\m:radio=compas:EndIf
EndIf
radio\x*compas\m/radio\m:radio\y*compas\m/radio\m:radio\z*compas\m/radio\m
For j=0 To nverticescirctuberia
If i=0
primercirculo(j)\x=sp0\x+radio\x:primercirculo(j)\y=sp0\y+radio\y:primercirculo(j)\z=sp0\z+radio\z
EndIf
Rotar_Vector3D_por_adicion_Angular(@vector,@radio)
MeshVertex(sp0\x+radio\x,sp0\y+radio\y,sp0\z+radio\z,texcoordu.f,texcoordv.f,color)
Next
Next
;Para no cerrar el lazo, comentar esta linea:
For j=0 To nverticescirctuberia:MeshVertex(primercirculo(j)\x,primercirculo(j)\y,primercirculo(j)\z,texcoordu.f,texcoordv.f,color):Next
For i=0 To nverticestuberia
For j=1 To nverticescirctuberia
ncara+1
MeshFace(ncara,ncara+nverticescirctuberia,ncara+nverticescirctuberia+1)
MeshFace(ncara,ncara-1,ncara+nverticescirctuberia)
Next
ncara+1
Next
FinishMesh(1); <- FinishMesh(0) si la malla va con 'node' en lugar de con 'entity'
; NormalizeMesh(#mallatuberia,0):UpdateMeshBoundingBox(#mallatuberia)
CreateEntity(#tuberia,MeshID(#mallatuberia),MaterialID(#materialtuberia),0,0,0); <- si la malla va con 'node' quitar esta linea
FreeArray(primercirculo.D3DXVECTOR3())
EndProcedure
Procedure ponequitatuberiabezier()
If tuberia.a
TuberiaBezier(0)
Else
If IsEntity(#tuberia):FreeEntity(#tuberia):EndIf
If IsMesh(#mallatuberia):FreeMesh(#mallatuberia):EndIf
If IsMaterial(#materialtuberia):FreeMaterial(#materialtuberia):EndIf
ForEach puntosbeziertuberia()
If IsEntity(#CoordenadaTuberia0+ListIndex(puntosbeziertuberia())):FreeEntity(#CoordenadaTuberia0+ListIndex(puntosbeziertuberia())):EndIf
Next
If IsMesh(#MallaCoordenadaTuberia):FreeMesh(#MallaCoordenadaTuberia):EndIf
If IsNode(#pivottuberia):FreeNode(#pivottuberia):EndIf
; If ListSize(puntosbeziertuberia.D3DXVECTOR3())>0:FreeList(puntosbeziertuberia.D3DXVECTOR3()):EndIf
FreeSpline(#splinetuberia)
EndIf
EndProcedure
Macro grabarformatuberia()
; Grab the road shape:
OpenConsole("Get coords",#PB_Ascii)
ClearConsole()
PrintN("Grab the coords of the spline points:")
PrintN("DataSection")
foreach puntosbeziertuberia()
PrintN(" Data.f "+StrF(puntosbeziertuberia()\x,2)+","+StrF(puntosbeziertuberia()\y,2)+","+StrF(puntosbeziertuberia()\z,2))
next
PrintN("EndDataSection")
PrintN("")
PrintN("Initial position fo the capsule: "+StrF(EntityX(#capsula),2)+","+StrF(EntityY(#capsula),2)+","+StrF(EntityZ(#capsula),2))
PrintN("")
PrintN("Number of rings in road: "+Str(nverticestuberia))
PrintN("")
PrintN("Press a key to exit")
While Inkey()="":Delay(20):Wend
CloseConsole()
EndMacro
Macro reiniciar()
posicion.D3DXVECTOR3:fiposicion.Vector3D
Restore tuberia3
For i.a=0 To npuntosbeziertuberia
AddElement(puntosbeziertuberia())
; If i=0
; puntosbeziertuberia()\x=10:puntosbeziertuberia()\y=0:puntosbeziertuberia()\z=0
; posicion=puntosbeziertuberia()
; ElseIf i=1
; puntosbeziertuberia()\x=posicion\x:puntosbeziertuberia()\y=posicion\y+10:puntosbeziertuberia()\z=posicion\z
; vector\x=puntosbeziertuberia()\x-posicion\x:vector\y=puntosbeziertuberia()\y-posicion\y:vector\z=puntosbeziertuberia()\z-posicion\z
; puntosbeziertuberia()\x=posicion\x+vector\x:puntosbeziertuberia()\y=posicion\y+vector\y:puntosbeziertuberia()\z=posicion\z+vector\z
; posicion=puntosbeziertuberia()
; Else
; fiposicion\x=0:fiposicion\y=0:fiposicion\z=2*#PI/npuntosbeziertuberia
; Rotar_Vector3D_por_adicion_Angular(@fiposicion,@vector)
; puntosbeziertuberia()\x=posicion\x+vector\x:puntosbeziertuberia()\y=posicion\y+vector\y:puntosbeziertuberia()\z=posicion\z+vector\z
; posicion=puntosbeziertuberia()
; EndIf
Read.f puntosbeziertuberia()\x:Read.f puntosbeziertuberia()\y:Read.f puntosbeziertuberia()\z
Next
MoveNode(#Pivotcamara,0,0,0,#PB_Absolute):RotateNode(#Pivotcamara,0,0,0,#PB_Absolute)
MoveCamera(#camara,0,0,54,#PB_Absolute)
ReleaseMouse(0)
ShowGUI(180,1,#camara,1)
InputEvent3D(0,0,0); <- para el cursor del ratón nada más.
MouseLocate(RX/2,RY/2)
tuberia.a=1:ponequitatuberiabezier()
capsula.a=1:ponequitacapsula()
wireframe.a=1:CameraRenderMode(#camara,#PB_Camera_Wireframe)
EntidadSeleccionada.i=0
run.a=1
EndMacro
reiniciar()
;/ BUCLE
Repeat
Repeat:evento.i=WindowEvent():Until evento=#PB_Event_None
ExamineMouse():ExamineKeyboard()
CursorX.f=MouseX():CursorY.f=MouseY():lmb.b=MouseButton(#PB_MouseButton_Left):rmb.b=MouseButton(#PB_MouseButton_Right):mmb.b=MouseButton(#PB_MouseButton_Middle)
DELTA\x=MouseDeltaX():DELTA\y=MouseDeltaY():DELTA\z=MouseWheel():DELTA\m=getmodulo(DELTA)
OrbitarObjeto1SobreObjeto0(LeftControl,Camera,#camara,Node,#Pivotcamara)
InputEvent3D(CursorX.f,CursorY.f,lmb.b); <- para el cursor del ratón nada más.
lmbe.b=MouseButtonEdgeDetection(#PB_MouseButton_Left,lmb)
mmbe.b=MouseButtonEdgeDetection(#PB_MouseButton_Middle,mmb)
If lmbe.b=1 Or mmbe.b=1
ShowGUI(180,0,#camara,1)
EntidadSeleccionada.i=MouseRayCast(#camara,CursorX,CursorY,-1)
Select EntidadSeleccionada.i
Case #capsula:EntidadSeleccionada$="capsula"
Case #CoordenadaTuberia0 To #CoordenadaTuberia0+255:EntidadSeleccionada$="Point"+Str(EntidadSeleccionada-#CoordenadaTuberia0)
Default
If tuberia.a
;buscar el punto spline más cercano al puntero
mindistanciaspuntostuberia.f=4000
ForEach puntosbeziertuberia()
px.f=CameraProjectionX(#Camara,puntosbeziertuberia()\x,puntosbeziertuberia()\y,puntosbeziertuberia()\z)
py.f=CameraProjectionY(#Camara,puntosbeziertuberia()\x,puntosbeziertuberia()\y,puntosbeziertuberia()\z)
distanciaspuntostuberia.f=Sqr(Pow(px-CursorX,2)+Pow(py-CursorY,2))
If mindistanciaspuntostuberia>distanciaspuntostuberia
mindistanciaspuntostuberia=distanciaspuntostuberia
nodotuberia.a=ListIndex(puntosbeziertuberia())
EndIf
Next
EntidadSeleccionada.i=#CoordenadaTuberia0+nodotuberia
EntidadSeleccionada$="Point"+Str(nodotuberia)
Else
EntidadSeleccionada$=""
EndIf
EndSelect
If IsEntity(EntidadSeleccionada)
anchuratexto.u=Len(EntidadSeleccionada$)*18
pick\x=PickX():pick\y=PickY():pick\z=PickZ()
pos\x=EntityX(EntidadSeleccionada):pos\y=EntityY(EntidadSeleccionada):pos\z=EntityZ(EntidadSeleccionada)
;pickv\x=CameraProjectionX(#camara,pick\x,pick\y,pick\z)-CameraProjectionX(#camara,pos\x,pos\y,pos\z)
;pickv\y=CameraProjectionY(#camara,pick\x,pick\y,pick\z)-CameraProjectionY(#camara,pos\x,pos\y,pos\z)
;pickv\z=0
pick=pos
OpenWindow3D(#TextoInfoObjeto,CameraProjectionX(#camara,pick\x,pick\y,pick\z),CameraProjectionY(#camara,pick\x,pick\y,pick\z),anchuratexto.u,32,"detalles objeto",#PB_Window3D_SizeGadget|#PB_Window3D_BorderLess)
TextGadget3D(#TextoInfoObjeto,0,-1,anchuratexto.u,32,EntidadSeleccionada$)
EndIf
ElseIf lmbe.b=-1 Or mmbe.b=-1
If IsWindow3D(#TextoInfoObjeto)
FreeGadget3D(#TextoInfoObjeto)
CloseWindow3D(#TextoInfoObjeto)
EndIf
ShowGUI(180,1,#camara,1)
If IsEntity(EntidadSeleccionada)
pos\x=EntityX(EntidadSeleccionada):pos\y=EntityY(EntidadSeleccionada):pos\z=EntityZ(EntidadSeleccionada)
MouseLocate(CameraProjectionX(#camara,pos\x,pos\y,pos\z),CameraProjectionY(#camara,pos\x,pos\y,pos\z))
EntidadSeleccionada$=""
EndIf
EndIf
If DELTA\m And IsWindow3D(#TextoInfoObjeto)
If IsEntity(EntidadSeleccionada)
THETA=DELTA:THETA\x/100:THETA\y/100:THETA\z/20:THETA\m=getmodulo(THETA)
If lmb.b; mover
pos\x=EntityX(EntidadSeleccionada):pos\y=EntityY(EntidadSeleccionada):pos\z=EntityZ(EntidadSeleccionada)
pick\x=CameraProjectionX(#camara,pos\x,pos\y,pos\z);+pickv\x
pick\y=CameraProjectionY(#camara,pos\x,pos\y,pos\z);+pickv\y
ResizeWindow3D(#TextoInfoObjeto,pick\x,pick\y,anchuratexto.u,#PB_Ignore)
desplazardesdepuntodevista(EntidadSeleccionada); <- mover
If tuberia.a And FindString(EntidadSeleccionada$,"Point")
nodotuberia.a=Val(RemoveString(EntidadSeleccionada$,"Point"))
SelectElement(puntosbeziertuberia(),nodotuberia)
puntosbeziertuberia()\x+THETA\x:puntosbeziertuberia()\y+THETA\y:puntosbeziertuberia()\z+THETA\z
; If Listindex(puntosbeziertuberia())=0
; CopyMemory(@puntosbeziertuberia(),@vector,Sizeof(D3DXVECTOR3))
; LastElement(puntosbeziertuberia())
; MoveEntity(#CoordenadaTuberia0+ListIndex(puntosbeziertuberia()),vector\x,vector\y,vector\z,#PB_Absolute|#PB_World); <- mover
; CopyMemory(@vector,@puntosbeziertuberia(),Sizeof(D3DXVECTOR3))
; FirstElement(puntosbeziertuberia())
; ElseIf Listindex(puntosbeziertuberia())=ListSize(puntosbeziertuberia())-1
; CopyMemory(@puntosbeziertuberia(),@vector,Sizeof(D3DXVECTOR3))
; FirstElement(puntosbeziertuberia())
; MoveEntity(#CoordenadaTuberia0,vector\x,vector\y,vector\z,#PB_Absolute|#PB_World); <- mover
; CopyMemory(@vector,@puntosbeziertuberia(),Sizeof(D3DXVECTOR3))
; LastElement(puntosbeziertuberia())
; EndIf
TuberiaBezier()
EndIf
ElseIf mmb.b; rotar
rotardesdepuntodevista(EntidadSeleccionada)
ElseIf rmb.b; menu
EndIf
Else
FreeGadget3D(#TextoInfoObjeto)
CloseWindow3D(#TextoInfoObjeto)
EndIf
EndIf
If EntidadSeleccionada$="":MouseLocate(CursorX,CursorY):EndIf
If KeyboardReleased(#PB_Key_W):wireframe.a!1:If wireframe.a:CameraRenderMode(#camara,#PB_Camera_Wireframe):Else:CameraRenderMode(#camara,#PB_Camera_Textured):EndIf
ElseIf KeyboardPushed(#PB_Key_Right)
If tuberia
If grosortuberia<20:grosortuberia+0.01:TuberiaBezier():EndIf
EndIf
ElseIf KeyboardPushed(#PB_Key_Left)
If tuberia
If grosortuberia>0.01:grosortuberia-0.01:TuberiaBezier():EndIf
EndIf
ElseIf KeyboardPushed(#PB_Key_Up)
If tuberia
If nverticestuberia<255:nverticestuberia+1:TuberiaBezier():EndIf
EndIf
ElseIf KeyboardPushed(#PB_Key_Down)
If tuberia
If nverticestuberia>npuntosbeziertuberia:nverticestuberia-1:TuberiaBezier():EndIf
EndIf
ElseIf KeyboardReleased(#PB_Key_Home)
If tuberia
If nverticescirctuberia<255:nverticescirctuberia+1:TuberiaBezier():EndIf
EndIf
ElseIf KeyboardReleased(#PB_Key_End)
If tuberia
If nverticescirctuberia>3:nverticescirctuberia-1:TuberiaBezier():EndIf
EndIf
ElseIf KeyboardReleased(#PB_Key_PageUp)
If tuberia
If npuntosbeziertuberia<30:npuntosbeziertuberia+1
ClearSpline(#splinetuberia)
LastElement(puntosbeziertuberia())
CopyMemory(@puntosbeziertuberia(),@vector,Sizeof(D3DXVECTOR3))
AddElement(puntosbeziertuberia())
CopyMemory(@vector,@puntosbeziertuberia(),Sizeof(D3DXVECTOR3))
ForEach puntosbeziertuberia()
If IsEntity(#CoordenadaTuberia0+ListIndex(puntosbeziertuberia())):FreeEntity(#CoordenadaTuberia0+ListIndex(puntosbeziertuberia())):EndIf
AddSplinePoint(#splinetuberia,puntosbeziertuberia()\x,puntosbeziertuberia()\y,puntosbeziertuberia()\z)
CreateEntity(#CoordenadaTuberia0+ListIndex(puntosbeziertuberia()),MeshID(#MallaCoordenadaTuberia),#PB_Material_None,puntosbeziertuberia()\x,puntosbeziertuberia()\y,puntosbeziertuberia()\z)
Next
TuberiaBezier()
EndIf
EndIf
ElseIf KeyboardReleased(#PB_Key_PageDown)
If tuberia
If npuntosbeziertuberia>2:npuntosbeziertuberia-1
ClearSpline(#splinetuberia)
LastElement(puntosbeziertuberia())
If IsEntity(#CoordenadaTuberia0+ListIndex(puntosbeziertuberia())):FreeEntity(#CoordenadaTuberia0+ListIndex(puntosbeziertuberia())):EndIf
DeleteElement(puntosbeziertuberia(),1)
ForEach puntosbeziertuberia()
If IsEntity(#CoordenadaTuberia0+ListIndex(puntosbeziertuberia())):FreeEntity(#CoordenadaTuberia0+ListIndex(puntosbeziertuberia())):EndIf
AddSplinePoint(#splinetuberia,puntosbeziertuberia()\x,puntosbeziertuberia()\y,puntosbeziertuberia()\z)
CreateEntity(#CoordenadaTuberia0+ListIndex(puntosbeziertuberia()),MeshID(#MallaCoordenadaTuberia),#PB_Material_None,puntosbeziertuberia()\x,puntosbeziertuberia()\y,puntosbeziertuberia()\z)
Next
TuberiaBezier()
EndIf
EndIf
ElseIf KeyboardReleased(#PB_Key_RightControl):run.a!1
EndIf
If run.a
Origenhaz\x=EntityX(#capsula)+EntityDirectionX(#capsula)*alturacapsula/2
Origenhaz\y=EntityY(#capsula)+EntityDirectionY(#capsula)*alturacapsula/2
Origenhaz\z=EntityZ(#capsula)+EntityDirectionZ(#capsula)*alturacapsula/2
haz0\x=EntityDirectionX(#capsula)*alturacapsula*longhaz0
haz0\y=EntityDirectionY(#capsula)*alturacapsula*longhaz0
haz0\z=EntityDirectionZ(#capsula)*alturacapsula*longhaz0
haz0\m=getmodulo(haz0)
FetchOrientation(EntityID(#capsula))
Pitch(EntityID(#capsula),90,#PB_Relative|#PB_Local)
haznormal\x=EntityDirectionX(#capsula)*alturacapsula*longhaz0*inclhazlat
haznormal\y=EntityDirectionY(#capsula)*alturacapsula*longhaz0*inclhazlat
haznormal\z=EntityDirectionZ(#capsula)*alturacapsula*longhaz0*inclhazlat
SetOrientation(EntityID(#capsula),GetX(),GetY(),GetZ(),GetW()); o bien Pitch(EntityID(#capsula),-90,#PB_Relative|#PB_Local)
sector\x=haz0\x*2*#PI/nhacescapsula/haz0\m:sector\y=haz0\y*2*#PI/nhacescapsula/haz0\m:sector\z=haz0\z*2*#PI/nhacescapsula/haz0\m
i.a=0
If RayCast(Origenhaz\x,Origenhaz\y,Origenhaz\z,haz0\x,haz0\y,haz0\z,-1)=#tuberia
hacescapsula(i)\x=PickX()-Origenhaz\x:hacescapsula(i)\y=PickY()-Origenhaz\y:hacescapsula(i)\z=PickZ()-Origenhaz\z:hacescapsula(i)\m=getmodulo(hacescapsula(i)); <- haces distancia
Else
FillMemory(@hacescapsula.Vector3D(i),SizeOf(Vector3D))
EndIf
For i.a=1 To nhacescapsula
If i.a=1
hacescapsula(i)\x=haz0\x+haznormal\x:hacescapsula(i)\y=haz0\y+haznormal\y:hacescapsula(i)\z=haz0\z+haznormal\z:hacescapsula(i)\m=getmodulo(hacescapsula(i))
Else
hacescapsula(i)=hacescapsula(i-1)
Rotar_Vector3D_por_adicion_Angular(@sector,@hacescapsula(i))
EndIf
If RayCast(Origenhaz\x,Origenhaz\y,Origenhaz\z,hacescapsula(i)\x,hacescapsula(i)\y,hacescapsula(i)\z,-1)=#tuberia
hacescapsula(i)\x=PickX()-Origenhaz\x:hacescapsula(i)\y=PickY()-Origenhaz\y:hacescapsula(i)\z=PickZ()-Origenhaz\z:hacescapsula(i)\m=getmodulo(hacescapsula(i)); <- haces distancia
Else
FillMemory(@hacescapsula.Vector3D(i),SizeOf(Vector3D))
EndIf
Next
FillMemory(@vector.Vector3D,SizeOf(Vector3D)); <- inicio nulo
For i.a=1 To nhacescapsula
vector\x+hacescapsula(i)\x
vector\y+hacescapsula(i)\y
vector\z+hacescapsula(i)\z
Next:vector\x/nhacescapsula:vector\y/nhacescapsula:vector\z/nhacescapsula:vector\m=getmodulo(vector)
If vector\m>1E-3
ProyeccionVectorialOrtogonal(vector,hacescapsula(0),medialat)
capsulacel\x=hacescapsula(0)\x*KF + medialat\x*KL
capsulacel\y=hacescapsula(0)\y*KF + medialat\y*KL
capsulacel\z=hacescapsula(0)\z*KF + medialat\z*KL
EntityDirection(#capsula,capsulacel\x,capsulacel\y,capsulacel\z,#PB_World,#PB_Vector_NegativeZ)
MoveEntity(#capsula,capsulacel\x,capsulacel\y,capsulacel\z,#PB_World|#PB_Relative)
EndIf
If KeyboardReleased(#PB_Key_G):grabarformatuberia():EndIf
EndIf
TimeSinceLastFrame.i=RenderWorld(50)
FlipBuffers()
Delay(6)
Until KeyboardPushed(#PB_Key_Escape)
https://mega.nz/file/c050WZTA#Y5SbOuzcd ... arqw2VwFps
The same absurd algorithm using the 2D tracks of these ones:
https://www.youtube.com/watch?v=wL7tSgUpy8w
https://www.youtube.com/watch?v=-sg-GgoFCP0 y
https://www.youtube.com/watch?v=3myQK1WXEqE
... by the way: in PB6.20(x86), this is much slower than PB6.04(x86), at least in windows.