3d without openGL

User projects written in or related to FreeBASIC.
dafhi
Posts: 1640
Joined: Jun 04, 2005 9:51

Re: 3d without openGL

Post by dafhi »

BasicCoder2 - Local or relative rotation are probably the terms you're looking for.

Note: paul doe wrote the 3d system with the paper airplane :D
BasicCoder2
Posts: 3906
Joined: Jan 01, 2009 7:03
Location: Australia

Re: 3d without openGL

Post by BasicCoder2 »

dafhi wrote:BasicCoder2 - Local or relative rotation are probably the terms you're looking for.
Note: paul doe wrote the 3d system with the paper airplane :D
A brain glitch I seem to have. Often I will be talking about one of my son's only to be questioned "You mean Harry don't you, not Sam?" The same thing happens with towns or restaurants with similar names. I noted warnings in one of the articles on 3d programming about terminology being unclear at times as to what the writer was making reference to. Not sure if it is local vs. relative or absolute world coordinates vs. object coordinates or what.
.
BasicCoder2
Posts: 3906
Joined: Jan 01, 2009 7:03
Location: Australia

Re: 3d without openGL

Post by BasicCoder2 »

dodicat wrote:I am not quite sure what you seek.
Predictable behavior. I don't want to push a key to find the shape turning on more than one of its axes. One axis should remain fixed from the viewers point of view which is the one around which the object is being rotated under user control while the other two axes rotate around it.

Also to be able to move the view around objects. In this old example the viewer moves around confined to a 2D plane with a fixed look ahead to the horizon much as my later raycaster views. The little screen at the top was my attempt at giving the game a 3D view of the world as I had seen in many retro games. Ideally in a 3D world you can move around any way you like and look in any direction.

Code: Select all

'viewer variables
dim as double mx,my 'position of viewer
mx = 320
my = 240

dim as double px(10),py(10) 'absolute position
dim as double dd(10) 'distance of post from viewer
dim as double ww1(10) 'angle of post to viewer
dim as ubyte red(10),grn(10),blu(10) 'color of post
dim as double pWidth,pHeight 'width and height of posts
for i as integer = 0 to 9
  read px(i),py(i)
next i

for i as integer = 0 to 9
  read red(i),grn(i),blu(i)
next i

'dim as double angle 'angle to viewer
dim as double ww    'direction of viewer
dim as integer mv   'velocity of viewer
dim as double dx,dy


screen 18,32,2  'two pages

screenset 1,0   'select page

'dim key as string
'dim k as integer

do
  cls

  if multikey(&H50) then mv = 0 'stop
  if multikey(&H48) then mv = 1 'go
    
  'rotate viewer
  if multikey(&H4D) then
    ww = ww - 1
    if ww < 0 then ww = ww + 360
  end if
  if multikey(&H4B) then
    ww = ww + 1
    if ww > 359 then
      ww = ww - 360
    end if
  end if

  
  dx = cos(ww * .0174533) * mv
  dy = sin(ww * .0174533) * mv
  
  'move viewer
  mx = mx + dx
  my = my + dy

  'compute distances/angle of posts from/to viewer
  for i as integer = 0 to 9
    dx = mx - px(i)
    dy = my - py(i)
    dd(i) = abs(sqr(dx^2+dy^2))
    ww1(i) = atan2(dy,dx)*57.2958
    if ww1(i)<0 then ww1(i) = ww1(i) + 360
    ww1(i)=ww1(i)-ww
    if ww1(i)<0 then ww1(i)=ww1(i)+360
  next i

  'sort post's properties lists according to distance
  for j as integer = 0 to 9
    for i as integer = 0 to 8
      if dd(i)<dd(i+1) then
        swap dd(i),dd(i+1)
        swap ww1(i),ww1(i+1)
        swap red(i),red(i+1)
        swap grn(i),grn(i+1)
        swap blu(i),blu(i+1)
        swap px(i),px(i+1)
        swap py(i),py(i+1)
      end if
    next i
  next j

  'draw 3d view
  locate 1,1
  line (0,0)-(360*2,40),rgb(128,127,255),bf
  line (0,40)-(360*2,80),rgb(127,255,127),bf
  line (180*2,0)-(180*2,80),rgb(255,255,255)
  for i as integer = 0 to 9
    pWidth = 1/dd(i)*600
    pHeight = 1/dd(i)*800
    line ((360-ww1(i))*2,40-pHeight)-((360-ww1(i)+pWidth)*2,40+pHeight),rgb(red(i),grn(i),blu(i)),bf
'    circle (360-ww1(i),40),1/dd(i)*800,rgb(red(i),grn(i),blu(i)),,,,f
'    print int(ww1(i));int(dd(i))
  next i
  'erase out of bounds drawing
