EntityDirection() , EntityDirectionX/Y/Z() and others

Everything related to 3D programming
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

EntityDirection() , EntityDirectionX/Y/Z() and others

Post by Psychophanta »

The purpose of this post are 2:
- To give some clearification thinks to take in account related to orientation of 3D objects in the 3D world.
- To correct the document about 'EntityDirectionX/Y/Z()' functions OR to provide these functions with same posibilities than the 'EntityDirection()' (see below).

In the current PB version 6.11LTS we have a powerful function called 'EntityDirection()' , which allows us to give a 3D world vector to make an entity to point to. It just does what EntityLookAt() does, but in a different posibilities. There is also 'SetOrientation()' and 'FetchOrientation()', which can be used to orientate objects too, but these ones belong to the 'Engine3D' library and with the so called 'quaternions'.
Next tip uses all those ways to orientate in space and can be useful to take a look to understand how those functions work.

When dealing with objects orientation in a 3D world we need only two vectors to take in account as parameters:
(0) a global 3D vector to which co-lineate the object.
(1) the local vector of the object we want to co-lineate.
NOTICE all vectors for orientation purposes should be normalized, i.e.: Sqr(x*x + y*y + z*z) = 1.0
NOTICE and take in account that after 'CreateEntity()', 'CreateNode()', 'CreateCamera()', etc. ,
by default, the object vector orientation in the world is such as its local 'y' is co-lineated with the global (0,+1,0), and that's why by default,
the entity yaw axis is (0,+1,0); you can modify this axis everytime, with 'EntityFixedYawAxis()', so the object can be "yawed" around the given arbitration axis.

For the doc manual there should be interesting to complete it for the 'EntityDirectionX/Y/Z()' functions, because these ones have not the options 'EntityDirection()' has, which are:
#PB_Vector_X
#PB_Vector_Y
#PB_Vector_Z
#PB_Vector_NegativeX
#PB_Vector_NegativeY
#PB_Vector_NegativeZ
The fact is that 'EntityDirectionX/Y/Z()' are forced its local to '#PB_Vector_Z', with no possibility to change it.
So, the manual should say:
Description
Get the 'x/y/z' direction of the entity referred to its local '#PB_Vector_Z'.

Code: Select all

