1686 lines
53 KiB
Plaintext
1686 lines
53 KiB
Plaintext
|
|
|||
|
|
|||
|
VECTORS, VECTORS, VECTORS ...
|
|||
|
|
|||
|
|
|||
|
How to code vectordemos - an introduction by Asterix of Movement ...
|
|||
|
---------------------------------------------------------------------
|
|||
|
|
|||
|
This text is an addition to How to code written by Comrade J of SAE.
|
|||
|
It was written by Carl-Henrik Sk}rstedt during his easter holidays.
|
|||
|
Hi ho to all friends of movements....
|
|||
|
Any comments on this text/additions to vectors.txt should be Emailed to
|
|||
|
mnlcst@cs.umu.se, or by mail: Rullstensg6-210,90655Ume},Swe
|
|||
|
If you think I should be clearer on some points, or if you think I've
|
|||
|
totally forgotten something, just report this for later issues..
|
|||
|
|
|||
|
_ _
|
|||
|
/| |\
|
|||
|
/|\ / \
|
|||
|
| / \
|
|||
|
|/_______\
|
|||
|
/
|
|||
|
|
|||
|
|
|||
|
-Contents-
|
|||
|
|
|||
|
1. Preface
|
|||
|
|
|||
|
2. ** Introduction to vectors
|
|||
|
.What is a vector?
|
|||
|
.Three dimensions?
|
|||
|
2.1 Vector operations
|
|||
|
|
|||
|
3. ** Coding techniques
|
|||
|
.How can I present a 3d point on a 2d monitor?
|
|||
|
.A way to represent real numbers with integer numbers
|
|||
|
.How can I use sin and cos in machine language?
|
|||
|
|
|||
|
4. ** Vector rotations
|
|||
|
.Two dimensions
|
|||
|
.Three dimensions
|
|||
|
.Optimizations
|
|||
|
|
|||
|
5. ** Polygons
|
|||
|
.Polygon algorithm
|
|||
|
.Creating objects from polygons
|
|||
|
|
|||
|
6. ** Planes in three dimensions
|
|||
|
.Lightsourcing
|
|||
|
|
|||
|
7. ** Special techniques
|
|||
|
.Different sorting algorithms
|
|||
|
.Vector Balls
|
|||
|
|
|||
|
-APPENDICES-
|
|||
|
|
|||
|
A ** Example sources
|
|||
|
1 Calculate rotation constants (optimized rotation)
|
|||
|
2 An old line-drawing routine for filled vectors
|
|||
|
3 The quicksort in 68000 assembler
|
|||
|
4 The insersort in 68020 assembler
|
|||
|
|
|||
|
B ** Some info...
|
|||
|
1 a little convincing about the polygon elimination formula
|
|||
|
2 how to make a fill-linedraw out of a normal blitter-line routine
|
|||
|
3 an alternate approach to rotations in 3-space by M. Vissers
|
|||
|
4 a little math hint for more accurate vector calculations
|
|||
|
|
|||
|
|
|||
|
|
|||
|
1. Preface
|
|||
|
=============
|
|||
|
|
|||
|
The sources of this text has more or less indirectly been some books
|
|||
|
from my school. Some sources worth mentioning are:
|
|||
|
Elementary Linear Algebra (by Howard Anton, released by John Wiley)
|
|||
|
Calculus - A complete course (By Robert K. Adams)
|
|||
|
The DiscWorld series (by T. Pratchett)
|
|||
|
|
|||
|
By reading this text, you should also be able to know what it
|
|||
|
is you're doing. Not just converting given formulas to 680x0 code,
|
|||
|
but also know what the background of it is. If you know
|
|||
|
the theory behind your routine, you also know how to optimize it!
|
|||
|
NO text will here mention GLENZ-vectors, since they are
|
|||
|
amazingly depressive.
|
|||
|
|
|||
|
This text is meant for democoders on all computers that
|
|||
|
supports a good graphic interface, which is fast enough
|
|||
|
to do normal inconvex objects in one frame (not PC).
|
|||
|
sqr() means sqare root in this text.
|
|||
|
|
|||
|
I'm curious about what support commodore has for this kind
|
|||
|
of programming in their latest OS, it could be a great Idea
|
|||
|
if rotations etc that used many multiplications was
|
|||
|
programmed in ROM. The methods described are used by
|
|||
|
most well-known demo-coders in "vector" demos.
|
|||
|
|
|||
|
The rights of this part stays with the author.
|
|||
|
I've coded Blue House I+2, most of Rebels Megademo II,
|
|||
|
my own fantasic and wonderful cruncher (not released),
|
|||
|
Amed (also not released), some intros, and the rubiks snake
|
|||
|
in Rebels Aars-intro, and the real slideshow from ECES.
|
|||
|
Sorry for most of my demos not working on other machines than
|
|||
|
real A500's, but that's the only computer I've used for
|
|||
|
bugtesting.
|
|||
|
|
|||
|
The meaning of this text is that it shall be a part of
|
|||
|
How To Code.txt and that the same rules works for this
|
|||
|
text as for that.
|
|||
|
The rights of this part stays with the author.
|
|||
|
Sourcecodes should work with most assemblers except for
|
|||
|
Insert sorting, which needs a 68020 assembler.
|
|||
|
|
|||
|
Hi to all my friends who really supported me by comments like:
|
|||
|
"How can you write all that text?"
|
|||
|
"Who will read all that?"
|
|||
|
"Can I have it first, so I can get more bbs-access?"
|
|||
|
"Why are you writing that?"
|
|||
|
"I want to play Zool!" (-My youngest brother)
|
|||
|
"My dog is sick..."
|
|||
|
"You definitely have the right approach to this!"
|
|||
|
"" (-Commodore Sweden)
|
|||
|
(But in swedish of course!)
|
|||
|
|
|||
|
The reason why Terry Pratchetts DiscWorld series is taken
|
|||
|
as a serious source is that he is a great visualizer of
|
|||
|
strange mathematical difficulties. If you ever have
|
|||
|
problems with inspiration, sit back, read and try to imagine
|
|||
|
how demos would look like in the DiscWorld...
|
|||
|
|
|||
|
Now read this text and impress me with a great AGA-demo...
|
|||
|
|
|||
|
(C) MOVEMENT 1993.
|
|||
|
|
|||
|
"Death to the pis" /T. Domar
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
2. Introduction to vectors.
|
|||
|
==============================
|
|||
|
|
|||
|
What is a vector?
|
|||
|
-----------------
|
|||
|
If you have seen demos, you have probably seen effects that is called,
|
|||
|
in a loose term, vectors. They may be balls, filled polygons, lines,
|
|||
|
objects and many other things.
|
|||
|
The thing that is in common of these demos are the vector
|
|||
|
calculations of the positions of the objects. It can be in one, two
|
|||
|
or three Dimensions (or more, but then you can't see the ones above 3)
|
|||
|
|
|||
|
You can for example have a cube. Each corner on the cube
|
|||
|
represent a vector TO the center of rotation.
|
|||
|
All vectors go FROM something TO something, normally we use
|
|||
|
vectors that goes from a point (0,0) to a point (a,b).
|
|||
|
This vector has the quantity of (a,b).
|
|||
|
|
|||
|
Definition of vector:
|
|||
|
A QUANTITY of both VALUE and DIRECTION.
|
|||
|
|
|||
|
or, in a laymans loose terms: a line.
|
|||
|
A line have a length that we can call r, and a direction we can
|
|||
|
call t.
|
|||
|
We can write this vector (r,t) = (length,angle).
|
|||
|
But there is also another way, which is more used
|
|||
|
when dealing with vector objects with given coordinates.
|
|||
|
|
|||
|
The line from (0,0) to (x,y) has the length sqr(x*x+y*y)
|
|||
|
and that is the VALUE of the vector. The direction can be
|
|||
|
seen as the angle between the x-axis and the line described
|
|||
|
by the vector.
|
|||
|
|
|||
|
If we study this in two dimensions, we can have an example vector
|
|||
|
as following:
|
|||
|
|
|||
|
^ y
|
|||
|
| _.(a,b)
|
|||
|
| /|
|
|||
|
| /
|
|||
|
| /
|
|||
|
| / V
|
|||
|
| /
|
|||
|
|/\ - t=angle between x-axis and vector V
|
|||
|
---+------------>
|
|||
|
(0,0) x
|
|||
|
|
|||
|
We can call this vector V, and, as we can see, it goes from
|
|||
|
the point (0,0) and (a,b). We can denote this vector as V=(a,b).
|
|||
|
Now we have both a value of V (The length between (0,0) and (a,b))
|
|||
|
and a direction of it (the angle in the diagram)
|
|||
|
|
|||
|
If we look at the diagram, we can see that the length of the vector
|
|||
|
can be computed with pythagoras theorem, like:
|
|||
|
r=sqr(a*a+b*b)
|
|||
|
|
|||
|
and t is the angle (Can be calculated with t=tan(y/x))
|
|||
|
|
|||
|
|
|||
|
Three Dimensions?
|
|||
|
-----------------
|
|||
|
Now, if we have seen what a vector is in two dimensions, what is
|
|||
|
a vector in three?
|
|||
|
|
|||
|
In three dimensions, every point has three coordinates,
|
|||
|
and so must then the vector have.
|
|||
|
|
|||
|
V=(a,b,c)
|
|||
|
|
|||
|
Now the length of the vector becomes:
|
|||
|
r=sqr(a*a+b*b+c*c)
|
|||
|
|
|||
|
What happens to the angle now?
|
|||
|
|
|||
|
Here we can have different definitions, but let's think a little.
|
|||
|
If we start with giving ONE angle, we can only reach points on one
|
|||
|
PLANE, but we want to get a direction in SPACE.
|
|||
|
|
|||
|
If we try with TWO angles, we will get a better result.
|
|||
|
One angle can represent the angle between the z-axis and the
|
|||
|
vector, the other the rotation AROUND the z-axis.
|
|||
|
|
|||
|
For more problems in this area (there's many) study calculus
|
|||
|
of several variables and specially polar transformations in
|
|||
|
triple integrals, or just surface integrals in vector fields.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
2.1 Vector operations:
|
|||
|
======================
|
|||
|
|
|||
|
(if you have two, or one dimension you have two or one variable instead of
|
|||
|
three. if you have more you have ofcourse as many variables as dimensions)
|
|||
|
|
|||
|
* The SUM of two vectors (U=V+W) are defined as:
|
|||
|
V=(vx,vy,vz), W=(wx,wy,wz)=>
|
|||
|
=> U=(vx+wx,vy+wy,vz+wz)
|
|||
|
|
|||
|
* The negation of a vector U=-V is defined as
|
|||
|
V=(x,y,z) => U=(-x,-y,-z)
|
|||
|
|
|||
|
* The differance between two vectors U=V-W are defined as
|
|||
|
U=V+(-W)
|
|||
|
|
|||
|
* A vector between two points (FROM P1(x1,y1,z1) TO P2(x2,y2,z2))
|
|||
|
can be computed:
|
|||
|
V=(x2-x1,y2-y1,z2-z1,...)
|
|||
|
(V goes FROM P1 TO P2)
|
|||
|
|
|||
|
* A vector can be multiplied by a constant like:
|
|||
|
U=k*V
|
|||
|
(x*k,y*k,z*k)=k*(x,y,z)
|
|||
|
|
|||
|
* A coordinate system can be "Translated" to a new point with the
|
|||
|
translation formula:
|
|||
|
x'=x-k
|
|||
|
y'=y-l
|
|||
|
z'=z-m
|
|||
|
Where (k,l,m) is the OLD point where the NEW coordinate system
|
|||
|
should have its point (0,0,0)
|
|||
|
This is a good operation if you want to ROTATE around A NEW POINT!
|
|||
|
|
|||
|
* A vector can be rotated (Check chapter 4)
|
|||
|
The vector is always rotated around the point (0,0,0) so you
|
|||
|
may have to TRANSLATE it.
|
|||
|
|
|||
|
* We can take scalar product and cross-product of vectors
|
|||
|
(see any book about introduction to linear algebra
|
|||
|
for these. everything is evaluated in this text, so you
|
|||
|
don't have to know what this is)
|
|||
|
|
|||
|
|
|||
|
3. Coding techniques
|
|||
|
====================
|
|||
|
|
|||
|
Presenting a three dimensional point on a two dimensinal screen
|
|||
|
---------------------------------------------------------------
|
|||
|
Assume that you have a point in space (3d) that you want to
|
|||
|
take a photo of. A photo is 2d, so this should give us
|
|||
|
some sort of an answer.
|
|||
|
|
|||
|
Look at the picture below:
|
|||
|
|
|||
|
Point
|
|||
|
/ Screen (="photo")
|
|||
|
. |/
|
|||
|
\ | ^y
|
|||
|
\| |
|
|||
|
|\|
|
|||
|
<---+-x <- Eye of observer
|
|||
|
z |
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|||
|
Inspecting this gives us the following formula:
|
|||
|
|
|||
|
Projected Y = Distance of screen * Old Y / ( Distance of point )
|
|||
|
(The distances is of course the Z coordinates from the Eyes position)
|
|||
|
|
|||
|
And a similar way of thinking gives us the projection of X.
|
|||
|
|
|||
|
|
|||
|
New Y=k*y/(z+dist)
|
|||
|
X=k*x/(z+dist)
|
|||
|
|
|||
|
(where k is a constant for this screen, dist is the
|
|||
|
distance from the ROTATION point to the EYE on the
|
|||
|
Z-axis)
|
|||
|
|
|||
|
|
|||
|
A way of presenting real numbers with Integers
|
|||
|
----------------------------------------------
|
|||
|
|
|||
|
Until now we have only seen a lot of formulas, but how can we
|
|||
|
use them in Assembler where we only can have bytes/words/longwords?
|
|||
|
(If you don't have a FPU, and only want people with FPU's to be
|
|||
|
able to see your demos)
|
|||
|
|
|||
|
For 68000 coding (compatible with all better processors)
|
|||
|
it is comfortable to be able to do multiplictations etc.
|
|||
|
with words (680x0,{x>=2} can do it with longwords, but this won't
|
|||
|
work very good with lower x's).
|
|||
|
|
|||
|
But we need the fract parts of the numbers too, so how do we do?
|
|||
|
We can try to use numbers that are multiplied by a constant p.
|
|||
|
Then we can do the following operation:
|
|||
|
|
|||
|
[cos(a)*p] * 75 (for example from a list with cos(x) mult. with p)
|
|||
|
|
|||
|
But as you can see this number grows for each time we do another
|
|||
|
multiplication, so what we have to do is to divide by p again:
|
|||
|
|
|||
|
[cos(a)*p] * 75 / p
|
|||
|
|
|||
|
If you are a knower of digital electronics, you say "Oh no,
|
|||
|
not a division that takes so long time". But if you choose
|
|||
|
p carefully (i.e. p = 2 or 4 or 8 ...) you can use
|
|||
|
shifting instead of clean division. Look at this example:
|
|||
|
|
|||
|
mulu 10(a0),d0 ;10(a0) is from a list of cos*256 values
|
|||
|
asr.l #8,d0 ;and we "divide" by 256!
|
|||
|
|
|||
|
Now we have done a multiplication of a fixed point number!
|
|||
|
(A hint to get the error a little smaller:
|
|||
|
clear a Dx-register and use an addx after the asr,
|
|||
|
and you will get a round-off error instead:
|
|||
|
|
|||
|
moveq #0,d7
|
|||
|
:
|
|||
|
:
|
|||
|
mulu 10(a0),d0
|
|||
|
asr.l #8,d0
|
|||
|
addx.l d7,d0
|
|||
|
:
|
|||
|
rts
|
|||
|
This halves the error!)
|
|||
|
|
|||
|
The same thinking comes in with divisions, but in the other way:
|
|||
|
|
|||
|
:
|
|||
|
ext.l d0
|
|||
|
ext.l d1
|
|||
|
asl.l #8,d0 ;"Multiply" by 256
|
|||
|
divs d1,d0 ;and divide by z*256 ...
|
|||
|
:
|
|||
|
rts
|
|||
|
|
|||
|
Additions and subtractions are the same as normal integer
|
|||
|
operations: (no shifting needed)
|
|||
|
|
|||
|
:
|
|||
|
add.w 10(a0),d0
|
|||
|
:
|
|||
|
|
|||
|
:
|
|||
|
sub.w 16(a1),d1
|
|||
|
:
|
|||
|
|
|||
|
|
|||
|
So, With multiplications you MUL first, then LSR.
|
|||
|
With divisions you LSL first, then DIV.
|
|||
|
|
|||
|
If you wish to have higher accuracy with the multiplications,
|
|||
|
the 68020 and higher processors offers a cheap way to do
|
|||
|
floating point operations (32-bit total) instead.
|
|||
|
You can also do integer multiplications 32*32->32,
|
|||
|
and use 16-bit coses and sins instead, which enables
|
|||
|
you to use 'swap' instead of 'lsr'.
|
|||
|
|
|||
|
How can I use Sin and Cos in my assembler code?
|
|||
|
-----------------------------------------------
|
|||
|
The easiest and fastest way is to include a sinus-list
|
|||
|
in you program. Make a basic-program that
|
|||
|
counts from 0 to 2*pi, for example 1024 times.
|
|||
|
Save the values and include them into your code.
|
|||
|
|
|||
|
If you have words and 1024 different sinus values
|
|||
|
then you can get sinus and cosinus this way:
|
|||
|
|
|||
|
lea sinuslist(pc),a0 ;sinuslist is calculated list
|
|||
|
and.w #$7fe,d0 ;d0 is angle
|
|||
|
move.w (a0,d0.w),d1 ;d1=sin(d0)
|
|||
|
add.w #$200,d0
|
|||
|
and.w #$7fe,d0
|
|||
|
move.w (a0,d0.w),d0 ;d0=cos(original d0)
|
|||
|
:
|
|||
|
:
|
|||
|
|
|||
|
Your program could look like this: (AmigaBasic, HiSoft basic)
|
|||
|
(NEVER use AmigaBasic on other processors than 68000)
|
|||
|
|
|||
|
open "ram:sinuslist.s" for output as 1
|
|||
|
pi=3.141592654#
|
|||
|
vals=1024
|
|||
|
nu=0
|
|||
|
pretxt=chr$(10)+chr$(9)+chr$(9)+'dc.w'+chr$(9)
|
|||
|
for L=0 to vals
|
|||
|
angle=L/vals*2*pi
|
|||
|
y='$'+hex$(int(sin(angle)*255.4))
|
|||
|
nu=nu+1
|
|||
|
if nu=8 then print #1,pretxt;:nu=0 else print #1,',';
|
|||
|
print #1,y$;
|
|||
|
next L
|
|||
|
close 1
|
|||
|
|
|||
|
You can of course do a program that calculates the
|
|||
|
sins in assembler code, by using ieee-libs or
|
|||
|
coding your own floating point routines.
|
|||
|
the relevant algoritm is... (for sinus)
|
|||
|
|
|||
|
indata: v=angle (given in radians)
|
|||
|
Laps=number of terms (less=faster and more error, integer)
|
|||
|
|
|||
|
1> Mlop=1
|
|||
|
DFac=1
|
|||
|
Ang2=angle*angle
|
|||
|
Talj=angle
|
|||
|
sign=1
|
|||
|
Result=0
|
|||
|
2> FOR terms=1 TO Laps
|
|||
|
2.1> Temp=Talj/Dfac
|
|||
|
2.2> Result=sign*(Result+Temp)
|
|||
|
2.3> Talj=Talj*Ang2
|
|||
|
2.4> Mlop=Mlop+1
|
|||
|
2.5> Dfac=Dfac*Mlop
|
|||
|
2.6> sign=-sign
|
|||
|
3> RETURN sin()=Result
|
|||
|
|
|||
|
where the returned sin() is between -1 and 1...
|
|||
|
The algorithm uses MacLaurin polynoms, and are therefore
|
|||
|
recommended only for values that are not very far away from 0.
|
|||
|
|
|||
|
|
|||
|
4. The rotation of vectors.
|
|||
|
===========================
|
|||
|
|
|||
|
* In two dimensions
|
|||
|
|
|||
|
Now we know what a vector is, and we want to rotate it.
|
|||
|
This is very simple, if we have a vector given
|
|||
|
with lenght and angle, we just add the rotation-angle to the
|
|||
|
angle and let the length be like it is:
|
|||
|
rotate V=(r,t) with a -> V'=(r,t+a)
|
|||
|
|
|||
|
But normally we don't have this simple case, we have a
|
|||
|
vector given by two coordinates like:
|
|||
|
V=(x,y) where x and y are coordinates in the xy-plane.
|
|||
|
|
|||
|
In THIS text we denote the rotation of a vector V=(r,t) with
|
|||
|
rot(V,a). With this I mean the rotation of the vector V with the
|
|||
|
angle a.
|
|||
|
|
|||
|
The rotation of this vector could have been done
|
|||
|
by transforming V to a vector of a length and a direction,
|
|||
|
but since this involves taking squares, tangens, squareroots
|
|||
|
etc., we would like a faster method.
|
|||
|
Here is where trigonometry comes in.
|
|||
|
|
|||
|
But let us first assume that we have a vector V=(x,0)
|
|||
|
what would be the rotation of this vector?
|
|||
|
|
|||
|
V
|
|||
|
----------->
|
|||
|
|
|||
|
Now, let us rotate with an angle of a:
|
|||
|
_
|
|||
|
/|\y' /|
|
|||
|
| /
|
|||
|
|V'/
|
|||
|
| /
|
|||
|
|/\a x'
|
|||
|
----->
|
|||
|
|
|||
|
What is the new vectors composants (x',y') ?
|
|||
|
|
|||
|
Remember these "definitions":
|
|||
|
|
|||
|
Cosine:
|
|||
|
Hypotenuse/side close to angle
|
|||
|
|
|||
|
Sine:
|
|||
|
Hypotenuse/side not close to angle
|
|||
|
,
|
|||
|
/|
|
|||
|
Length>/ |< Length * sin(a)
|
|||
|
/a |
|
|||
|
'---+
|
|||
|
Length * cos(a)
|
|||
|
|
|||
|
If we put in this in the original rotation formula (V'=rot(V,a)=V(r,t+a))
|
|||
|
we can see that we can convert r and t to x and y with:
|
|||
|
|
|||
|
x=r*cos(t)
|
|||
|
y=r*sin(t)
|
|||
|
|
|||
|
Let's get back to our problem of the rotated vector V=(x,0).
|
|||
|
Here is r=x (=sqrt(x*x+0*0)), t=0 (=arctan(0/x)
|
|||
|
if we put this in our formula we get:
|
|||
|
|
|||
|
V=(r,t) if r=x, t=0
|
|||
|
|
|||
|
If we rotate this vector with the angle a we get:
|
|||
|
|
|||
|
V=(r,t+a)
|
|||
|
|
|||
|
And if we translate back to our coordinate denotion:
|
|||
|
|
|||
|
V=(r*cos(t+a),r*sin(t+a))=(x*cos(a),x*sin(a))
|
|||
|
^We insert x=r, t=0
|
|||
|
|
|||
|
And that is the formula for rotation of a vector that has no y-composant.
|
|||
|
For a vector V=(0,y) we get:
|
|||
|
r=y, t=pi/2 (=90 degrees) since we now are in the y-axis,
|
|||
|
which is 90 degrees from the x-axis.
|
|||
|
|
|||
|
V=(r,t) => V'=(r,t+a) => V'=(r*cos(t+a),r*sin(t+a)) =>
|
|||
|
V'=(y*cos(pi/2+a),y*sin(pi/2+a))
|
|||
|
|
|||
|
Now, there are a few trigonometric formulas that says that cos(pi/2+a)=
|
|||
|
=sin(a) and sin(pi/2+a)=-cos(a)
|
|||
|
|
|||
|
We get:
|
|||
|
V'=( y * sin(a) , y * ( -cos(a) ) )
|
|||
|
|
|||
|
But if we look in the general case, we have a vector V that
|
|||
|
has both x and y composants. Now we can use the single-cases
|
|||
|
rotation formulas for calculating the general case with
|
|||
|
an addition:
|
|||
|
|
|||
|
Vx'=rot((x,0),a) = (x*cos(a) ,x*sin(a))
|
|||
|
+ Vy'=rot((0,y),a) = ( +y*sin(a), -y*cos(a))
|
|||
|
----------------------------------------------------------
|
|||
|
V' =rot((x,y),a) = (x*cos(a)+y*sin(a),x*sin(a)-y*cos(a))
|
|||
|
|
|||
|
(Vx' means rotation of V=(x,0) and Vy' is rotation of V=(0,y))
|
|||
|
And we have the rotation of a vector given in coordinates!
|
|||
|
|
|||
|
FINAL FORMULA OF ROTATION IN TWO DIMENSIONS
|
|||
|
.. .
|
|||
|
. rot( (x,y), a)=( x*cos(a)+y*sin(a) , x*sin(a)-y*cos(a) )
|
|||
|
x-composant ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ y-composant
|
|||
|
|
|||
|
|
|||
|
* Three dimensions
|
|||
|
|
|||
|
Now we are getting somewhere!
|
|||
|
|
|||
|
In the 2 dimensions case, we rotated x and y coordinates,
|
|||
|
and we didn't see any z coordinates that changed.
|
|||
|
Therefore we call this a rotation around the Z axis.
|
|||
|
|
|||
|
Now, the simpliest thing to do in three dimensions is to
|
|||
|
still do the same thing, just rotate around any axis
|
|||
|
to get the new coordinate. Leave out the variable that
|
|||
|
represents the coordinate of the current rotation-axis,
|
|||
|
and you can use the very same expression.
|
|||
|
|
|||
|
If you want to rotate only one or two coordinates, you can use
|
|||
|
the normal method of rotation, because then you
|
|||
|
won't have to calculate a 3x3 transformation matrix.
|
|||
|
But if you have more points, I recommend the
|
|||
|
optimized version.
|
|||
|
|
|||
|
But there are optimizations in this field, but let's first
|
|||
|
look at ONE way to do this:
|
|||
|
|
|||
|
NORMAL METHOD OF ROTATING A VECTOR WITH THREE GIVEN ANGLES IN 3D:
|
|||
|
|
|||
|
Assume we want to rotate V=(x,y,z) around the z-axis
|
|||
|
with the angle a, around y with b and around x with c.
|
|||
|
The first rotation we do is around the Z-axis:
|
|||
|
U=(x,y) (x,y from V-vector) =>
|
|||
|
=> U'=rot(U,a)=rot((x,y),a)=(x',y')
|
|||
|
|
|||
|
Then we want to rotate around the Y-axis:
|
|||
|
W=(x',z) (x' is from U' and z is from V) =>
|
|||
|
=> W'=rot(W,b)=rot((x',z),b)=(x'',z')
|
|||
|
|
|||
|
And finally around the X-axis:
|
|||
|
T=(y',z') (y' is from U' and z' is from W') =>
|
|||
|
=> T'=rot(T,c)=rot((y',z'),c)=(y'',z'')
|
|||
|
|
|||
|
The rotated vector V' is the coordinate vector
|
|||
|
(x'',y'',z'') !
|
|||
|
|
|||
|
With this method we can extend out rot-command to:
|
|||
|
|
|||
|
V''= rot(V,angle1,angle2,angle3) where V is the original vector!
|
|||
|
( V''= rot((x,y,z),angle1,angle2,angle3) )
|
|||
|
|
|||
|
|
|||
|
I hope that didn't look too complicated.
|
|||
|
As I said, there are optimizations of this method.
|
|||
|
These optimizations can be skipping one rotation of the
|
|||
|
above ones, or some precalculations.
|
|||
|
|
|||
|
ORDER is very important. You won't get the same answer
|
|||
|
if you rotate X,Y,Z with the same angles as before.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Optimizations:
|
|||
|
==============
|
|||
|
For xyz vectors we can write the equations to
|
|||
|
form the rotations:
|
|||
|
|
|||
|
let c1=cos(angle1), c2=cos(angle2), c3=cos(angle3),
|
|||
|
s1=sin(angle1), s2=sin(angle2), s3=sin(angle3)
|
|||
|
|
|||
|
(x*cos(a)+y*sin(a),x*sin(a)-y*cos(a))
|
|||
|
|
|||
|
x' = x*c1+y*s1
|
|||
|
y' = x*s1-y*c1
|
|||
|
|
|||
|
x''= x'*c2+z*s2 <- Rotated x-coordinate
|
|||
|
z' = x'*s2-z*c2
|
|||
|
|
|||
|
y''= y'*c3+z'*s3 <- Rotated y-coordinate
|
|||
|
z''= y'*s3-z'*c3 <- Rotated z-coordinate
|
|||
|
|
|||
|
which gives:
|
|||
|
|
|||
|
x''= (x*c1+y*s1)*c2+z*s2= c2*c1 *x + c2*s1 *y + s2 *z
|
|||
|
^^^^^^^^^^^=x' ^^^^^ xx ^^^^^ xy ^^ xz
|
|||
|
|
|||
|
y''= (x*s1-y*c1)*c3+((x*c1+y*s1)*s2-z*c2)*s3=
|
|||
|
c3*s1 *x - c3*c1 *y + s3*s2*c1 *x + s3*s2*s1 *y - s3*c2 *z=
|
|||
|
|
|||
|
(s3*s2*c1+c3*s1) *x + (s3*s2*s1-c3*c1) *y + (-s3*c2) *z
|
|||
|
^^^^^^^^^^^^^^^^ yx ^^^^^^^^^^^^^^^^ yy ^^^^^^^^ yz
|
|||
|
|
|||
|
z''= (x*s1-y*c1)*s3-((x*c1+y*s1)*s2-z*c2)*c3=
|
|||
|
s3*s1 *x - s3*c1 *y - c3*s2*c1 *x - c3*s2*s1 *y + c3*c2 *z=
|
|||
|
|
|||
|
(-c3*s2*c1+s3*s1) *x + (-c3*s2*s1-c3*c1) *y + (c3*c2) *z
|
|||
|
^^^^^^^^^^^^^^^^^ zx ^^^^^^^^^^^^^^^^^ zy ^^^^^^^ zz
|
|||
|
|
|||
|
|
|||
|
Now, look at the pattern of the solutions,
|
|||
|
for x'' we have calculated something times the original (x,y,z),
|
|||
|
the same for y'' and z'', What is the connection?
|
|||
|
|
|||
|
Say that you rotate many given vectors with three angles that are the
|
|||
|
same for all vectors, then you get this scheme of multiplications.
|
|||
|
When you rotated as above you had to use twelve multiplications
|
|||
|
to do one rotation, but now we precalculate these 'constants'
|
|||
|
and manage to get down to nine multiplications!
|
|||
|
^^^^
|
|||
|
FINAL FORMULA FOR ROTATIONS IN THREE DIMENSION WITH THREE ANGLES
|
|||
|
(x,y,z is the original (x,y,z) coordinate.
|
|||
|
c1=cos(angle1), s1=sin(angle1), c2=cos(angle2) and so on...)
|
|||
|
|
|||
|
If you want to rotate a lot of coordinates with the same angles you
|
|||
|
first calculate these values:
|
|||
|
xx=c2*c1
|
|||
|
xy=c2*s1
|
|||
|
xz=s2
|
|||
|
yx=c3*s1+s3*s2*c1
|
|||
|
yy=-c3*c1+s3*s2*s1
|
|||
|
yz=-s3*c2
|
|||
|
zx=s3*s1-c3*s2*c1;s2*c1+c3*s1
|
|||
|
zy=-s3*c1-c3*s2*s1;c3*c1-s2*s1
|
|||
|
zz=c3*c2
|
|||
|
|
|||
|
Then, for each coordinate, you use the following multiplication
|
|||
|
to get the rotated coordinates:
|
|||
|
x''=xx * x + xy * y + xz * z
|
|||
|
y''=yx * x + yy * y + yz * z
|
|||
|
z''=zx * x + zy * y + zz * z
|
|||
|
|
|||
|
So, you only have to calculate the constants once for every new angle,
|
|||
|
and THEN you use nine multiplications for every point you wish to
|
|||
|
rotate to get the new set of points.
|
|||
|
|
|||
|
Look in the end of this text for an example of how this can be
|
|||
|
implemented in 68000-assembler.
|
|||
|
|
|||
|
If you wish to skip on angle, you can optimize further.
|
|||
|
if you want to remove angle3, set c3=1 and all s3=0
|
|||
|
and put into your constant-calculation and it will be
|
|||
|
optimized for you.
|
|||
|
|
|||
|
What method you want to use depends of course on how much you want to
|
|||
|
code, but I prefer the optimized version since it's more
|
|||
|
to be proud of... If you only rotate a few points with the same
|
|||
|
angles, the first (non-optimized) version might be the choice.
|
|||
|
|
|||
|
If you want to, you can check that the transformation matrix has
|
|||
|
a determinant equal to 1.
|
|||
|
|
|||
|
|
|||
|
5. Polygons!
|
|||
|
============
|
|||
|
|
|||
|
The word "polygon" means many corners, which means that it
|
|||
|
has a lot of points (corners) with lines drawn to.
|
|||
|
If you have, for example, 5 points, you can draw
|
|||
|
lines from point 1 to point 2, from point 2 to point 3,
|
|||
|
from point 3 to point 4 and from point 4 to point 5.
|
|||
|
If you want a CLOSED polygon you also draw a line from
|
|||
|
point 5 to point 1.
|
|||
|
|
|||
|
Points:2
|
|||
|
.
|
|||
|
|
|||
|
.3
|
|||
|
1
|
|||
|
.
|
|||
|
5..4
|
|||
|
|
|||
|
Open polygon of points above:
|
|||
|
|
|||
|
/|
|
|||
|
/ |
|
|||
|
/ /
|
|||
|
/ /
|
|||
|
_/
|
|||
|
|
|||
|
Closed polygon of points above:
|
|||
|
|
|||
|
/|
|
|||
|
/ |
|
|||
|
/ /
|
|||
|
/ /
|
|||
|
\_/
|
|||
|
|
|||
|
|
|||
|
"Filled vectors" is created by drawing polygons, and filling inside.
|
|||
|
Normally the following algorithm is used:
|
|||
|
|
|||
|
First you define all "corners" on the polygon as vectors,
|
|||
|
which allows you to rotate it and draw it in new angles,
|
|||
|
and then you draw one line from point 1 to point 2, and so on.
|
|||
|
The last line is from point 5 to point 1.
|
|||
|
When you're finished you use a BLITTER-FILL operation to fill the
|
|||
|
area.
|
|||
|
|
|||
|
You will need a special line drawing routine for drawing these lines
|
|||
|
so the BLITTER-FILL works, I have an example of a working line-drawing
|
|||
|
routine in the appendices (K-seka! Just for CJ!).
|
|||
|
Further theory about what demands there are on the line drawing
|
|||
|
routine will be discussed later (App. B 2).
|
|||
|
|
|||
|
There are also other ways to get a filled area (mostly for computers
|
|||
|
without blitter, or for special purposes on those that have)
|
|||
|
Information about that will be in later issues.
|
|||
|
|
|||
|
|
|||
|
Creating objects from polygons
|
|||
|
===============================
|
|||
|
|
|||
|
An object is in this text a three-dimensional thing created
|
|||
|
with polygons. We don't have to think about
|
|||
|
what's inside, we just surround a mathematically defined
|
|||
|
sub-room with polygons.
|
|||
|
|
|||
|
But what happends to surfaces that is on the other side of the object?
|
|||
|
and if there are hidden "parts" of the object, what can we do about them?
|
|||
|
|
|||
|
We begin with a cube, it is easy to imagine, and also the rotation of it.
|
|||
|
we can see that no part of the cube is over another part of
|
|||
|
the cube in the viewers eyes. (compare, for example, with a torus, where
|
|||
|
there are sometimes parts that hides other parts of the same object)
|
|||
|
Some areas are of course AIMING AWAY FROM THE VIEWER, but we can
|
|||
|
calculate in what direction the polygon is facing (to or from the viewer)
|
|||
|
|
|||
|
Always define the polygons in objects in the same direction
|
|||
|
(clockwise or non-clockwise) in all of the object. imagine that
|
|||
|
you stand on the OUTSIDE MIDDLE of the plane, and pick all points
|
|||
|
in a clockwise order. Which one you start with has nothing to
|
|||
|
do with it, just the order of them.
|
|||
|
|
|||
|
Pick three points from a plane (point1, point2 and point 3)
|
|||
|
If all three points are not equal to any of the other points,
|
|||
|
these points define a plane.
|
|||
|
You will then only need three points to define the direction
|
|||
|
of the plane. Examine the following calculation:
|
|||
|
|
|||
|
c=(x3-x1)*(y2-y1)-(x2-x1)*(y3-y1)
|
|||
|
(This is AFTER 3d->2d projection, so there's no z-coordinate.
|
|||
|
If you want to know what this does, look in appendix b)
|
|||
|
|
|||
|
This needs three points, which is the minimum number of coordinates
|
|||
|
a polygon must be, to not be a line or a point (THINK).
|
|||
|
This involves two multiplications per plane, but that isn't
|
|||
|
very much compared to rotation and 3d->2d projection.
|
|||
|
|
|||
|
But let us study what this equation gives:
|
|||
|
If c is negative, the normal vector of the plane which the three points
|
|||
|
span is heading INTO the viewer ( = The plane is fronting the
|
|||
|
viewer => plane should be drawed )...
|
|||
|
If c is POSITIVE, the normal vector of the plane is heading
|
|||
|
AWAY from the viewer ( = The plane cannot be seen by the viewer =>
|
|||
|
DON'T draw the plane) ...
|
|||
|
|
|||
|
But to question 2, what happends if parts of the object
|
|||
|
covers OTHER parts of the object...
|
|||
|
|
|||
|
Convex and INCONVEX objects
|
|||
|
===========================
|
|||
|
|
|||
|
"Definitions"
|
|||
|
A convex object has NO parts that covers other parts of the
|
|||
|
same object, viewed from all angles.
|
|||
|
|
|||
|
An inconvex object has parts that covers other parts of the
|
|||
|
same object, viewed from some angle.
|
|||
|
--
|
|||
|
|
|||
|
For convex objects, this means that you can draw a straigt
|
|||
|
line from every point inside the object to every other point
|
|||
|
in the object without having no line that passes outside
|
|||
|
the domain of the object.
|
|||
|
|
|||
|
If you have a CONVEX object, you can draw ALL lines around
|
|||
|
the visible planes and then fill with the blitter, because
|
|||
|
no drawn polygon will ever cover another polygon.
|
|||
|
With some struggle you can also find ways to omit some lines,
|
|||
|
since they will be drawn twice.
|
|||
|
|
|||
|
INCONVEX objects offers some further problems,
|
|||
|
the easiest way to use INCONVEX objects is
|
|||
|
to split them into smaller CONVEX objects. This works for
|
|||
|
all objects, even if you can have some problem doing it.
|
|||
|
|
|||
|
Of course, you can skip a lot of planes that will be "inside"
|
|||
|
the Inconvex object.
|
|||
|
|
|||
|
When you have splitted the object you simply draw
|
|||
|
each convex object into a TEMPORARY memory area,
|
|||
|
and treat it like a VECTORBALL (Sorting and stuff),
|
|||
|
Which should be discussed in later parts of this text.
|
|||
|
|
|||
|
The Z coordinate can be taken from the middle of all z-values
|
|||
|
in the object (The sum of all Z's in the object divided by
|
|||
|
the number of coordinates)
|
|||
|
|
|||
|
When you're sorting the objects, you can sometimes have problems
|
|||
|
with parts of the inconvex object getting in the wrong order
|
|||
|
since you've picked a point at random from the OUTSIDE of the
|
|||
|
convex object, which the current object is sharing with
|
|||
|
another convex object. One way to solve this problem
|
|||
|
is to take a middle point that is inside the convex object,
|
|||
|
by adding all the Z-values around the object and dividing
|
|||
|
by the number of coordinates that you have added.
|
|||
|
In this case, you should take points from at least two planes in
|
|||
|
the object.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Object optimization
|
|||
|
====================
|
|||
|
|
|||
|
Assume that you have a CONVEX object.
|
|||
|
If it is closed, you have almost as few points as you have
|
|||
|
planes. If you have a list to every coordinate
|
|||
|
that exist (no points are the same in this list) that
|
|||
|
for each polygon shows what point you should fetch for
|
|||
|
this coordinate, you can cut widely on the number of
|
|||
|
ROTATIONS.
|
|||
|
For example:
|
|||
|
|
|||
|
/* A cube */
|
|||
|
/* order is important! Here is clockwise */
|
|||
|
|
|||
|
end_of_plane=0
|
|||
|
|
|||
|
pointlist
|
|||
|
dc.l pt4,pt3,pt2,pt1,end_of_plane
|
|||
|
dc.l pt5,pt6,pt2,pt1,end_of_plane
|
|||
|
dc.l pt6,pt7,pt3,pt2,end_of_plane
|
|||
|
dc.l pt7,pt8,pt4,pt3,end_of_plane
|
|||
|
dc.l pt8,pt5,pt1,pt4,end_of_plane
|
|||
|
dc.l pt5,pt6,pt7,pt8,end_of_plane
|
|||
|
pt1 dc.w -1,-1,-1
|
|||
|
pt2 dc.w 1,-1,-1
|
|||
|
pt3 dc.w 1,-1,1
|
|||
|
pt4 dc.w -1,-1,1
|
|||
|
pt5 dc.w -1,1,-1
|
|||
|
pt6 dc.w 1,1,-1
|
|||
|
pt7 dc.w 1,1,1
|
|||
|
pt8 dc.w -1,1,1
|
|||
|
|
|||
|
Now, you only have to rotate the points pt1-pt8, which is eight points.
|
|||
|
If you had computed four points for each plane, you would have to
|
|||
|
compute 24 rotations instead.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
6. Planes in three dimensions
|
|||
|
=============================
|
|||
|
|
|||
|
Lightsourcing
|
|||
|
=============
|
|||
|
|
|||
|
Lightsourcing is a way to find out how much light a
|
|||
|
plane recieves from either a point of light (spherical)
|
|||
|
or a plane of light (planar). If the colour of the plane
|
|||
|
represents the light that falls on it, the object will
|
|||
|
be a bit more realistic.
|
|||
|
|
|||
|
What we are interested in is the ANGLE of the VECTOR from the
|
|||
|
NORMAL of the plane to the LIGHTSOURCE (=point of light)
|
|||
|
(this is for a spherical lightsource, like a lamp or
|
|||
|
something. If you are interested in planar light, like
|
|||
|
from the sun, you are interested in the ANGLE between the
|
|||
|
NORMAL of the plane and the LIGHTSOURCE VECTOR)
|
|||
|
|
|||
|
We are interested of the COSINE of the given angle.
|
|||
|
|
|||
|
Anyway, to get the normal of the plane you can pick three
|
|||
|
points in the polygon, create two vectors of these.
|
|||
|
Example:
|
|||
|
* we pick (x1,y1,z1) and (x2,y2,z2) and (x3,y3,z3)
|
|||
|
we create two vectors V1 and V2:
|
|||
|
V1=(x2-x1,y2-y1,z2-z1)
|
|||
|
V2=(x3-x1,y3-y1,z3-z1)
|
|||
|
|
|||
|
To get the normal of these we take the cross product of them:
|
|||
|
|
|||
|
| i j k |
|
|||
|
N = V1xV2 = |x2-x1 y2-y1 z2-z1| =
|
|||
|
|x3-x1 y3-y1 z3-z1|
|
|||
|
|
|||
|
n1 n2
|
|||
|
* = ((y2-y1)*(z3-z1)-(y3-y1)*(z2-z1),-((x2-x1)*(z3-z1)-(x3-x1)*(z2-z1)),
|
|||
|
* ,(x2-x1)*(y3-y1)-(x3-x1)*(y2-y1))
|
|||
|
n3
|
|||
|
|
|||
|
Now, we have N. We also have the LIGHTSOURCE coordinates (given)
|
|||
|
|
|||
|
To get COS of the ANGLE between two vectors we can use the scalar
|
|||
|
product between N and L (=lightsource vector) divided
|
|||
|
by the length of N and L:
|
|||
|
|
|||
|
<N,L>/(||N||*||L||) =
|
|||
|
|
|||
|
* (n1*l1+n2*l2+n3*l3)/(sqr(n1*n1+n2*n2+n3*n3)*sqr(l1*l1+l2*l2+l3*l3))
|
|||
|
|
|
|||
|
* (can be (n1*l1+n2*l2+n3*l3)/k if k is a precalculated constant)
|
|||
|
|
|||
|
This number is between -1 and 1 and is cos of the angle between
|
|||
|
the vectors L and N. the SQUARE ROOTS take much time, but
|
|||
|
if you keep the object intact (do only rotations/translatins etc.)
|
|||
|
and always pick the same points in the object,
|
|||
|
then ||N|| is intact and can be precalculated.
|
|||
|
|
|||
|
If you make sure the length of L is always 1, you won't have
|
|||
|
to devide by this, which saves many cycles.
|
|||
|
|
|||
|
The number will, as said, be between -1 and 1. You may have
|
|||
|
to multiply the number with something before dividing
|
|||
|
so that you have a larger range to pick colours from.
|
|||
|
If the number is negative, set it to zero.
|
|||
|
|
|||
|
The number can be NEGATIVE when it should be POSITIVE,
|
|||
|
this is because you took the points in the wrong order,
|
|||
|
but you only have to negate the result instead.
|
|||
|
|
|||
|
If you didn't understand a thing of this, look on the formulas
|
|||
|
with a '*' in the border. n1 means the x-coordinate of N, n2
|
|||
|
the y-coordinate and so on, and the same thing with L.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
7. Special techniques
|
|||
|
======================
|
|||
|
|
|||
|
Sorting Algorithms
|
|||
|
==================
|
|||
|
|
|||
|
When you come to sorting, most books begin with "Bubble-sorting"
|
|||
|
Bubble sorting is enourmously slow, and is described here only
|
|||
|
for explaining terms. But I don't advise you to code routines
|
|||
|
that uses this method since it's SLOOOOOOOOOOOOW....
|
|||
|
A better way is to use Insert Sorting (which, in fact, is sorting,
|
|||
|
Acro!) or Quick Sorting or whatever you can find in
|
|||
|
old books (you have them, I'm sure!) about basic/pascal/c/
|
|||
|
or whatever (and even a few assembler books!!!) contains
|
|||
|
different methods of sorting. Just make sure you don't use
|
|||
|
BUBBLE SORTING!!!!!
|
|||
|
|
|||
|
|
|||
|
Method 1) Bubble sorting
|
|||
|
-------------------------
|
|||
|
|
|||
|
Assume that you have a list of VALUES and WEIGHTS.
|
|||
|
The heaviest weights must fall to the bottom, and bringing the
|
|||
|
VALUES with it. The values in this case can be the
|
|||
|
2d->3d projected x and y coordinates plus bob information.
|
|||
|
The Weights can be the Z coordinates before projection.
|
|||
|
|
|||
|
Begin with the first two elements, check what element
|
|||
|
is the HEAVIEST, and if it is ABOVE the lighter element,
|
|||
|
move all information connected with the WEIGHT and the
|
|||
|
WEIGHT to the place where the light element was,
|
|||
|
and put the light data where the heavy was.
|
|||
|
(This operation is called a 'swap' operation)
|
|||
|
|
|||
|
Step down ONE element and check element 2 and 3..
|
|||
|
step further until you're at the bottom of
|
|||
|
the list.
|
|||
|
|
|||
|
The first round won't fix the sorting, you will have to
|
|||
|
go round the list THE SAME NUMBER OF TIMES AS YOU HAVE
|
|||
|
OBJECTS minus one!!!!
|
|||
|
|
|||
|
The many comparisions vote for a faster technique...
|
|||
|
|
|||
|
Algorithm:
|
|||
|
|
|||
|
1> FOR outer loop=1 TO Items-1
|
|||
|
1.1> FOR inner loop=1 TO Items-1
|
|||
|
1.1.1> IF Item(inner loop)>Item(inner loop+1)
|
|||
|
1.1.1.1> Swap Item(inner loop),Item(inner loop+1)
|
|||
|
|
|||
|
(Items is the number of entries to sort, Item() is the weight of
|
|||
|
the current item)
|
|||
|
|
|||
|
|
|||
|
Method 2) Insert sorting
|
|||
|
-------------------------
|
|||
|
|
|||
|
Assume the same VALUES and WEIGHTS as above.
|
|||
|
To do this, you have to select a wordlength for the
|
|||
|
sorting-table (checklist) and a size of the checklist.
|
|||
|
|
|||
|
The wordlength depends on the number of entries you have,
|
|||
|
and the size of every entry. Normally, it's convienient
|
|||
|
to use WORDS. The size of the checklist is the range
|
|||
|
of Z-values to sort, or transformed Z-values.
|
|||
|
If you, for example, know that your Z-values lies within
|
|||
|
512-1023 you can first decrease every z-value by 512,
|
|||
|
and then lsr' it once, which will give you a checklist size
|
|||
|
of 256 words.
|
|||
|
You will also need a second buffer to put your sorted
|
|||
|
data in, this 2ndBUF will be like a copy of the original
|
|||
|
list but with the entries sorted.
|
|||
|
|
|||
|
For this method I only present an algorithm, it's
|
|||
|
easier to see how it works from that than from some
|
|||
|
strange text.
|
|||
|
|
|||
|
checklist(x) is the x'th word in the checklist.
|
|||
|
|
|||
|
Algorithm:
|
|||
|
1> CLEAR the checklist (set all words=0)
|
|||
|
2> TRANSFORM all weights if necessary.
|
|||
|
3> FOR L=0 TO number of objects
|
|||
|
3.1> ADD ENTRYSIZE TO checklist(transformed weight)
|
|||
|
4> FOR L=0 TO checklist size-1
|
|||
|
4.1> ADD checklist(L),checklist(L+1)
|
|||
|
5> FOR L=0 TO number of objects
|
|||
|
5.1> PUT ENTRY at 2ndBUF(checklist(transformed weight))
|
|||
|
5.2> ADD ENTRYSIZE TO checklist(transformed weigth)
|
|||
|
|
|||
|
Now, your data is nicely sorted in the list 2ndBUF, the
|
|||
|
original list is left as it was (except for Z-transformation).
|
|||
|
(ENTRYSIZE is the size of the entry, so if you have x,y,z coordinates
|
|||
|
in words, your size is 3 words=6 bytes.)
|
|||
|
Also try to think a little about what you get when you
|
|||
|
transform. The subtraction is useful since it minimizes the
|
|||
|
loops, but lsr-ing the weights take time and makes the
|
|||
|
result worse. Of course you don't have to scan the list every time,
|
|||
|
just make sure that you know what the lowest possible and the
|
|||
|
higest possible weight is.
|
|||
|
|
|||
|
|
|||
|
Method 3) the Quick-Sort
|
|||
|
------------------------
|
|||
|
This is another kind of sorting, and here it is most efficient
|
|||
|
to use pointers, so that each entry have a pointer to NEXT entry.
|
|||
|
|
|||
|
you can one entry like this:
|
|||
|
|
|||
|
NEXT OFFSET=word
|
|||
|
x,y,z=coordinates.
|
|||
|
|
|||
|
(offsets are from sortlist start address...)
|
|||
|
|
|||
|
To access this routine you will have to give a FIRST entry
|
|||
|
and number of entries. In the original execution, first entry
|
|||
|
is of course 0 (=first entry) and the number of entries is
|
|||
|
of course the total number of entries.
|
|||
|
You must set all previous/next pointers to link a chain.
|
|||
|
|
|||
|
Quicksort is recursive, which means that you will have to
|
|||
|
call the routine from within itself. This is not at
|
|||
|
all complicated, you just have to put some of your
|
|||
|
old variables on the stack for safe-keeping.
|
|||
|
|
|||
|
What it does is this:
|
|||
|
+> The first entry in the list is the PIVOT ENTRY.
|
|||
|
| For each other ENTRY, we put it either BEFORE or AFTER
|
|||
|
| the PIVOT. If it is lighter than the PIVOT we put it BEFORE,
|
|||
|
| otherwise we put it AFTER.
|
|||
|
| Now we have two new lists, All entries BEFORE the PIVOT,
|
|||
|
| and all entries AFTER the PIVOT (but not the pivot itself,
|
|||
|
| which is already sorted).
|
|||
|
| Now we quicksort All entries BEFORE the pivot separately
|
|||
|
+< and then we quicksort all entries AFTER the pivot.
|
|||
|
(We do this by calling on the routine we're already in)
|
|||
|
This may cause problems with the stack if there's too
|
|||
|
many things to sort.
|
|||
|
|
|||
|
The recursion loop is broken when there's <=1 entry
|
|||
|
to sort.
|
|||
|
|
|||
|
Contrary to some peoples belief, you don't need any extra
|
|||
|
lists to solve this.
|
|||
|
|
|||
|
Algorithm:
|
|||
|
|
|||
|
Inparameters: (PivotEntry=first element of list
|
|||
|
List size=size of current list)
|
|||
|
1> If list size <= 1 then exit
|
|||
|
2> PivotWeight=Weight(PivotEntry)
|
|||
|
3> for l=2nd Entry to list size-1
|
|||
|
3.1> if weight(l) > PivotWeight
|
|||
|
3.1.1> insert entry in list 1
|
|||
|
3.2> ELSE
|
|||
|
3.2.1> insert entry in list 2
|
|||
|
4> Sort list 1 (bsr quicksort(first entry list 1, size of list 1))
|
|||
|
5> Sort list 1 (bsr quicksort(first entry list 2, size of list 2))
|
|||
|
6> Link list 1 -> PivotEntry -> list 2
|
|||
|
|
|||
|
(PivotEntry = FirstEntry, it don't have to be like this, but I prefer
|
|||
|
it since I find it easier.)
|
|||
|
|
|||
|
|
|||
|
Vector Balls
|
|||
|
============
|
|||
|
Vector balls is simple. It is just to calculate where
|
|||
|
the ball is (with rotations, translations or whatever
|
|||
|
it can be). Sometimes you also calculate the size
|
|||
|
of the ball and so on.
|
|||
|
|
|||
|
You don't have to have balls. You can have the Convex
|
|||
|
parts of an Inconvex filled object, or you can
|
|||
|
have images of whatever you like. In three dimensions
|
|||
|
you will have the problem with images (balls or whatever)
|
|||
|
that should be in front of others because it is
|
|||
|
further away from you. Here is where SORTING
|
|||
|
comes in. If you BEGIN blitting the image that
|
|||
|
is most distant to you, and step closer for
|
|||
|
each object, you get a 3d-looking screen.
|
|||
|
The closest image will be the closest.
|
|||
|
|
|||
|
Normally, you start with clearing the screen you're not
|
|||
|
displaying at the moment (Parts of it anyway. A person
|
|||
|
in Silents only cleared every second line...)
|
|||
|
|
|||
|
Then (while the blitter is working) you start rotating,
|
|||
|
sorting and preparing to finally bob the images out
|
|||
|
|
|||
|
and when you've checked that the blitter is
|
|||
|
finished, you start bobbing out all images,
|
|||
|
and when the frame is displayed, you swap
|
|||
|
screens so you display your finished screen
|
|||
|
the next frame.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
APPENDICES
|
|||
|
|
|||
|
|
|||
|
Appendix A: Example sources.
|
|||
|
|
|||
|
A 1. An example of an optimized rotation matrix calculation
|
|||
|
============================================================
|
|||
|
|
|||
|
* For this routine, you must have a sinus table of 1024 values,
|
|||
|
* and three words with angles and a place (9 words) to store
|
|||
|
* the transformation matrix.
|
|||
|
* __ .
|
|||
|
* /( |( )|\/ '(|)
|
|||
|
* / )|(|\|/\ |)
|
|||
|
|
|||
|
Calculate_Constants
|
|||
|
|
|||
|
lea Coses_Sines(pc),a0
|
|||
|
lea Angles(pc),a2
|
|||
|
lea Sintab(pc),a1
|
|||
|
|
|||
|
move.w (a2),d0
|
|||
|
and.w #$7fe,d0
|
|||
|
move.w (a1,d0.w),(a0)
|
|||
|
add.w #$200,d0
|
|||
|
and.w #$7fe,d0
|
|||
|
move.w (a1,d0.w),2(a0)
|
|||
|
move.w 2(a2),d0
|
|||
|
and.w #$7fe,d0
|
|||
|
move.w (a1,d0.w),4(a0)
|
|||
|
add.w #$200,d0
|
|||
|
and.w #$7fe,d0
|
|||
|
move.w (a1,d0.w),6(a0)
|
|||
|
move.w 4(a2),d0
|
|||
|
and.w #$7fe,d0
|
|||
|
move.w (a1,d0.w),8(a0)
|
|||
|
add.w #$200,d0
|
|||
|
and.w #$7fe,d0
|
|||
|
move.w (a1,d0.w),10(a0)
|
|||
|
|
|||
|
;xx=c2*c1
|
|||
|
;xy=c2*s1
|
|||
|
;xz=s2
|
|||
|
;yx=c3*s1+s3*s2*c1
|
|||
|
;yy=-c3*c1+s3*s2*s1
|
|||
|
;yz=-s3*c2
|
|||
|
;zx=s3*s1-c3*s2*c1;s2*c1+c3*s1
|
|||
|
;zy=-s3*c1-c3*s2*s1;c3*c1-s2*s1
|
|||
|
;zz=c3*c2
|
|||
|
|
|||
|
lea Constants(pc),a1
|
|||
|
move.w 6(a0),d0
|
|||
|
move.w (a0),d1
|
|||
|
move.w d1,d2
|
|||
|
muls d0,d1
|
|||
|
asr.l #8,d1
|
|||
|
move.w 2(a0),d3
|
|||
|
muls d3,d0
|
|||
|
asr.l #8,d0
|
|||
|
move.w d0,(a1)
|
|||
|
;neg.w d1
|
|||
|
move.w d1,2(a1)
|
|||
|
move.w 4(a0),4(a1)
|
|||
|
move.w 8(a0),d4
|
|||
|
move.w d4,d6
|
|||
|
muls 4(a0),d4
|
|||
|
asr.l #8,d4
|
|||
|
move.w d4,d5
|
|||
|
muls d2,d5
|
|||
|
muls 10(a0),d2
|
|||
|
muls d3,d4
|
|||
|
muls 10(a0),d3
|
|||
|
add.l d4,d2
|
|||
|
sub.l d5,d3
|
|||
|
asr.l #8,d2
|
|||
|
asr.l #8,d3
|
|||
|
move.w d2,6(a1)
|
|||
|
neg.w d3
|
|||
|
move.w d3,8(a1)
|
|||
|
muls 6(a0),d6
|
|||
|
asr.l #8,d6
|
|||
|
neg.w d6
|
|||
|
move.w d6,10(a1)
|
|||
|
move.w 10(a0),d0
|
|||
|
move.w d0,d4
|
|||
|
muls 4(a0),d0
|
|||
|
asr.l #8,d0
|
|||
|
move.w d0,d1
|
|||
|
move.w 8(a0),d2
|
|||
|
move.w d2,d3
|
|||
|
muls (a0),d0
|
|||
|
muls 2(a0),d1
|
|||
|
muls (a0),d2
|
|||
|
muls 2(a0),d3
|
|||
|
sub.l d1,d2
|
|||
|
asr.l #8,d2
|
|||
|
move.w d2,12(a1)
|
|||
|
add.l d0,d3
|
|||
|
asr.l #8,d3
|
|||
|
neg.w d3
|
|||
|
move.w d3,14(a1)
|
|||
|
muls 6(a0),d4
|
|||
|
asr.l #8,d4
|
|||
|
move.w d4,16(a1)
|
|||
|
|
|||
|
rts
|
|||
|
|
|||
|
Coses_Sines dc.w 0,0,0,0,0,0
|
|||
|
Angles dc.w 0,0,0
|
|||
|
Constants dc.w 0,0,0,0,0,0,0,0,0
|
|||
|
|
|||
|
;Sintab is a table of 1024 sinus values with a radius of 256
|
|||
|
;that I have further down my code...
|
|||
|
|
|||
|
|
|||
|
A 2. A line drawing routine for filled vectors in assembler:
|
|||
|
============================================================
|
|||
|
|
|||
|
* written for kuma-seka ages ago, works fine and
|
|||
|
* can be optimized for special cases...
|
|||
|
* the line is (x0,y0)-(x1,y1) = (d0,d1)-(d2,d3) ...
|
|||
|
* Remember that you must have DFF000 in a6 and
|
|||
|
* The screen start address in a0.
|
|||
|
* Only a1-a7 and d7 is left unchanged.
|
|||
|
* __ .
|
|||
|
* /( |( )|\/ '(|)
|
|||
|
* / )|(|\|/\ |)
|
|||
|
|
|||
|
Screen_widht=40 ;40 bytes wide screen...
|
|||
|
fill_lines: ;(a6=$dff000, a0=start of bitplane to draw in)
|
|||
|
|
|||
|
cmp.w d1,d3
|
|||
|
beq.s noline
|
|||
|
ble.s lin1
|
|||
|
exg d1,d3
|
|||
|
exg d0,d2
|
|||
|
lin1: sub.w d2,d0
|
|||
|
move.w d2,d5
|
|||
|
asr.w #3,d2
|
|||
|
ext.l d2
|
|||
|
sub.w d3,d1
|
|||
|
muls #Screen_Widht,d3 ;can be optimized here..
|
|||
|
add.l d2,d3
|
|||
|
add.l d3,a0
|
|||
|
and.w #$f,d5
|
|||
|
move.w d5,d2
|
|||
|
eor.b #$f,d5
|
|||
|
ror.w #4,d2
|
|||
|
or.w #$0b4a,d2
|
|||
|
swap d2
|
|||
|
tst.w d0
|
|||
|
bmi.s lin2
|
|||
|
cmp.w d0,d1
|
|||
|
ble.s lin3
|
|||
|
move.w #$41,d2
|
|||
|
exg d1,d0
|
|||
|
bra.s lin6
|
|||
|
lin3: move.w #$51,d2
|
|||
|
bra.s lin6
|
|||
|
lin2: neg.w d0
|
|||
|
cmp.w d0,d1
|
|||
|
ble.s lin4
|
|||
|
move.w #$49,d2
|
|||
|
exg d1,d0
|
|||
|
bra.s lin6
|
|||
|
lin4: move.w #$55,d2
|
|||
|
lin6: asl.w #1,d1
|
|||
|
move.w d1,d4
|
|||
|
move.w d1,d3
|
|||
|
sub.w d0,d3
|
|||
|
ble.s lin5
|
|||
|
and.w #$ffbf,d2
|
|||
|
lin5: move.w d3,d1
|
|||
|
sub.w d0,d3
|
|||
|
or.w #2,d2
|
|||
|
lsl.w #6,d0
|
|||
|
add.w #$42,d0
|
|||
|
bltwt: btst #6,2(a6)
|
|||
|
bne.s bltwt
|
|||
|
bchg d5,(a0)
|
|||
|
move.l d2,$40(a6)
|
|||
|
move.l #-1,$44(a6)
|
|||
|
move.l a0,$48(a6)
|
|||
|
move.w d1,$52(a6)
|
|||
|
move.l a0,$54(a6)
|
|||
|
move.w #Screen_Widht,$60(a6) ;width
|
|||
|
move.w d4,$62(a6)
|
|||
|
move.w d3,$64(a6)
|
|||
|
move.w #Screen_Widht,$66(a6) ;width
|
|||
|
move.l #-$8000,$72(a6)
|
|||
|
move.w d0,$58(a6)
|
|||
|
noline: rts
|
|||
|
|
|||
|
|
|||
|
A 3. The quicksort in 68000 assembler:
|
|||
|
============================================================
|
|||
|
|
|||
|
* Sorts a list that looks like:
|
|||
|
* Next entry offset.w, (x,y,z).w.
|
|||
|
* all offsets must be set except for first entry's previous offset
|
|||
|
* and the last entry's next offset.
|
|||
|
* Offsets are FROM FIRST ADDRESS of sorting list
|
|||
|
* a5=first address of sorting list!
|
|||
|
* __ .
|
|||
|
* /( |( )|\/ '(|)
|
|||
|
* / )|(|\|/\ |)
|
|||
|
|
|||
|
|
|||
|
WghtOffs=6
|
|||
|
NextOffs=0
|
|||
|
|
|||
|
QuickSort ;(a5=start of sortlist,
|
|||
|
; d0=0 (pointer to first entry, first time=0)
|
|||
|
; d1=number of entries)
|
|||
|
|
|||
|
|
|||
|
cmp.w #1,d1
|
|||
|
ble.s .NothingToSort ;don't sort if <=1 entries
|
|||
|
moveq #0,d4 ;size list 1
|
|||
|
moveq #0,d5 ;size list 2
|
|||
|
move.w d0,d6 ;first Nentry=d0
|
|||
|
|
|||
|
move.w WghtOffs(a5,d0.w),d2 ;d2=Pivot weight
|
|||
|
move.w NextOffs(a5,d0.w),d3 ;d3=2nd entry
|
|||
|
subq.w #2,d1 ;Dbf-loop+skip first
|
|||
|
|
|||
|
..Permute cmp.w WghtOffs(a5,d3.w),d2 ;entry weight<pivot weight?
|
|||
|
ble.s .Lower
|
|||
|
|
|||
|
move.w d6,NextOffs(a5,d3.w) ;Insert BEFORE Nentry
|
|||
|
addq.w #1,d4 ;increase size of list 1
|
|||
|
move.w d3,d6 ;Set new Nentry
|
|||
|
|
|||
|
bra.s .Done ;Continue the loop...
|
|||
|
|
|||
|
..Lower move.w NextOffs(a5,d0.w),NextOffs(a5,d3.w)
|
|||
|
move.w d3,NextOffs(a5,d0.w) ;insert AFTER first entry
|
|||
|
addq.w #1,d5 ;size of list 2
|
|||
|
|
|||
|
..Done move.w NextOffs(a5,d3.w),d3 ;Get next entry
|
|||
|
dbf d1,.permute
|
|||
|
|
|||
|
move.w d0,-(a7) ;save Fentry..
|
|||
|
|
|||
|
move.w NextOffs(a5,d0.w),d0 ;Sort at entry after first
|
|||
|
move.w d5,d1 ;Size of list 2
|
|||
|
|
|||
|
movem.w d4/d6,-(a7) ;Save important registers
|
|||
|
bsr QuickSort ;and sort list 2
|
|||
|
movem.w (a7)+,d4/d6 ;d1 is now First Entry...
|
|||
|
move.w (a7)+,d1
|
|||
|
|
|||
|
move.w d0,NextOffs(a5,d1.w) ;Put first entry of
|
|||
|
;list 2 after Fentry...
|
|||
|
move.w d6,d0 ;Sort at Nentry
|
|||
|
move.w d4,d1 ;size of list 1
|
|||
|
|
|||
|
bsr QuickSort ;no important registers
|
|||
|
;left...
|
|||
|
..NothingToSort
|
|||
|
;Now the offset to the first entry is in d0!
|
|||
|
;to get the other values in the correct order
|
|||
|
;just go down the list (using nextoffs.)
|
|||
|
;First object is the heaviest...
|
|||
|
|
|||
|
rts
|
|||
|
|
|||
|
|
|||
|
A 4. The Insert Sort in 68020 assembler:
|
|||
|
========================================
|
|||
|
|
|||
|
* This isn't exactly as the algorithm described earlier,
|
|||
|
* it begins with creating a list and then stores the ADDRESSES of the
|
|||
|
* sorted data in 2ndBUF instead...
|
|||
|
* This sorts all lists, just specify offset to weight (word) and
|
|||
|
* size of each entry. You don't need any pre-formatting.
|
|||
|
* note that you HAVE TO change a line if you want this to work
|
|||
|
* on 68000.. I've got a scaled index at one point. replace it
|
|||
|
* with the lines after the semicolon.
|
|||
|
* __ .
|
|||
|
* /( |( )|\/ '(|)
|
|||
|
* / )|(|\|/\ |)
|
|||
|
|
|||
|
WghtOffs=4
|
|||
|
EntrySize=6
|
|||
|
|
|||
|
InsertSort
|
|||
|
;(a5=start of data
|
|||
|
; a4=start of checklist
|
|||
|
; a3=start of 2ndBUF
|
|||
|
; d0 is lowest value of entries
|
|||
|
; d1 is highest value
|
|||
|
; d2 is number of entries
|
|||
|
|
|||
|
movem.l a4/a5,-(a7)
|
|||
|
|
|||
|
sub.w d0,d1 ;max size of checklist this sort.
|
|||
|
subq.w #1,d2
|
|||
|
subq.w #1,d1 ;Dbf-loops...
|
|||
|
|
|||
|
move.w d1,d3 ;clear used entries
|
|||
|
..ClearChecklist clr.w (a4)+
|
|||
|
dbf d3,.ClearCheckList
|
|||
|
|
|||
|
move.w d2,d3 ;transform...
|
|||
|
..Transform sub.w d0,WghtOffs(a5)
|
|||
|
addq.w #EntrySize,a5
|
|||
|
dbf d3,.Transform
|
|||
|
|
|||
|
movem.l (a7),a4/a5
|
|||
|
|
|||
|
move.w d2,d3 ;Insert next line instead for
|
|||
|
..AddisList move.w WghtOffs(a5),d0 ;68000 compatibility...
|
|||
|
addq.w #4,(a5,d0.w*2) ;add.w d0,d0 addq.w #4,(a5,d0.w)
|
|||
|
addq.w #EntrySize,a5
|
|||
|
dbf d3,.AddisList
|
|||
|
|
|||
|
moveq #-4,d0 ; #-lwdsize
|
|||
|
..GetMemPos add.w d0,(a4)
|
|||
|
move.w (a4)+,d0
|
|||
|
dbf d1,.GetMemPos
|
|||
|
|
|||
|
movem.l (a7)+,a4/a5
|
|||
|
..PutNewList move.w WghtOffs(a5),d0
|
|||
|
move.w (a4,d0.w),d0
|
|||
|
move.l a5,(a3,d0.w)
|
|||
|
addq.w #EntrySize,a5
|
|||
|
dbf d2,.PutNewList
|
|||
|
|
|||
|
;In this case you have a list of ADDRESSES to
|
|||
|
;each object. I made it this way to
|
|||
|
;make it more flexible (you maybe have more
|
|||
|
;data in each entry than me?).
|
|||
|
|
|||
|
rts
|
|||
|
|
|||
|
|
|||
|
Appendix B: Some other info...
|
|||
|
|
|||
|
B 1: 'Proof' of hidden-plane elimination equation
|
|||
|
================================================
|
|||
|
|
|||
|
I presented the following equation:
|
|||
|
c=(x3-x1)*(y2-y1)-(x2-x1)*(y3-y1)
|
|||
|
as a calculation of the normal vector of the plane
|
|||
|
that the polygon in question spanned.
|
|||
|
|
|||
|
We had three points:
|
|||
|
p1(x1,y1)
|
|||
|
p2(x2,y2)
|
|||
|
p3(x3,y3)
|
|||
|
|
|||
|
If we select p1 as base-point, we can construct the following
|
|||
|
vectors of the rest of the points:
|
|||
|
|
|||
|
V1=(x3-x1,y3-y1,p)
|
|||
|
V2=(x2-x1,y2-y1,q)
|
|||
|
|
|||
|
Where p and q in the z value denotes that we are not interested in
|
|||
|
this value, but we must take it in our calculations anyway.
|
|||
|
(These values are NOT the same as the original z-values
|
|||
|
after the 2d->3d projection)
|
|||
|
|
|||
|
|
|||
|
Now, we can get the normal vector of the plane that these vectors
|
|||
|
span by a simple cross-product:
|
|||
|
|
|||
|
V1 x V2 =
|
|||
|
|
|||
|
| i j k|
|
|||
|
= |(x3-x1) (x2-x1) p| (if i=(1,0,0), j=(0,1,0), k=(0,0,1))
|
|||
|
|(y3-y1) (y2-y1) q| (p and q are non-important)
|
|||
|
|
|||
|
But we are only interested in the Z-direction of the
|
|||
|
result-vector of this operation, which is the same as
|
|||
|
getting only the Z-coordinate out of the cross-product:
|
|||
|
|
|||
|
Z of (V1xV2) = (x3-x1)*(y2-y1)-(x2-x1)*(y3-y1)
|
|||
|
|
|||
|
Now if Z is positive, this means that the resultant vector
|
|||
|
is aiming INTO the screen (positive z-values)
|
|||
|
QED /Asterix
|
|||
|
|
|||
|
B 2. How to make a fill line out of the blitters line-drawing
|
|||
|
==============================================================
|
|||
|
You can't use the blitter line-drawing as it is and
|
|||
|
draw lines around a polygon without a few special changes.
|
|||
|
|
|||
|
To make a fill-line routine out of a normal-lineroutine:
|
|||
|
|
|||
|
First, make sure it draws lines as it should,
|
|||
|
many line-drawers I've seen draws lines to wrong points
|
|||
|
Make sure you use Exclusive or- instead of or-minterm
|
|||
|
Always draw lines DOWNWARDS. (or UPWARDS, if you prefer that)
|
|||
|
Before drawing the line and before blit-check, eor the FIRST
|
|||
|
POINT ON THE SCREEN THAT THE LINE WILL PASS.
|
|||
|
Use fill-type line mode.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
B 3: An alternate approach to rotations in 3-space by M. Vissers
|
|||
|
================================================================
|
|||
|
|
|||
|
/* This is a text supplied by Michael Vissers, and was a little
|
|||
|
longer. I removed the part about projection from 3d->2d,
|
|||
|
which was identical to parts of my other text in chapter 3.
|
|||
|
If you know some basic linear algebra, this text might be
|
|||
|
easier to melt than the longer version discussed in chapter 4.
|
|||
|
If you didn't get how you were supposed to use the result in
|
|||
|
chapter 4 then try this part instead. */
|
|||
|
|
|||
|
[ ] All you have to do is using these 3D matrices :
|
|||
|
|
|||
|
(A/B/G are Alpha,Beta and Gamma.) /* A,B,C = Angles of rotation */
|
|||
|
|
|||
|
| cosA -sinA 0 | | cosB 0 -sinB | | 1 0 0 |
|
|||
|
| sinA cosA 0 | | 0 1 0 | | 0 cosG -sinG |
|
|||
|
| 0 0 1 | | sinB 0 cosB | | 0 sinG cosG |
|
|||
|
|
|||
|
These are the rotation matrices around the x,y and z axis'. If you would
|
|||
|
use these you'll get 12 muls'. 4 four for each axis. But, if you multiply
|
|||
|
these three matrices with eachother you'll get only 9 muls'. Why 9 ???
|
|||
|
Simple : after multiplying you'll get a 3x3 matrice, and 3*3=9 !
|
|||
|
|
|||
|
It doesn't matter if you do not know how to multiply these matrices. It's
|
|||
|
not important here so I'll just give the 3x3 matrice after multiplying :
|
|||
|
|
|||
|
(c = cos, s = sin, A/B/G are Alpha,Beta and Gamma.)
|
|||
|
|
|||
|
| cA*cB -cB*sA sB |
|
|||
|
| cG*sA-sB*cA*sG cA*cG+sG*sA*sB cB*sG |
|
|||
|
|-sG*sA-sB*cA*cG -cA*sG+sA*sB*cG cG*cB |
|
|||
|
|
|||
|
I hope I typed everything without errors :) Ok, how can we make some
|
|||
|
coordinates using this matrice. Again, the trick is all in multiplying.
|
|||
|
To get the new (x,y,x) we need the original points and multiply these with
|
|||
|
the matrice. I'll work with a simplyfied matrice. (e.g. H = cA*cB etc...)
|
|||
|
|
|||
|
x y z ( <= original coordinates)
|
|||
|
-------------
|
|||
|
New X = | H I J |
|
|||
|
New Y = | K L M |
|
|||
|
New Z = | N O P |
|
|||
|
|
|||
|
So...
|
|||
|
|
|||
|
New X = x * H + y * I + z * J
|
|||
|
New Y = x * K + y * L + z * M
|
|||
|
New Z = x * N + y * O + z * P
|
|||
|
|
|||
|
Ha ! That's a lot more than 9 muls'. Well, actually not. To use the matrice
|
|||
|
you'll have to precalculate the matrice.
|
|||
|
|
|||
|
Always rotate with your original points and store them somewhere else.
|
|||
|
Just change the angles to the sintable to rotate the shape.
|
|||
|
If you rotate the points rotated the previous frame you will lose all detail
|
|||
|
until nothing is left.
|
|||
|
|
|||
|
So, every frame looks like this : - pre calculate new matrice with
|
|||
|
given angles.
|
|||
|
- Calculate points with stored
|
|||
|
matrice.
|
|||
|
[ ]
|
|||
|
The resulting points are relative to (0,0). So they can be negative to.
|
|||
|
Just use a add to get it in the middle of the screen.
|
|||
|
|
|||
|
NOTE: Always use muls,divs,asl,asr etc. Data can be both positive and
|
|||
|
negative. Also, set the original coordinates as big as possible,
|
|||
|
and after rotating divide them again. This will improve the
|
|||
|
quality of the movement.
|
|||
|
|
|||
|
(Michael Vissers)
|
|||
|
|
|||
|
|
|||
|
B 4: A little math hint for more accurate vector calculations
|
|||
|
=============================================================
|
|||
|
|
|||
|
When doing a muls with a value and then downshifting the value, use
|
|||
|
and 'addx' to get roundoff error instead of truncated error, for
|
|||
|
example:
|
|||
|
moveq #0,d7
|
|||
|
DoMtxMul
|
|||
|
:
|
|||
|
muls (a0),d0 ;Do a muls with a sin value *256
|
|||
|
asr.l #8,d0
|
|||
|
addx.w d7,d0 ;roundoff < trunc
|
|||
|
:
|
|||
|
|
|||
|
When you do a 'asr' the last outshifted bit goes to the x-flag.
|
|||
|
if you use an addx with source=0 => dest=dest+'x-flag'.
|
|||
|
This halves the error, and makes complicated vector objects
|
|||
|
less 'hacky'.
|
|||
|
|
|||
|
|
|||
|
/) __ .
|
|||
|
(( /( |( )|\/ '(|)
|
|||
|
)) / )|(|\|/\ |)
|
|||
|
(/
|
|||
|
|
|||
|
|