'  line (360,0)-(639,479),rgb(0,0,0),bf
  line (0,0)-(180,80),rgb(128,128,128),bf
  line (534,0)-(639,80),rgb(128,128,128),bf
  line (0,81)-(639,479),rgb(0,0,0),bf
  
  'draw 2D view of posts
  for i as integer = 0 to 9
    circle (px(i),py(i)),4,rgb(red(i),grn(i),blu(i))
  next i
  
  'draw 2d view of viewer (if not in 3d area)
  if my > 80 then
    circle (mx,my),5,rgb(255,255,255),,,,f
    'compute direction pointer angle
    dx = cos(ww * .0174533) * 10
    dy = sin(ww * .0174533) * 10
    line (mx,my)-(mx+dx,my+dy),rgb(255,25,255)
  end if
  
  
  screencopy
  
  sleep 15     'ADJUST THIS TO CHANGE FRAME RATES PER SECOND
  
loop until multikey(&H01)

end

 
 'absolute positions of posts px() py()
 data 224,133  ' px(0),py(0)
 data 329,444  ' px(1),py(1)
 data 244,377  ' px(2),py(2)
 data 374,285  ' px(3),py(3)
 data 52,370   ' px(4),py(4)
 data 569,206  ' px(5),py(5)
 data 359,391  ' px(6),py(6)
 data 569,349  ' px(7),py(7)
 data 142,432  ' px(8),py(8)
 data 497,335  ' px(9),py(9)
 
 'color of posts red() grn() blu()
 data 255,0,0  ' red
 data 0,255,0  ' green
 data 0,0,255  ' blue
 data 255,255,0
 data 0,255,255
 data 255,0,255
 data 128,0,0
 data 0,128,0
 data 0,0,128
 data 128,128,0
 data 0,128,128
 data 128,0,128
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: 3d without openGL

Post by dodicat »

Thanks basiccoder2.
Kinda reminds me of those endless highways.
Been about a year and a half since old Quark posted.
Code is really a bit stupid looking rotating around an axis, points rotating around a point.
You actually end up substituting the x axis for the y axis.

But I am sure you will find your own way, I assume you will be sticking to your standard three rotations.

I'll finish off here by posting the previous grid using Rodrigues rotator.

Rodrigues is OK, but it uses cross and dot products and normalising vectors e.t.c.
So you have a plethora of vector operators.
I did actually make a Rodrigue a few years ago with all the stuff inside the function (no operators).
I couldn't find it though and I didn't post it here.

Code: Select all

#include "fbgfx.bi"

Using fb
Screen 19,32
Const pi=4*Atn(1)
Type sincos
    As Single Sin,Cos
End Type

Type v3
    As Single x,y,z
    As Ulong colour
    Declare Function length As Single
    Declare Function unit As v3
    Declare Function AxialRotate(As v3,As Sincos,As v3,As Single) As v3
    Declare Function PointRotate(As v3,As v3,As v3=Type<v3>(1,1,1)) As v3
    Declare Function perspective(eyepoint As v3) As v3
    #define dot *
    #define cross ^
End Type


Type float As v3

Operator + (v1 As v3,v2 As v3) As v3
Return Type<v3>(v1.x+v2.x,v1.y+v2.y,v1.z+v2.z)
End Operator
Operator -(v1 As v3,v2 As v3) As v3
Return Type<v3>(v1.x-v2.x,v1.y-v2.y,v1.z-v2.z)
End Operator
Operator * (f As Single,v1 As v3) As v3 
Return type(f*v1.x,f*v1.y,f*v1.z)
End Operator
Operator *(v1 As v3,f As Single) As v3
Return f*v1
End Operator
Operator * (v1 As v3,v2 As v3) As Single 'dot product
Return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z
End Operator
Operator ^ (v1 As v3,v2 As v3) As v3     'cross product
Return Type<v3>(v1.y*v2.z-v2.y*v1.z,-(v1.x*v2.z-v2.x*v1.z),v1.x*v2.y-v2.x*v1.y)
End Operator

Function v3.length As Single
    Return Sqr(this.x*this.x+this.y*this.y+this.z*this.z)
End Function

Function v3.unit As v3
    Dim n As Single=this.length
    If n=0 Then n=1e-20
    Return type(this.x/n,this.y/n,this.z/n)
End Function

Function v3.AxialRotate(centre As v3,Angle As sincos,norm As v3,T As Single=1) As v3
    Dim As v3 V=T*(This-centre),result
    result=(V*(Angle.cos)+(Norm cross V)*Angle.sin+Norm*(Norm dot V)*(1-Angle.cos))+centre
    result.colour=this.colour
    Return result
End Function

Function v3.perspective(eyepoint As v3) As v3
    Dim As Single   w=1+(this.z/eyepoint.z)
    If w=0 Then w=1e-20
    Return type((this.x-eyepoint.x)/w+eyepoint.x,(this.y-eyepoint.y)/w+eyepoint.y,(this.z-eyepoint.z)/w+eyepoint.z)
End Function


