SvgaLib/threeDKit/3dinit.c

334 lines
7 KiB
C

/*
3DKIT version 1.3
High speed 3D graphics and rendering library for Linux.
Copyright (C) 1996, 1997 Paul Sheer psheer@icon.co.za
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/
/*
File: 3dinit.c
Contains the utility function initcolor for initialising the normal color
vectors of a surface, and the a function to initialise a 3D ellipse.
This file is incomplete and should contain a number of useful
tools to initialise different 3D primitives.
*/
#include <config.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#ifndef DO_NOT_USE_VGALIB
#include <vga.h>
#endif
#include <vgagl.h>
#include "./3dkit.h"
#include "./3dinit.h"
double mag (Vec v)
{
double r;
if ((r = sqrt (v.x * v.x + v.y * v.y + v.z * v.z)) == 0)
return 1;
return r;
}
/* adds the normal vector to v at point (i,j), calculated from the
panel d. d is one of the four panels at (i,j).
i
-->
0|3 |
-+- | j
1|2 v
*/
void norm_vec (TD_Surface * surf, int i, int j, Vec * v, int d)
{
int i1 = 0, i2 = 0, j1 = 0, j2 = 0, w = surf->w;
double x, y, z, r;
double x1, y1, z1;
double x2, y2, z2;
Vec u;
switch (d & 3) {
case 0:
j1 = -1;
i2 = -1;
break;
case 1:
i1 = -1;
j2 = 1;
break;
case 2:
j1 = 1;
i2 = 1;
break;
case 3:
i1 = 1;
j2 = -1;
break;
}
x = surf->point[i + j * w].x;
y = surf->point[i + j * w].y;
z = surf->point[i + j * w].z;
x1 = surf->point[i + i1 + (j + j1) * w].x - x;
y1 = surf->point[i + i1 + (j + j1) * w].y - y;
z1 = surf->point[i + i1 + (j + j1) * w].z - z;
x2 = surf->point[i + i2 + (j + j2) * w].x - x;
y2 = surf->point[i + i2 + (j + j2) * w].y - y;
z2 = surf->point[i + i2 + (j + j2) * w].z - z;
u.x = y1 * z2 - z1 * y2;
u.y = z1 * x2 - x1 * z2;
u.z = x1 * y2 - y1 * x2;
r = mag(u);
v->x += u.x / r;
v->y += u.y / r;
v->z += u.z / r;
}
/*Following routine initialise a surface's normal vectors*/
/*(FIXME: this doesn't work 100% at the edges, I think it
needs Frenet-Sneret (spelling?) formula) */
/* n gives the brightness of the surface and the direction of the normal.
normally +256 or -256 (can be less to give a darker surface) */
void TD_initcolor (TD_Surface * surf, int n)
{
int i, j, k, w = surf->w, l = surf->l, m;
double r, ru;
int w0, ww;
int l0, ll;
Vec v, u;
if (w > 2) {
w0 = 1;
ww = w - 1;
} else {
w0 = 0;
ww = w;
}
if (l > 2) {
l0 = 1;
ll = l - 1;
} else {
l0 = 0;
ll = l;
}
for (j = 0; j < l; j++)
for (i = 0; i < w; i++) {
/* normal at a point is the average of the four cross products
except at the edge points where the gradient of the normal near
the edge is considered as well */
v.x = v.y = v.z = 0;
u.x = u.y = u.z = 0;
m = 0;
if (i == 0) {
m = 1;
if (j != 0) {
norm_vec (surf, i, j, &v, 3);
norm_vec (surf, w0, j, &u, 3);
}
if (j != (l - 1)) {
norm_vec (surf, i, j, &v, 2);
norm_vec (surf, w0, j, &u, 2);
}
}
if (i == (w - 1)) {
m = 1;
if (j != 0) {
norm_vec (surf, i, j, &v, 0);
norm_vec (surf, ww, j, &u, 0);
}
if (j != (l - 1)) {
norm_vec (surf, i, j, &v, 1);
norm_vec (surf, ww, j, &u, 1);
}
}
if (j == 0) {
m = 1;
if (i != 0) {
norm_vec (surf, i, j, &v, 1);
norm_vec (surf, i, l0, &u, 1);
}
if (i != (w - 1)) {
norm_vec (surf, i, j, &v, 2);
norm_vec (surf, i, l0, &u, 2);
}
}
if (j == (l - 1)) {
m = 1;
if (i != 0) {
norm_vec (surf, i, j, &v, 0);
norm_vec (surf, i, ll, &u, 0);
}
if (i != (w - 1)) {
norm_vec (surf, i, j, &v, 3);
norm_vec (surf, i, ll, &u, 3);
}
}
if (m) {
r = mag (v);
ru = mag (u);
v.x = (float) 3 * v.x / (2 * r) - u.x / (2 * ru);
v.y = (float) 3 * v.y / (2 * r) - u.y / (2 * ru);
v.z = (float) 3 * v.z / (2 * r) - u.z / (2 * ru);
} else {
for (k = 0; k < 4; k++)
norm_vec (surf, i, j, &v, k);
}
r = mag (v);
surf->point[i + j * w].dirx = (double) v.x * n / r;
surf->point[i + j * w].diry = (double) v.y * n / r;
surf->point[i + j * w].dirz = (double) v.z * n / r;
}
}
static inline void fxchg (double *a, double *b)
{
double t = *a;
*a = *b;
*b = t;
}
void TD_initellipsoidpart (TD_Surface * surf, int x, int y, int z,
int a, int b, int c, int w, int dir, int col)
{
int i, j;
Vec v;
float r;
surf->w = surf->l = 2 * w + 1;
for (i = -w; i <= w; i++)
for (j = -w; j <= w; j++) {
v.x = (float) j / w;
v.y = (float) i / w;
v.z = 1;
switch (dir) {
case 0:
v.z = -v.z;
fxchg (&v.x, &v.y);
break;
case 1:
v.y = -v.y;
fxchg (&v.x, &v.z);
break;
case 2:
v.z = -v.z;
fxchg (&v.x, &v.z);
break;
case 3:
v.y = -v.y;
fxchg (&v.y, &v.z);
break;
case 4:
v.z = -v.z;
fxchg (&v.y, &v.z);
break;
}
r = mag (v);
v.x *= (float) a / r;
v.y *= (float) b / r;
v.z *= (float) c / r;
surf->point[i + w + (j + w) * surf->w].x = v.x + x;
surf->point[i + w + (j + w) * surf->w].y = v.y + y;
surf->point[i + w + (j + w) * surf->w].z = v.z + z;
v.x /= (float) a * a; /*normal vector*/
v.y /= (float) b * b;
v.z /= (float) c * c;
r = mag (v);
surf->point[i + w + (j + w) * surf->w].dirx = (float) col * v.x / r;
surf->point[i + w + (j + w) * surf->w].diry = (float) col * v.y / r;
surf->point[i + w + (j + w) * surf->w].dirz = (float) col * v.z / r;
}
}
void TD_initellipsoid (TD_Surface * surf1, TD_Surface * surf2, TD_Surface * surf3,
TD_Surface * surf4, TD_Surface * surf5, TD_Surface * surf6, int x,
int y, int z, int a, int b, int c, int w, int col)
{
TD_initellipsoidpart (surf1, x, y, z, a, b, c, w, 0, col);
TD_initellipsoidpart (surf2, x, y, z, a, b, c, w, 1, col);
TD_initellipsoidpart (surf3, x, y, z, a, b, c, w, 2, col);
TD_initellipsoidpart (surf4, x, y, z, a, b, c, w, 3, col);
TD_initellipsoidpart (surf5, x, y, z, a, b, c, w, 4, col);
TD_initellipsoidpart (surf6, x, y, z, a, b, c, w, 5, col);
}
void TD_initsellipsoid (TD_Solid *s, int n, int x,
int y, int z, int a, int b, int c, int w, int col)
{
TD_initellipsoid(&s->surf[n], &s->surf[n+1], &s->surf[n+2],
&s->surf[n+3], &s->surf[n+4], &s->surf[n+5], x, y, z,
a, b, c, w, col);
}