## 3d without openGL

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

### Re: 3d without openGL

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: 3157
Joined: Jan 01, 2009 7:03

### Re: 3d without openGL

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: 3157
Joined: Jan 01, 2009 7:03

### Re: 3d without openGL

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
next i

for i as integer = 0 to 9
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: 4947
Joined: Jan 10, 2006 20:30
Location: Scotland

### Re: 3d without openGL

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
Posts: 535
Joined: Jul 25, 2017 17:22
Location: Argentina

### Re: 3d without openGL

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.
.

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

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: 3157
Joined: Jan 01, 2009 7:03

### Re: 3d without openGL

@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
Posts: 535
Joined: Jul 25, 2017 17:22
Location: Argentina

### Re: 3d without openGL

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: 1015
Joined: Jun 04, 2005 9:51

### Re: 3d without openGL

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

### Re: 3d without openGL

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

### Re: 3d without openGL

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
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
Posts: 535
Joined: Jul 25, 2017 17:22
Location: Argentina

### Re: 3d without openGL

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
Posts: 535
Joined: Jul 25, 2017 17:22
Location: Argentina

### Re: 3d without openGL

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):

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
Posts: 535
Joined: Jul 25, 2017 17:22
Location: Argentina

### Re: 3d without openGL

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).

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: 1015
Joined: Jun 04, 2005 9:51

### Re: 3d without openGL

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: 2585
Joined: Jun 02, 2013 9:27
Location: Switzerland

### Re: 3d without openGL

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>