Function v3.PointRotate(Fulcrum As v3,Angle As v3,scale As v3=Type<v3>(1,1,1)) As v3
    Dim As v3 p=type(This-Fulcrum)
    Dim As v3 rot,temp
    Dim As Single s=Sin(angle.x),c=Cos(angle.x)
    temp=type((p.y)*C+(-p.z)*S,(p.z)*C+(p.y)*S)
    rot.y=temp.x
    s=Sin(angle.y):c=Cos(angle.y)
    temp=type((temp.y)*C+(-p.x)*S,(p.x)*C+(temp.y)*S)
    rot.z=temp.x
    s=Sin(angle.z):c=Cos(angle.z)
    temp=type((temp.y)*C+(-rot.y)*S,(rot.y)*C+(temp.y)*S)
    rot.x=temp.x:rot.y=temp.y
    Return type((scale.x*rot.x+Fulcrum.x),(scale.y*rot.y+Fulcrum.y),(scale.z*rot.z+Fulcrum.z))
End Function

Sub setgrid(sx As Long,bx As Long,sy As Long,by As Long,st As Long,p() As v3)
    #define U Ubound(p)
    Redim p(0)
    For y As Long=sy To by Step st
        Redim Preserve p(1 To U+1)
        p(U)=Type(sx,y,0)
        Redim Preserve p(1 To U+1)
        p(U)=Type(bx,y,0)
        Line(sx,y)-(bx,y)
    Next
    For x As Long=sx To bx Step st
        Redim Preserve p(1 To U+1)
        p(U)=Type(x,sy,0)
        Redim Preserve p(1 To U+1)
        p(U)=Type(x,by,0)
        Line(x,sy)-(x,by)
    Next
End Sub

Sub drawgrid(p() As V3)
    Dim As Ulong clr=Rgb(0,100,255)
    For n As Long=Lbound(p) To Ubound(p)-1
        Line (p(n).x,p(n).y)-(p(n+1).x,p(n+1).y),clr
        If n=Ubound(p)-3 Then clr=Rgb(0,200,0)'the axis green
        n+=1
    Next
End Sub

Function Regulate(Byval MyFps As Long,Byref fps As Long) As Long
    Static As Double timervalue,_lastsleeptime,t3,frames
    frames+=1
    If (Timer-t3)>=1 Then t3=Timer:fps=frames:frames=0
    Var sleeptime=_lastsleeptime+((1/myfps)-Timer+timervalue)*1000
    If sleeptime<1 Then sleeptime=1
    _lastsleeptime=sleeptime
    timervalue=Timer
    Return sleeptime
End Function 


Function keys(a As v3) As Integer
    If Multikey(SC_LEFT)  Then a.y=a.y+.005:Return 1
    If Multikey(SC_RIGHT) Then a.y=a.y-.005:Return 1
    If Multikey(SC_UP   ) Then a.x=a.x-.005:Return 1
    If Multikey(SC_DOWN ) Then a.x=a.x+.005:Return 1
    If Multikey(SC_Q)     Then a.z=a.z-.005:Return 1
    If Multikey(SC_W)     Then a.z=a.z+.005:Return 1
    If Multikey(SC_SPACE) Then Return 2
    Return 0
End Function


Redim As v3 a()'the main array of 3D points
Print "The initial grid"

setgrid(200,600,100,500,40,a())
'get dead centre of grid -- fulcrum
Dim As Single cx,cy,cz
For n As Long=Lbound(a) To Ubound(a)
    cx+=a(n).x
    cy+=a(n).y
    cz+=a(n).z
Next
Dim As Long sz=(Ubound(a) - Lbound(a) +1)
cx=cx/sz
cy=cy/sz
cz=cz/sz
Print "Grid fulcrum:"
Print cx,cy,cz

'put in an axis (normal to the grid)
Redim Preserve a(Lbound(a) To (Ubound(a)+2))
a(Ubound(a)-1)=Type(cx,cy,0)
a(Ubound(a))  =Type(cx,cy,-200)
Print "Now rotate to set the points on x z plane "
Print "Press a key"
Sleep
Cls
For n As Long=Lbound(a) To Ubound(a)
    a(n)=a(n).pointrotate(Type(cx,cy,cz),Type(-pi/2,pi/2,0))
Next
drawgrid(a())
'create a reference array to be able to reset the points

Redim As v3 ref(Lbound(a) To Ubound(a))
for n as long=lbound(a) to ubound(a)
            ref(n)=a(n)
            next


Print "View on x z plane"
Print "Press a key"
Sleep

Redim As V3 rot(Lbound(a) To Ubound(a))'to hold all rotated points

Dim As float ang
Dim As Single angle
Dim As Long fps

Dim As V3 axis=Type(0,-1,0) 'y axis

Dim As String key