InitEngine3D(#PB_Engine3D_NoLog,#PB_Compiler_Home+"Compilers\Engine3d.dll")
InitSprite():InitKeyboard():InitMouse()
OpenWindow(0,0,0,800,600,"v",#PB_Window_BorderLess|#PB_Window_ScreenCentered)
OpenWindowedScreen(WindowID(0),0,0,800,600,1,0,0,#PB_Screen_WaitSynchronization)
Add3DArchive(#PB_Compiler_Home+"Examples\3D\Data\fonts",#PB_3DArchive_FileSystem)
Parse3DScripts()
#el=0
CreateCamera(0,0,0,100,100)
CreateNode(0):AttachNodeObject(0,CameraID(0)):MoveNode(0,0,0,10,#PB_Absolute)
CreateIcoSphere(1,0.2)
CreateCone(0,1,2,4,1)
CreateMaterial(1,0,$28BBEF):SetMaterialColor(1,#PB_Material_DiffuseColor,$28BBEF)
CreateMaterial(0,0,$BB28EF):SetMaterialColor(0,#PB_Material_DiffuseColor,$BB28EF)
CreateEntity(1,MeshID(1),MaterialID(1),0,2,0)
CreateEntity(0,MeshID(0),MaterialID(0))
CreateText3D(0,"epale!","",1,$DDDDDDDD)
Text3DAlignment(0,#PB_Text3D_HorizontallyCentered|#PB_Text3D_Top)
ScaleText3D(0,0.3681,0.621,0.01,#PB_Relative)
AttachNodeObject(0,Text3DID(0))
MoveText3D(0,0,2,-10,#PB_Absolute|#PB_World)
CreateLight(0,$EEEEEE,4,4,2,#PB_Light_Point):SetLightColor(0,#PB_Light_DiffuseColor,$EEEEEE):MoveLight(0,4,4,2,#PB_Absolute)

Structure D3DXVECTOR3
  x.f
  y.f
  z.f
EndStructure
Structure Vector3D Extends D3DXVECTOR3
  m.f;<-length(modulo)
EndStructure
vector.Vector3D
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
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)
  ;Esta funcion halla un vector resultado de una rotacion en el espacio de otro vector inicial.
  ;Esta funcion admite entonces, como parametros de entrada, 2 vectores:
  ; - 'Vector radio' ( *R0-> ) que se desea rotar. Este vector tiene direccion no colineal con el eje de rotación.
  ; - 'Vector angulo' ( *fi-> 'velocidad angular'-> * 'tiempo'), es el angulo en el que se rota el 'Vector radio' dado.
  ;     Su modulo indica el numero de radianes a rotar, su direccion indica el angulo en el espacio en el que se rota (eje de rotación)
  ;     su sentido indica el sentido de la rotacion
  ;NOTA: la funcion extrae el vector velocidad rectilinea y el nuevo vector radio.
  ;       se devuelve nuevo radio en el mismo parametro de entrada ( *R0-> )
  ; El resultado es:
  ; Proyección de *R0-> sobre *fi-> MÁS proyección ortogonal de *R0-> sobre *fi-> por el Cos(|fi->|) MÁS módulo de la proyección ortogonal de *R0-> sobre *fi-> por el Sen(|fi->|) por u^,
  ; siendo u-> = *fi-> X *R0-> .
  
  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=0.0; <= no hay giro ya que *R0-> y *fi-> son colineales. Pero hay "torsion" vectorial:
      ;Esta "torsion" significa que el 'Vector radio' ( *R0-> ) aumenta su amplitud en la medida de la amplitud del 'Vector angulo' ( *fi-> )
      *R0\x+*fi\x
      *R0\y+*fi\y
      *R0\z+*fi\z
      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 GetOrientacion(entidad)
  ; El vector 'fi->' señala el eje sobre el que se establece el ángulo 'fi\m', que es el ángulo de inclinación de un objeto 3D con respecto a la 'y' global.
  ; Tras llamar a la función FetchOrientation(), su módulo es |fi->| = 2·ACos(GetW()) , y sus componentes:
  ; fi\x = GetX()/Sin(|fi->|/2) * |fi->| ( o bien fi\x = GetX()/Sqr(1-GetW()^2) * |fi->| )
  ; fi\y = GetY()/Sin(|fi->|/2) * |fi->| ( o bien fi\y = GetY()/Sqr(1-GetW()^2) * |fi->| )
  ; fi\z = GetZ()/Sin(|fi->|/2) * |fi->| ( o bien fi\z = GetZ()/Sqr(1-GetW()^2) * |fi->| )
  ; 'fi-^' es el vector unidad de 'fi->' y por tanto tiene por componentes ( fi\x/|fi->|, fi\y/|fi->|, fi\z/|fi->| )
  entidad#\x=0.0:entidad#\y=1.0:entidad#\z=0.0:entidad#\m=1.0
  fi#entidad#.Vector3D
  FetchOrientation(EntityID(#entidad#),#PB_Absolute)
  fi#entidad#\m=2*ACos(GetW())
  If fi#entidad#\m
    var.f=fi#entidad#\m/Sin(fi#entidad#\m/2)
    fi#entidad#\x=GetX()*var:fi#entidad#\y=GetY()*var:fi#entidad#\z=GetZ()*var
    Rotar_Vector3D_por_adicion_Angular(@fi#entidad#,@entidad#)
  Else
    fi#entidad#\x=0:fi#entidad#\y=0:fi#entidad#\z=0
  EndIf
EndMacro
Macro SetOrientacion(entidad)
  ; El vector 'fi->' señala el eje sobre el que se establece el ángulo 'fi\m', que es el ángulo de inclinación de un objeto 3D con respecto a la 'y' global.
  ; El vector entrado como parámetro no es 'fi->', sino que 'fi->' es el ángulo formado por (0,1,0) y 'entidad->':
  fi#entidad#.Vector3D
  vector\x=0.0:vector\y=1.0:vector\z=0.0:vector\m=1.0
  AnguloFormadoPor2Vectores(vector,entidad#,fi#entidad#)
  ; Las 4 variables que hay que entregar a la función 'SetOrientation()' son:
  ; X = fi\x*Sin(fi\m/2)/fi\m
  ; Y = fi\y*Sin(fi\m/2)/fi\m
  ; Z = fi\z*Sin(fi\m/2)/fi\m
  ; W = Cos(fi\m/2)
  If fi#entidad#\m>1E-5
    var.f=Sin(fi#entidad#\m/2)/fi#entidad#\m
  Else
    var.f=0.0
  EndIf
  SetOrientation(EntityID(#entidad#),fi#entidad#\x*var,fi#entidad#\y*var,fi#entidad#\z*var,Cos(fi#entidad#\m/2))
EndMacro
Macro ponermodo()
  el\m=getmodulo(el):el\x/el\m:el\y/el\m:el\z/el\m
  MoveEntity(1,2*el\x,2*el\y,2*el\z,#PB_World|#PB_Absolute)
  mensaje$=""
  Select modo.a
  Case 1:EntityDirection(0,el\x,el\y,el\z,#PB_World,#PB_Vector_Y):mensaje$="modo EntityDirection"+#LFCR$
  Case 2:EntityLookAt(0,el\x,el\y,el\z,0,1,0):mensaje$="modo EntityLookAt"+#LFCR$
  Case 0:SetOrientacion(el):mensaje$="modo My SetOrientacion"+#LFCR$
  EndSelect
  GetOrientacion(el)
  mensaje$+
      "EntityDirectionX/Y/Z: "+StrF(EntityDirectionX(0),2)+","+StrF(EntityDirectionY(0),2)+","+StrF(EntityDirectionZ(0),2)+#LFCR$+
      "My GetOrientacion: "+StrF(el\x,2)+","+StrF(el\y,2)+","+StrF(el\z,2)
  Text3DCaption(0,mensaje$)
EndMacro

el.Vector3D
el\x=0:el\y=1:el\z=0:el\m=1
Repeat
  Repeat:Eventodeventana.i=WindowEvent():Until Eventodeventana=0
  ExamineKeyboard()
  If KeyboardReleased(#PB_Key_F1):modo.a=1
  ElseIf KeyboardReleased(#PB_Key_F2):modo.a=2
  ElseIf KeyboardReleased(#PB_Key_F3):modo.a=0
  ElseIf KeyboardReleased(#PB_Key_Up):el\y+0.1
  ElseIf KeyboardReleased(#PB_Key_Down):el\y-0.1
  ElseIf KeyboardReleased(#PB_Key_Left):el\x-0.1
  ElseIf KeyboardReleased(#PB_Key_Right):el\x+0.1
  ElseIf KeyboardReleased(#PB_Key_PageUp):el\z+0.1
  ElseIf KeyboardReleased(#PB_Key_PageDown):el\z-0.1
  EndIf
  ponermodo()
  RenderWorld()
  FlipBuffers():Delay(16)
Until KeyboardPushed(#PB_Key_Escape)
Last edited by Psychophanta on Mon Aug 05, 2024 8:13 am, edited 1 time in total.
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
User avatar
minimy
Enthusiast
Enthusiast
Posts: 619
Joined: Mon Jul 08, 2013 8:43 pm
Location: off world

Re: EntityDirection() , EntityDirectionX/Y/Z() and others

Post by minimy »

Hello Psychophanta very interesting example. Thanks for share.

I got an error in line 17 because line 5 have wrong path.

can solve if change line 5 for PB root examples 3D, some thing like this.

This is mi local PB 3D examples path.
Add3DArchive("C:\Program Files\PureBasic_LTS_6.11\Examples\3D\Data\fonts",#PB_3DArchive_FileSystem)

Replace 'PureBasic_LTS_6.11' with your PB system folder to test.
If translation=Error: reply="Sorry, Im Spanish": Endif
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Re: EntityDirection() , EntityDirectionX/Y/Z() and others

Post by Psychophanta »

You are right, damn!, I proced to replace line 5 :wink:
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
Post Reply