Okay. Here's the file. Of course I didn't write down every single equation I use in this file. :-) And there's some discussions I kept between me and some physics/math guys on a few of the more difficult subjects.
-------------------------------
"I wish I could remember the math term for and how to caclulate that sort of comparison. Like if you have 5 vertcies, it's 5+4+3+2+1 which is 15 comparisons."
- n(n+1)/2 is probably what you are looking for.
---------------------------------------
Anyway, here's the logic for the friction force: suppose we've
got a surface normal norm = (nx, ny, nz), and a gravity vector
running in the y direction grav = (0, 1, 0). We take their vector
cross-product to get a "sideways" vector
sideways = (sx, sy, sz) = (-nz / n, 0, -nx / n)
...where n = sqrt( nx * nx + nz * nz) is a normalizing factor, to
keep 'sideways' of unit length. n is also equal to the sine of the
angle between 'norm' and 'grav'.
Next thing to do is to vector-cross-product 'sideways' with 'norm',
thereby getting a unit vector that's in the plane of the surface,
going uphill:
uphill = sideways x norm = (-ny * nx / n, (nz * nz + nx * nx) / n, -ny * nz / n)
= (-ny * nx / n, n, -ny * nz / n)
Now we gotta multiply 'uphill' by the sine of the inclination... which,
as I mentioned a few paragraphs ago, happens to be n. So:
friction_force = uphill * n = (-ny * nx, n^2, -ny * nz)
= (-ny * nx, nx * nx + nz * nz, -ny * nz)
...which means that you can compute the force vector without having to
do square roots or other unpleasant forms of math.
Reducing velocity only in a direction perpendicular to the face collided with:
------------------------------------------------------------------------------
Assume V is the velocity vector and N is the normal.
N is perpendicular to the surface and has length 1.
The component of V in the direction of N is given by the dot product.
If you wanted to reduce velocity by 10% in the N direction then you would subtract:
V = V - 0.1 * (N dot V) * N ; pseudocode.
Of course, in Blitz you have to handle the x,y,z components separately:
Assume dot#, nx#, vx# etc. are all defined.
dot = nx*vx + ny*vy + nz*vz
vx = vx - 0.1*dot*nx
vy = vy - 0.1*dot*ny
vz = vz - 0.1*dot*nz
(N dot V) * N is the projection of V onto N.
OR..............
Dot# = NX * Item\SpdX + NY * Item\SpdY + NZ * Item\SpdZ
ReflectX# = NX * Dot#
ReflectY# = NY * Dot#
ReflectZ# = NZ * Dot#
; 1.0 stops bouncing
; 2.0 produces a perfect bounce (no loss of energy)
bounce# = 1.1 ; Retain only 10% of the energy
Item\SpdX = Item\SpdX - ReflectX# * bounce#
Item\SpdY = Item\SpdY - ReflectY# * bounce#
Item\SpdZ = Item\SpdZ - ReflectZ# * bounce#
<Polytope> max(|x|,|y|)=r, where max(a,b) = (|a+b|+|a-b|)/2
<sswift> if C is less than like 1...
<sswift> That would tell you the points to plot.;
<PdoX> so you mean, |x| + |y| - |xy| <= 1
<Polytope> combine them and you get ||x|+|y||+||x|-|y||=2r
<sswift> is there an equation for abs too? :-)
<PdoX> |x| = sqrt(x^2)
<PdoX> though thats cheating
<PdoX> sswift: When two objects collide with initial energies E_1 and E_2, they end up with final energies E_1' E_2' where E_1 + E_2 = E_1' + E_2' + Q, where Q is the energy lost
<PdoX> (energy lost to vibration,etc. everything taken from kinetic energy)
<PdoX> For elastic collisions, you have Q=0
<PdoX> For inelastic, Q=E_1+E_2 (all)
<PdoX> actually, I need to be more careful here..
<PdoX> I'm in the Center of mass frame.
Fixed point numbers:
--------------------
Let's say that your range is [Ra .. Rb]. Ra can be a negative number or a positive number.
So let's say you want to compress an angle from -180 to +180 in your fixed point representation, and let's say you want to use 2 bytes for your fixed point number. For that we use a value of 65536. (256*256)
Then what you do to convert to fixed point is this:
FixedNum = ((FloatNum-Float(Ra)) / Float(Rb-Ra)) * 65536.0
And then to convert back you do this:
FloatNum# = ((Float(FixedNum) / 65536.0) * Float(Rb-Ra)) + Float(Ra)
And THAT will work for any range. Before I made a mistake, and the range always had to start with 0.
What THIS does is it first shifts the floating point number so it is in the range of 0..(Rb-Ra) So for example with -180 .. 180, RB-Ra is 360. So we get a range of 0..360.
Then after doing that it's divided by the number of numbers in the range, in this case 360, which scales it down to a number 0..1, THEn that number is multiplied by our fixed point maximum number, which is 65536.0, and we end up with an integer from 0..65536.
Then on the other end 65536 is converted back to 0..1, and then from there to 0..360, and then from there -180 is added to it which gives us our floating point number in the range of -180..180.
Don't concern yourself with the MOD stuff, that's only if you want to wrap values, and you can do that to the value before you do this equation.
Quaternions:
------------
A unit quaternion adheres to the following equation:
W^2+X^2+Y^2+Z^2 = 1
finding uniform random points around a point on a sphere.
---------------------------------------------------------
Find a point on the unit sphere:
P = (a,b,c)
The first step is to find a point N on the unit sphere which is 90 degrees (arc length) away.
N = (a/sqrt(a^2+b^2) , -b/sqrt(a^2+b^2), 0)
will work unless a=b=0.
now let T = PxN. so P,N,T are at right angles to each other
now let's see. we want to parameterize the circle of radius r centered at P
R = P*cos(r) + N*sin(r)*cos(t) + T*sin(r)*sin(t) should work
pick a random angle t between 0 and 360 and you have a random point in the circle
So in sphere space using P(0,0,1) N(0,1,0) T(1,0,0):
r = radius from point at 0,0,1
t = random number between 0 and 360
Rx = sin(r)*sin(t)
Ry = sin(r)*cos(t)
Rz = cos(r)
Radius should be set randonly too. I think. I think I can clip radius to create a hole.
Finding UNFORM random points on a sphere: (Ie: no pole clustering)
----------------------------------
choose a random angle t and a random number z between -1 and 1, then let r=sqrt(1-z^2), x=r*cos(t), y=r*sin(t).
then (x,y,z) is a random point on the sphere
(of radius 1, centered at the origin)
OR:
To generate a random point on the sphere, it is necessary only to generate two random numbers,
z between -R and R,
phi between 0 and 2 pi, each with a uniform distribution
To find the latitude (theta) of this point, note that z=Rsin(theta), so:
theta=sin-1(z/R)
its longitude is phi.
In rectilinear coordinates:
x=R*cos(theta)*cos(phi)
y=R*cos(theta)*sin(phi)
z=R*sin(theta)=z
Note that in both methods points will appear to be clustered if you attempt to step through it. The lattitude lines become more sparse as the longitude lines become more dense, so random points will be evenly distributed, but if you plot even lonigtude lines it will look wrong.
<Polytope> there is an amazing fact, which I don't know how to explain without calculus.
<Polytope> if you enclose a sphere in a cylinder (with no top or bottom) then the sphere and the cylinder have the same surface area.
<Polytope> moreover,if you project from the cylinder to the sphere, towards the axis of the cylinder, then this projection preserves area.
<Polytope> so, to choose random points on a sphere, you can just choose random points on a vertical cylinder, then project the points horizontally to the sphere.
<monochrom> The height of the cylinder is the diameter of the sphere, right?
<Polytope> monochrom: yes
<Polytope> archimedes discovered this somehow, waay before calculus. this is astonishing to me
---------------------------
"The extra 1m/s is therefore stored like a battery in form of kinetic energy in the object. If it would go upwards while it has the energy stored, it would use up that energy until the kinetic "battery" is used up."
Hm... that gives me an idea...
The correct way to model a ball would be to deform it when it hits the ground, and then undeform it over a period of time pushing it against the ground... This way the ball would actually be in contact with the ground much more often cause as it deforms it deforms downward maintaining contact with the ground.
In my current system the ball doesn't deform. When it hits the gorund it instantly changes direction and loses contact with the ground. This means that ground friction is only applied for a split second.
Of course, it seems odd to consider applying ground friction for an object moving in a direction away from the ground. You usually think of ground friction like ab object being slid over sandpaper. It doesn't make sense for it to affect the ball when it simply bounces straight up and down on the sandpaper, even if it's only doing it for a split second.
So I wonder if perhaps the physics which I am using is far too simplified. But these are the physics they teach in high school and all over the web! Why are they teaching stuff that's wrong?
I think maybe my idea about applying certain forces to certain "axis" would work.
For example, the ground friction would be applied only to forces in the XZ axis relative to the normal of the surface collided with. IE, a sliding force along the surface would be damped, but vertical forces, (vertical relative to the normal of the surface that is) would not.
If I went that route then I could apply a seprate model to forces which lie on the Y axis relative to the surface normal. So the "downward" force (downward realtive to the surface normal) when the ball hits the surface, would be cut by 50% on the rebound but the velocity on the XZ plane would remain untouched by the damping force.
Then, if I combine that with the idea about deforming the ball, so that the ball stays in constant contact with the plane if it is rolling along it, and stays in contact much longer when it collides at a shallow angle to it an ounces back up... If I do that, then ground friction should be modeled more accurately and more consistently on systems which calculate the physics at diffrent speeds! :-)
---------
quaterion is four numbers. One of them is w.
The other three are x,y,z which give a vector ( axis ) around which to rotate.
w is the cosine of half the angle of rotation.
Dtermining if two line segments intersect:
------------------------------------------
Let A,B,C,D be 2-space position vectors. Then the directed line segments AB & CD are given by:
AB=A+r(B-A), r in [0,1]
CD=C+s(D-C), s in [0,1]
If AB & CD intersect, then
A+r(B-A)=C+s(D-C), or
Ax+r(Bx-Ax)=Cx+s(Dx-Cx)
Ay+r(By-Ay)=Cy+s(Dy-Cy) for some r,s in [0,1]
Solving the above for r and s yields
(Ay-Cy)(Dx-Cx)-(Ax-Cx)(Dy-Cy)
r = ----------------------------- (eqn 1)
(Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
(Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
s = ----------------------------- (eqn 2)
(Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
Let P be the position vector of the intersection point, then
P=A+r(B-A) or
Px=Ax+r(Bx-Ax)
Py=Ay+r(By-Ay)
By examining the values of r & s, you can also determine some other limiting conditions:
If 0<=r<=1 & 0<=s<=1, intersection exists
r<0 or r>1 or s<0 or s>1 line segments do not intersect
* If the denominator in eqn 1 is zero, AB & CD are parallel
* If the numerator in eqn 1 is also zero, AB & CD are coincident
If the intersection point of the 2 lines are needed (lines in this context mean infinite lines) regardless whether the two line segments intersect, then
* If r>1, P is located on extension of AB
* If r<0, P is located on extension of BA
* If s>1, P is located on extension of CD
* If s<0, P is located on extension of DC
Also note that the denominators of eqn 1 & 2 are identical.
Plane equation and Sphere/Frustum Intersection Tests
----------------------------
Ax + By + Cz = D
A = y1 (z2 - z3) + y2 (z3 - z1) + y3 (z1 - z2)
B = z1 (x2 - x3) + z2 (x3 - x1) + z3 (x1 - x2)
C = x1 (y2 - y3) + x2 (y3 - y1) + x3 (y1 - y2)
- D = x1 (y2 z3 - y3 z2) + x2 (y3 z1 - y1 z3) + x3 (y1 z2 - y2 z1)
We'll need to figure out the plane equation for each one of those six planes:
A*x + B*y + C*z + D = 0
...that plane equation calculation can be done one-time whenever the view frustum is set up.
Note that for an arbitary point (px,py,pz), the distance to the plane is:
d = A*px + B*py + C*pz + D
...so for each vertex of your bounding volume, you can measure the distance to each of those planes.
For a sphere: Just toss the center point of the sphere into the equation above - and if the (signed) distance places the center point more than 'radius' units outside any of the planes then you can cull the object.
* In the plane equation mentioned above, (A,B,C) is the surface normal of the plane and D is the distance from the origin to the plane - measured such that D is positive for planes that face the origin and negative from those that don't.
------------------
Diffuse and phong lighting:
^N
|
Lÿÿÿ | R V
\ÿÿ | / __/
\ÿ | / __/
\ | / __/
\ | /__/
\|//
-------------.--------------
P
^
point under consideration
It's important you know what these values actually are:
N = surface normal
L = unit vector between point and light
V = unit vector between point and view
R = light reflection unit vector (mirror of L about N)
First, the diffuse relfection is given by the Lamertian Relfection
equation:
diffuse = Kd * (N dot L)
Where Kd is the diffuse relfection constant. (N dot L) is the same as
the cosine of the angle between N and L, so as the angle decrease, the
resulting diffuse value is higher.
Phong gave spectral reflectivity as:
diffuse + Ks * (R dot V)^n
other source:
lambert's "the light is dependant on the angle it hits the surface" law.
Use the dotproduct to get that value.
Angle = Dotproduct( Normal, vLightVector );
If Angle is negative then the light doesn't reach the point, otherwise use the
Angle multiplied with the light's color as the diffuse component. Oh, by the way,
colors are in the range [0.0, 1.0].
How to calculate the new vector of a ball which has struck a wall.
(Also the normal force?)
------------------------------------------------------------------
v = v - 2*n*(v . n)
(v . n = dot product of v and n... see below.)
Dot product of two vectors:
---------------------------
AdotB = ax*bx + ay*by + az*bz
Air Friction:
-------------
Here's how to compute air friction:
air_friction_force = air_friction_constant * vehicle_velocity^2
A good air friction constant might be 0.2
Then:
new_velocity = old_velocity - air_friction_force * time
---------------------------------------------------------------------------------------------------------------
The "normal force" - How to make an object "richochet" off a surface, or move downhill when pulled by gravity.
---------------------------------------------------------------------------------------------------------------
When an object impacts a surface, whether it is fired from a cannon, or is simply being pulled downwards by
gravity, a "normal force" is generated which is equal in strength to the forces which were acting on the object
when it hit.
This "normal force" (NF for short), is ALWAYS perpendicular the the surface.
Making pool balls bounce off bumpers and making a ball roll down a hill is done in the same way, and is very
simple to do.
All you have to do is find the velocity vector of the ball when it hit. In other words, add up the forces acting on it, like gravity, and the force applied to it when it was fired from the cannon.
Then when the ball hits the surface, calclulate a vector which is perpendicular to the surface, and which has the same magnitude as the forces which were acting on the ball at that moment, and add theforce vector of the ball to the normal force... The result will be a vactor which points in the expected direction.
For example, if you have a flat section of land, the normal force is straight up. And the force acting on the ball, gravity, points straight down. When you add them, they cancel eachother out exactly and the ball doesn't move at all, just as you'd expect.
If on the other hand the ball is rolling across the surface in addition to being pulled down by gravity, then the ball will continue to roll across the surface, but will still not move downwards at all.
Now if you have a hill, then the normal force points away from the hill... not straight up like on the flat plane example. So when you add the NF to the gravity acting on the ball, the result is a vector which points down the hill... so the ball rolls down the hill as expected!
-----------------------------------------------------
Finding the closest point on a line to another point.
-----------------------------------------------------
(x1, y1, z1), (x2, y2, z2) = ends of line segment
(x3, y3, y3) = point
u =
(x3 - x1)(x2 - x1) + (y3 - y1)(y2 - y1) + (z3 - z1)(z2 - z1)
-----------------------------------------------------------
(x2 - x1)(x2 - x1) + (y2 - y1)(y2 - y1) + (z2 - z1)(z2 - z1)
u = the location between the two end points of the line which is closest to the point.
If u is not between 0 and 1 then the closest point is not between P1 and P2
----------------------------
LINE-SPHERE-INTERSECTION
----------------------------
To find out if a line intersects a sphere, find the closest point on the line to the center of the sphere.
Then calculate the location of that point using U.
And finally, find out the if the distance from that point to the center of the sphere is less than the radius of the sphere.
------------------
DEGREES TO RADIANS
------------------
R = D * (Pi / 180)
------------------
RADIANS TO DEGREES
------------------
D = R * 180/pi
--------------------------------------------------------------
The angle to change a wheel for movement over a given distance.
--------------------------------------------------------------
A = angle in radians
R = Radius of wheel
D= Distance moved
D = R * A
A = D/R
-----------
SOUND
-----------
Volume = the volume of the sound.
MinDist =
The distance at which the sound is at 100% volume. The sound will not get any louder closer than this.
This has a big effect on how quickly the sound falls off. Since the sound card cannot make sounds that are extemely loud, it's probably best to set this value to around 1 meter, in your game units. Ie, 64, if 64 units equals a meter in your game.
SoundDistance = The distance to the sound.
maxvolume = the maximum volume of your sound.
Volume = (MinDist^2 * MaxVolume) / (SoundDistance + MinDist)^2
-----------
DISTANCE
-----------
dist = sqrt( (x1 - x2)^2 + (y1 - y2)^2 + (z1 - z2)^2 )
-------------
CIRCUMFERENCE
-------------
C = 2PiR
Circumfurence = 2 * Pi * Radius
--------
MOMENTUM
--------
P=MV
Momentum = Mass x Velocity
------------------------------
POINT BETWEEN TWO OTHER POINTS
------------------------------
CX = AX + D * (BX-AX)
CY = AY + D * (BY-AY)
D = Distance 0 .. 1
(AX,AY)(BX,BY) = Endpoints of line segment AB.
------------------------------------------
POLAR COORDINATES TO CARTESIAN COORDINATES
------------------------------------------
X = Radius * Cos(Theta)
Y = Radius * Sin(Theta)
Converts polar coordinates into cartesian coordinates. Drawing a pixel at X,Y for Theta 0..360 at a fixed radius would draw a circle.
------------------------------------------
Angle of a point from 0,0
------------------------------------------
Angle = ARCSIN(Y/R)
or
Angle = ARCCOS(X/R)
------------
NORMAL FORCE
------------
Fn = Fg * Cos(Theta)
If on a slope,
Fn = Normal Force
Fg = Force of gravity
Theta = Angle of the slope
The normal force is the force perpendicular to the angle of the slope, which needs to be added to the
force of gravity to determine how much accleration from gravity will actually affect the object.
-----------------------------------
CONVERTING A FORCE INTO XYZ VECTORS
-----------------------------------
Mxz = M * Cos(Ty)
Fy = M * Sin(Ty)
Fx = Mxz * Cos(Txz)
Fz = Mxz * Sin(Txz)
This equation converts a vector described in the polar coordinate system to one in the cartesian coordinate system.
The force is described with a magnitude (M) and two angles, an inclination (Ty) and a direction (Txz) on the XZ plane.
Fx, Fy, and Fz are the resulting forces on each axis.
------------------------------
CALCULATING THE CENTER OF MASS
------------------------------
Total_Mass = (mass1 + mass2 + ...)
x3 = (mass1*x1 + mass2*x2 + ...) / Total_Mass
y3 = (mass1*y1 + mass2*y2 + ...) / Total_Mass
z3 = (mass1*z1 + mass2*z2 + ...) / Total_Mass
Calculates a scaled average of the locations so that the center is closest to the more massive objects.
--------------------------------------------
CALCULATING THE CROSS-PRODUCT OF TWO VECTORS
--------------------------------------------
v and w are two vectors in 3-dimensional space
v x w (read "v cross w") is a vector whose magnitude is the product of the magnitudes of v and w, that is perpendicular to both v and w, and whose direction is given by the right-hand rule.
To compute a cross-product, usually what you end up doing is computing a determinant. Write a 3x3 matrix with the first row being the components of v, the second row being the components of w, and the third row being the unit vectors, i j k. The determinant of that is the cross-product of v and w
abc = xyz force 1
def = xyz force 2
(a,b,c) * (d,e,f) = (bf-ce, cd-af, ae-bd)
------
TORQUE
------
The torque generated by a force on a system is given by r x F.
r = The position vector of the point where the force is applied relatively to the center of mass.
F = A force vector.
r = (a,b,c)
F = (d,e,f)
T_F = (bf-ce, cd-af, ae-bd)
T_F is the torque generated by F.
So you compute the torques generated by all forces
you sum them (vector sum) and then you divide the resulting vector by the "moment of inertia"
"moment of intertia":
it's a measure of the system's "resistance" to rotation, if you want
it's a function of the point masses and their distance from the center of mass
are your "rods" mass-less?
then you just need to sum up m_i * r_i^2, i *think*
where m_i is a point mass, and r_i is its distance from the center of mass
divide the sum of torques by that quantity, and that's the angular acceleration vector
so if you have 2 objects, one with mass 1kg at 2 meters, and another with mass 2kg at 3 meters, the unnamed quantity is 1*2^2 + 2*3^2 = 22
to get the linear acceleration of a point from that, you compute (angular acceleration) x r
where r is the position vector of a point relative to the center of mass
x denotes a cross-product, again
the angular acceleration is a global measure of the rotational acceleration of the system
but, something close to the center of mass accelerates less (linearly) than something far from it
you take the A x r cross-product to take that into account
A x r will give you an acceleration vector, which you can add to the acceleration vector resulting from the sum of forces (which we computed a while ago)
the sum of those two is the acceleration of an individual point in the system
acceleration of system's center of mass = sum of forces / mass of system
--------
SPRINGS
--------
http://www.pcug.org.au/~apurdam/doublespring/doublespring.htmltwo masses, connected by one spring:
M1, M2 = masses
k = coefficient of stiffness of the spring
x1, x2 = the displacements of the masses from their positions of equilibrium
x1'', x2'' = the acceleration for M1 and M2
r = viscous damping
x1', x2' = velocity of masses (from previous frame?)
x1''= k / (M1*(x2-x1)) - r*x1'
x2''= k / (M2*(x1-x2)) - r*x2'
misc notes:
Logarithmic Impulse formula.
Impulse = Log(RestSpan) - Log(CurrentSpan)
Note: this is a different from the usual linear spring formula which is taught in physics classes. With the logarithmic formula, it should be impossible to completely compress the spring. It also means that springs that have long rest lengths vibrate much slower than short ones.
---------
Jet thrust against atmosphere
---------
throtte = 0..1
thrust = throttle * max( max_thrust * (max_height - height) / max_height, 0)
------------------------------------------------------------------------
Only allowing velocity along a specific axis or in a specific diredction
------------------------------------------------------------------------
If you have a vehicle like a hovercraft, then it's okay to allow the hovercraft to have inertia and slide
around in any direction. But if you have a wheeled vehicle, or a tank, then you won't want the vehicle to slide
sideways if it's on an incline... you only want it to be able to roll forward or backward.
This is pretty easy to fix.
Take your vehicle's momentum which you've just calculated by adding it's moemntum from the previous frame and adding the forces acting on the vehicle, and convert it into an angle and a magnitude. A = The angle, B = the magnitude.
C = The angle your tank is facing. Note that this can be totally diffrent from the angle the tank is trying to move in.
Now do the following calculation:
D = A-C
D is the angle of your momentum vector if it were translated into a space where A = 0... where your tank is facing along the X axis.
Now, create a new vector... where D is it's angle, and B is it's magnitude.
Next, convert the vector into X and Y components. Then discard the Y component of the velocty, because that doesn't lie on the axis the tank can roll along. The value you have remaining, X, is the amount of velcoity which is along the axis the tank can move along.
Finally, use X as your magnitude, and C (your tanks face driection) as your angle. That is your tank's new momentum vector. Convert it into X and Y components if need be.
Note that you only want to do this sort of thing when your tank is on the ground. And you might want to do some aditional calculations... for example, your tank might be able to move along the Y axis, but there might be a lot of friction if it does so. So if you tank is moving sideways through the air fast, when it lands, you might want it to slide a bit before coming to a stop. If you give that axis a lot of friction and add the remaining velcoity to the forwar dmomentum then you can allow for that.
Bresenham's lines drawing algorithm:
public void lineBresenham(int x0, int y0, int x1, int y1, Color color)
{
int pix = color.getRGB();
int dy = y1 - y0;
int dx = x1 - x0;
int stepx, stepy;
if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; }
if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; }
dy <<= 1; // dy is now 2*dy
dx <<= 1; // dx is now 2*dx
raster.setPixel(pix, x0, y0);
if (dx > dy) {
int fraction = dy - (dx >> 1); // same as 2*dy - dx
while (x0 != x1) {
if (fraction >= 0) {
y0 += stepy;
fraction -= dx; // same as fraction -= 2*dx
}
x0 += stepx;
fraction += dy; // same as fraction -= 2*dy
raster.setPixel(pix, x0, y0);
}
} else {
int fraction = dx - (dy >> 1);
while (y0 != y1) {
if (fraction >= 0) {
x0 += stepx;
fraction -= dy;
}
y0 += stepy;
fraction += dx;
raster.setPixel(pix, x0, y0);
}
}
}
Lighting:
<Jaia> 1/x^2 is all nice and good
<Jaia> but there are 2 problems
<Jaia> first it blows up at the source, x=0
<Jaia> so we must start at some small but non-0 x
<Jaia> 2nd it never quite hits 0 though it gets close
<Jaia> so we must modify it a bit
<Jaia> the distance we need to get to is R
<sswift> So lets say I wanted the version that blows up... how would I do that, just for the sake of argument?
<Jaia> well you can use it directly B(x) = 1/x^2
<Jaia> but the 2 problems remain
<Jaia> near x=0 it blows up
<sswift> Cause I can make 0 distance a special case, and clamp the value to 255.
<Jaia> that's no good
<sswift> But that doesn't take R into account
<Jaia> since for x small but non0 it gets arbitrarily high
<Jaia> so simple claming is not a good idea
<Jaia> well we need to take R into account
<Jaia> this relates to the 2nd problem
<Jaia> basically R sets our scale
<Jaia> so really x/R is what is important here
<Jaia> x/R is 'normalised', it goes from 0 to 1
<sswift> I know someone gave me a simpler equation a long time ago which created an expoential curve between two points...
<sswift> I used it to make craters.
<Jaia> do you want exponential or 1/x^2?
<Jaia> they are quite different
<sswift> They are?
<sswift> :-)
<sswift> I tohught light fell off expoentially.
<Jaia> yes
<Jaia> e^(-x) is finite at x=0
<Jaia> and decays must more rapidly
<Jaia> 1/x^2 is a more accurate model
<sswift> And you know this how? :-)
<Jaia> since as you propagate the area of the spherical fronts of the light wave
<Jaia> goes as x^2
<sswift> You know about lighting? :-)
<Jaia> so the light intensity goes as 1/x^2
<Jaia> it's an area so it goes as ~ x^2
<Jaia> so we want the inverse of that
<Jaia> (with some constant of course but we care about overall dependence)
<Jaia> here is something you can do
<Jaia> x/R goes from 0 to 1
<Jaia> so let's split it into 100
<Jaia> x/100R
<Jaia> this is our tick mark
<Jaia> i mean, R/100
<Jaia> so we have something like
<Jaia> (R/100)^2 / (x + R/100)^2
<Jaia> notice this is equal to 1 at x=0
<Jaia> and then fall off as the inverse square as we want
<Jaia> so all it remains is to multiply by 255
<Jaia> 255(R/100)^2 / (x + R/100)^2
<Jaia> so at x=0 the value is 255 as desired and it will fall off in the desired way
<sswift> okay
<Jaia> at x=R we have 255/(101)^2 << 1
<Jaia> which is quite small
<Jaia> basically the tick mark is R/100
<Jaia> we have 100 of these to get to R
<Jaia> each time i double the tick marks, i quarter the intesity, etc.
<sswift> So what's better or worse aobut this question than the other one you gave me?
<sswift> equation
<Jaia> well ... this one is slightly more accurate
<Jaia> the other one was exactly 0 at x=R
<Jaia> but at the expense of bending the 1/x^2 falling off rate a bit
<Jaia> to push it down enough
<sswift> If it's below 1 at R, then it'll be clamped cause I have integers.
<Jaia> but here it's so small at x=R it's totally fine
<sswift> So...
<sswift> this 100 bit...
<Jaia> 255(R/5)^2 / (x + R/5)^2
<Jaia> use that then
<sswift> Does it matter at all what that number is>
<Jaia> this is enough
<Jaia> no no ...
<Jaia> 255(R/20)^2 / (x + R/20)^2
<Jaia> yes
<Jaia> 255/(number+1)^2 is the value at x=R
<Jaia> so i use 20
<Jaia> 255/(21^2) < 255/400 < 0.5
<Jaia> so we're ok
<Jaia> 255/(21^2) < 255/400 < 0.8
<Jaia> anyway
<sswift> So....
<sswift> What if I use a really large number there?
<sswift> That would mnake it more accurate?
<sswift> closer to 0?
<Jaia> no, not really ...
<Jaia> this is a little bit cheating
<Jaia> the requirement that it's "negligible" at x=R is somewhat artificial
<Jaia> and not precise
<Jaia> it's very vague in fact
<Jaia> the most sensible thing to do i think, without any other info
<sswift> Will it change the visible appearance of the lighting in any way?
<Jaia> is to make it just barely small enough at x=R
<Jaia> oh yes
<sswift> How so?
<Jaia> if you make that number huge
<Jaia> it will drop off immediately
<Jaia> near the source
<sswift> oh
<Jaia> and will be almost 0 throughout the whole range from 0 to R
<Jaia> that's like pushing R very far off
<Jaia> so that the tail of 1/x^2 is very small
<Jaia> i would make the tail just barely small enough at x=R
<Jaia> just below 1
<Jaia> so it fits your requirement
<Jaia> but still shows decay over the range
<sswift> That sounds like in my crater simulation... I could increase the exponent to ^3 ^4... and that would push the crater wall off into the distance.
<Jaia> without dying too quickly
<Jaia> and being uninteresting
<Jaia> right
<Jaia> 20 might be ok
<Jaia> 255(R/20)^2 / (x + R/20)^2
<Jaia> this is saying "R is quite far, but not extremely far so that most of the range the intensity is tiny"
<sswift> Okay
<sswift> I think I understand. If I have a problem I can tweak it to get it to look right by increasing ro decreasing that value.
<Jaia> to give you an idea
<Jaia> look at the value at x = R/2
<Jaia> halfway
<Jaia> it will be 255/(11)^2 = 255/121 ~ 2
<Jaia> from 255 to 2 halfway
<Jaia> this is a good check to see if it fits what you want
<Jaia> whatever c you pick
<Jaia> 255(R/N)^2 / (x + R/N)^2
<Jaia> N rather
<Jaia> for x=0 it's always 255
<Jaia> for x=R/2 it's 255/(N/2 + 1)^2
<Jaia> and for x=R it's 255/(N+1)^1
<Jaia> and for x=R it's 255/(N+1)^2
<Jaia> so this should give you a good idea
<sswift> The lightness will drop to 2 halfway to R with a value of R/2?
<Jaia> from 255 to 2
<Jaia> as you go from 0 to R/2
<Jaia> (on your way to R)
<Jaia> if that seems too low, drop N a bit
<Jaia> to 16 say
<sswift> Tht's increasing N. :-)
<sswift> Okay
<Jaia> then at x=R it's 255/17^2 = 255/289 < 1
The Attenuation Function
1 / ( c0 + c1 * d + c2 * d * d )
*By Setting the constants c0, c1 & c2, you can
adjust the falloff ramp of the light
*Setting c1 = 0, c2 > 0 will give you a radial
distance squared falloff
*Setting c1 > 0, c2 = 0 gives a linear falloff
*Attenuation denominators less than one will give
very large factors, thus making the light too
bright close up
*To avoid this, make sure c0 is set To some
positive number, 1 Or greater
<Jaia> 1/(x^2 + c)
<sswift> what is C
<Jaia> well, let's see
<Jaia> at x=0 we get 1/c
<Jaia> so we'll add another constant d
<Jaia> d/(x^2 + c)
<Jaia> d/c = 255
<Jaia> so x is given in the same units as R is?
<sswift> yes
<Jaia> ok
<Jaia> d/(x^2/R^2 + c)
<Jaia> at x=R we'll set it to 1
<Jaia> d/(1+c) = 1
<Jaia> d = 1+c
<Jaia> 255c = 1+c
<Jaia> c = 1/254
<Jaia> d = 255/254
<Jaia> change the value of d/(1+c) if you want
<Jaia> this is the value at x=R
<Jaia> d/c=255 is fixed
<Jaia> d/(1+c) is the final value, play with it if d/(1+c) = 1 seems no good
<Jaia> so we get as an idea
<Jaia> (255/254) / (x^2/R^2 + 1/254)
<sswift> Is that it?
<sswift> :-)
<Jaia> yup
<Jaia> play with d/(1+c) as i said
<Jaia> (you always have d/c = 255)
<Jaia> d/(x^2 + c) is our general rule
<Jaia> d/(1+c) gives the value of intensity at R
<Jaia> i set it to 1 here
<Jaia> (255/254) / (x^2/R^2 + 1/254)
<Jaia> and got that
<Jaia> and got that
<sswift> Why did you not set it to 0?
<Jaia> you can't
<Jaia> this function is never 0
<Jaia> d/(1+c) = 0
<Jaia> d = 0
<Jaia> oops
<Jaia> and besides
<Jaia> make it too small
<Jaia> will push out R too far
<Jaia> effectively speaking
<Jaia> as we discussed before
<sswift> hm...
<Jaia> your inensity will die out immediately
<Jaia> and be tiny for most of the range
<sswift> Could you perhaps make the range start at 256, go to 1, and then subtract 1 from the end result?
<Jaia> if you want
<Jaia> sure
<sswift> Is there anyhtign bad about that?
<Jaia> (256/255) / (x^2/R^2 + 1/255) - 1
<Jaia> not at all
<Jaia> in fact it's a good idea
<sswift> (256/255) / (x^2/r^2 + 1/255) - 1
<sswift> You typed it first
<sswift> :-)
<Jaia> that looks ok
<sswift> at least I know I was right
<Jaia> in general
<Jaia> d / (x^2/R^2 + c) - d/(1+c)
<Jaia> d/(1+c) is what your value at R would have been
<Jaia> so it will be useful to see how far you're pushing R
<Jaia> as before
<Jaia> 256c / (x^2/R^2 + 1/255) - 256c/(1+c)
<Jaia> c is our free parameter
<Jaia> the last term, 256c/(1+c) sets the scale
<Jaia> is it's much less than 1, it probably dies too quickly
<Jaia> i'd keep it around 1 or 2 or so
<Jaia> but maybe higher even
<Jaia> play with it
<Jaia> in all cases the value at R is 0
<Jaia> oh no ...
<Jaia> ack
<Jaia> d / (x^2/R^2 + c) - d/(1+c)
<sswift> ack what
<sswift> :-)
<Jaia> d/c - d/(1+c) = 255
<Jaia> d = 255/(1/c + 1/(1+c))
<Jaia> so this is d in terms of c
<Jaia> 255/(1/c + 1/(1+c)) / (x^2/R^2 + c) - 255/(1/c + 1/(1+c)) / (1+c)
<Jaia> that last term
<Jaia> - 255/blah blah
<Jaia> that's what you don't want too small
<Jaia> set it to 1-5 or so
<Jaia> and see how it works
<Jaia> the lower it is, the faster it decays
<sswift> what was wrong with this? (256/255) / (x^2/r^2 + 1/255) - 1
<Jaia> this is the case for 1
<Jaia> it may work well
<Jaia> i just gave you a more general case
<sswift> ah.
<Jaia> where you can tweak the parameter at will
<Jaia> just solve that last term
<Jaia> 255(1/(c(1+c)) + 1)
<Jaia> 255/(1/c + 1/(1+c)) / (x^2/R^2 + c) - 255/(1/(c(1+c) + 1)
<Jaia> ...
<Jaia> same thing
<Jaia> just cleaned up a bit
<Jaia> 255/(1/c + 1/(1+c)) / (x^2/R^2 + c) - 255/(1/(c(1+c)) + 1)
<Jaia> the one you gave has - 1
<Jaia> as a case
<Jaia> by changing c
<Jaia> you can make the - number different
<Jaia> for a certain c you get - 1
<Jaia> as you showed
<Jaia> change c to get different values - something
<sswift> Okay
<Jaia> making that something bigger makes the decay slower
<Jaia> etc.
<Jaia> etc.
<Jaia> unless i made a mistake
<Jaia> let me see
<Jaia> d/(x^2/R^2 + c)
<Jaia> d/(x^2/R^2 + c) - d/(1+c)
<Jaia> d/c - d/(1+c) = 255
<Jaia> d = 255/(1/c + 1/(1+c))
<Jaia> ya, looks ok
lighting
--------
; DX7 Lighting equation:
; Attenuation = 1 / (C0 + C1*D + C2*D*D)
;
; For some reason this equation does not seem to produce the expected results!
;
; In Blitz:
; C0,C2 = 0. C1=Light radius.
;
; Mark has said that Blitz's lighting follows this equation:
; Brightness = 1.0 / (Distance / Light_Radius)
;
; Which does what the DX7 equation should do, but I don't know why they're so different.
;
; The shadow system on the other hand uses a modified linear falloff.
;
; This is so that the distance at which the shadow attenuates completely is not really
; far away from it's origin. If we used the other equations, a caster would almost
; never be outside a light's range, and that would be very bad for the framerate!
; Standard linear falloff equation:
;
; L = Lightness
; R = Light max radius
; D = Distance from light
;
; L = (R - D) / R
; Linear falloff, modified to be offset so that:
;
; If D < R1 then L > 1
; If D = R1 then L = 1
; If D >= R2 then L = 0
;
; L = (R2 - (D-R1)) / R2
Light_Falloff# = (R2# - (Vdist# - R1#)) / R2#
; Clamp the falloff value if we are outside max_range, or inside min_range.
If (Light_Falloff# < 0) Then Light_Falloff# = 0
If (Light_Falloff# > 1) Then Light_Falloff# = 1
; Convert the falloff value to a color for this vertex.
Light_Falloff# = 255.0 * (1.0 - Light_Falloff#)
a = 1 / (c0 + c1*d + c2*d*d)
converts to:
v = light_r * light_min_radius / distance
Which can be inverted to:
d = light_r * light_min_radius / v
To find out the distance at which v becomes some value.