Do
    key=Inkey
    Var k=keys(ang)
    If k=1 Then 
        For n As Long=Lbound(a) To Ubound(a)
            a(n)=a(n).pointrotate(Type(cx,cy,cz),ang)
        Next
        axis=axis.pointrotate(Type(0,0,0),ang):ang=Type(0,0,0)
    End If
    
    If k=2 Then 
        axis=Type(0,-1,0):ang=Type(0,0,0)
        for n as long=lbound(a) to ubound(a)
            a(n)=ref(n)
            next
    End If
    
    axis=(axis).unit 'must be a unit vector for rodrigues
    
    angle+=.01  'the turning speed of the grid 
    Var  ff=Type<sincos>(Sin(angle),Cos(angle))
    
    Screenlock
    Cls
    Draw String(10,10),"FPS " &fps
    Draw String(10,30),"Use Up,Down,Left,Right,q and w to turn the axis"
    Draw String(10,50),"Up/down for x axis, Left/right for y axis, q/w for z axis"
    Draw String(10,70),"Space resets"
    Draw String(10,110),"NOTE - Positive Z is into the screen"
    
    For n As Long=Lbound(a) To Ubound(a)
        rot(n)=a(n).Axialrotate(Type(cx,cy,cz),ff,Axis,1)'(the 1 is a scale)
        rot(n)=rot(n).perspective(Type(cx,cy,1000))
    Next
    
    drawgrid(rot())
draw string(rot(ubound(rot)).x,rot(ubound(rot)).y),str(int(rot(ubound(rot)).x))+ ","+str(int(rot(ubound(rot)).y)) _
           + ","+str(int(rot(ubound(rot)).z))
    
    Screenunlock
    Sleep regulate(64,fps),1
Loop Until key=Chr(27)


Sleep

Sleep
 
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: 3d without openGL

Post by paul doe »

UPDATE: some bugfixes in camera.bas. Check the corresponding post.
BasicCoder2 wrote:
paul doe wrote:And there you have it, your own 3D playground. Read the comments in the code to see how it works. Should you have any questions, or need some explanation on what the code does, I'm all ears ;-)
You get an A+ on the code. It does exactly what I would have liked to have done.
It is unlikely I will ever understand it but I wonder to what extent I could use the code myself?
I imagine it could be extended to have a world of filled polygons to do something like the old F/A-18 Interceptor Amiga game.
https://www.youtube.com/watch?v=oHsUbGTIXyE
.
Well thanks, but that's not the right mindset, pal. Fiddle around with it to grasp how it works, and if you don't understand something, just ask whoever coded that crap ;)
The code can be extended to make something like that, of course, but doing it purely in software is not as easy as you may think. Dodicat already told you:
dodicat wrote: My equations for the ordinary rotations (not rodrigue) were derived from the 3D rotation matrix.
But Matrix multiplication by looping is far too slow, and the matrix is only 3 X 3 anyway, so it can be written straight down.
Thus the contorted looking return in the rotator.
What it means is, basically, that its code was written for raw speed, using other rotational representations and specialized matrices. Mine doesn't, it is designed to be as straightforward, generic and unobfuscated as is humanly possible, so you (and others that want to grasp the basics of 3D) can understand how it's done, and why. It's written so you can read the literature about 3D coding, and compare the implementation with it. Only when you fully understand the concepts, can you comfortably use other, far more efficient methods of representing rotations, such as quaternions or Rodrigues rotations as dodicat and dafhi suggested.
It does not, however, perform matrix multiplication by looping (matrix multiplication is performed by computing row * column, and summing the factors), so it is not a 'generic' matrix x vector multiplicator. You can only multiply 4x4 matrices by 4x1 vectors, no more, no less.

It is a port of a great C++ tutorial that, sadly, it's gone it seems. It was in this page:

http://lodev.org/cgtutor/

and it had extensive explanations on 3d basics, vectors and matrices. I found that the camera 'class' was a neat thing to have, so I ported it to VB6 (waaay back then). The code you see here is slightly modified and fixed, as the original had several bugs. I keep squishing them, as you can tell ;)
dafhi wrote: BasicCoder2 - Local or relative rotation are probably the terms you're looking for.

Note: paul doe wrote the 3d system with the paper airplane :D
Yeah, I thought the same thing. I will prepare a simple code snippet to help him 'visualize' the difference between an arbitrary axis and a local axis. And that of a pure rotation and a composite rotation.

By the way:
dafhi wrote: It was obvious from your first demo 'main' that you've got a 'complete solution' - based on my own experience one typically needs a 'camera,' an object list, or 'world' or 'object manager' - something many of us haven't got into yet. Your next demo 'grid test,' combined with what it does, and how 'trivially' it does it is, well, unprecedented.

A lot of us are eager to step into 3d programming and you've dropped a deluxe-model scene composition lab on our doorstep
Made me LOL ;)

I don't know if you actually noticed, but the grid example is a purely 2D one to illustrate what the matrix x vector thing is all about (converting to and from different coordinate systems, or 'spaces'), no 3D computation was harmed in the making of it XD

Paul
Last edited by paul doe on Oct 12, 2017 12:20, edited 2 times in total.
BasicCoder2
Posts: 3906
Joined: Jan 01, 2009 7:03
Location: Australia

