How to do dot balls?
category: code [glöplog]
I know it's not 1993 anymore, but dot balls are still on my (very ling) list of "things I once wanted to do in real-time". Back then I pre-calced them and blitted them. But on the one hand I know that all those "x hundreds of pixel balls" where probably done diffrently and on the other hand but my current target platform hardly has any RAM left for that.
Any examples for me to learn from?
Any examples for me to learn from?
long time ago, i did "realtime" dotballs in Qbasic through drawing a spiral, but in the form of a sphere.... The other approach, and the slightly faster one was to draw dot circles with different diameters.
we need a dot balls tutorial.
Are you talking about vectorballs/bobs, or spheres made up of dots?
pixel, sprite.. who cares :) "draw x on position y". adok, some wise words bitte!
I should have written dot sphere, right. A sphere mde of dots in 3d, maybe with projection and z<0 detection and rotation around 2 or 3 axis. Dots may be spread regular or like this
Assuming you have some kind of x/y/z 3D rotation routine already, here's how you'd set up a bunch of random points on a sphere (rnd being the good ol' basic function to generate a random number from 0 to 1 :):
and here's something to distribute the points evenly:
err.. roughly at least.
Code:
for p = 1 to points
angle = rnd * 2 * pi
' put dot somewhere on a 2D circle
x(p) = cos(angle) * radius
y(p) = sin(angle) * radius
' rotate dot along a circle facing the x-axis of radius y(p)
zangle = rnd * 2 * pi
z(p) = cos(zangle) * y(p)
y(p) = sin(zangle) * y(p)
next p
and here's something to distribute the points evenly:
Code:
for p = 1 to longpts * latpts
angle = (pi / longpts) * ((pi - 1) mod longpts)
x(p) = cos(angle) * radius
y(p) = sin(angle) * radius
zangle = ((2 * pi) / latpts) * ((p - 1) \ longpts)
z(p) = cos(zangle) * y(p)
y(p) = sin(zangle) * y(p)
next p
err.. roughly at least.
2nd box edit: (p - 1), not (pi - 1)
Or, if we're talking about an oldskool platform where this sort of thing matters: you can save a few multiplications by treating each point on the sphere as a rotation of (1,0,0) - i.e. just add a random (or regular) offset per point to your angles when doing the rotation, rather than plotting them in 3D space in advance.
Many dotballs on the Atari ST (and some were nice) were made with rotation around the vertical axis only. This saves a lot of time (no muls at all if i'm correct, only table lookup).
Breaking it made it a lot more interesting:
http://studio.sketchpad.cc/sp/pad/view/yldXhh44XZ/latest
http://studio.sketchpad.cc/sp/pad/view/yldXhh44XZ/latest
Lol, at first I thought this was a spin off of Adok's article :)
I was also wondering about this effect and how to do it on the CPC (even if it has been done once in unique iirc, though not a complete sphere with many many of them :)
I was searching along the lines of a different approach, not just do regular 3d rotations of points in a sphere, because I thought the insistence of this kind of dot objects was there for a reason, for example one approach I was thinking, having precalced x,y coordinates in an order that goes along in a theta,phi spherical like coords grid, so that you can "scroll" the pattern of random dots (or also text forming dots like in Unique or any bitmap you like on your balls XD) in their new directions and get the x,y coords very fast without real rotation.
Yet I was too lazy to try back then :P
I was also wondering about this effect and how to do it on the CPC (even if it has been done once in unique iirc, though not a complete sphere with many many of them :)
I was searching along the lines of a different approach, not just do regular 3d rotations of points in a sphere, because I thought the insistence of this kind of dot objects was there for a reason, for example one approach I was thinking, having precalced x,y coordinates in an order that goes along in a theta,phi spherical like coords grid, so that you can "scroll" the pattern of random dots (or also text forming dots like in Unique or any bitmap you like on your balls XD) in their new directions and get the x,y coords very fast without real rotation.
Yet I was too lazy to try back then :P
JAC!: on what platform?
The table lookup is my current plan (1 axis only, hard enough for the platform). But thinking about the symmetrie, I thought there is some well know optimization so I can stay with some add/sub and a reasonable table size.
@rudi: Very oldschool but If I tell, I'll spoil the compo ;-)
@rudi: Very oldschool but If I tell, I'll spoil the compo ;-)
JAC!: ok, maybe you've looked into harmonic oscillators.
If you do a dot-sphere with evenly distributed dots, and then skip the 3d-projection (balls tend to look OK even without it) you can get away with some serious optimizations using mirroring.
Still on the ST, quite cool effect showing objects with only one axis of rotation but morphing between objects can be seen here:
http://pouet.net/prod.php?which=15439
http://pouet.net/prod.php?which=15439
I think you can have (x;y) and (-x;y) plotted in different colors (back/front) without any visual hint of it. Thus you only need half a sinus table before looping, which gains space too.
Assuming phoenix notation.
for p = 1 to points
tmpX = rnd - .5 // tmpX in ]-.5, .5[
tmpY = rnd - .5 // tmpY in ]-.5, .5[
tmpZ = rnd - .5 // tmpZ in ]-.5, .5[
d = radius / sqrt(tmpX*tmpX + tmpY*tmpY + tmpZ*tmpZ) // d is radius / norm(vect(tmpX, tmpY, tmpZ)
x(p) = tmpX * d;
y(p) = tmpY * d;
z(p) = tmpZ * d;
// p coord is : vect(tmpX, tmpY, tmpZ) * radius / norm(vect(tmpX, tmpY, tmpZ))
// so norm(p) = radius
next p
With this method :
- the point repartition is homogeneous unlike phoenix method. (more point on poles)
- you only have one "/" and one "sqrt" instead of 4 trigonometric calls.
Maybe I say shits
for p = 1 to points
tmpX = rnd - .5 // tmpX in ]-.5, .5[
tmpY = rnd - .5 // tmpY in ]-.5, .5[
tmpZ = rnd - .5 // tmpZ in ]-.5, .5[
d = radius / sqrt(tmpX*tmpX + tmpY*tmpY + tmpZ*tmpZ) // d is radius / norm(vect(tmpX, tmpY, tmpZ)
x(p) = tmpX * d;
y(p) = tmpY * d;
z(p) = tmpZ * d;
// p coord is : vect(tmpX, tmpY, tmpZ) * radius / norm(vect(tmpX, tmpY, tmpZ))
// so norm(p) = radius
next p
With this method :
- the point repartition is homogeneous unlike phoenix method. (more point on poles)
- you only have one "/" and one "sqrt" instead of 4 trigonometric calls.
Maybe I say shits
FFFUUUU
You also can use simple coordinate rotation :
But the result can be to mush regular
Code:
for p = 1 to points
tmpX = rnd - .5 // tmpX in ]-.5, .5[
tmpY = rnd - .5 // tmpY in ]-.5, .5[
tmpZ = rnd - .5 // tmpZ in ]-.5, .5[
// d = radius / norm(vect(tmpX, tmpY, tmpZ))
d = radius / sqrt(tmpX*tmpX + tmpY*tmpY + tmpZ*tmpZ)
// p = vect(tmpX, tmpY, tmpZ) * radius / norm(vect(tmpX, tmpY, tmpZ))
// so norm(p) = radius
x(p) = tmpX * d;
y(p) = tmpY * d;
z(p) = tmpZ * d;
next p
You also can use simple coordinate rotation :
Code:
for p = 1 to points by 48
tmpX = rnd - .5 // tmpX in ]-.5, .5[
tmpY = rnd - .5 // tmpY in ]-.5, .5[
tmpZ = rnd - .5 // tmpZ in ]-.5, .5[
// d = radius / norm(vect(tmpX, tmpY, tmpZ))
d = radius / sqrt(tmpX*tmpX + tmpY*tmpY + tmpZ*tmpZ)
// p = vect(tmpX, tmpY, tmpZ) * radius / norm(vect(tmpX, tmpY, tmpZ))
// so norm(p) = radius
tmpX = tmpX * d;
tmpY = tmpY * d;
tmpZ = tmpZ * d;
p[1 to 8] = (+/-) tmpX, (+/-) tmpY, (+/-) tmpZ
p[9 to 16] = (+/-) tmpX, (+/-) tmpZ, (+/-) tmpY
p[16 to 24] = (+/-) tmpY, (+/-) tmpX, (+/-) tmpZ
p[24 to 32] = (+/-) tmpY, (+/-) tmpZ, (+/-) tmpX
p[32 to 40] = (+/-) tmpZ, (+/-) tmpX, (+/-) tmpY
p[40 to 48] = (+/-) tmpZ, (+/-) tmpY, (+/-) tmpX
next 48 p
But the result can be to mush regular
1 pass.
1 points and is 47 symmetry.
OK
Different POV.
To much regular.
3 pass. 3x48 points.
To mush regular.
48 pass without any symmetry.
Nice
3x48 pass without any symmetry.
Very Nice
1 points and is 47 symmetry.
OK
Different POV.
To much regular.
3 pass. 3x48 points.
To mush regular.
48 pass without any symmetry.
Nice
3x48 pass without any symmetry.
Very Nice
NIAN NIAN
Code:
private FloatBuffer buildData(double radius, int passNumber) {
double[] signs = new double[] {-1, 1};
FloatBuffer dataBuffer = FloatBuffer.allocate(passNumber * 48 * 4);
dataBuffer.rewind();
for (int i = 0 ; i < passNumber ; i++) {
double x = Math.random() - .5;
double y = Math.random() - .5;
double z = Math.random() - .5;
double d = radius / Math.sqrt(x * x + y * y + z * z);
x *= d;
y *= d;
z *= d;
for (double xSign: signs) {
for (double ySign: signs) {
for (double zSign: signs) {
for (int perm = 0 ; perm < 6 ; perm++) {
float[] value = getPerm(perm, (float) (xSign * x), (float) (ySign * y), (float) (zSign * z));
dataBuffer.put(value); // put X,Y,Z
dataBuffer.put(1); // put W = 1
}
}
}
}
}
dataBuffer.rewind();
return dataBuffer;
}
private float[] getPerm(int perm, float x, float y, float z) {
switch (perm) {
case 0:
return new float[] {x, y, z};
case 1:
return new float[] {x, z, y};
case 2:
return new float[] {y, x, z};
case 3:
return new float[] {y, z, x};
case 4:
return new float[] {z, x, y};
case 5:
return new float[] {z, y, x};
default:
return new float[] {x, y, z};
}
}
http://mathworld.wolfram.com/SpherePointPicking.html
Another easy way to pick a random point on a sphere is to generate three ***Gaussian** random variables x, y, and z. Then the distribution of the vectors [x; y; z] / sqrt(x^2+y^2+z^2) is uniform over the surface S^2 (Muller 1959, Marsaglia 1972).
Another easy way to pick a random point on a sphere is to generate three ***Gaussian** random variables x, y, and z. Then the distribution of the vectors [x; y; z] / sqrt(x^2+y^2+z^2) is uniform over the surface S^2 (Muller 1959, Marsaglia 1972).
Thanks for all the responses. Actually the creation of the pixel distribution was not my intend question, but how to do the rotation itself with minimum of computing/RAM, based on the symmetrie. Today I'll leave for my vacation and so I'm look forward to some coding when I'm back ;-)
Hey Jac, here is how to do it:
- you only have to calculate the rotation of one half circle
- you can mirror this through the center to get a full circle (swap x,y)
- you can use this full circle to build the ball
- calculate the rotation of a vector perpendicular to the center of the main circle
- scale this vector
- scale the circle to smaller versions with tables
- use the vector to offset the smaller circle versions
- you only have to calculate the rotation of one half circle
- you can mirror this through the center to get a full circle (swap x,y)
- you can use this full circle to build the ball
- calculate the rotation of a vector perpendicular to the center of the main circle
- scale this vector
- scale the circle to smaller versions with tables
- use the vector to offset the smaller circle versions