It seems not to do with the explained issue.
Really I suspect the requested function is not hard using the correct way with quaternions.
However, i must say that quaternions DO NOT use less computing resources than matrix, as is written in some texts.
And also some texts say that matrix also lacks about the known problem of "gimbal lock", but that is absolutely incorrect; "gimbal lock" problem is only for the use of Euler angles, i.e. Pitch, Yaw and Roll.
And moreover: quaternions use for 3D engines lacks when dealing with multi-turn rotations, which matrix do not.
Hopefully, not all academic texts about 3D engines, and robotics, talks nice about quaternions in front of homogeneous matrix.
In my own observations, I have founded suspects that there is nor matrix neither quaternions the ideal or optimal way to manage rotations. But nowadays is the tool used everywhere.
Well, time ago I wrote a tip to show it, using the MP3D lib:
Code: Select all
; Rota una entidad sobre su centro geométrico (rotación intrínseca si ese centro corresponde con su centro de masas). Diciembre 2014
MP_Graphics3D(640,480,0,3)
SetWindowTitle(0,"TurnEntity over relative to eye axis")
maincam.i=MP_CreateCamera()
pivotcam.i=MP_CreateMesh():MP_EntitySetParent(maincam,pivotcam,0):MP_PositionEntity(maincam,0,0,-4)
light.i=MP_CreateLight(1)
If CreateImage(0,255,255)
Font.i=LoadFont(#PB_Any,"Arial",138)
StartDrawing(ImageOutput(0))
Box(0, 0, 128, 128,RGB(255,0,0))
Box(128, 0, 128, 128,RGB(0,255,0))
Box(0, 128, 128, 128,RGB(0,0,255))
Box(128, 128, 128, 128,RGB(255,255,0))
DrawingFont(FontID(Font))
DrawingMode(#PB_2DDrawing_Transparent)
DrawText(73,35,"5",RGB(0,0,0))
StopDrawing()
EndIf
Structure D3DMATRIX
StructureUnion
_11.f
Xi.f
EndStructureUnion
StructureUnion
_12.f
Xj.f
EndStructureUnion
StructureUnion
_13.f
Xk.f
EndStructureUnion
_14.f
StructureUnion
_21.f
Yi.f
EndStructureUnion
StructureUnion
_22.f
Yj.f
EndStructureUnion
StructureUnion
_23.f
Yk.f
EndStructureUnion
_24.f
StructureUnion
_31.f
Zi.f
EndStructureUnion
StructureUnion
_32.f
Zj.f
EndStructureUnion
StructureUnion
_33.f
Zk.f
EndStructureUnion
_34.f
StructureUnion
_41.f
Pi.f
EndStructureUnion
StructureUnion
_42.f
Pj.f
EndStructureUnion
StructureUnion
_43.f
Pk.f
EndStructureUnion
_44.f
EndStructure
Structure D3DXVECTOR3
x.f
y.f
z.f
EndStructure
Structure Vector3D Extends D3DXVECTOR3
m.f;<-length(modulo)
EndStructure
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
Procedure.i Matriz3x3xMatriz3x3(*a.D3DMATRIX,*b.D3DMATRIX,*o.D3DMATRIX); producto
*o\_11=*a\_11**b\_11+*a\_12**b\_21+*a\_13**b\_31:*o\_12=*a\_11**b\_12+*a\_12**b\_22+*a\_13**b\_32:*o\_13=*a\_11**b\_13+*a\_12**b\_23+*a\_13**b\_33
*o\_21=*a\_21**b\_11+*a\_22**b\_21+*a\_23**b\_31:*o\_22=*a\_21**b\_12+*a\_22**b\_22+*a\_23**b\_32:*o\_23=*a\_21**b\_13+*a\_22**b\_23+*a\_23**b\_33
*o\_31=*a\_31**b\_11+*a\_32**b\_21+*a\_33**b\_31:*o\_32=*a\_31**b\_12+*a\_32**b\_22+*a\_33**b\_32:*o\_33=*a\_31**b\_13+*a\_32**b\_23+*a\_33**b\_33
ProcedureReturn *o
EndProcedure
Procedure.i Matriz4x4xMatriz4x4(*a.D3DMATRIX,*b.D3DMATRIX,*o.D3DMATRIX); producto
*o\_11=*a\_11**b\_11+*a\_12**b\_21+*a\_13**b\_31+*a\_14**b\_41:*o\_12=*a\_11**b\_12+*a\_12**b\_22+*a\_13**b\_32+*a\_14**b\_42:*o\_13=*a\_11**b\_13+*a\_12**b\_23+*a\_13**b\_33+*a\_14**b\_43:*o\_14=*a\_11**b\_14+*a\_12**b\_24+*a\_13**b\_34+*a\_14**b\_44
*o\_21=*a\_21**b\_11+*a\_22**b\_21+*a\_23**b\_31+*a\_24**b\_41:*o\_22=*a\_21**b\_12+*a\_22**b\_22+*a\_23**b\_32+*a\_24**b\_42:*o\_23=*a\_21**b\_13+*a\_22**b\_23+*a\_23**b\_33+*a\_24**b\_43:*o\_24=*a\_21**b\_14+*a\_22**b\_24+*a\_23**b\_34+*a\_24**b\_44
*o\_31=*a\_31**b\_11+*a\_32**b\_21+*a\_33**b\_31+*a\_34**b\_41:*o\_32=*a\_31**b\_12+*a\_32**b\_22+*a\_33**b\_32+*a\_34**b\_42:*o\_33=*a\_31**b\_13+*a\_32**b\_23+*a\_33**b\_33+*a\_34**b\_43:*o\_34=*a\_31**b\_14+*a\_32**b\_24+*a\_33**b\_34+*a\_34**b\_44
*o\_41=*a\_41**b\_11+*a\_42**b\_21+*a\_43**b\_31+*a\_44**b\_41:*o\_42=*a\_41**b\_12+*a\_42**b\_22+*a\_43**b\_32+*a\_44**b\_42:*o\_43=*a\_41**b\_13+*a\_42**b\_23+*a\_43**b\_33+*a\_44**b\_43:*o\_44=*a\_41**b\_14+*a\_42**b\_24+*a\_43**b\_34+*a\_44**b\_44
ProcedureReturn *o
EndProcedure
Procedure.i flecha(x.f=1,y.f=0,z.f=0,color.l=$EE3333,lados.a=8)
Protected mod.f=Sqr(x*x+y*y+z*z)
If mod>0.0001
Protected cil.i=MP_CreateCylinder(lados,mod),cono.i=MP_CreateCone(lados,mod/8),ciltexture.i=MP_CreateTextureColor(8,8,color)
MP_ResizeMesh(cil,mod/80,mod/80,mod-mod/20)
MP_TranslateMesh(cil,0,0,(mod-mod/20)/2)
mod*7/8
MP_RotateMesh(cono,180,0,180)
MP_ResizeMesh(cono,mod/20,mod/20,mod/10)
MP_TranslateMesh(cono,0,0,mod+mod/10)
MP_AddMesh(cono,cil):MP_FreeEntity(cono)
MP_EntityLookAt(cil,x+0.000001,y,z,0,0)
MP_EntitySetTexture(cil,ciltexture,0,0)
ProcedureReturn cil
EndIf
ProcedureReturn 0
EndProcedure
Procedure.i BaseSistemaCoordenadas(size.f=1,lados.a=8,color.l=$22AADD)
Protected cil.i=MP_CreateCylinder(lados,size),cono.i=MP_CreateCone(lados,size/8),cil2.i
MP_ResizeMesh(cil,size/80,size/80,size-size/20)
MP_TranslateMesh(cil,0,0,(size-size/20)/2)
size*7/8
MP_RotateMesh(cono,180,0,180)
MP_ResizeMesh(cono,size/20,size/20,size/10)
MP_TranslateMesh(cono,0,0,size+size/10)
MP_AddMesh(cono,cil):MP_FreeEntity(cono)
cil2.i=MP_CopyEntity(cil)
MP_RotateMesh(cil2,-90,0,0)
MP_AddMesh(cil2,cil):MP_FreeEntity(cil2)
cil2.i=MP_CopyEntity(cil)
MP_RotateMesh(cil2,0,90,0)
MP_AddMesh(cil2,cil):MP_FreeEntity(cil2)
MP_EntitySetTexture(cil,MP_CreateTextureColor(8,8,color),0,0)
ProcedureReturn cil
EndProcedure
Procedure.i BaseSistemaCoordenadas0(px.f=0,py.f=0,pz.f=0,size.f=1,lados.a=8,colorx.l=$DD3333,colory.l=$99DD99,colorz.l=$2299DD)
Protected vx.i=flecha(size,0,0,colorx,lados)
Protected vy.i=flecha(0,size,0,colory,lados)
Protected vz.i=flecha(0,0,size,colorz,lados)
MP_AddMesh(vx.i,vy.i);:MP_FreeEntity(vx.i)
MP_AddMesh(vz.i,vy.i);:MP_FreeEntity(vz.i)
MP_PositionEntity(vy,px,py,pz)
ProcedureReturn vy.i
EndProcedure
Procedure.i Turn_Body_by_Angle_adding(Body.i,*fi.Vector3D,*matriz.D3DMATRIX=0,vx.f=0,vy.f=0,vz.f=0); <- usar esta cuando no disponemos del módulo angular de antemano (antes de llamar a la función), sino solo del propio vector ángulo
;Rotar una entity (una matriz) un ángulo dado '*fi.Vector3D':
Protected rot.D3DMATRIX,rot1.D3DMATRIX,cos.f,sen.f,coss.f,*u.Vector3D
*fi\m=getmodulo(*fi)
If *fi\m
*u.Vector3D=AllocateMemory(SizeOf(Vector3D)):CopyMemory(*fi,*u,SizeOf(Vector3D))
*u\x/*u\m:*u\y/*u\m:*u\z/*u\m; <- obtener vector unidad (llamado "vector normalizado")
If *matriz=0
*matriz=MP_EntityGetMatrix(Body)
EndIf
cos=Cos(*u\m):sen=Sin(*u\m):coss=1.0-cos
;Matriz de Rotación de un ángulo dado 'A' alrededor de un eje dado definido por el vector unidad (*u\x,*u\y,*u\z)
rot1\Xi=cos+*u\x**u\x*coss:rot1\Xj=*u\x**u\y*coss-*u\z*sen:rot1\Xk=*u\x**u\z*coss+*u\y*sen
rot1\Yi=*u\y**u\x*coss+*u\z*sen:rot1\Yj=cos+*u\y**u\y*coss:rot1\Yk=*u\y**u\z*coss-*u\x*sen
rot1\Zi=*u\z**u\x*coss-*u\y*sen:rot1\Zj=*u\z**u\y*coss+*u\x*sen:rot1\Zk=cos+*u\z**u\z*coss
CopyMemory(*matriz,@rot,SizeOf(D3DMATRIX))
rot\_41+vx:rot\_42+vy:rot\_43+vz
MP_EntitySetMatrix(Body,Matriz3x3xMatriz3x3(*matriz,@rot1,@rot)); <- se multiplica la matriz de inclinación del entity por la matriz rotación y se obtiene la matriz original rotada
FreeMemory(*u)
ProcedureReturn @rot
EndIf
ProcedureReturn 0
EndProcedure
Procedure.i MatrizRotacionaldeunAngulodadoalrededordeunejedefinidoporunvectorunidad(Body.i,*u.Vector3D,A.f,*matriz.D3DMATRIX=0,vx.f=0,vy.f=0,vz.f=0); <- usar esta cuando disponemos de antemano (antes de llamar a la función) del módulo angular y del vector unitario del ángulo
;Rotar un entity un ángulo dado cuya dirección viene dada por el vector unidad '*u.Vector3D' y además por un módulo 'A'
Protected rot.D3DMATRIX,rot1.D3DMATRIX,cos.f,sen.f,coss.f
If A
If *matriz=0
*matriz=MP_EntityGetMatrix(Body)
EndIf
cos=Cos(A):sen=Sin(A):coss=1.0-cos
;Matriz de Rotación de un ángulo dado 'A' alrededor de un eje dado definido por el vector unidad (*u\x,*u\y,*u\z)
rot1\Xi=cos+*u\x**u\x*coss:rot1\Xj=*u\x**u\y*coss-*u\z*sen:rot1\Xk=*u\x**u\z*coss+*u\y*sen
rot1\Yi=*u\y**u\x*coss+*u\z*sen:rot1\Yj=cos+*u\y**u\y*coss:rot1\Yk=*u\y**u\z*coss-*u\x*sen
rot1\Zi=*u\z**u\x*coss-*u\y*sen:rot1\Zj=*u\z**u\y*coss+*u\x*sen:rot1\Zk=cos+*u\z**u\z*coss
CopyMemory(*matriz,@rot,SizeOf(D3DMATRIX))
rot\_41+vx:rot\_42+vy:rot\_43+vz
MP_EntitySetMatrix(Body,Matriz3x3xMatriz3x3(*matriz,@rot1,@rot)); <- se multiplica la matriz de inclinación del entity por la matriz rotación y se obtiene la matriz original rotada
ProcedureReturn @rot
EndIf
ProcedureReturn 0
EndProcedure
; Object0.i=MP_CreateSkySphere(20)
; Object0.i=BaseSistemaCoordenadas()
; Object0.i=MP_CreateSphere(20)
Object0.i=MP_CreateCube()
MP_EntitySetTexture(Object0,MP_ImageToTexture(0),0,0)
baseglobal.i=BaseSistemaCoordenadas():MP_PositionEntity(baseglobal,-2,0.5,0)
*rot.D3DMATRIX=MP_EntityGetMatrix(pivotcam)
rot.D3DMATRIX
rot1.D3DMATRIX
rotx.D3DMATRIX
roty.D3DMATRIX
rotz.D3DMATRIX
; MP_RotateEntity(Object0,Random(360),Random(360),Random(360),0)
MP_MouseInWindow()
MP_UseCursor(0)
While MP_KeyDown(#PB_Key_Escape)=0 And WindowEvent()<>#PB_Event_CloseWindow
mdz.f=MP_MouseDeltaWheel()/400
If MP_KeyDown(#PB_Key_Up):mdy.f=-0.01
ElseIf MP_KeyDown(#PB_Key_Down):mdy.f=0.01
ElseIf MP_KeyDown(#PB_Key_Left):mdx.f=-0.01
ElseIf MP_KeyDown(#PB_Key_Right):mdx.f=0.01
Else
mdx.f=MP_MouseDeltaX()/200:mdy.f=MP_MouseDeltaY()/200
EndIf
If MP_KeyDown(#PB_Key_LeftControl) Or MP_MouseButtonDown(1); <- mover el punto de vista
MP_TurnEntity(pivotcam,mdy.f*60,mdx.f*60,0,0)
If mdz.f
MP_EntitySetZ(maincam,MP_EntityGetZ(maincam)+mdz); <- MP_MoveEntity(cam,0,0,mdz)
EndIf
*rot=MP_EntityGetMatrix(pivotcam)
ElseIf MP_KeyDown(#PB_Key_RightShift); <- rotar el objeto sobre eje 'x' global y sobre eje 'y' global
;Para rotar la entidad alrededor del eje, en el plano XY del mundo, que define el desplazamiento del raton
;matriz de rotación sobre eje x del mundo:
rotx\_11=1:rotx\_12=0:rotx\_13=0:rotx\_14=0
rotx\_21=0:rotx\_22=Cos(-mdy):rotx\_23=Sin(-mdy):rotx\_24=0
rotx\_31=0:rotx\_32=-Sin(-mdy):rotx\_33=Cos(-mdy):rotx\_34=0
rotx\_41=0:rotx\_42=0:rotx\_43=0:rotx\_44=1
;matriz de rotación sobre eje y del mundo:
roty\_11=Cos(mdx):roty\_12=0:roty\_13=Sin(mdx):roty\_14=0
roty\_21=0:roty\_22=1:roty\_23=0:roty\_24=0
roty\_31=-Sin(mdx):roty\_32=0:roty\_33=Cos(mdx):roty\_34=0
roty\_41=0:roty\_42=0:roty\_43=0:roty\_44=1
;matriz de rotación sobre eje z del mundo:
rotz\_11=Cos(mdz):rotz\_12=0:rotz\_13=Sin(mdz):rotz\_14=0
rotz\_21=0:rotz\_22=1:rotz\_23=0:rotz\_24=0
rotz\_31=-Sin(mdz):rotz\_32=0:rotz\_33=Cos(mdz):rotz\_34=0
rotz\_41=0:rotz\_42=0:rotz\_43=0:rotz\_44=1
;obtener matriz de rotación sobre eje, en el plano XY del mundo, que define el desplazamiento del raton:
Matriz4x4xMatriz4x4(@rotx,@roty,@rot1); o lo que es lo mismo: Matriz4x4xMatriz4x4(@roty,@rotx,@rot1)
MP_EntitySetMatrix(Object0,Matriz4x4xMatriz4x4(MP_EntityGetMatrix(Object0),@rot1,@rot))
Else; <- rotar el objeto sobre eje 'x' y 'y' relativo al ojo.
;Para rotar la entidad alrededor del eje, en el plano XY del ojo, que define el desplazamiento del raton.
;El tema está en que el raton se mueve en 2 dimensiones, es decir en el plano, entonces de la entrada del raton obtenemos:
;1. de su desplazamiento vertical un vector en dirección X, cuya amplitud y sentido nos indica el ángulo a rotar alrededor de ese eje X
;2. de su desplazamiento horizontal un vector en dirección Y, cuya amplitud y sentido nos indica el ángulo a rotar alrededor de ese eje Y
;Tenemos pues un vector definido por el movimiento del ratón.
;El ojo se puede mover en el espacio del mundo y apuntar la vista hacia cualquier parte,
;por lo que el plano frontal del ojo puede variar y no coincidir con el plano XY del mundo.
;Entonces el vector que obtenemos del movimiento del ratón ha de estar contenido en el plano frontal del ojo.
;Hay que disponer el vector (mdx,mdy,mdz) sobre el plano XY de la base de coordenadas del pivotcam (plano frontal del ojo), no del mundo.
;Para ello, el vector (0,mdx,0) hay que ponerlo sobre el eje Y del plano frontal de ojo
;y el vector (mdy,0,0) hay que ponerlo sobre el eje X del plano frontal de ojo:
;Tenemos en *rot la dirección del eje X, a través del vector X-> del plano frontal del ojo ( *rot\_11,*rot\_12,*rot\_13 )
;y la dirección del eje Y, a través del vector Y-> del plano frontal del ojo ( *rot\_21,*rot\_22,*rot\_23 )
;Por tanto procedemos a obtener ambos vectores unidad:
;el colineal al eje de abscisas en el plano frontal del ojo y el colineal al eje de ordenadas en este plano:
THETA.d=Sqr(mdx.f*mdx.f+mdy.f*mdy.f+mdz.f*mdz.f)
If THETA.d>0
ux_m.d=Sqr(*rot\_11**rot\_11+*rot\_12**rot\_12+*rot\_13**rot\_13)
If ux_m.d>0
;los cosenos directores de un vector cualquiera son las coordenadas escalares de ese mismo vector con módulo unidad, es decir,
;son las proyecciones de ese vector unitario sobre cada uno de los ejes principales
ux_x.d=*rot\_11/ux_m:ux_y.d=*rot\_12/ux_m:ux_z.d=*rot\_13/ux_m; <- son los cosenos directores
;ahora multiplicando este vector unitario, por el modulo de (mdy,0,0) obtenemos ya la componente X pero en el plano frontal del ojo:
ux_x*mdy:ux_y*mdy:ux_z*mdy
EndIf
;Hacemos lo mismo para obtener la componente Y del movimiento del ratón pero en el plano frontal del ojo
uy_m.d=Sqr(*rot\_21**rot\_21+*rot\_22**rot\_22+*rot\_23**rot\_23)
If uy_m>0
;los cosenos directores de un vector cualquiera son las coordenadas escalares de ese mismo vector con módulo unidad, es decir,
;son las proyecciones de ese vector unitario sobre cada uno de los ejes principales
uy_x.d=*rot\_21/uy_m:uy_y.d=*rot\_22/uy_m:uy_z.d=*rot\_23/uy_m; <- son los cosenos directores.
;ahora multiplicando este vector unitario, por el modulo de (0,mdx,0) obtenemos ya la componente Y pero en el plano frontal del ojo:
uy_x*mdx:uy_y*mdx:uy_z*mdx
EndIf
;Hacemos lo mismo para obtener la componente Z del movimiento del ratón pero en el plano frontal del ojo
uz_m.d=Sqr(*rot\_31**rot\_31+*rot\_32**rot\_32+*rot\_33**rot\_33)
If uz_m>0
;los cosenos directores de un vector cualquiera son las coordenadas escalares de ese mismo vector con módulo unidad, es decir,
;son las proyecciones de ese vector unitario sobre cada uno de los ejes principales
uz_x.d=*rot\_31/uz_m:uz_y.d=*rot\_32/uz_m:uz_z.d=*rot\_33/uz_m; <- son los cosenos directores.
;ahora multiplicando este vector unitario, por el modulo de (0,0,mdz) obtenemos ya la componente Z pero en el plano frontal del ojo:
uz_x*mdz:uz_y*mdz:uz_z*mdz
EndIf
;Para obtener el vector que define la recta alrededor de la cual ha de rotar el objeto, basta con sumar esas 2 componentes obtenidas,
;y dado que ambas componentes están contenidas en el plano frontal del ojo, la suma de ambas también lo estará:
u.Vector3D
u\x=ux_x.d+uy_x.d+uz_x.d:u\y=ux_y.d+uy_y.d+uz_y.d:u\z=ux_z.d+uy_z.d+uz_z.d
If MP_KeyDown(#PB_Key_LeftShift)=0; <- METODO MIO
Turn_Body_by_Angle_adding(Object0.i,@u.Vector3D,0,0.0,0.0,0.0)
Else ; METODO 2:
;el módulo del vector original en el plano XY del mundo (THETA) es el mismo al vector rotado al plano XY frontal del ojo, así que: Sqr(u\x*u\x+u\y*u\y+u\z*u\z) = THETA
;Convertimos este vector en unitario:
u\x/THETA:u\y/THETA:u\z/THETA
MatrizRotacionaldeunAngulodadoalrededordeunejedefinidoporunvectorunidad(Object0.i,@u.Vector3D,THETA,0,0.0,0.0,0.0)
EndIf
EndIf
EndIf
MP_RenderWorld()
MP_Flip():Delay(8)
Wend
If you do not have that lib installed then this is the compiled executeable an should work for you there, it is a normal windows 32bit executeable.
Moving the mouse can rotate the object. With left CTRL + mouse moving orbitates the eye over the object.
The program is able to perform the object rotation ALWAYS over the eye view plane, this is, with the rotation axis contained inside de front view plane.
https://mega.nz/#!QpgSVKDB!HNlntXQCuwNF ... cjAwvEzlhE
In that code, i just replaced conversion functions (quaternion--->rotation matrix) and viceversa, in order it works in the native PB.
Result:
No success.
Previous MP3D code converted to PB native code. Here can see the used functions conversions for quaternion--->rotation matrix and viceversa:
Code: Select all
; Rota una entidad sobre su centro geométrico (rotación intrínseca si ese centro corresponde con su centro de masas). Diciembre 2014
;/ inits
Global pantallacompleta.b=0,Titulo$="Par secundario en los cuerpos celestes"
Select MessageRequester("Pantalla para graficos 3D:","¿Usar pantalla completa?",#PB_MessageRequester_YesNoCancel)
Case #PB_MessageRequester_Yes:pantallacompleta.b=1
Case #PB_MessageRequester_Cancel:End
EndSelect
inicio:
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
RX=FRX:RY=FRY
Else
If FRX<1280 Or FRY<720:RX=FRX*2/3:RY=FRY*2/3:Else:RX=1280:RY=720:EndIf
EndIf
If InitEngine3D()=0
MessageRequester("Error","The 3D Engine can't be initialized",0):End
EndIf
;AntialiasingMode(#PB_AntialiasingMode_None)
;AntialiasingMode(#PB_AntialiasingMode_x2)
AntialiasingMode(#PB_AntialiasingMode_x4)
;AntialiasingMode(#PB_AntialiasingMode_x6)
;
InitSprite():InitKeyboard():InitMouse()
If pantallacompleta.b=2
OpenScreen(RX,RY,bitplanes.a,Titulo$,#PB_Screen_WaitSynchronization,FrecuenciadeMuestreo.u)
Else
OpenWindow(0,0,0,RX,RY,Titulo$,#PB_Window_BorderLess|#PB_Window_ScreenCentered)
OpenWindowedScreen(WindowID(0),0,0,RX,RY,1,0,0,#PB_Screen_WaitSynchronization)
EndIf
Add3DArchive(#PB_Compiler_Home+"examples/3D/Data/Textures",#PB_3DArchive_FileSystem)
Add3DArchive(#PB_Compiler_Home+"examples/3D/Data/fonts",#PB_3DArchive_FileSystem)
Add3DArchive(#PB_Compiler_Home+"examples/3D/Data/Models",#PB_3DArchive_FileSystem)
Add3DArchive(#PB_Compiler_Home+"examples/3D/Data/Packs/desert.zip",#PB_3DArchive_Zip)
Add3DArchive(#PB_Compiler_Home+"examples/3D/Data/Scripts",#PB_3DArchive_FileSystem)
Add3DArchive(#PB_Compiler_Home+"examples/3D/Data/Packs/skybox.zip",#PB_3DArchive_Zip)
Add3DArchive(#PB_Compiler_Home+"examples/3D/Data/GUI",#PB_3DArchive_FileSystem)
Add3DArchive("media/",#PB_3DArchive_FileSystem)
Add3DArchive("media/cielo1.zip",#PB_3DArchive_Zip)
Add3DArchive("media/cielo2.zip",#PB_3DArchive_Zip)
Add3DArchive("media/cielo3.zip",#PB_3DArchive_Zip)
Add3DArchive("media/cielo4.zip",#PB_3DArchive_Zip)
Parse3DScripts()
; Lo que sigue es una plantilla de los elementos de un mundo 3D que ofrecen las bibliotecas nativas el PureBasic:
Enumeration; Camaras
#Camara
EndEnumeration
Enumeration; Luces
#Luz
EndEnumeration
Enumeration; Texturas
#Textura
EndEnumeration
Enumeration; Materiales
#Material
EndEnumeration
Enumeration; Mallas
#Object0malla
EndEnumeration
Enumeration; Entidades
#Object0
EndEnumeration
Enumeration; Nodos
#Nodo
#Pivotcam
EndEnumeration
Global ventanaprincipalx.l=10,ventanaprincipaly.l=10,ventanaprincipalw.l=RX/2.5,ventanaprincipalh.l=RY/2
;\
CreateLight(#luz,$EEEEEE,4,4,2,#PB_Light_Point)
CreateCamera(#Camara,0,0,100,100):CreateNode(#Pivotcam,0,0,0):AttachNodeObject(#Pivotcam,CameraID(#Camara)):CameraRange(#Camara,0.1,10000):CameraBackColor(#Camara,$181911)
MoveCamera(#Camara,0,0,3,#PB_Absolute)
Structure D3DMATRIX
StructureUnion
_11.f
Xi.f
EndStructureUnion
StructureUnion
_12.f
Xj.f
EndStructureUnion
StructureUnion
_13.f
Xk.f
EndStructureUnion
_14.f
StructureUnion
_21.f
Yi.f
EndStructureUnion
StructureUnion
_22.f
Yj.f
EndStructureUnion
StructureUnion
_23.f
Yk.f
EndStructureUnion
_24.f
StructureUnion
_31.f
Zi.f
EndStructureUnion
StructureUnion
_32.f
Zj.f
EndStructureUnion
StructureUnion
_33.f
Zk.f
EndStructureUnion
_34.f
StructureUnion
_41.f
Pi.f
EndStructureUnion
StructureUnion
_42.f
Pj.f
EndStructureUnion
StructureUnion
_43.f
Pk.f
EndStructureUnion
_44.f
EndStructure
Structure D3DXVECTOR3
x.f
y.f
z.f
EndStructure
Structure Vector3D Extends D3DXVECTOR3
m.f;<-length(modulo)
EndStructure
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
Procedure.i EntityGetMatrixFromQuaternion(objeto.i,*matriz.D3DMATRIX,modo.i=#PB_Absolute)
FetchOrientation(objeto,modo):Protected x.f=GetX(),y.f=GetY(),z.f=GetZ(),w.f=GetW(),s.f=2.0/(x*x+y*y+z*z+w*w)
*matriz\Xi=1-s*(y*y+z*z); <- 0,0
*matriz\Xj=s*(x*y-z*w); <- 0,1
*matriz\Xk=s*(x*z+y*w); <- 0,2
*matriz\Yi=s*(x*y+z*w); <- 1,0
*matriz\Yj=1-s*(x*x+z*z); <- 1,1
*matriz\Yk=s*(y*z-x*w); <- 1,2
*matriz\Zi=s*(x*z-y*w); <- 2,0
*matriz\Zj=s*(y*z+x*w); <- 2,1
*matriz\Zk=1-s*(x*x+y*y); <- 2,2
ProcedureReturn *matriz
EndProcedure
Procedure EntityGetQuaternionFromMatrix1(objeto.i,*matriz.D3DMATRIX)
Protected x.f,y.f,z.f,w.f
w=Sqr(*matriz\Xi+*matriz\Yj+*matriz\Zk+1)/2
x=Sign(*matriz\Zj-*matriz\Yk)*Sqr(*matriz\Xi-*matriz\Yj-*matriz\Zk+1)/2
y=Sign(*matriz\Xk-*matriz\Zi)*Sqr(*matriz\Yj-*matriz\Xi-*matriz\Zk+1)/2
z=Sign(*matriz\Yi-*matriz\Xj)*Sqr(*matriz\Zk-*matriz\Xi-*matriz\Yj+1)/2
SetOrientation(objeto,x,y,z,w)
EndProcedure
Procedure EntityGetQuaternionFromMatrix(objeto.i,*matriz.D3DMATRIX)
Protected x.f,y.f,z.f,w.f,tr.f=*matriz\Xi+*matriz\Yj+*matriz\Zk,S.f
If tr>0
S=Sqr(tr+1)*2; S=4*w
w=S/4
x=(*matriz\Zj-*matriz\Yk)/S
y=(*matriz\Xk-*matriz\Zi)/S
z=(*matriz\Yi-*matriz\Xj)/S
ElseIf *matriz\Xi>*matriz\Yj And *matriz\Xi>*matriz\Zk
S=Sqr(1+*matriz\Xi-*matriz\Yj-*matriz\Zk)*2; S=4*x
w=(*matriz\Zj-*matriz\Yk)/S
x=S/4
y=(*matriz\Xj+*matriz\Yi)/S
z=(*matriz\Xk+*matriz\Zi)/S
ElseIf *matriz\Yj>*matriz\Zk
S=Sqr(1+*matriz\Yj-*matriz\Xi-*matriz\Zk)*2; S=4*y
w=(*matriz\Xk-*matriz\Zi)/S
x=(*matriz\Xj+*matriz\Yi)/S
y=S/4
z=(*matriz\Yk+*matriz\Zj)/S
Else
S=Sqr(1+*matriz\Zk-*matriz\Xi-*matriz\Yj)*2; S=4*z
w=(*matriz\Yi-*matriz\Xj)/S
x=(*matriz\Xk+*matriz\Zi)/S
y=(*matriz\Yk+*matriz\Zj)/S
z=S/4
EndIf
SetOrientation(objeto,x,y,z,w)
EndProcedure
Procedure.i Matriz3x3xMatriz3x3(*a.D3DMATRIX,*b.D3DMATRIX,*o.D3DMATRIX); producto
*o\_11=*a\_11**b\_11+*a\_12**b\_21+*a\_13**b\_31:*o\_12=*a\_11**b\_12+*a\_12**b\_22+*a\_13**b\_32:*o\_13=*a\_11**b\_13+*a\_12**b\_23+*a\_13**b\_33
*o\_21=*a\_21**b\_11+*a\_22**b\_21+*a\_23**b\_31:*o\_22=*a\_21**b\_12+*a\_22**b\_22+*a\_23**b\_32:*o\_23=*a\_21**b\_13+*a\_22**b\_23+*a\_23**b\_33
*o\_31=*a\_31**b\_11+*a\_32**b\_21+*a\_33**b\_31:*o\_32=*a\_31**b\_12+*a\_32**b\_22+*a\_33**b\_32:*o\_33=*a\_31**b\_13+*a\_32**b\_23+*a\_33**b\_33
ProcedureReturn *o
EndProcedure
Procedure.i Matriz4x4xMatriz4x4(*a.D3DMATRIX,*b.D3DMATRIX,*o.D3DMATRIX); producto
*o\_11=*a\_11**b\_11+*a\_12**b\_21+*a\_13**b\_31+*a\_14**b\_41:*o\_12=*a\_11**b\_12+*a\_12**b\_22+*a\_13**b\_32+*a\_14**b\_42:*o\_13=*a\_11**b\_13+*a\_12**b\_23+*a\_13**b\_33+*a\_14**b\_43:*o\_14=*a\_11**b\_14+*a\_12**b\_24+*a\_13**b\_34+*a\_14**b\_44
*o\_21=*a\_21**b\_11+*a\_22**b\_21+*a\_23**b\_31+*a\_24**b\_41:*o\_22=*a\_21**b\_12+*a\_22**b\_22+*a\_23**b\_32+*a\_24**b\_42:*o\_23=*a\_21**b\_13+*a\_22**b\_23+*a\_23**b\_33+*a\_24**b\_43:*o\_24=*a\_21**b\_14+*a\_22**b\_24+*a\_23**b\_34+*a\_24**b\_44
*o\_31=*a\_31**b\_11+*a\_32**b\_21+*a\_33**b\_31+*a\_34**b\_41:*o\_32=*a\_31**b\_12+*a\_32**b\_22+*a\_33**b\_32+*a\_34**b\_42:*o\_33=*a\_31**b\_13+*a\_32**b\_23+*a\_33**b\_33+*a\_34**b\_43:*o\_34=*a\_31**b\_14+*a\_32**b\_24+*a\_33**b\_34+*a\_34**b\_44
*o\_41=*a\_41**b\_11+*a\_42**b\_21+*a\_43**b\_31+*a\_44**b\_41:*o\_42=*a\_41**b\_12+*a\_42**b\_22+*a\_43**b\_32+*a\_44**b\_42:*o\_43=*a\_41**b\_13+*a\_42**b\_23+*a\_43**b\_33+*a\_44**b\_43:*o\_44=*a\_41**b\_14+*a\_42**b\_24+*a\_43**b\_34+*a\_44**b\_44
ProcedureReturn *o
EndProcedure
Procedure.i MatrizRotacionaldeunAngulodadoalrededordeunejedefinidoporunvectorunidad(Body.i,*u.Vector3D,A.f,*matriz.D3DMATRIX); <- usar esta cuando disponemos de antemano (antes de llamar a la función) del módulo angular y del vector unitario del ángulo
;Rotar un entity un ángulo dado cuya dirección viene dada por el vector unidad '*u.Vector3D' y además por un módulo 'A'
Protected rot.D3DMATRIX,rot1.D3DMATRIX,cos.f,sen.f,coss.f
If A
cos=Cos(A):sen=Sin(A):coss=1.0-cos
;Matriz de Rotación de un ángulo dado 'A' alrededor de un eje dado definido por el vector unidad (*u\x,*u\y,*u\z)
rot1\Xi=cos+*u\x**u\x*coss:rot1\Xj=*u\x**u\y*coss-*u\z*sen:rot1\Xk=*u\x**u\z*coss+*u\y*sen
rot1\Yi=*u\y**u\x*coss+*u\z*sen:rot1\Yj=cos+*u\y**u\y*coss:rot1\Yk=*u\y**u\z*coss-*u\x*sen
rot1\Zi=*u\z**u\x*coss-*u\y*sen:rot1\Zj=*u\z**u\y*coss+*u\x*sen:rot1\Zk=cos+*u\z**u\z*coss
CopyMemory(*matriz,@rot,SizeOf(D3DMATRIX))
EntityGetQuaternionFromMatrix(Body,Matriz3x3xMatriz3x3(*matriz,@rot1,@rot)); <- se multiplica la matriz de inclinación del entity por la matriz rotación y se obtiene la matriz original rotada
ProcedureReturn @rot
EndIf
ProcedureReturn 0
EndProcedure
Procedure.b KeyEdgeDetection(key.a)
Static pka.a
If KeyboardPushed(key);<-if current key status is PUSHED
If pka=0:pka=key:ProcedureReturn 1:EndIf;<-if previous key status was NOT PUSHED, then assign previous state to current one, and EXIT.
ElseIf pka=key;<-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
CreateCube(#Object0malla,1)
LoadTexture(#textura,"soil_wall.jpg")
CreateMaterial(#material,TextureID(#textura))
CreateEntity(#Object0,MeshID(#Object0malla),MaterialID(#material),0,0,0)
; BaseSistemaCoordenadas(#baseglobal):MoveEntity(#baseglobal,-2,0.5,0,#PB_Absolute)
matrizpivot.D3DMATRIX
EntityGetMatrixFromQuaternion(NodeID(#pivotcam),@matrizpivot)
matrizobjeto.D3DMATRIX
EntityGetMatrixFromQuaternion(EntityID(#Object0),@matrizobjeto)
rot.D3DMATRIX
rot1.D3DMATRIX
rotx.D3DMATRIX
roty.D3DMATRIX
rotz.D3DMATRIX
Macro TeclaControldecamara(tecla=LeftControl)
If KeyEdgeDetection(#PB_Key_#tecla#); <- inicia control camara
pasocam.f=0.001:pasocamincr.f=0.0001
ElseIf KeyboardReleased(#PB_Key_#tecla#)
ElseIf KeyboardPushed(#PB_Key_#tecla#); <- mover el punto de vista
;para desplazar la camara hacia delante, atras, arriba, abajo, izq o der
If mdx Or mdy Or mdz
If mmb.b
MoveNode(#Pivotcam,mdx,-mdy,0,#PB_Local); o MoveCamera(0,mdx,-mdy,0,#PB_Local) o MoveCamera(0,mdx,-mdy,0,#PB_Relative)
Else
RotateNode(#Pivotcam,-mdy*60,-mdx*60,0,#PB_Relative)
If mdz
MoveCamera(#Camara,0,0,-mdz,#PB_Relative)
EndIf
EndIf
ElseIf KeyboardPushed(#PB_Key_Add)
MoveCamera(#Camara,0,0,-pasocam,#PB_Relative)
pasocam+pasocamincr
ElseIf KeyboardPushed(#PB_Key_Subtract)
MoveCamera(#Camara,0,0,pasocam,#PB_Relative)
pasocam+pasocamincr
ElseIf KeyboardPushed(#PB_Key_Pad8)
MoveCamera(#Camara,0,pasocam,0,#PB_Relative)
pasocam+pasocamincr
ElseIf KeyboardPushed(#PB_Key_Pad2)
MoveCamera(#Camara,0,-pasocam,0,#PB_Relative)
pasocam+pasocamincr
ElseIf KeyboardPushed(#PB_Key_Pad6)
MoveCamera(#Camara,pasocam,0,0,#PB_Relative)
pasocam+pasocamincr
ElseIf KeyboardPushed(#PB_Key_Pad4)
MoveCamera(#Camara,-pasocam,0,0,#PB_Relative)
pasocam+pasocamincr
ElseIf KeyboardPushed(#PB_Key_Pad1)
RotateNode(#Pivotcam,0,-0.5,0,#PB_Relative)
ElseIf KeyboardPushed(#PB_Key_Pad7)
RotateNode(#Pivotcam,0,0.5,0,#PB_Relative)
ElseIf KeyboardPushed(#PB_Key_Pad3) Or lmb.b
RotateNode(#Pivotcam,0,0,-0.5,#PB_Relative)
ElseIf KeyboardPushed(#PB_Key_Pad9) Or rmb.b
RotateNode(#Pivotcam,0,0,0.5,#PB_Relative)
EndIf
EndMacro
Repeat
ExamineMouse():ExamineKeyboard()
WaitWindowEvent()
CursorX.f=MouseX():CursorY.f=MouseY():lmb.b=MouseButton(#PB_MouseButton_Left):rmb.b=MouseButton(#PB_MouseButton_Right):mmb.b=MouseButton(#PB_MouseButton_Middle)
mdx.f=MouseDeltaX()/200:mdy.f=MouseDeltaY()/200:mdz.f=MouseWheel()/20
If KeyboardPushed(#PB_Key_Up):mdy.f=-0.01
ElseIf KeyboardPushed(#PB_Key_Down):mdy.f=0.01
ElseIf KeyboardPushed(#PB_Key_Left):mdx.f=-0.01
ElseIf KeyboardPushed(#PB_Key_Right):mdx.f=0.01
EndIf
TeclaControldecamara(LeftControl)
ElseIf KeyboardPushed(#PB_Key_RightShift); <- rotar el objeto sobre eje 'x' global y sobre eje 'y' global
;Para rotar la entidad alrededor del eje, en el plano XY del mundo, que define el desplazamiento del raton
;matriz de rotación sobre eje x del mundo:
rotx\_11=1:rotx\_12=0:rotx\_13=0:rotx\_14=0
rotx\_21=0:rotx\_22=Cos(-mdy):rotx\_23=Sin(-mdy):rotx\_24=0
rotx\_31=0:rotx\_32=-Sin(-mdy):rotx\_33=Cos(-mdy):rotx\_34=0
rotx\_41=0:rotx\_42=0:rotx\_43=0:rotx\_44=1
;matriz de rotación sobre eje y del mundo:
roty\_11=Cos(mdx):roty\_12=0:roty\_13=Sin(mdx):roty\_14=0
roty\_21=0:roty\_22=1:roty\_23=0:roty\_24=0
roty\_31=-Sin(mdx):roty\_32=0:roty\_33=Cos(mdx):roty\_34=0
roty\_41=0:roty\_42=0:roty\_43=0:roty\_44=1
;matriz de rotación sobre eje z del mundo:
rotz\_11=Cos(mdz):rotz\_12=0:rotz\_13=Sin(mdz):rotz\_14=0
rotz\_21=0:rotz\_22=1:rotz\_23=0:rotz\_24=0
rotz\_31=-Sin(mdz):rotz\_32=0:rotz\_33=Cos(mdz):rotz\_34=0
rotz\_41=0:rotz\_42=0:rotz\_43=0:rotz\_44=1
;obtener matriz de rotación sobre eje, en el plano XY del mundo, que define el desplazamiento del raton:
Matriz4x4xMatriz4x4(@rotx,@roty,@rot1); o lo que es lo mismo: Matriz4x4xMatriz4x4(@roty,@rotx,@rot1)
EntityGetQuaternionFromMatrix(EntityID(#Object0),Matriz4x4xMatriz4x4(EntityGetMatrixFromQuaternion(EntityID(#Object0),@matrizobjeto),@rot1,@rot))
Else; <- rotar el objeto sobre eje 'x' y 'y' relativo al ojo.
EntityGetMatrixFromQuaternion(NodeID(#pivotcam),@matrizpivot)
;Para rotar la entidad alrededor del eje, en el plano XY del ojo, que define el desplazamiento del raton.
;El tema está en que el raton se mueve en 2 dimensiones, es decir en el plano, entonces de la entrada del raton obtenemos:
;1. de su desplazamiento vertical un vector en dirección X, cuya amplitud y sentido nos indica el ángulo a rotar alrededor de ese eje X
;2. de su desplazamiento horizontal un vector en dirección Y, cuya amplitud y sentido nos indica el ángulo a rotar alrededor de ese eje Y
;Tenemos pues un vector definido por el movimiento del ratón.
;El ojo se puede mover en el espacio del mundo y apuntar la vista hacia cualquier parte,
;por lo que el plano frontal del ojo puede variar y no coincidir con el plano XY del mundo.
;Entonces el vector que obtenemos del movimiento del ratón ha de estar contenido en el plano frontal del ojo.
;Hay que disponer el vector (mdx,mdy,mdz) sobre el plano XY de la base de coordenadas del pivotcam (plano frontal del ojo), no del mundo.
;Para ello, el vector (0,mdx,0) hay que ponerlo sobre el eje Y del plano frontal de ojo
;y el vector (mdy,0,0) hay que ponerlo sobre el eje X del plano frontal de ojo:
;Tenemos en matrizpivot la dirección del eje X, a través del vector X-> del plano frontal del ojo ( matrizpivot\_11,matrizpivot\_12,matrizpivot\_13 )
;y la dirección del eje Y, a través del vector Y-> del plano frontal del ojo ( matrizpivot\_21,matrizpivot\_22,matrizpivot\_23 )
;Por tanto procedemos a obtener ambos vectores unidad:
;el colineal al eje de abscisas en el plano frontal del ojo y el colineal al eje de ordenadas en este plano:
THETA.d=Sqr(mdx.f*mdx.f+mdy.f*mdy.f+mdz.f*mdz.f)
If THETA.d>0
ux_m.d=Sqr(matrizpivot\_11*matrizpivot\_11+matrizpivot\_12*matrizpivot\_12+matrizpivot\_13*matrizpivot\_13)
If ux_m.d>0
;los cosenos directores de un vector cualquiera son las coordenadas escalares de ese mismo vector con módulo unidad, es decir,
;son las proyecciones de ese vector unitario sobre cada uno de los ejes principales
ux_x.d=matrizpivot\_11/ux_m:ux_y.d=matrizpivot\_12/ux_m:ux_z.d=matrizpivot\_13/ux_m; <- son los cosenos directores
;ahora multiplicando este vector unitario, por el modulo de (mdy,0,0) obtenemos ya la componente X pero en el plano frontal del ojo:
ux_x*mdy:ux_y*mdy:ux_z*mdy
EndIf
;Hacemos lo mismo para obtener la componente Y del movimiento del ratón pero en el plano frontal del ojo
uy_m.d=Sqr(matrizpivot\_21*matrizpivot\_21+matrizpivot\_22*matrizpivot\_22+matrizpivot\_23*matrizpivot\_23)
If uy_m>0
;los cosenos directores de un vector cualquiera son las coordenadas escalares de ese mismo vector con módulo unidad, es decir,
;son las proyecciones de ese vector unitario sobre cada uno de los ejes principales
uy_x.d=matrizpivot\_21/uy_m:uy_y.d=matrizpivot\_22/uy_m:uy_z.d=matrizpivot\_23/uy_m; <- son los cosenos directores.
;ahora multiplicando este vector unitario, por el modulo de (0,mdx,0) obtenemos ya la componente Y pero en el plano frontal del ojo:
uy_x*mdx:uy_y*mdx:uy_z*mdx
EndIf
;Hacemos lo mismo para obtener la componente Z del movimiento del ratón pero en el plano frontal del ojo
uz_m.d=Sqr(matrizpivot\_31*matrizpivot\_31+matrizpivot\_32*matrizpivot\_32+matrizpivot\_33*matrizpivot\_33)
If uz_m>0
;los cosenos directores de un vector cualquiera son las coordenadas escalares de ese mismo vector con módulo unidad, es decir,
;son las proyecciones de ese vector unitario sobre cada uno de los ejes principales
uz_x.d=matrizpivot\_31/uz_m:uz_y.d=matrizpivot\_32/uz_m:uz_z.d=matrizpivot\_33/uz_m; <- son los cosenos directores.
;ahora multiplicando este vector unitario, por el modulo de (0,0,mdz) obtenemos ya la componente Z pero en el plano frontal del ojo:
uz_x*mdz:uz_y*mdz:uz_z*mdz
EndIf
;Para obtener el vector que define la recta alrededor de la cual ha de rotar el objeto, basta con sumar esas 2 componentes obtenidas,
;y dado que ambas componentes están contenidas en el plano frontal del ojo, la suma de ambas también lo estará:
u.Vector3D
u\x=ux_x.d+uy_x.d+uz_x.d:u\y=ux_y.d+uy_y.d+uz_y.d:u\z=ux_z.d+uy_z.d+uz_z.d
;el módulo del vector original en el plano XY del mundo (THETA) es el mismo al vector rotado al plano XY frontal del ojo, así que: Sqr(u\x*u\x+u\y*u\y+u\z*u\z) = THETA
;Convertimos este vector en unitario:
u\x/THETA:u\y/THETA:u\z/THETA
EntityGetMatrixFromQuaternion(EntityID(#Object0),@matrizobjeto)
MatrizRotacionaldeunAngulodadoalrededordeunejedefinidoporunvectorunidad(EntityID(#Object0),@u.Vector3D,THETA,@matrizobjeto)
;Beep_(200,5)
EndIf
EndIf
TimeSinceLastFrame.i=RenderWorld(50)
; If IsSprite(#Sprite2D):showhelpsprite():EndIf
FlipBuffers():Delay(9)
Until KeyboardPushed(#PB_Key_Escape)