Re: 3d without openGL

Post by BasicCoder2 »

@Paul Doe,
Essentially I am still interested in the subject so I will still spend time trying to get my head around it all.
It is a port of a great C++ tutorial that, sadly, is gone it seems. It was in this page:
http://lodev.org/cgtutor/
Looks interesting. I was programming in c++ using the Bloodshed Dev-C++ IDE that came with a teach yourself c++ book and had just got SDL working with it when I discovered FreeBASIC and decided it would be easier to use FreeBASIC for my limited purposes at the time.

Welcome to FreeBASIC, I see you only joined 25 July, I hope you stick around a while.
.
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: 3d without openGL

Post by paul doe »

BasicCoder2 wrote: @Paul Doe,
Essentially I am still interested in the subject so I will still spend time trying to get my head around it all.
No prob. It can be daunting at first, indeed. Just ask, no matter how trivial the question may look. Other people may have the same questions, so we all benefit from them.

@the demo: it looks great! Reminds me of Star Fox. We played the hell out of it when we were kids. It was just amazing at the time, and I still play now and then, thanks to the time magic of emulators ;)
BasicCoder2 wrote: Welcome to FreeBASIC, I see you only joined 25 July, I hope you stick around a while.
Many thanks. I was around here since a little longer (as the forum allows anonymous browsing), was just slacking off XD
You may want to update 'camera.bas', I fixed some bugs, and added some more comments. The snippet I'm about to post needs it.

Paul
dafhi
Posts: 1640
Joined: Jun 04, 2005 9:51

Re: 3d without openGL

Post by dafhi »

[comment deleted]
Last edited by dafhi on Oct 12, 2017 12:06, edited 4 times in total.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: 3d without openGL

Post by dodicat »

I have all your files paul doe.
The camera test runs well, nice and smooth.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: 3d without openGL

Post by dodicat »

As my last effort here, I have re-done the axial rotate without vector operators.
Similar to Dafhi's code above (which he has removed), a cube of 10000 elements, painted by circle and quicksorted each loop I set at 90 fps and I get 90 fps.
So I would say that Rodrigues method is faster without all those operators.

Code: Select all




Type angle
    As Single sx,sy,sz
    As Single cx,cy,cz
End Type

Type AxialAngle
    As Single Sin,Cos
End Type

Type v3
    As Single x,y,z
    As Ulong colour'unused here
End Type

function normalize(v as V3) as V3
    dim as single L= Sqr(v.x*v.x+v.y*v.y+v.z*v.z)
    return type(v.x/L,v.y/L,v.z/L)
end function

Function AxialRotate(centre As v3,Pt as V3,Angle As AxialAngle,norm As v3,T As Single=1) byref  As v3
#define crossP(v1,v2,N) Type<v3>( N*(v1.y*v2.z-v2.y*v1.z),N*(-(v1.x*v2.z-v2.x*v1.z)),N*(v1.x*v2.y-v2.x*v1.y))
#define plus(v1,v2) Type<v3>(v1.x+v2.x,v1.y+v2.y,v1.z+v2.z)
#define dotP(v1,v2) (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)
#define mlt(f,v1) type<v3>(f*v1.x,f*v1.y,f*v1.z) 
    static as v3 result
    dim as V3 V=type(T*(Pt.x-centre.x),T*(Pt.y-centre.y),T*(Pt.z-centre.z))
    dim as V3 T1=crossP(norm,V,Angle.sin)
    dim as single tmpS=dotP(Norm,V)
    dim as V3 tmpV=mlt(tmpS,norm)
    tmpV=mlt((1-Angle.cos),tmpV)
    T1=plus(T1,tmpV)
    dim as V3 tt=mlt(Angle.cos,V) 
    result=plus(tt,T1)
    result=plus(result,centre)
    result.colour=Pt.colour
    Return result
End Function

Function pointRotate(c As V3,p As V3,a As Angle,scale As v3=type(1,1,1)) As V3
    Dim As Single dx=p.x-c.x,dy=p.y-c.y,dz=p.z-c.z
    Return Type<V3>((scale.x)*((a.cy*a.cz)*dx+(-a.cx*a.sz+a.sx*a.sy*a.cz)*dy+(a.sx*a.sz+a.cx*a.sy*a.cz)*dz)+c.x,_
    (scale.y)*((a.cy*a.sz)*dx+(a.cx*a.cz+a.sx*a.sy*a.sz)*dy+(-a.sx*a.cz+a.cx*a.sy*a.sz)*dz)+c.y,_
    (scale.z)*((-a.sy)*dx+(a.sx*a.cy)*dy+(a.cx*a.cy)*dz)+c.z)',p.col)
End Function 

Function perspective(p As V3,eyepoint As V3) As V3
    Dim As Single   w=1+(p.z/eyepoint.z)
    Return type((p.x-eyepoint.x)/w+eyepoint.x,_
    (p.y-eyepoint.y)/w+eyepoint.y,_
    (p.z-eyepoint.z)/w+eyepoint.z)
