//cube.cpp
//I am having trouble with the following C++ code (aiming to be C, but
with a declaration error if compiled in C)--or perhaps mainly the
math--to rotate a cube. The code is for Watcom C++, but I added a
#define for Turbo C++ but do not recall if I tested that. This was
originally for Watcom 11, and I compiled it in Open Watcom 1.9 under
DOSbox. Depending on how a,b (3D alpha, beta) are defined, the code has
shown an elongated pyramid from the top left ((1,1)) to the screen
centre, and has moved it around a bit (this version just shows a few
lines coming from (1,1).) However, I think perhaps what I read about 3D
is wrong. It seems the way I have my 101-unit-sized cube defined, when
vertices' z-values are translated from the range (-50,50) to (1,101),
101 is too big to divide the x and y by and divides those down to about
1. Is it even right that to calculate x,y from 3D onto the plane you let
x=x/z, y=y/z? Each line() in my draw_cube() uses those calculations. It
is hard for me to figure out if my equations are wrong, or if there is
some bug in my code. I would like to learn more C and graphics
programming... can anyone help with this 3D cube problem?
void put_pixel(int x, int y, int colour);
void line(int x0, int y0, int x1, int y1, int colour);
//void circle();
void draw_square(int square[][4], int colour);
void draw_cube(int cube[][8], int colour);
//void translate_shape(int shape[][sizeof(int / 2)], int colour);
//Watcom
//void wait_retrace();
void clrscr();
int main(void)
{
int i;
//define polyhedra
int cube_100[3][8]={{-50, -50, 50, 50, -50, -50, 50, 50},
{-50, 50, 50, -50, -50, 50, 50, -50},
{-50, -50, -50, -50, 50, 50, 50, 50}};
int cube_a[3][8]={{-50, -50, 50, 50, -50, -50, 50, 50},
{-50, 50, 50, -50, -50, 50, 50, -50},
{-50, -50, -50, -50, 50, 50, 50, 50}};
float x,y,z;
//define 3D angles and rotation matrices
//alpha, beta, lambda
float a=0.785398; //pi/4;
float b=0.785398;
float l=0.785398;
//matrices for rotation in 3D realspace by a,b,l. When compiled as a
C file, I get an error for these array declarations that they must be
constant. I want my program to be C, and it seems the trigonometric
functions do have only one value. Other than that, I am not declaring
these as constants; I do not really understand the error. If I recall,
array assignments may have to be separate from declarations in C. Is
this so--or perhaps at least if they use functions?
float mR3a[3][3]={{1, 0, 0}, {0, cos(b), sin(b)}, {0, -sin(l), cos(l)}};
float mR3b[3][3]={{cos(a), 0, -sin(a)}, {0, 1, 0}, {sin(l), 0, cos(l)}};
float mR3l[3][3]={{cos(a), sin(a), 0}, {-sin(b), cos(b), 0}, {0, 0, 1}};
float mR3r[3][3]={{cos(b)*sin(l), cos(b)*sin(l), -sin(b)},
{sin(a)*sin(b)*cos(l)-cos(b)*sin(l), sin(a)*sin(b)*sin(l)+cos(a)*cos(b),
sin(a)*cos(b)}, {cos(a)*sin(b)*cos(l)+sin(a)*sin(l),
cos(a)*sin(b)*sin(l)-sin(a)*cos(b), cos(a)*cos(b)}};
//clearscreen, set video mode 640x480x16; return error if impossible
//initgraph(VGA, 2, ""); //almost right in Turbo C
clrscr();
if(_setvideomode(_VRES16COLOR)==0)
{
printf("%s", "Cannot switch to 640x480x16 colour mode.\r\n");
return 1;
}
//increment lambda and rotate cube on that or also other angles
for(l=0;l<=6.283185;l+=0.104720)
{
mR3r[0][0]=cos(b)*sin(l);
mR3r[0][1]=cos(b)*sin(l);
mR3r[0][2]=-sin(b);
mR3r[1][0]=sin(a)*sin(b)*cos(l)-cos(b)*sin(l);
mR3r[1][1]=sin(a)*sin(b)*sin(l)+cos(a)*cos(b);
mR3r[1][2]=sin(a)*cos(b);
mR3r[2][0]=cos(a)*sin(b)*cos(l)+sin(a)*sin(l);
mR3r[2][1]=cos(a)*sin(b)*sin(l)-sin(a)*cos(b);
mR3r[2][2]=cos(a)*cos(b);
for(i=0; i<=7; i++)
{
//wait; set video mode to text; exit
//For some reason, the program does not always get to the following
getch(), depending on how source is edited! It seems just to depend on
a,b angles and usually works when they are changed in a way that
displays an elongated pyramid. I have gone back and forth many times.
getch();
_setvideomode(_TEXTC80);
return 0;
I also asked on Freenode IRC ##math and someone mentioned projecting the
polyhedron to a plane. So, I tried different positive numbers to
multiply x,y coordinates in line() by. Eventually, this drew an object
sort of like a long box in the middle of the screen. Apparently, my
draw_cube() has a couple errors, but that should be easy to fix.
My next question is though, how do you know what a good z-coordinate is
to define a plane parallel to XY to project to, and why do you even need
to do this; how does this even work? Also, why is what should be a cube
so elongated as to look like a rectangular box rather than regular cube?
Does this all depend on the size of the cube and plane it is projected to?
Someone on ##math defined this something like projecting (x,y,z) when
z=s to (xs/z,ys/z,s) (or maybe the final z-coordinate was unchanged, not
s... anyway, z is finally dropped when the polyhedron is projected to a
plane.)
My intuition about the C vs C++ declarations seems to be correct; never
mind that question. Also, the program gets to getch() as long as the
cube is in the middle of the screen. Perhaps when it was near the
corner, something had been rounded down to zero.
//cube.cpp
//Here is an example of what I did since my last post. It shows a small
but visible cube with slightly warped perspective close to (1,1). What
do I need to do to bring this out to the middle of the screen and have
normal-looking perspective? (see comments in last post)
void put_pixel(int x, int y, int colour);
void line(int x0, int y0, int x50, int y50, int colour);
void draw_square(int square[][4], int colour);
void draw_cube(int cube[][8], int colour);
void clrscr();
//clearscreen, set video mode 640x480x16; return error if impossible
//initgraph(VGA, 2, ""); //almost right in Turbo C
clrscr();
if(_setvideomode(_VRES16COLOR)==0)
{
printf("%s", "Cannot switch to 640x480x16 colour mode.\r\n");
return 1;
}
for(i=0;i<=7;i++)
{
cube_a[0][i]+=316;
cube_a[1][i]+=236;
cube_a[2][i]+=301;
}
draw_cube(cube_a,2);
getch();
//increment lambda and rotate cube on that or also other angles
for(l=0;l<=6.283185;l+=0.104720)
{
mR3r[0][0]=cos(b)*sin(l);
mR3r[0][1]=cos(b)*sin(l);
mR3r[0][2]=-sin(b);
mR3r[1][0]=sin(a)*sin(b)*cos(l)-cos(b)*sin(l);
mR3r[1][1]=sin(a)*sin(b)*sin(l)+cos(a)*cos(b);
mR3r[1][2]=sin(a)*cos(b);
mR3r[2][0]=cos(a)*sin(b)*cos(l)+sin(a)*sin(l);
mR3r[2][1]=cos(a)*sin(b)*sin(l)-sin(a)*cos(b);
mR3r[2][2]=cos(a)*cos(b);
for(i=0; i<=7; i++)
{
I did not read your code, but what you need to read/google/whatever seems to be "perspective projection". The division thing essentially causes those parts farther away to appear smaller. Parallel projection or orthographic basically omits this step. what is left to do afterwards is scaling the resulting image to your screen coordinates.
As I hardly use the openwatcom graphics thingy anymore, I do not know the correct factors to use.
You should look up perspective projection and coordinate transformations in a decent maths book. You may also want to read 'The Red Book'. It is available online and contains a primer on the various types of transformations(Chapter 3). The coordinate systems are openGL specific, and may require adaptation to your case. 'The Blue Book' may also be worth a look.
> I did not read your code, but what you need to read/google/whatever
> seems to be "perspective projection". The division thing essentially
> causes those parts farther away to appear smaller. Parallel projection
> or orthographic basically omits this step. what is left to do afterwards
> is scaling the resulting image to your screen coordinates.
> As I hardly use the openwatcom graphics thingy anymore, I do not know
> the correct factors to use.
Thanks; I found that Abe's Demo School discusses it, though it
contradicts Denthor's tutorials, which seem to have a mistake. I had
also tried to derive rotation matrices myself, but failed, so I got them
from the VLA text files again. Open Watcom is not a graphics thing: it
is a compiler.
> You should look up perspective projection and coordinate transformations
> in a decent maths book. You may also want to read 'The Red Book'. It is
> available online and contains a primer on the various types of
> transformations(Chapter 3). The coordinate systems are openGL specific,
> and may require adaptation to your case. 'The Blue Book' may also be
> worth a look.
Can you or anyone recommend the top 3D maths or 3D graphics books, even
if they are over 1000 pages? I would prefer something that uses C or
C-style C++ if anything. I looked at 'The Red Book,' but it was not as
clear as Abe's Demo School.
I will attach my fixed code, but I am going to do a new version, because
now some stuff is hard, coded in.
//cube.cpp
//This is my new code. Apparently I had derived the rotation matrices
wrong, so I re-checked VLA's texts. I had read Denthor's tutorials, but
one thing contradicted Abe's Demo School, but by reading these I learned
what I was doing wrong. Apparently it is ok to rotate an object defined
around the origin, and then translate it to the centre and forward all
at once when drawing it. The distance from the screen and to the centre
is hard-coded into the draw_cube() this code uses, but I will do a new
version to control those or at least input them to functions.
void put_pixel(int x, int y, int colour);
void line(int x0, int y0, int x5, int y5, int colour);
void draw_cube(int cube[][8], int colour);
#ifdef wc
void clrscr();
#endif
//clearscreen, set video mode 6256x4256x16; return error if impossible
//initgraph(VGA, 2, ""); //almost right in Turbo C
clrscr();
if(_setvideomode(_VRES256COLOR)==0)
{
printf("%s", "Cannot switch to 6256x480x256 colour mode.\r\n");
return 1;
}
draw_cube(cube_a,2);
getch();
//increment lambda and rotate cube on that or also other angles
for(l=0;l<=6.2831;l+=0.1047)
{
mR3r[0][0]=cos(b)*cos(l)+sin(a)*sin(b)*sin(l);
mR3r[0][1]=-cos(a)*sin(l)+cos(l)*sin(a)*sin(b);
mR3r[0][2]=cos(a)*sin(b);
mR3r[1][0]=cos(a)*sin(l);
mR3r[1][1]=cos(a)*cos(l);
mR3r[1][2]=-sin(a);
mR3r[2][0]=-cos(l)*sin(b)+cos(b)*sin(a)*sin(l);
mR3r[2][1]=sin(b)*sin(l)+cos(b)*cos(l)*sin(a);
mR3r[2][2]=cos(a)*cos(b);
for(i=0; i<=7; i++)
{
On Sat, 23 Jun 2012 19:29:27 -0700, David Melik wrote:
> Can you or anyone recommend the top 3D maths or 3D graphics books, even
> if they are over 1000 pages?
The one I learned from is:
Computer Graphics - Principles and Practice
Foley, van Dam, Feiner, Hughes
Addison Wesley
At the time, it was widely considered to be the bible of 3D graphics, but
that was in 1995. It's still a good book for the maths and general
principles, but it doesn't cover OpenGL or DirectX, nor modern hardware or
algorithms tailored to such.
> At the time, it was widely considered to be the bible of 3D graphics, but
> that was in 1995. It's still a good book for the maths and general
> principles, but it doesn't cover OpenGL or DirectX, nor modern hardware or
> algorithms tailored to such.
Which, given the OP doesn't use any of those things, should be just fine.
In comp.graphics.algorithms David Melik <dchme...@hipplanet.com> wrote:
> //cube.cpp
> //This is my new code. Apparently I had derived the rotation matrices
> wrong,
There's a real elegant solution at
http://paulbourke.net/geometry/rotate/ including C code. I successfully used his stuff
to generate the rotating figure on the banner
near the top of the page
http://www.forkosh.com/lineart.html Here's the function for those rotations, with
structs POINT3D and LINE3D defined in the obvious way.
See how very easy that is with Bourke's stuff?
I wonder if there's a yet easier way.
/* ==========================================================================
* Function: rotate3dpt ( POINT3D p, LINE3D axis, double theta )
* Purpose: rotate point p around axis by angle theta (in degrees)
* --------------------------------------------------------------------------
* Arguments: p (I) POINT3D p={double x,y,z} containing coords
* of point to be rotated.
* axis (I) LINE3D axis={POINT3D pt1,pt2} defines the
* rotation axis, with pt1 its "tail", and
* pt2 its "head", such that positive rotation
* angles are interpreted by the right-hand
* screw rule, pointing toward the head.
* theta (I) double containing rotation in degrees.
* --------------------------------------------------------------------------
* Returns: ( POINT3D ) {double x,y,z} of rotated coords,
* or just returning p's coords for any error
* --------------------------------------------------------------------------
* Notes: o See http://paulbourke.net/geometry/rotate/ * for discussion/derivation and original source code.
* o we could interpret axislen as theta, thus eliminating
* that arg, but we're now just normalizing axis.
* ======================================================================= */
/* --- entry point --- */
POINT3D rotate3dpt ( POINT3D p, LINE3D axis, double theta )
{
POINT3D r = p; /*p rotated by theta back to caller*/
double costheta=1.0, sintheta=0.0, /* cos,sin(theta in radians) */
pi = 3.14159265359; /* pi (convert theta to radians) */
POINT3D axispt = {0.0,0.0,0.0}; /*axis.pt2 after axis.pt1 xlated to origin*/
double axislen = 1.0; /* to normalize axispt */
/* ---
* xlate p and axispt=axis.pt2 so that axis.pt1 goes to origin
* (note: axis.pt1 is tail, and axis.pt2 is head of rotation vector)
* ----------------------------------------------------------------- */
axispt.x = axis.pt2.x - axis.pt1.x;
axispt.y = axis.pt2.y - axis.pt1.y;
axispt.z = axis.pt2.z - axis.pt1.z;
p.x -= axis.pt1.x;
p.y -= axis.pt1.y;
p.z -= axis.pt1.z;
/* ---
* normalize axispt
* ---------------- */
axislen = sqrt( ((axispt.x)*(axispt.x))
+ ((axispt.y)*(axispt.y)) + ((axispt.z)*(axispt.z)) );
if ( axislen < 0.0000001 ) goto end_of_job; /* axis.pt2=axis.pt1 */
axispt.x /= axislen;
axispt.y /= axislen;
axispt.z /= axislen;
/* ---
* apply rotation
* -------------- */
costheta = cos(pi*theta/180.);
sintheta = sin(pi*theta/180);
r.x = r.y = r.z = 0.0; /* initialization */
/* --- x --- */
r.x += (costheta + (1.-costheta) * axispt.x * axispt.x)* p.x;
r.x += ((1.-costheta) * axispt.x * axispt.y - axispt.z * sintheta) * p.y;
r.x += ((1.-costheta) * axispt.x * axispt.z + axispt.y * sintheta) * p.z;
/* --- y --- */
r.y += ((1.-costheta) * axispt.x * axispt.y + axispt.z * sintheta) * p.x;
r.y += (costheta + (1.-costheta) * axispt.y * axispt.y)* p.y;
r.y += ((1.-costheta) * axispt.y * axispt.z - axispt.x * sintheta) * p.z;
/* --- z --- */
r.z += ((1.-costheta) * axispt.x * axispt.z - axispt.y * sintheta) * p.x;
r.z += ((1.-costheta) * axispt.y * axispt.z + axispt.x * sintheta) * p.y;
r.z += (costheta + (1.-costheta) * axispt.z * axispt.z)* p.z;
/* ---
* translate rotated point back to original coords
* ----------------------------------------------- */
r.x += axis.pt1.x;
r.y += axis.pt1.y;
r.z += axis.pt1.z;
/* ---
* return rotated point to caller
* ------------------------------ */
end_of_job:
return ( r );
} /* --- end-of-function rotate3dpt --- */
-- John Forkosh ( mailto: j...@f.com where j=john and f=forkosh )
> In comp.graphics.algorithms David Melik <dchme...@hipplanet.com> wrote:
>> //cube.cpp
>> //This is my new code. Apparently I had derived the rotation matrices
>> wrong,
> There's a real elegant solution at
> http://paulbourke.net/geometry/rotate/ > including C code. I successfully used his stuff
> to generate the rotating figure on the banner
> near the top of the page
> http://www.forkosh.com/lineart.html > Here's the function for those rotations, with
> structs POINT3D and LINE3D defined in the obvious way.
> See how very easy that is with Bourke's stuff?
> I wonder if there's a yet easier way.
Thanks; this does look like an elegant solution. It uses either some
matrix transformations, or also quaternions, which I am trying to
understand now. I have not actually tried this code yet because I want
to read more about how it works first.