End Function

Sub QsortZ(array() As V3,begin As Long,Finish As long)
    Dim As Long i=begin,j=finish
    Dim As V3 x =array(((I+J)\2))
    While I <= J
        While array(I).z > X .z:I+=1:Wend
            While array(J).z < X .z:J-=1:Wend
                If I<=J Then Swap array(I),array(J): I+=1:J-=1
            Wend
            If J >begin Then QsortZ(array(),begin,J)
            If I <Finish Then QsortZ(array(),I,Finish)
        End Sub
        
function setPointangle(x as single,y as single,z as single) as angle
     return type(Sin(x),Sin(y),Sin(z),Cos(x),Cos(y),Cos(z))
end function

function setAxialangle(angle as single) as AxialAngle
   return Type(Sin(angle),Cos(angle))
   end function
'=====================================================
'demo procedures and variables
#include "fbgfx.bi"
using fb

 Function Regulate(Byval MyFps As Long,Byref fps As Long) As Long
            Static As Double timervalue,_lastsleeptime,t3,frames
            frames+=1
            If (Timer-t3)>=1 Then t3=Timer:fps=frames:frames=0
            Var sleeptime=_lastsleeptime+((1/myfps)-Timer+timervalue)*1000
            If sleeptime<1 Then sleeptime=1
            _lastsleeptime=sleeptime
            timervalue=Timer
            Return sleeptime
        End Function
        
Function keys(a As v3) As Integer
    If Multikey(SC_LEFT)  Then a.y=a.y+.005:Return 1
    If Multikey(SC_RIGHT) Then a.y=a.y-.005:Return 1
    If Multikey(SC_UP   ) Then a.x=a.x-.005:Return 1
    If Multikey(SC_DOWN ) Then a.x=a.x+.005:Return 1
    If Multikey(SC_Q)     Then a.z=a.z-.005:Return 1
    If Multikey(SC_W)     Then a.z=a.z+.005:Return 1
    If Multikey(SC_SPACE) Then Return 2
    Return 0
End Function

#define range(f,l) int(Rnd*((l+1)-(f))+(f))
#define map(a,b,x,c,d) ((d)-(c))*((x)-(a))/((b)-(a))+(c)

'========== set up points =============
 dim as v3 centre=(400,300,0)
 
redim as v3 a(1 to 10000)
redim as V3 rot()'to hold rotated points and sort
for n as long=lbound(a) to ubound(a)
    with a(n)
        .x=range(300,500)
        .y=range(200,400)
        .z=range(-100,100)
    end with
   
next n
'the axis bit
dim as long count=100
for n as long=ubound(a)-100 to ubound(a)
    a(n)= type(400,count,0)
    count+=1
    next n
   
    'got all the points now
    redim rot(lbound(a) to ubound(a))
    
    redim as v3 ref(lbound(a) to ubound(a))'copy original
    for n as long=lbound(a) to ubound(a)
        ref(n)=a(n)
    next
  '====================  
   dim as v3 Axis=(0,1,0)
   dim as angle z=setpointangle(.6,-1,.5)  'arbitary starter direction
   Axis=pointrotate(type(0,0,0),Axis,z)    'swing the y axis
   Axis=normalize(Axis) 'axis must be a unit vector.
  'swing all the points
    for n as long=lbound(a) to ubound(a)
   var tmp=pointrotate(centre,a(n),z)
     a(n)=tmp
   next n
 
 
screen 19,32
dim as single x
dim as v3 ca '(use the thee fields of a V3 to alter by keys)
dim as long fps
dim as long max,min
do
    max=-1000:min=1000
    x+=.05  'rotate speed
    var Aa=setAxialAngle(x)
    var k=keys(ca)
    select case as const k
    case 1
        var tmp=setpointangle(ca.x,ca.y,ca.z) 'use the three fields
        axis=pointrotate(type(0,0,0),axis,tmp)
        axis=normalize(axis)  'just to be sure
        for n as long=lbound(a) to ubound(a)
            a(n)=pointrotate(centre,a(n),tmp)
        next
        ca=type(0,0,0)
    case 2
        axis=Type(0,1,0)
        for n as long=lbound(a) to ubound(a)
            a(n)=ref(n)
        next
         ca=type(0,0,0)
    end select
    
    var axend=rot(ubound(rot))'the axis tip(the last element is not sorted)
    screenlock
    cls

   'rotate all
    for n as long=lbound(a) to ubound(a)
        rot(n)=AxialRotate(centre,a(n),Aa,Axis)
        rot(n)=perspective(rot(n),type(400,300,600))
        if min>rot(n).z then min=rot(n).z  'for shading
        if max<rot(n).z then max=rot(n).z  '   " 
    next n
    
    qsortz(rot(),lbound(rot),ubound(rot)-1)
    
    for n as long=lbound(a) to ubound(a)-1
        var r=map(min,max,rot(n).z,255,0)  'shade
        var rad=map(min,max,rot(n).z,3,2)  'slight radius phoney perspective
        circle(rot(n).x,rot(n).y),rad,rgb(r,r,r),,,,f
    next n
    
    Draw String(10,10),"FPS " &fps
    Draw String(10,30),"Use Up,Down,Left,Right,q and w to turn the axis"
    Draw String(10,50),"Up/down for x axis, Left/right for y axis, q/w for z axis"
    Draw String(10,70),"Space resets"
    Draw String(10,110),"NOTE - Positive Z is into the screen"
    draw string(300,550),"Axis tip    ("+str(int(Axend.x))+ ","+str(int(Axend.y)) _
           + ","+str(int(Axend.z))+")"
    screenunlock
    sleep regulate(90,fps),1
    loop until inkey=chr(27)
    sleep
   

 
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: 3d without openGL

Post by paul doe »

dodicat wrote:I have all your files paul doe.
The camera test runs well, nice and smooth.
:)

Thanks. It could be smoother still, but that will complicate the main loop a little. I will eventually change it, but for now it's ok. This' supossed to be a toy, anyway. Use it however you like. No credits asked ;)
dodicat wrote:As my last effort here, I have re-done the axial rotate without vector operators.
Similar to Dafhi's code above (which he has removed), a cube of 10000 elements, painted by circle and quicksorted each loop I set at 90 fps and I get 90 fps.
So I would say that Rodrigues method is faster without all those operators.
Yo, looks really nice! You even bothered to add shading, too! The code also looks better, more streamlined. I'm currently in the process of refactoring mine a little, too. As you can surely see, there are many inconsistencies there, mostly because, well, the implementation is a port, and a rushed one at that. In the process, I'm correcting a few bugs here and there (mostly typos), adding a few utilities here and there, and generally decoupling it a little (it is too tightly coupled right now to allow for some rather cool features). I'll post it in his own thread when I'm finished.

Paul
Last edited by paul doe on Oct 13, 2017 15:42, edited 2 times in total.
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: 3d without openGL

Post by paul doe »

BasicCoder2 wrote: Predictable behavior. I don't want to push a key to find the shape turning on more than one of its axes. One axis should remain fixed from the viewers point of view which is the one around which the object is being rotated under user control while the other two axes rotate around it.

Also to be able to move the view around objects. In this old example the viewer moves around confined to a 2D plane with a fixed look ahead to the horizon much as my later raycaster views. The little screen at the top was my attempt at giving the game a 3D view of the world as I had seen in many retro games. Ideally in a 3D world you can move around any way you like and look in any direction.
Everything is predictable in 3D, if you know what you're doing ;)

No, seriously. The paper plane thingy was not chosen randomly (although it does make for a rather nice 3D 'turtle', no?), but to help you conceptualize better what 'rotating about an arbitrary axis' means.

Update your 'camera.bas' -I just added a bugfix. When you're done, we will be adding a little code to the camera test. Go just outside the main loop and declare these two variables:

Code: Select all

dim as string keyP '' holds a keypress
dim as boolean followPlane = false '' to toggle plane following mode
Now, inside the main loop, read a key (with inkey()):

Code: Select all

	'' get a key press
	keyP = lcase( inkey() )
You can put this right below the call to 'getMouse'. And, finally, add these little snippet of code to the main loop (you can put it right under the above code):

Code: Select all

	if( keyP = "0" ) then
		'' toggles plane following mode
		followPlane xor= true
	end if
	
	if( followPlane = true ) then
		/'
			makes the camera follow the plane from behind
			
			this is done by setting the position of the camera to a point (in this
			case, behind and a little up of the position of the paper plane) and
			then 'lookAt' it
		'/
			cam.setPos( o->getPos() - normalize( o->getDir() ) * 1.0 + vec4( 0.0, 0.5, 0.0 ) )
			cam.lookAt( o->getPos(), vec4( 0.0, 1.0, 0.0 ) ) '' use the WORLD'S up axis (the GLOBAL axis)
			'cam.lookAt( o->getPos(), o->getV() ) '' use the PLANE'S up axis (the LOCAL axis)
	end if
Now, if you press '0', the camera will start following the plane from behind. Play with it a little, and you will eventually end up with something like this (and also get pretty confused in the process):
Image
What is happenning here? the key to understand it lies in these two lines:

Code: Select all

cam.lookAt( o->getPos(), vec4( 0.0, 1.0, 0.0 ) ) '' use the WORLD'S up axis (the GLOBAL axis)
'cam.lookAt( o->getPos(), o->getV() ) '' use the PLANE'S up axis (the LOCAL axis)
As you can see, there the camera is looking at the plane using the world vertical axis (the GLOBAL axis) as a reference. Comment that line, and uncomment the other one (so that the camera uses the vertical axis of the plane (the LOCAL axis, in this case, that of the plane). There, much better, right?
You'll also note that the plane gets rolled, even though you didn't rolled it (if plane following mode is activated, the camera gets rolled also, whereas it was not rolled before). This is the effect of rotation about a local axis (what you get if you use Euler angles). Press '0' now, to toggle following mode off. The camera stays rolled, and remains that way (since you changed its axes using the lookAt method before). To correct this, simply press 'space' (to lookAt the plane) when plane following is turned off. See? The horizon is horizontal again.
Look at the code that controls free look:

Code: Select all

	'' if the left mouse button is pressed, activate free look mode
	if( mouseButton = 1 ) then
		'' rotation about the Y axis of the WORLD (aka Yaw)
		cam.rotate( vec4( 0.0, 1.0, 0.0 ), 5.0 * ( oldMouseX - mouseX ) / cam.projectionPlane.width )
		'' rotation about the X axis of the CAMERA (aka Pitch)
		cam.rotate( cam.getU(), -5.0 * ( oldMouseY - mouseY ) / cam.projectionPlane.height )
	end if
As you can see, the yaw rotates the V axis of the camera about the global Y axis of the world, where as the pitch rotates the U axis of the camera itself (its local axis).
In other words:
  • the plane is rotating about its own axes (its LOCAL coordinate system)
    the camera is rotating its vertical axis about the Y axis of the world (which is the GLOBAL coordinate system) and is, thus, rotating about an arbitrary axis. The camera is rotating about an arbitrary axis.
That is why the axes of the camera and object are named 'U', 'V', and 'Dir'. Because they are local axes. Why not name the 'Dir' vector 'W' (as it's more conventional in texts)? Simple: it is used as the 'heading' vector. When you are doing 3D code, orientation can become confusing (I'm sure you can tell), so this is the vector chosen to represent the heading, or the direction the camera or any object is facing. Look at the definition of the paper plane in the code. You'll notice that it is defined around the (0,0,0) point (is what dodicat called the 'fulcrum', in one of his responses; its the origin of the coordinate system) and its tip is defined pointing to the positive Z axis (which becomes the 'Dir' vector of the local axis of the object).
Last edited by paul doe on Oct 20, 2017 2:46, edited 6 times in total.
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: 3d without openGL

Post by paul doe »

The analogy of the paper plane was also chosen because that's how planes really work. They rotate about their own axes when they are in the air (they have no other reference but their own axes). And the camera was made to work just like a person (or a car) would perform its rotation (about the vertical axis of a global coordinate system).

The reason why the coordinate system gets another additional rotation (in the case of the plane, it gets rolled) is simple. If you stick to pure rotations (about one axis at a time), everything looks fine. But as soon as you do rotation about two axes at the same time (press the 'I' and 'J' keys together to see what I mean), the plane gets rolled, as the other two rotations 'drag' the other axis around. You can visualize it if you run the demo and put the fingers of your right hand like this: the index pointing to the screen, the thumb pointing up, and the middle finger normal to them (which will make it go to the left). That's the current coordinate system of the camera (remember, the camera is looking to the positive 'Dir' vector, and thus, the X and Z axis of the world look reversed, because you're seeing them as if you're standing in front of them).

Now, if you rotate your hand around your index or your middle finger you'll see that they stay parallel to the ground (the Y axis hasn't been rotated), so this is a 'pure' rotation. Now, try to rotate with both fingers at the same time. The Y axis (your thumb) must follow suit, or else they wouldn't be perpendicular ('normal' is the term used) anymore. And, if you leave the hand like this, and move your head (to get a relative, or 'local' view of the coordinates) so that your middle finger is parallel to your eyes, you can see that your view has been rolled, even tough you didn't rotate the thumb. If you were able to follow all this, congratulations! You just broke your wrist.

If you have fingers to spare, try holding 'space' while manipulating the paper plane. You can see how the plane contorts while the camera never rolls, as if you were following the plane with your head. That would help understand what I mean. If you need it, shout and I'll try to explain it again in other terms.

Paul
Last edited by paul doe on Oct 20, 2017 2:54, edited 5 times in total.
dafhi
Posts: 1640
Joined: Jun 04, 2005 9:51

Re: 3d without openGL

Post by dafhi »

dodicat wrote:As my last effort here, I have re-done the axial rotate without vector operators.
Similar to Dafhi's code above (which he has removed)
Ad bots think I'm a chick. Thanks for blowing my cover man.

Been a rough / confusing week for me. Thought I'd give paul's code a chance to settle.

@paul doe - I'm still getting a feel for my own coding style as well as integration of deeper 3d concepts. So the grid demo was 'only' 2d .. I suppose some part of me always knew xD
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: 3d without openGL

Post by MrSwiss »

dafhi wrote:Ad bots think I'm a chick. Thanks for blowing my cover man.
Since when, can a piece of software *think* ??? Ad Bot's are just that ...<grin>
Post Reply